diff --git a/README.md b/README.md index bec59a36..48b44618 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,20 @@ # Bonsai -Bonsai is a library for building interactive browser-based UI. +[Bonsai](https://github.com/janestreet/bonsai) is a library for building incremental, composable state machines. -Documentation can be found in the [docs](./docs) directory, and API documentation -can be found in [src/bonsai.mli](./src/bonsai.mli). +[Bonsai_web](https://github.com/janestreet/bonsai_web) is a library for building +interactive browser-based UI using `bonsai`. + +[Bonsai_examples](https://github.com/janestreet/bonsai_examples) contains example websites built using `bonsai_web`. + +[Bonsai_web_components](https://github.com/janestreet/bonsai_web_components) contains a collection of component libraries +for building web applications using `bonsai_web`. + +[Bonsai_test](https://github.com/janestreet/bonsai_test) contains a library for testing bonsai state machines. + +[Bonsai_web_test](https://github.com/janestreet/bonsai_web_test) contains a library for testing bonsai web applications. + +[Bonsai_bench](https://github.com/janestreet/bonsai_bench) contains a library for benchmarking bonsai applications. + +Documentation can be found in [Bonsai web's docs](https://github.com/janestreet/bonsai_web) +directory, and API documentation can be found in [src/proc_intf.ml](https://github.com/janestreet/bonsai_web/tree/master/docs). diff --git a/bench/example/dune b/bench/example/dune deleted file mode 100644 index b1d8e94b..00000000 --- a/bench/example/dune +++ /dev/null @@ -1,6 +0,0 @@ -(executables - (modes byte exe) - (names main) - (libraries bonsai bonsai_bench) - (preprocess - (pps ppx_bonsai ppx_jane js_of_ocaml-ppx))) diff --git a/bench/example/main.ml b/bench/example/main.ml deleted file mode 100644 index 8ac217ac..00000000 --- a/bench/example/main.ml +++ /dev/null @@ -1,301 +0,0 @@ -open! Core -open Bonsai.For_open -open! Bonsai.Let_syntax -open! Bonsai_bench - -let const = - Bonsai_bench.create - ~name:"Bonsai.const" - ~component:(Bonsai.const 4) - ~get_inject:(fun _ -> Nothing.unreachable_code) - (Interaction.many []) -;; - -let state = - Bonsai_bench.create - ~name:"Bonsai.state" - ~component:(Bonsai.state 0 ~sexp_of_model:[%sexp_of: Int.t] ~equal:[%equal: Int.t]) - ~get_inject:(fun (_, inject) -> inject) - Interaction.(many_with_stabilizations [ inject 1; reset_model ]) -;; - -module State_machine = struct - module Action = struct - type t = - | Incr - | Decr - [@@deriving sexp, equal] - end - - let component = - Bonsai.state_machine0 - () - ~sexp_of_model:[%sexp_of: Int.t] - ~equal:[%equal: Int.t] - ~sexp_of_action:[%sexp_of: Action.t] - ~default_model:0 - ~apply_action:(fun (_ : _ Bonsai.Apply_action_context.t) model action -> - match action with - | Incr -> model + 1 - | Decr -> model - 1) - ;; - - let incr = Interaction.inject Action.Incr - let decr = Interaction.inject Action.Decr -end - -(* The state does not get reset by default between benchmark runs. We choose - an interaction which is idempotent so each test run will be identical. *) -let state_machine_idempotent = - [ State_machine.incr; State_machine.decr; State_machine.incr; State_machine.decr ] - |> Interaction.many_with_stabilizations - |> Bonsai_bench.create - ~name:"Bonsai.state_machine0: idempotent" - ~component:State_machine.component - ~get_inject:(fun (_, inject) -> inject) -;; - -(* This state machine benchmark does not have an idempotent interaction. As a result, this - test will cause the model to grow by 2 every time the test is run during benchmarking. - Since (number of test runs * 2) won't exceed the range of int and the performance of - incrementing/decrementing by 1 is ~identical between int values, this won't cause - skewed benchmark results. *) -let state_machine_without_reset = - [ State_machine.incr; State_machine.decr; State_machine.incr; State_machine.incr ] - |> Interaction.many_with_stabilizations - |> Bonsai_bench.create - ~name:"Bonsai.state_machine0: not idempotent; no model reset" - ~component:State_machine.component - ~get_inject:(fun (_, inject) -> inject) -;; - -(* If it was important that the state machine restarted from the same model every time, - then we could use [Test.create_with_resetter] to explicitly reset the model. This - comes with an overhead cost, though, as we must reset the model and perform a - stabilization. Note that the interaction performed in [state_machine_without_reset] - and [state_machine_with_reset] are identical. *) -let state_machine_with_reset = - [ State_machine.incr; State_machine.decr; State_machine.incr; State_machine.incr ] - |> Interaction.many_with_stabilizations - |> Bonsai_bench.create_with_resetter - ~name: - "Bonsai.state_machine0: not idempotent; model reset using create_with_resetter" - ~component:State_machine.component - ~get_inject:(fun (_, inject) -> inject) -;; - -(* We can also manually reset the component's model with [Interaction.reset_model]. The - test above with [Test.create_with_resetter] is equivalent to this one. *) -let state_machine_with_manual_reset = - [ State_machine.incr - ; State_machine.decr - ; State_machine.incr - ; State_machine.incr - ; Interaction.reset_model - ; Interaction.stabilize - ] - |> Interaction.many_with_stabilizations - |> Bonsai_bench.create - ~name:"Bonsai.state_machine0: not idempotent; model reset manually" - ~component:State_machine.component - ~get_inject:(fun (_, inject) -> inject) -;; - -module My_triple = struct - let component first second third = - let%arr first = first - and second = second - and third = third in - first, second, third - ;; -end - -(* This benchmark calls stabilize in between setting each of the components in the - [My_triple] component. *) -let piecewise_triple_stabilize_between_each = - let first = Bonsai.Var.create 0 in - let second = Bonsai.Var.create "" in - let third = Bonsai.Var.create 0. in - Interaction. - [ change_input first 1 - ; change_input second "second" - ; change_input third 3. - ; change_input first 0 - ; change_input second "" - ; change_input third 0. - ] - |> Interaction.many_with_stabilizations - |> Bonsai_bench.create - ~name:"My_triple setting components and stabilizing between each one" - ~component: - (My_triple.component - (Bonsai.Var.value first) - (Bonsai.Var.value second) - (Bonsai.Var.value third)) - ~get_inject:(fun _ -> Nothing.unreachable_code) -;; - -(* If we wanted to ensure stabilization only happened after all of the inputs were set, - we could do the following. Since [many_with_stabilizations] just intersperses - [stabilize]s in the list of interactions, stabilization is only inserted between the - two [many] groups below. *) -let piecewise_triple_stabilize_after_all = - let first = Bonsai.Var.create 0 in - let second = Bonsai.Var.create "" in - let third = Bonsai.Var.create 0. in - Interaction. - [ many [ change_input first 1; change_input second "second"; change_input third 3. ] - ; many [ change_input first 0; change_input second ""; change_input third 0. ] - ] - |> Interaction.many_with_stabilizations - |> Bonsai_bench.create - ~name:"My_triple setting components and stabilizing after all three" - ~component: - (My_triple.component - (Bonsai.Var.value first) - (Bonsai.Var.value second) - (Bonsai.Var.value third)) - ~get_inject:(fun _ -> Nothing.unreachable_code) -;; - -module Do_work_every_second = struct - let component = - Bonsai.Incr.with_clock (fun clock -> - let%map.Ui_incr () = - Bonsai.Time_source.at_intervals clock (Time_ns.Span.of_sec 1.0) - in - for _ = 1 to 1000 do - () - done) - ;; - - let advance_by_zero = Interaction.advance_clock_by (Time_ns.Span.of_sec 0.) - let advance_by_second = Interaction.advance_clock_by (Time_ns.Span.of_sec 1.0) -end - -let do_work_every_second_advance_by_zero = - Bonsai_bench.create - ~name:"Component that does work every second: advance clock by zero each run" - ~component:Do_work_every_second.component - ~get_inject:(fun _ -> Nothing.unreachable_code) - Do_work_every_second.advance_by_zero -;; - -let do_work_every_second_advance_by_second = - Bonsai_bench.create - ~name:"Component that does work every second: advance clock by a second each run" - ~component:Do_work_every_second.component - ~get_inject:(fun _ -> Nothing.unreachable_code) - Do_work_every_second.advance_by_second -;; - -let two_state_machines_that_alternate = - let component = - let%map.Computation which, set_which = - Bonsai.state true ~sexp_of_model:[%sexp_of: Bool.t] ~equal:[%equal: Bool.t] - and state_1, inject_1 = State_machine.component - and state_2, inject_2 = State_machine.component in - let inject action = - match which with - | true -> Effect.Many [ set_which false; inject_1 action ] - | false -> Effect.Many [ set_which true; inject_2 action ] - in - (state_1, state_2), inject - in - [ State_machine.incr; State_machine.incr; State_machine.decr; State_machine.decr ] - |> Interaction.many_with_stabilizations - |> Bonsai_bench.create - ~name:"Alternating state machines" - ~component - ~get_inject:(fun (_, inject) -> inject) -;; - -let () = print_endline "======== Basic benchmarking ========" - -(* These computations will all be benchmarked by [Core_bench_js]. *) -let () = - let quota = Core_bench_js.Quota.Span (Time_float.Span.of_sec 1.0) in - [ const - ; state - ; state_machine_idempotent - ; state_machine_without_reset - ; state_machine_with_reset - ; state_machine_with_manual_reset - ; piecewise_triple_stabilize_after_all - ; piecewise_triple_stabilize_between_each - ; do_work_every_second_advance_by_zero - ; do_work_every_second_advance_by_second - ; two_state_machines_that_alternate - ] - |> Bonsai_bench.benchmark ~run_config:(Core_bench_js.Run_config.create () ~quota) -;; - -let () = print_endline "======== Benchmarking a computation with a bug ========" - -(* Sometimes, you may notice that a benchmark is suspiciously slow. In that case, it may - be helpful to [profile] the computation to see what's taking so long. For example, - consider the following: *) - -type r = - { a : int - ; b : Time_ns.t - } - -let do_some_work a = - for _ = 1 to a do - Sys.opaque_identity () - done -;; - -let component_that_does_work_too_often = - let component = - let%sub now = Bonsai.Clock.now in - let%sub r = - let%arr now = now in - { a = 1000000; b = now } - in - (* BUG: The below [let%arr] will get fired every time any field in the record changes, - i.e, whenever [Bonsai.Clock.now] updates. The body of this is expensive and depends - only on [a]. *) - let%arr { a; _ } = r in - do_some_work a - in - Interaction.advance_clock_by (Time_ns.Span.of_ms 1.) - |> Bonsai_bench.create - ~name:"Component that fires too often" - ~component - ~get_inject:(fun _ -> Nothing.unreachable_code) -;; - -let component_that_does_work_the_right_amount = - let component = - let%sub now = Bonsai.Clock.now in - let%sub r = - let%arr now = now in - { a = 1000000; b = now } - in - (* This [let%sub] ensures that the below [let%arr] only depends on [a], and hence - doesn't run when [Bonsai.Clock.now] updates. *) - let%sub { a; _ } = return r in - let%arr a = a in - do_some_work a - in - Interaction.advance_clock_by (Time_ns.Span.of_ms 1.) - |> Bonsai_bench.create - ~name:"Component that fires the right amount" - ~component - ~get_inject:(fun _ -> Nothing.unreachable_code) -;; - -let () = - let quota = Core_bench_js.Quota.Span (Time_float.Span.of_sec 1.0) in - [ component_that_does_work_too_often; component_that_does_work_the_right_amount ] - |> Bonsai_bench.benchmark ~run_config:(Core_bench_js.Run_config.create () ~quota) -;; - -let () = print_endline "======== Profiling computations to find the bug ========" - -let () = - Bonsai_bench.profile - [ component_that_does_work_too_often; component_that_does_work_the_right_amount ] -;; diff --git a/bench/example/main.mli b/bench/example/main.mli deleted file mode 100644 index 8b434a57..00000000 --- a/bench/example/main.mli +++ /dev/null @@ -1 +0,0 @@ -(* This file intentionally left blank *) diff --git a/bench/src/bonsai_bench.ml b/bench/src/bonsai_bench.ml deleted file mode 100644 index ce95c1e0..00000000 --- a/bench/src/bonsai_bench.ml +++ /dev/null @@ -1,34 +0,0 @@ -open! Core -module Interaction = Interaction -include Config - -let to_core_bench_test (T { clock; name; component; get_inject; interaction } : t) = - let bonsai_bench_initialize_run `init = - let runner = - Runner.initialize - ~clock - ~component - ~wrap_driver_creation:{ f = (fun create_driver -> create_driver ()) } - ~get_inject - ~interaction - ~filter_profiles:true - in - Cleanup.register_driver runner; - fun () -> Runner.run_interactions runner ~handle_profile:(Fn.const ()) - in - Core_bench_js.Test.create_with_initialization ~name bonsai_bench_initialize_run -;; - -let benchmark ?run_config ?analysis_configs ?display_config ?save_to_file ?libname ts = - ts - |> List.map ~f:to_core_bench_test - |> List.intersperse ~sep:Cleanup.invalidate_observers - |> Core_bench_js.bench - ?run_config - ?analysis_configs - ?display_config - ?save_to_file - ?libname -;; - -let profile profiles = List.iter ~f:Profile.profile profiles diff --git a/bench/src/bonsai_bench.mli b/bench/src/bonsai_bench.mli deleted file mode 100644 index 2f1c6503..00000000 --- a/bench/src/bonsai_bench.mli +++ /dev/null @@ -1,92 +0,0 @@ -open! Core -open Bonsai.For_open - -(** [t] is roughly equivalent to [Core_bench_js.Test.t], but can also be used to obtain - [profile]s of the benchmarks. See [profile] below for more details. *) -type t - -module Interaction : sig - (** A ['action Interaction.t] represents an interaction that occurs with a - [Component] whose action type is ['action]. *) - type 'action t - - (** [change_input] sets the given [Var.t] to the supplied value. *) - val change_input : 'a Bonsai.Var.t -> 'a -> _ t - - (** [inject] calls the [inject_action] function for the component being benchmarked - with the supplied action. *) - val inject : 'action -> 'action t - - (** [advance_clock_by] advances the current clock by the supplied amount. Note that the - clock is not reset between benchmark runs. *) - val advance_clock_by : Time_ns.Span.t -> _ t - - (** [stabilize] forces a stabilization of the incremental graph for the component being - benchmarked. *) - val stabilize : _ t - - (** [reset_model] resets the benchmarked component's model back to the value it was at - the beginning of the benchmark. *) - val reset_model : _ t - - (** [profile] indicates that the [profile] function below should print a snapshot of the - time spent in each part of your computation since the previous snapshot. - - Note: profiling interactions are filtered out in [benchmark] runs, so you needn't - worry about - *) - val profile : name:string -> _ t - - (** [many] is used to create lists of interactions all at once. Interactions created - this way will get flattened prior to benchmark runtime, so that there isn't - performance cost to using the constructor. *) - val many : 'action t list -> 'action t - - (** [many_with_stabilizations] is similar to many, but intersperses [stabilize]s in the - supplied list of actions. *) - val many_with_stabilizations : 'action t list -> 'action t -end - -(** [create] produces a benchmark which performs [interactions] on [component]. - The computation is shared between runs within the benchmark runner. Since they are - run a non-deterministic amount of times, benchmarks created this way should either - have an interaction which is idempotent on the state, or have similar performance - when the interaction is repeated many times. *) -val create - : ?clock:Bonsai.Time_source.t - -> name:string - -> component:'r Computation.t - -> get_inject:('r -> 'a -> unit Effect.t) - -> 'a Interaction.t - -> t - -(** [create_with_resetter] is equivalent to calling [create], with interactions equal to - [Interaction.many interactions; Interaction.reset_model; Interaction.stabilize]. *) -val create_with_resetter - : ?clock:Bonsai.Time_source.t - -> name:string - -> component:'r Computation.t - -> get_inject:('r -> 'a -> unit Effect.t) - -> 'a Interaction.t - -> t - -(** [benchmark] works identically to [Core_bench_js.bench], but ensures that [Observer]s - involved in benchmarks are cleaned up between consecutive benchmarks. *) -val benchmark - : ?run_config:Core_bench_js.Run_config.t - -> ?analysis_configs:Core_bench_js.Analysis_config.t list - -> ?display_config:Core_bench_js.Display_config.t - -> ?save_to_file:(Core_bench_js.Measurement.t -> string) - -> ?libname:string - -> t list - -> unit - -(** [profile] runs a given [t] as an instrumented computation, and provides snapshots of - how much time is spent within different parts of bonsai code. It also provides - statistics on incremental overhead. - - Note: because [profile] runs on an instrumented computation, the total running time - of the test may be higher. Furthermore, because [profile] only runs the computation - once, timing may vary between runs. It is useful for drilling into slow benchmarks, - but [benchmark] should be the source of truth for timing interactions. *) -val profile : t list -> unit diff --git a/bench/src/cleanup.ml b/bench/src/cleanup.ml deleted file mode 100644 index 8d4ea949..00000000 --- a/bench/src/cleanup.ml +++ /dev/null @@ -1,17 +0,0 @@ -open! Core - -type packed = T : Runner.t -> packed - -let (most_recent_driver : packed option ref) = ref None -let register_driver driver = most_recent_driver := Some (T driver) - -let invalidate_observers = - Core_bench_js.Test.create_with_initialization - ~name:"cleaning up observers..." - (fun `init -> - (match !most_recent_driver with - | None -> () - | Some (T driver) -> Runner.invalidate_observers driver); - most_recent_driver := None; - fun () -> ()) -;; diff --git a/bench/src/cleanup.mli b/bench/src/cleanup.mli deleted file mode 100644 index 77455f19..00000000 --- a/bench/src/cleanup.mli +++ /dev/null @@ -1,4 +0,0 @@ -open! Core - -val register_driver : Runner.t -> unit -val invalidate_observers : Core_bench_js.Test.t diff --git a/bench/src/config.ml b/bench/src/config.ml deleted file mode 100644 index eb7fb1d9..00000000 --- a/bench/src/config.ml +++ /dev/null @@ -1,40 +0,0 @@ -open! Core -open! Bonsai - -type ('a, 'r) unpacked = - { clock : Bonsai.Time_source.t - ; name : string - ; component : 'r Computation.t - ; get_inject : 'r -> 'a -> unit Effect.t - ; interaction : 'a Interaction.t - } - -type t = T : (_, _) unpacked -> t - -let create - ?(clock = Bonsai.Time_source.create ~start:Time_ns.epoch) - ~name - ~component - ~get_inject - interaction - = - T { clock; name; component; get_inject; interaction } -;; - -let create_with_resetter - ?(clock = Bonsai.Time_source.create ~start:Time_ns.epoch) - ~name - ~component - ~get_inject - interaction - = - T - { clock - ; name - ; component - ; get_inject - ; interaction = - [ interaction; Interaction.Reset_model; Interaction.Stabilize ] - |> Interaction.many - } -;; diff --git a/bench/src/config.mli b/bench/src/config.mli deleted file mode 100644 index 37705d71..00000000 --- a/bench/src/config.mli +++ /dev/null @@ -1,28 +0,0 @@ -open! Core -open! Bonsai - -type ('a, 'r) unpacked = - { clock : Bonsai.Time_source.t - ; name : string - ; component : 'r Computation.t - ; get_inject : 'r -> 'a -> unit Effect.t - ; interaction : 'a Interaction.t - } - -type t = T : (_, _) unpacked -> t - -val create - : ?clock:Bonsai.Time_source.t - -> name:string - -> component:'r Computation.t - -> get_inject:('r -> 'a -> unit Effect.t) - -> 'a Interaction.t - -> t - -val create_with_resetter - : ?clock:Bonsai.Time_source.t - -> name:string - -> component:'r Computation.t - -> get_inject:('r -> 'a -> unit Effect.t) - -> 'a Interaction.t - -> t diff --git a/bench/src/dune b/bench/src/dune deleted file mode 100644 index b0cff986..00000000 --- a/bench/src/dune +++ /dev/null @@ -1,7 +0,0 @@ -(library - (name bonsai_bench) - (public_name bonsai.bench) - (libraries bonsai bonsai_driver core_bench.js incr_dom.javascript_profiling - js_of_ocaml) - (preprocess - (pps ppx_jane js_of_ocaml-ppx ppx_pattern_bind))) diff --git a/bench/src/interaction.ml b/bench/src/interaction.ml deleted file mode 100644 index 8982792f..00000000 --- a/bench/src/interaction.ml +++ /dev/null @@ -1,19 +0,0 @@ -open! Core - -type 'action t = - | Profile : string -> _ t - | Change_input : 'a Bonsai.Var.t * 'a -> _ t - | Inject : 'action -> 'action t - | Advance_clock_by : Time_ns.Span.t -> _ t - | Stabilize : _ t - | Reset_model : _ t - | Many : 'action t list -> 'action t - -let change_input var value = Change_input (var, value) -let inject action = Inject action -let advance_clock_by span = Advance_clock_by span -let stabilize = Stabilize -let reset_model = Reset_model -let profile ~name = Profile name -let many ts = Many ts -let many_with_stabilizations ts = Many (List.intersperse ts ~sep:Stabilize) diff --git a/bench/src/interaction.mli b/bench/src/interaction.mli deleted file mode 100644 index 2de58c80..00000000 --- a/bench/src/interaction.mli +++ /dev/null @@ -1,19 +0,0 @@ -open! Core - -type 'action t = - | Profile : string -> _ t - | Change_input : 'a Bonsai.Var.t * 'a -> _ t - | Inject : 'action -> 'action t - | Advance_clock_by : Time_ns.Span.t -> _ t - | Stabilize : _ t - | Reset_model : _ t - | Many : 'action t list -> 'action t - -val advance_clock_by : Time_ns.Span.t -> _ t -val change_input : 'a Bonsai.Var.t -> 'a -> _ t -val inject : 'action -> 'action t -val stabilize : _ t -val reset_model : _ t -val profile : name:string -> 'a t -val many : 'action t list -> 'action t -val many_with_stabilizations : 'action t list -> 'action t diff --git a/bench/src/profile.ml b/bench/src/profile.ml deleted file mode 100644 index 2098b572..00000000 --- a/bench/src/profile.ml +++ /dev/null @@ -1,293 +0,0 @@ -open! Core -open Js_of_ocaml -module Graph_info = Bonsai.Private.Graph_info - -module Id = struct - let instance = String.Table.create () - - let of_node_path node_path = - let key = Bonsai.Private.Node_path.to_string node_path in - match Hashtbl.find instance key with - | Some id -> id - | None -> - let id = Hashtbl.length instance in - Hashtbl.set instance ~key ~data:id; - id - ;; -end - -module Measurement = struct - module Kind = struct - module T = struct - type t = - | Startup - | Snapshot - | Named of string - [@@deriving sexp, equal, compare] - end - - include T - include Comparable.Make_plain (T) - - let time_to_first_stabilization = "Bonsai_bench profile: first stabilization" - let time_since_snapshot_began = "Bonsai_bench profile: current snapshot" - - let to_string = function - | Named s -> s - | Startup -> time_to_first_stabilization - | Snapshot -> time_since_snapshot_began - ;; - - let of_string s = - if String.equal time_to_first_stabilization s - then Startup - else if String.equal time_since_snapshot_began s - then Snapshot - else Named s - ;; - - let is_bonsai_measurement = function - | Named _ -> true - | _ -> false - ;; - end - - type t = - { kind : Kind.t - ; duration : float - ; id : int option - } - [@@deriving sexp] - - let create ~label ~duration = { kind = Kind.of_string label; duration; id = None } - let before label = label ^ "_before" - let after label = label ^ "_after" - let mark_before t = Javascript_profiling.Manual.mark (before (Kind.to_string t)) - - let mark_after_and_measure t = - let name = Kind.to_string t in - let before = before name in - let after = after name in - Javascript_profiling.Manual.mark after; - Javascript_profiling.Manual.measure ~name ~start:before ~end_:after - ;; -end - -module Accumulated_measurement = struct - type t = - { kind : Measurement.Kind.t - ; total_duration : float - ; count : int - ; id : int option - } - [@@deriving sexp] - - let compare - { kind; total_duration; _ } - { kind = kind'; total_duration = total_duration'; _ } - = - match kind, kind' with - | Named _, Named _ -> Float.descending total_duration total_duration' - | _, _ -> Measurement.Kind.compare kind kind' - ;; - - let of_measurement { Measurement.kind; duration; id } = - { kind; count = 1; total_duration = duration; id } - ;; - - let add { kind; total_duration; count; id } ~measurement = - assert (Measurement.Kind.equal kind measurement.Measurement.kind); - { kind - ; count = count + 1 - ; total_duration = total_duration +. measurement.duration - ; id - } - ;; -end - -let create_summary_table ~total_time ~incremental_time = - let open Ascii_table_kernel in - to_string_noattr - [ Column.create "Statistic" fst; Column.create "Value" snd ] - ~limit_width_to:Int.max_value - ~bars:`Unicode - [ "Total time (ms)", Float.to_string total_time - ; "Incremental time (ms)", Float.to_string incremental_time - ; "Incremental Overhead (ms)", Float.to_string (total_time -. incremental_time) - ; ( "Incremental Overhead (%)" - , Percent.Always_percentage.to_string - (Percent.of_percentage ((total_time -. incremental_time) /. total_time *. 100.)) - ) - ] -;; - -let create_snapshot_table data ~incremental_time = - let open Ascii_table_kernel in - let columns = - [ Column.create "Id" (fun { Accumulated_measurement.id; _ } -> - match id with - | Some int -> Int.to_string int - | None -> "N/A") - ; Column.create "Name" (fun { Accumulated_measurement.kind; _ } -> - Measurement.Kind.to_string kind) - ; Column.create "Times fired" (fun { Accumulated_measurement.count; _ } -> - Int.to_string count) - ; Column.create - "Total time (ms)" - (fun { Accumulated_measurement.total_duration; _ } -> - Float.to_string total_duration) - ; Column.create - "Percent of incremental time" - (fun { Accumulated_measurement.total_duration; _ } -> - Percent.Always_percentage.to_string - (Percent.of_percentage (total_duration /. incremental_time *. 100.))) - ] - in - to_string_noattr columns data ~limit_width_to:Int.max_value ~bars:`Unicode -;; - -let print_statistics data = - let sorted_data = List.sort (Map.data data) ~compare:Accumulated_measurement.compare in - let incremental_measurements, bonsai_bench_internals = - List.partition_tf sorted_data ~f:(fun { kind; _ } -> - Measurement.Kind.is_bonsai_measurement kind) - in - let total_time = - match bonsai_bench_internals with - | [ { Accumulated_measurement.total_duration; _ } ] -> total_duration - | _ -> - raise_s - [%message - "An error occurred while profiling your computation. Bonsai bench expected \ - only one internal measurement. Please report this error to the bonsai team." - ~internal_measurements: - (bonsai_bench_internals : Accumulated_measurement.t list)] - in - let incremental_time = - List.sum - (module Float) - incremental_measurements - ~f:(fun { kind; total_duration; _ } -> - if Measurement.Kind.is_bonsai_measurement kind then total_duration else 0.) - in - print_endline "Summary:"; - print_endline (create_summary_table ~total_time ~incremental_time); - print_endline "Details:"; - print_endline (create_snapshot_table incremental_measurements ~incremental_time) -;; - -let accumulate_measurements - ~(source_locations : Graph_info.Node_info.t Bonsai.Private.Node_path.Map.t) - measurements - = - let with_ids, without_ids = - List.map measurements ~f:(fun measurement -> - match measurement.Measurement.kind with - | Snapshot | Startup -> measurement - | Named label -> - Option.value - ~default:measurement - (let%bind.Option node_path = - Bonsai.Private.Instrumentation.extract_node_path_from_entry_label label - in - let%bind.Option { node_type; here } = Map.find source_locations node_path in - let%map.Option here = here in - { measurement with - kind = Named [%string "%{node_type} (%{here#Source_code_position})"] - ; id = Some (Id.of_node_path node_path) - })) - |> List.fold - ~init:(Int.Map.empty, Measurement.Kind.Map.empty) - ~f:(fun (with_ids, without_ids) measurement -> - let accumulate_measurements = function - | None -> Accumulated_measurement.of_measurement measurement - | Some accumulated -> Accumulated_measurement.add accumulated ~measurement - in - match measurement.id with - | None -> - with_ids, Map.update without_ids measurement.kind ~f:accumulate_measurements - | Some id -> Map.update with_ids id ~f:accumulate_measurements, without_ids) - in - Map.fold without_ids ~init:with_ids ~f:(fun ~key:_ ~data:measurement acc -> - let id = - match Map.max_elt acc with - (* This could happen if the user never [let%sub]s. It's not very realistic for a - practical app, but totally possible to write. *) - | None -> 0 - | Some (id, _) -> id + 1 - in - let measurement = { measurement with id = Some id } in - Map.set acc ~key:id ~data:measurement) -;; - -let take_profile_snapshot ~name graph_info performance_entries = - match List.length !performance_entries with - | 0 | 1 -> () - | _ -> - print_endline [%string "Bonsai_bench Profile: %{name}"]; - let source_locations = - Graph_info.pull_source_locations_from_nearest_parent graph_info - in - print_statistics (accumulate_measurements ~source_locations !performance_entries); - performance_entries := [] -;; - -let profile (T { clock; component; get_inject; interaction; name } : Config.t) = - print_endline [%string "Running Bonsai_bench profile of %{name}"]; - let graph_info = ref Graph_info.empty in - let component = - Bonsai.Debug.instrument_computation - component - ~start_timer:(fun s -> Measurement.mark_before (Named s)) - ~stop_timer:(fun s -> Measurement.mark_after_and_measure (Named s)) - in - let component = - Graph_info.iter_graph_updates - (Bonsai.Private.top_level_handle component) - ~on_update:(fun gi -> graph_info := gi) - in - let component graph = Bonsai.Private.perform graph component in - let performance_entries = ref [] in - let performance_observer = - if PerformanceObserver.is_supported () - then - PerformanceObserver.observe ~entry_types:[ "measure" ] ~f:(fun entries _ -> - Array.iter - (Js.to_array entries##getEntries) - ~f:(fun entry -> - let label = Js.to_string entry##.name in - let duration = entry##.duration in - performance_entries - := Measurement.create ~label ~duration :: !performance_entries)) - else - failwith - "PerformanceObserver could not be found. Please reach out to webdev-public on \ - symphony for assistance." - in - let handle_profile name = - Measurement.mark_after_and_measure Snapshot; - take_profile_snapshot ~name !graph_info performance_entries; - Measurement.mark_before Snapshot - in - let runner = - Runner.initialize - ~filter_profiles:false - ~wrap_driver_creation: - { f = - (fun create_driver -> - Measurement.mark_before Startup; - let driver = create_driver () in - Measurement.mark_after_and_measure Startup; - driver) - } - ~clock - ~component - ~get_inject - ~interaction - in - take_profile_snapshot ~name:"startup" !graph_info performance_entries; - Measurement.mark_before Snapshot; - Runner.run_interactions runner ~handle_profile; - performance_observer##disconnect; - Runner.invalidate_observers runner -;; diff --git a/bench/src/profile.mli b/bench/src/profile.mli deleted file mode 100644 index 9458baf6..00000000 --- a/bench/src/profile.mli +++ /dev/null @@ -1,3 +0,0 @@ -open! Core - -val profile : Config.t -> unit diff --git a/bench/src/runner.ml b/bench/src/runner.ml deleted file mode 100644 index 34c4107d..00000000 --- a/bench/src/runner.ml +++ /dev/null @@ -1,86 +0,0 @@ -open! Core -open Bonsai - -type t = - | T : - { driver : 'r Bonsai_driver.t - ; clock : Bonsai.Time_source.t - ; inject_action : 'a -> unit Effect.t - ; interactions : 'a Interaction.t array - } - -> t - -type wrap_create = { f : 'a. (unit -> 'a) -> 'a } [@@unboxed] - -let handle_interaction ~driver ~clock ~inject_action ~handle_profile interaction = - match (interaction : _ Interaction.t) with - | Profile name -> handle_profile name - | Stabilize -> Bonsai_driver.flush driver - | Reset_model -> Bonsai_driver.Expert.reset_model_to_default driver - | Change_input (var, value) -> Var.set var value - | Inject action -> Bonsai_driver.schedule_event driver (inject_action action) - | Advance_clock_by span -> Bonsai.Time_source.advance_clock_by clock span - | Many _ -> - (* We flatten the interaction structure prior to running the benchmark. *) - assert false -;; - -let rec flatten_interactions_to_list = function - | Interaction.Many nested -> List.concat_map nested ~f:flatten_interactions_to_list - | t -> [ t ] -;; - -let dedup_stabilizations interactions = - let both_stabilize (t : _ Interaction.t) (t' : _ Interaction.t) = - match t, t' with - | Stabilize, Stabilize -> true - | _ -> false - in - List.remove_consecutive_duplicates interactions ~equal:both_stabilize -;; - -(* We perform two optimizations in this step: flattening the interactions and deduping - stabilizations. Flattening the structure ensures that there's no additional - overhead to nesting lots of [Many]s when creating benchmarks. Consecutive - [Stabilize]s don't add anything to benchmarks and would add a function call of - overhead. *) -let initialize - ~filter_profiles - ~wrap_driver_creation - ~clock - ~component - ~get_inject - ~interaction - = - let driver = wrap_driver_creation.f (fun () -> Bonsai_driver.create ~clock component) in - let inject_action action = - (* Calling Driver.result every time that inject_action is called - is important because the value can change during stabilization *) - let result = Bonsai_driver.result driver in - (get_inject result) action - in - let interactions = - Interaction.many - [ Interaction.stabilize - ; interaction - ; Interaction.stabilize - ; Interaction.profile ~name:"end of run" - ] - |> flatten_interactions_to_list - |> List.filter ~f:(fun interaction -> - match filter_profiles, interaction with - | true, Profile _ -> false - | _ -> true) - |> dedup_stabilizations - |> Array.of_list - in - T { driver; clock; inject_action; interactions } -;; - -let run_interactions (T { driver; clock; inject_action; interactions }) ~handle_profile = - Array.iter - interactions - ~f:(handle_interaction ~driver ~clock ~inject_action ~handle_profile) -;; - -let invalidate_observers (T t) = Bonsai_driver.Expert.invalidate_observers t.driver diff --git a/bench/src/runner.mli b/bench/src/runner.mli deleted file mode 100644 index fcf82185..00000000 --- a/bench/src/runner.mli +++ /dev/null @@ -1,16 +0,0 @@ -open Bonsai.For_open - -type t -type wrap_create = { f : 'a. (unit -> 'a) -> 'a } [@@unboxed] - -val initialize - : filter_profiles:bool - -> wrap_driver_creation:wrap_create - -> clock:Bonsai.Time_source.t - -> component:'r Computation.t - -> get_inject:('r -> 'a -> unit Effect.t) - -> interaction:'a Interaction.t - -> t - -val run_interactions : t -> handle_profile:(string -> unit) -> unit -val invalidate_observers : t -> unit diff --git a/bindings/dygraph/README.md b/bindings/dygraph/README.md deleted file mode 100644 index f7009a85..00000000 --- a/bindings/dygraph/README.md +++ /dev/null @@ -1,47 +0,0 @@ -# Dygraph - -Bindings to dygraphs, a javascript graphing library, using gen_js_api. - - Dygraphs: http://dygraphs.com/ - API: http://dygraphs.com/jsdoc/symbols/Dygraph.html - Options Reference: http://dygraphs.com/options.html - -## High level overview - -- Graph: A dygraph. This API is actually surprisingly small since - almost all options live in... - -- Options: Where you can control almost any aspect of the graph. Each - option should be documented, which is copied from - http://dygraphs.com/options.html. - -- Data: Dygraphs can take data of a few formats (the x-values can be - floats, dates, or times). This module provides nice [create] - functions for each of these options. - -- With_bonsai: **The recommended way to use a dygraph (with bonsai).** - -## Sharp corners - -### CSP (content security policy) - -The default content security policy for simple web server does not -allow unsafe inline styles, even from self. Unfortunately, dygraphs -does this. So in order to not have error messages in your console -when using dygraphs, you should use -`Content_security_policy.default_for_clientside_rendering_internal` in -your web server. - -## Examples - -See lib/dygraph/examples for a few ocaml and javascript examples. -`Stock chart` in particular is good because we have both the ocaml and -javascript example so that you can compare. - -## References: - -- http://dygraphs.com/ -- http://dygraphs.com/jsdoc/symbols/Dygraph.html -- http://dygraphs.com/options.html. -- http://dygraphs.com/data.html -- http://dygraphs.com/gallery diff --git a/bindings/dygraph/dist/LICENSE-dygraph b/bindings/dygraph/dist/LICENSE-dygraph deleted file mode 100644 index 536c0a8b..00000000 --- a/bindings/dygraph/dist/LICENSE-dygraph +++ /dev/null @@ -1,22 +0,0 @@ -Copyright (c) 2009 Dan Vanderkam - -Permission is hereby granted, free of charge, to any person -obtaining a copy of this software and associated documentation -files (the "Software"), to deal in the Software without -restriction, including without limitation the rights to use, -copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the -Software is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -OTHER DEALINGS IN THE SOFTWARE. diff --git a/bindings/dygraph/dist/LICENSE-lodash b/bindings/dygraph/dist/LICENSE-lodash deleted file mode 100644 index 4773fd8e..00000000 --- a/bindings/dygraph/dist/LICENSE-lodash +++ /dev/null @@ -1,49 +0,0 @@ -The MIT License - -Copyright JS Foundation and other contributors - -Based on Underscore.js, copyright Jeremy Ashkenas, -DocumentCloud and Investigative Reporters & Editors - -This software consists of voluntary contributions made by many -individuals. For exact contribution history, see the revision history -available at https://github.com/lodash/lodash - -The following license applies to all parts of this software except as -documented below: - -==== - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -==== - -Copyright and related rights for sample code are waived via CC0. Sample -code is defined as all source code displayed within the prose of the -documentation. - -CC0: http://creativecommons.org/publicdomain/zero/1.0/ - -==== - -Files located in the node_modules and vendor directories are externally -maintained libraries used by this software which have their own -licenses; we recommend you read them, as their terms may differ from the -terms above. diff --git a/bindings/dygraph/dist/dune b/bindings/dygraph/dist/dune deleted file mode 100644 index e69de29b..00000000 diff --git a/bindings/dygraph/dist/dygraph.css b/bindings/dygraph/dist/dygraph.css deleted file mode 100644 index 75d58685..00000000 --- a/bindings/dygraph/dist/dygraph.css +++ /dev/null @@ -1,117 +0,0 @@ -/** - * Default styles for the dygraphs charting library. - */ - -.dygraph-legend { - position: absolute; - font-size: 14px; - z-index: 10; - width: 250px; /* labelsDivWidth */ - /* - dygraphs determines these based on the presence of chart labels. - It might make more sense to create a wrapper div around the chart proper. - top: 0px; - right: 2px; - */ - background: white; - line-height: normal; - text-align: left; - overflow: hidden; -} - -/* styles for a solid line in the legend */ -.dygraph-legend-line { - display: inline-block; - position: relative; - bottom: .5ex; - padding-left: 1em; - height: 1px; - border-bottom-width: 2px; - border-bottom-style: solid; - /* border-bottom-color is set based on the series color */ -} - -/* styles for a dashed line in the legend, e.g. when strokePattern is set */ -.dygraph-legend-dash { - display: inline-block; - position: relative; - bottom: .5ex; - height: 1px; - border-bottom-width: 2px; - border-bottom-style: solid; - /* border-bottom-color is set based on the series color */ - /* margin-right is set based on the stroke pattern */ - /* padding-left is set based on the stroke pattern */ -} - -.dygraph-roller { - position: absolute; - z-index: 10; -} - -/* This class is shared by all annotations, including those with icons */ -.dygraph-annotation { - position: absolute; - z-index: 10; - overflow: hidden; -} - -/* This class only applies to annotations without icons */ -/* Old class name: .dygraphDefaultAnnotation */ -.dygraph-default-annotation { - border: 1px solid black; - background-color: white; - text-align: center; -} - -.dygraph-axis-label { - /* position: absolute; */ - /* font-size: 14px; */ - z-index: 10; - line-height: normal; - overflow: hidden; - color: black; /* replaces old axisLabelColor option */ -} - -.dygraph-axis-label-x { -} - -.dygraph-axis-label-y { -} - -.dygraph-axis-label-y2 { -} - -.dygraph-title { - font-weight: bold; - z-index: 10; - text-align: center; - /* font-size: based on titleHeight option */ -} - -.dygraph-xlabel { - text-align: center; - /* font-size: based on xLabelHeight option */ -} - -/* For y-axis label */ -.dygraph-label-rotate-left { - text-align: center; - /* See http://caniuse.com/#feat=transforms2d */ - transform: rotate(90deg); - -webkit-transform: rotate(90deg); - -moz-transform: rotate(90deg); - -o-transform: rotate(90deg); - -ms-transform: rotate(90deg); -} - -/* For y2-axis label */ -.dygraph-label-rotate-right { - text-align: center; - /* See http://caniuse.com/#feat=transforms2d */ - transform: rotate(-90deg); - -webkit-transform: rotate(-90deg); - -moz-transform: rotate(-90deg); - -o-transform: rotate(-90deg); - -ms-transform: rotate(-90deg); -} diff --git a/bindings/dygraph/dist/dygraph.js b/bindings/dygraph/dist/dygraph.js deleted file mode 100644 index d112708c..00000000 --- a/bindings/dygraph/dist/dygraph.js +++ /dev/null @@ -1,9516 +0,0 @@ -(function(f){{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Dygraph = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o 1) { - for (var i = 1; i < arguments.length; i++) { - args[i - 1] = arguments[i]; - } - } - queue.push(new Item(fun, args)); - if (queue.length === 1 && !draining) { - runTimeout(drainQueue); - } -}; - -// v8 likes predictible objects -function Item(fun, array) { - this.fun = fun; - this.array = array; -} -Item.prototype.run = function () { - this.fun.apply(null, this.array); -}; -process.title = 'browser'; -process.browser = true; -process.env = {}; -process.argv = []; -process.version = ''; // empty string to avoid regexp issues -process.versions = {}; - -function noop() {} - -process.on = noop; -process.addListener = noop; -process.once = noop; -process.off = noop; -process.removeListener = noop; -process.removeAllListeners = noop; -process.emit = noop; -process.prependListener = noop; -process.prependOnceListener = noop; - -process.listeners = function (name) { return [] } - -process.binding = function (name) { - throw new Error('process.binding is not supported'); -}; - -process.cwd = function () { return '/' }; -process.chdir = function (dir) { - throw new Error('process.chdir is not supported'); -}; -process.umask = function() { return 0; }; - -},{}],2:[function(require,module,exports){ -/** - * @license - * Copyright 2013 David Eberlein (david.eberlein@ch.sauter-bc.com) - * MIT-licensed (http://opensource.org/licenses/MIT) - */ - -/** - * @fileoverview DataHandler implementation for the custom bars option. - * @author David Eberlein (david.eberlein@ch.sauter-bc.com) - */ - -/*global Dygraph:false */ -"use strict"; - -Object.defineProperty(exports, '__esModule', { - value: true -}); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } - -var _bars = require('./bars'); - -var _bars2 = _interopRequireDefault(_bars); - -/** - * @constructor - * @extends Dygraph.DataHandlers.BarsHandler - */ -var CustomBarsHandler = function CustomBarsHandler() {}; - -CustomBarsHandler.prototype = new _bars2['default'](); - -/** @inheritDoc */ -CustomBarsHandler.prototype.extractSeries = function (rawData, i, options) { - // TODO(danvk): pre-allocate series here. - var series = []; - var x, y, point; - var logScale = options.get('logscale'); - for (var j = 0; j < rawData.length; j++) { - x = rawData[j][0]; - point = rawData[j][i]; - if (logScale && point !== null) { - // On the log scale, points less than zero do not exist. - // This will create a gap in the chart. - if (point[0] <= 0 || point[1] <= 0 || point[2] <= 0) { - point = null; - } - } - // Extract to the unified data format. - if (point !== null) { - y = point[1]; - if (y !== null && !isNaN(y)) { - series.push([x, y, [point[0], point[2]]]); - } else { - series.push([x, y, [y, y]]); - } - } else { - series.push([x, null, [null, null]]); - } - } - return series; -}; - -/** @inheritDoc */ -CustomBarsHandler.prototype.rollingAverage = function (originalData, rollPeriod, options) { - rollPeriod = Math.min(rollPeriod, originalData.length); - var rollingData = []; - var y, low, high, mid, count, i, extremes; - - low = 0; - mid = 0; - high = 0; - count = 0; - for (i = 0; i < originalData.length; i++) { - y = originalData[i][1]; - extremes = originalData[i][2]; - rollingData[i] = originalData[i]; - - if (y !== null && !isNaN(y)) { - low += extremes[0]; - mid += y; - high += extremes[1]; - count += 1; - } - if (i - rollPeriod >= 0) { - var prev = originalData[i - rollPeriod]; - if (prev[1] !== null && !isNaN(prev[1])) { - low -= prev[2][0]; - mid -= prev[1]; - high -= prev[2][1]; - count -= 1; - } - } - if (count) { - rollingData[i] = [originalData[i][0], 1.0 * mid / count, [1.0 * low / count, 1.0 * high / count]]; - } else { - rollingData[i] = [originalData[i][0], null, [null, null]]; - } - } - - return rollingData; -}; - -exports['default'] = CustomBarsHandler; -module.exports = exports['default']; - -},{"./bars":5}],3:[function(require,module,exports){ -/** - * @license - * Copyright 2013 David Eberlein (david.eberlein@ch.sauter-bc.com) - * MIT-licensed (http://opensource.org/licenses/MIT) - */ - -/** - * @fileoverview DataHandler implementation for the error bars option. - * @author David Eberlein (david.eberlein@ch.sauter-bc.com) - */ - -/*global Dygraph:false */ -"use strict"; - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } - -var _bars = require('./bars'); - -var _bars2 = _interopRequireDefault(_bars); - -/** - * @constructor - * @extends BarsHandler - */ -var ErrorBarsHandler = function ErrorBarsHandler() {}; - -ErrorBarsHandler.prototype = new _bars2["default"](); - -/** @inheritDoc */ -ErrorBarsHandler.prototype.extractSeries = function (rawData, i, options) { - // TODO(danvk): pre-allocate series here. - var series = []; - var x, y, variance, point; - var sigma = options.get("sigma"); - var logScale = options.get('logscale'); - for (var j = 0; j < rawData.length; j++) { - x = rawData[j][0]; - point = rawData[j][i]; - if (logScale && point !== null) { - // On the log scale, points less than zero do not exist. - // This will create a gap in the chart. - if (point[0] <= 0 || point[0] - sigma * point[1] <= 0) { - point = null; - } - } - // Extract to the unified data format. - if (point !== null) { - y = point[0]; - if (y !== null && !isNaN(y)) { - variance = sigma * point[1]; - // preserve original error value in extras for further - // filtering - series.push([x, y, [y - variance, y + variance, point[1]]]); - } else { - series.push([x, y, [y, y, y]]); - } - } else { - series.push([x, null, [null, null, null]]); - } - } - return series; -}; - -/** @inheritDoc */ -ErrorBarsHandler.prototype.rollingAverage = function (originalData, rollPeriod, options) { - rollPeriod = Math.min(rollPeriod, originalData.length); - var rollingData = []; - var sigma = options.get("sigma"); - - var i, j, y, v, sum, num_ok, stddev, variance, value; - - // Calculate the rolling average for the first rollPeriod - 1 points - // where there is not enough data to roll over the full number of points - for (i = 0; i < originalData.length; i++) { - sum = 0; - variance = 0; - num_ok = 0; - for (j = Math.max(0, i - rollPeriod + 1); j < i + 1; j++) { - y = originalData[j][1]; - if (y === null || isNaN(y)) continue; - num_ok++; - sum += y; - variance += Math.pow(originalData[j][2][2], 2); - } - if (num_ok) { - stddev = Math.sqrt(variance) / num_ok; - value = sum / num_ok; - rollingData[i] = [originalData[i][0], value, [value - sigma * stddev, value + sigma * stddev]]; - } else { - // This explicitly preserves NaNs to aid with "independent - // series". - // See testRollingAveragePreservesNaNs. - v = rollPeriod == 1 ? originalData[i][1] : null; - rollingData[i] = [originalData[i][0], v, [v, v]]; - } - } - - return rollingData; -}; - -exports["default"] = ErrorBarsHandler; -module.exports = exports["default"]; - -},{"./bars":5}],4:[function(require,module,exports){ -/** - * @license - * Copyright 2013 David Eberlein (david.eberlein@ch.sauter-bc.com) - * MIT-licensed (http://opensource.org/licenses/MIT) - */ - -/** - * @fileoverview DataHandler implementation for the combination - * of error bars and fractions options. - * @author David Eberlein (david.eberlein@ch.sauter-bc.com) - */ - -/*global Dygraph:false */ -"use strict"; - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } - -var _bars = require('./bars'); - -var _bars2 = _interopRequireDefault(_bars); - -/** - * @constructor - * @extends Dygraph.DataHandlers.BarsHandler - */ -var FractionsBarsHandler = function FractionsBarsHandler() {}; - -FractionsBarsHandler.prototype = new _bars2["default"](); - -/** @inheritDoc */ -FractionsBarsHandler.prototype.extractSeries = function (rawData, i, options) { - // TODO(danvk): pre-allocate series here. - var series = []; - var x, y, point, num, den, value, stddev, variance; - var mult = 100.0; - var sigma = options.get("sigma"); - var logScale = options.get('logscale'); - for (var j = 0; j < rawData.length; j++) { - x = rawData[j][0]; - point = rawData[j][i]; - if (logScale && point !== null) { - // On the log scale, points less than zero do not exist. - // This will create a gap in the chart. - if (point[0] <= 0 || point[1] <= 0) { - point = null; - } - } - // Extract to the unified data format. - if (point !== null) { - num = point[0]; - den = point[1]; - if (num !== null && !isNaN(num)) { - value = den ? num / den : 0.0; - stddev = den ? sigma * Math.sqrt(value * (1 - value) / den) : 1.0; - variance = mult * stddev; - y = mult * value; - // preserve original values in extras for further filtering - series.push([x, y, [y - variance, y + variance, num, den]]); - } else { - series.push([x, num, [num, num, num, den]]); - } - } else { - series.push([x, null, [null, null, null, null]]); - } - } - return series; -}; - -/** @inheritDoc */ -FractionsBarsHandler.prototype.rollingAverage = function (originalData, rollPeriod, options) { - rollPeriod = Math.min(rollPeriod, originalData.length); - var rollingData = []; - var sigma = options.get("sigma"); - var wilsonInterval = options.get("wilsonInterval"); - - var low, high, i, stddev; - var num = 0; - var den = 0; // numerator/denominator - var mult = 100.0; - for (i = 0; i < originalData.length; i++) { - num += originalData[i][2][2]; - den += originalData[i][2][3]; - if (i - rollPeriod >= 0) { - num -= originalData[i - rollPeriod][2][2]; - den -= originalData[i - rollPeriod][2][3]; - } - - var date = originalData[i][0]; - var value = den ? num / den : 0.0; - if (wilsonInterval) { - // For more details on this confidence interval, see: - // http://en.wikipedia.org/wiki/Binomial_confidence_interval - if (den) { - var p = value < 0 ? 0 : value, - n = den; - var pm = sigma * Math.sqrt(p * (1 - p) / n + sigma * sigma / (4 * n * n)); - var denom = 1 + sigma * sigma / den; - low = (p + sigma * sigma / (2 * den) - pm) / denom; - high = (p + sigma * sigma / (2 * den) + pm) / denom; - rollingData[i] = [date, p * mult, [low * mult, high * mult]]; - } else { - rollingData[i] = [date, 0, [0, 0]]; - } - } else { - stddev = den ? sigma * Math.sqrt(value * (1 - value) / den) : 1.0; - rollingData[i] = [date, mult * value, [mult * (value - stddev), mult * (value + stddev)]]; - } - } - - return rollingData; -}; - -exports["default"] = FractionsBarsHandler; -module.exports = exports["default"]; - -},{"./bars":5}],5:[function(require,module,exports){ -/** - * @license - * Copyright 2013 David Eberlein (david.eberlein@ch.sauter-bc.com) - * MIT-licensed (http://opensource.org/licenses/MIT) - */ - -/** - * @fileoverview DataHandler base implementation for the "bar" - * data formats. This implementation must be extended and the - * extractSeries and rollingAverage must be implemented. - * @author David Eberlein (david.eberlein@ch.sauter-bc.com) - */ - -/*global Dygraph:false */ -/*global DygraphLayout:false */ -"use strict"; - -Object.defineProperty(exports, '__esModule', { - value: true -}); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } - -var _datahandler = require('./datahandler'); - -var _datahandler2 = _interopRequireDefault(_datahandler); - -var _dygraphLayout = require('../dygraph-layout'); - -var _dygraphLayout2 = _interopRequireDefault(_dygraphLayout); - -/** - * @constructor - * @extends {Dygraph.DataHandler} - */ -var BarsHandler = function BarsHandler() { - _datahandler2['default'].call(this); -}; -BarsHandler.prototype = new _datahandler2['default'](); - -// TODO(danvk): figure out why the jsdoc has to be copy/pasted from superclass. -// (I get closure compiler errors if this isn't here.) -/** - * @override - * @param {!Array.} rawData The raw data passed into dygraphs where - * rawData[i] = [x,ySeries1,...,ySeriesN]. - * @param {!number} seriesIndex Index of the series to extract. All other - * series should be ignored. - * @param {!DygraphOptions} options Dygraph options. - * @return {Array.<[!number,?number,?]>} The series in the unified data format - * where series[i] = [x,y,{extras}]. - */ -BarsHandler.prototype.extractSeries = function (rawData, seriesIndex, options) { - // Not implemented here must be extended -}; - -/** - * @override - * @param {!Array.<[!number,?number,?]>} series The series in the unified - * data format where series[i] = [x,y,{extras}]. - * @param {!number} rollPeriod The number of points over which to average the data - * @param {!DygraphOptions} options The dygraph options. - * TODO(danvk): be more specific than "Array" here. - * @return {!Array.<[!number,?number,?]>} the rolled series. - */ -BarsHandler.prototype.rollingAverage = function (series, rollPeriod, options) { - // Not implemented here, must be extended. -}; - -/** @inheritDoc */ -BarsHandler.prototype.onPointsCreated_ = function (series, points) { - for (var i = 0; i < series.length; ++i) { - var item = series[i]; - var point = points[i]; - point.y_top = NaN; - point.y_bottom = NaN; - point.yval_minus = _datahandler2['default'].parseFloat(item[2][0]); - point.yval_plus = _datahandler2['default'].parseFloat(item[2][1]); - } -}; - -/** @inheritDoc */ -BarsHandler.prototype.getExtremeYValues = function (series, dateWindow, options) { - var minY = null, - maxY = null, - y; - - var firstIdx = 0; - var lastIdx = series.length - 1; - - for (var j = firstIdx; j <= lastIdx; j++) { - y = series[j][1]; - if (y === null || isNaN(y)) continue; - - var low = series[j][2][0]; - var high = series[j][2][1]; - - if (low > y) low = y; // this can happen with custom bars, - if (high < y) high = y; // e.g. in tests/custom-bars.html - - if (maxY === null || high > maxY) maxY = high; - if (minY === null || low < minY) minY = low; - } - - return [minY, maxY]; -}; - -/** @inheritDoc */ -BarsHandler.prototype.onLineEvaluated = function (points, axis, logscale) { - var point; - for (var j = 0; j < points.length; j++) { - // Copy over the error terms - point = points[j]; - point.y_top = _dygraphLayout2['default'].calcYNormal_(axis, point.yval_minus, logscale); - point.y_bottom = _dygraphLayout2['default'].calcYNormal_(axis, point.yval_plus, logscale); - } -}; - -exports['default'] = BarsHandler; -module.exports = exports['default']; - -},{"../dygraph-layout":13,"./datahandler":6}],6:[function(require,module,exports){ -/** - * @license - * Copyright 2013 David Eberlein (david.eberlein@ch.sauter-bc.com) - * MIT-licensed (http://opensource.org/licenses/MIT) - */ - -/** - * @fileoverview This file contains the managment of data handlers - * @author David Eberlein (david.eberlein@ch.sauter-bc.com) - * - * The idea is to define a common, generic data format that works for all data - * structures supported by dygraphs. To make this possible, the DataHandler - * interface is introduced. This makes it possible, that dygraph itself can work - * with the same logic for every data type independent of the actual format and - * the DataHandler takes care of the data format specific jobs. - * DataHandlers are implemented for all data types supported by Dygraphs and - * return Dygraphs compliant formats. - * By default the correct DataHandler is chosen based on the options set. - * Optionally the user may use his own DataHandler (similar to the plugin - * system). - * - * - * The unified data format returend by each handler is defined as so: - * series[n][point] = [x,y,(extras)] - * - * This format contains the common basis that is needed to draw a simple line - * series extended by optional extras for more complex graphing types. It - * contains a primitive x value as first array entry, a primitive y value as - * second array entry and an optional extras object for additional data needed. - * - * x must always be a number. - * y must always be a number, NaN of type number or null. - * extras is optional and must be interpreted by the DataHandler. It may be of - * any type. - * - * In practice this might look something like this: - * default: [x, yVal] - * errorBar / customBar: [x, yVal, [yTopVariance, yBottomVariance] ] - * - */ -/*global Dygraph:false */ -/*global DygraphLayout:false */ - -"use strict"; - -/** - * - * The data handler is responsible for all data specific operations. All of the - * series data it receives and returns is always in the unified data format. - * Initially the unified data is created by the extractSeries method - * @constructor - */ -Object.defineProperty(exports, "__esModule", { - value: true -}); -var DygraphDataHandler = function DygraphDataHandler() {}; - -var handler = DygraphDataHandler; - -/** - * X-value array index constant for unified data samples. - * @const - * @type {number} - */ -handler.X = 0; - -/** - * Y-value array index constant for unified data samples. - * @const - * @type {number} - */ -handler.Y = 1; - -/** - * Extras-value array index constant for unified data samples. - * @const - * @type {number} - */ -handler.EXTRAS = 2; - -/** - * Extracts one series from the raw data (a 2D array) into an array of the - * unified data format. - * This is where undesirable points (i.e. negative values on log scales and - * missing values through which we wish to connect lines) are dropped. - * TODO(danvk): the "missing values" bit above doesn't seem right. - * - * @param {!Array.} rawData The raw data passed into dygraphs where - * rawData[i] = [x,ySeries1,...,ySeriesN]. - * @param {!number} seriesIndex Index of the series to extract. All other - * series should be ignored. - * @param {!DygraphOptions} options Dygraph options. - * @return {Array.<[!number,?number,?]>} The series in the unified data format - * where series[i] = [x,y,{extras}]. - */ -handler.prototype.extractSeries = function (rawData, seriesIndex, options) {}; - -/** - * Converts a series to a Point array. The resulting point array must be - * returned in increasing order of idx property. - * - * @param {!Array.<[!number,?number,?]>} series The series in the unified - * data format where series[i] = [x,y,{extras}]. - * @param {!string} setName Name of the series. - * @param {!number} boundaryIdStart Index offset of the first point, equal to the - * number of skipped points left of the date window minimum (if any). - * @return {!Array.} List of points for this series. - */ -handler.prototype.seriesToPoints = function (series, setName, boundaryIdStart) { - // TODO(bhs): these loops are a hot-spot for high-point-count charts. In - // fact, - // on chrome+linux, they are 6 times more expensive than iterating through - // the - // points and drawing the lines. The brunt of the cost comes from allocating - // the |point| structures. - var points = []; - for (var i = 0; i < series.length; ++i) { - var item = series[i]; - var yraw = item[1]; - var yval = yraw === null ? null : handler.parseFloat(yraw); - var point = { - x: NaN, - y: NaN, - xval: handler.parseFloat(item[0]), - yval: yval, - name: setName, // TODO(danvk): is this really necessary? - idx: i + boundaryIdStart - }; - points.push(point); - } - this.onPointsCreated_(series, points); - return points; -}; - -/** - * Callback called for each series after the series points have been generated - * which will later be used by the plotters to draw the graph. - * Here data may be added to the seriesPoints which is needed by the plotters. - * The indexes of series and points are in sync meaning the original data - * sample for series[i] is points[i]. - * - * @param {!Array.<[!number,?number,?]>} series The series in the unified - * data format where series[i] = [x,y,{extras}]. - * @param {!Array.} points The corresponding points passed - * to the plotter. - * @protected - */ -handler.prototype.onPointsCreated_ = function (series, points) {}; - -/** - * Calculates the rolling average of a data set. - * - * @param {!Array.<[!number,?number,?]>} series The series in the unified - * data format where series[i] = [x,y,{extras}]. - * @param {!number} rollPeriod The number of points over which to average the data - * @param {!DygraphOptions} options The dygraph options. - * @return {!Array.<[!number,?number,?]>} the rolled series. - */ -handler.prototype.rollingAverage = function (series, rollPeriod, options) {}; - -/** - * Computes the range of the data series (including confidence intervals). - * - * @param {!Array.<[!number,?number,?]>} series The series in the unified - * data format where series[i] = [x, y, {extras}]. - * @param {!Array.} dateWindow The x-value range to display with - * the format: [min, max]. - * @param {!DygraphOptions} options The dygraph options. - * @return {Array.} The low and high extremes of the series in the - * given window with the format: [low, high]. - */ -handler.prototype.getExtremeYValues = function (series, dateWindow, options) {}; - -/** - * Callback called for each series after the layouting data has been - * calculated before the series is drawn. Here normalized positioning data - * should be calculated for the extras of each point. - * - * @param {!Array.} points The points passed to - * the plotter. - * @param {!Object} axis The axis on which the series will be plotted. - * @param {!boolean} logscale Weather or not to use a logscale. - */ -handler.prototype.onLineEvaluated = function (points, axis, logscale) {}; - -/** - * Optimized replacement for parseFloat, which was way too slow when almost - * all values were type number, with few edge cases, none of which were strings. - * @param {?number} val - * @return {number} - * @protected - */ -handler.parseFloat = function (val) { - // parseFloat(null) is NaN - if (val === null) { - return NaN; - } - - // Assume it's a number or NaN. If it's something else, I'll be shocked. - return val; -}; - -exports["default"] = DygraphDataHandler; -module.exports = exports["default"]; - -},{}],7:[function(require,module,exports){ -/** - * @license - * Copyright 2013 David Eberlein (david.eberlein@ch.sauter-bc.com) - * MIT-licensed (http://opensource.org/licenses/MIT) - */ - -/** - * @fileoverview DataHandler implementation for the fractions option. - * @author David Eberlein (david.eberlein@ch.sauter-bc.com) - */ - -/*global Dygraph:false */ -"use strict"; - -Object.defineProperty(exports, '__esModule', { - value: true -}); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } - -var _datahandler = require('./datahandler'); - -var _datahandler2 = _interopRequireDefault(_datahandler); - -var _default = require('./default'); - -var _default2 = _interopRequireDefault(_default); - -/** - * @extends DefaultHandler - * @constructor - */ -var DefaultFractionHandler = function DefaultFractionHandler() {}; - -DefaultFractionHandler.prototype = new _default2['default'](); - -DefaultFractionHandler.prototype.extractSeries = function (rawData, i, options) { - // TODO(danvk): pre-allocate series here. - var series = []; - var x, y, point, num, den, value; - var mult = 100.0; - var logScale = options.get('logscale'); - for (var j = 0; j < rawData.length; j++) { - x = rawData[j][0]; - point = rawData[j][i]; - if (logScale && point !== null) { - // On the log scale, points less than zero do not exist. - // This will create a gap in the chart. - if (point[0] <= 0 || point[1] <= 0) { - point = null; - } - } - // Extract to the unified data format. - if (point !== null) { - num = point[0]; - den = point[1]; - if (num !== null && !isNaN(num)) { - value = den ? num / den : 0.0; - y = mult * value; - // preserve original values in extras for further filtering - series.push([x, y, [num, den]]); - } else { - series.push([x, num, [num, den]]); - } - } else { - series.push([x, null, [null, null]]); - } - } - return series; -}; - -DefaultFractionHandler.prototype.rollingAverage = function (originalData, rollPeriod, options) { - rollPeriod = Math.min(rollPeriod, originalData.length); - var rollingData = []; - - var i; - var num = 0; - var den = 0; // numerator/denominator - var mult = 100.0; - for (i = 0; i < originalData.length; i++) { - num += originalData[i][2][0]; - den += originalData[i][2][1]; - if (i - rollPeriod >= 0) { - num -= originalData[i - rollPeriod][2][0]; - den -= originalData[i - rollPeriod][2][1]; - } - - var date = originalData[i][0]; - var value = den ? num / den : 0.0; - rollingData[i] = [date, mult * value]; - } - - return rollingData; -}; - -exports['default'] = DefaultFractionHandler; -module.exports = exports['default']; - -},{"./datahandler":6,"./default":8}],8:[function(require,module,exports){ -/** - * @license - * Copyright 2013 David Eberlein (david.eberlein@ch.sauter-bc.com) - * MIT-licensed (http://opensource.org/licenses/MIT) - */ - -/** - * @fileoverview DataHandler default implementation used for simple line charts. - * @author David Eberlein (david.eberlein@ch.sauter-bc.com) - */ - -/*global Dygraph:false */ -"use strict"; - -Object.defineProperty(exports, '__esModule', { - value: true -}); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } - -var _datahandler = require('./datahandler'); - -var _datahandler2 = _interopRequireDefault(_datahandler); - -/** - * @constructor - * @extends Dygraph.DataHandler - */ -var DefaultHandler = function DefaultHandler() {}; - -DefaultHandler.prototype = new _datahandler2['default'](); - -/** @inheritDoc */ -DefaultHandler.prototype.extractSeries = function (rawData, i, options) { - // TODO(danvk): pre-allocate series here. - var series = []; - var logScale = options.get('logscale'); - for (var j = 0; j < rawData.length; j++) { - var x = rawData[j][0]; - var point = rawData[j][i]; - if (logScale) { - // On the log scale, points less than zero do not exist. - // This will create a gap in the chart. - if (point <= 0) { - point = null; - } - } - series.push([x, point]); - } - return series; -}; - -/** @inheritDoc */ -DefaultHandler.prototype.rollingAverage = function (originalData, rollPeriod, options) { - rollPeriod = Math.min(rollPeriod, originalData.length); - var rollingData = []; - - var i, j, y, sum, num_ok; - // Calculate the rolling average for the first rollPeriod - 1 points - // where - // there is not enough data to roll over the full number of points - if (rollPeriod == 1) { - return originalData; - } - for (i = 0; i < originalData.length; i++) { - sum = 0; - num_ok = 0; - for (j = Math.max(0, i - rollPeriod + 1); j < i + 1; j++) { - y = originalData[j][1]; - if (y === null || isNaN(y)) continue; - num_ok++; - sum += originalData[j][1]; - } - if (num_ok) { - rollingData[i] = [originalData[i][0], sum / num_ok]; - } else { - rollingData[i] = [originalData[i][0], null]; - } - } - - return rollingData; -}; - -/** @inheritDoc */ -DefaultHandler.prototype.getExtremeYValues = function (series, dateWindow, options) { - var minY = null, - maxY = null, - y; - var firstIdx = 0, - lastIdx = series.length - 1; - - for (var j = firstIdx; j <= lastIdx; j++) { - y = series[j][1]; - if (y === null || isNaN(y)) continue; - if (maxY === null || y > maxY) { - maxY = y; - } - if (minY === null || y < minY) { - minY = y; - } - } - return [minY, maxY]; -}; - -exports['default'] = DefaultHandler; -module.exports = exports['default']; - -},{"./datahandler":6}],9:[function(require,module,exports){ -/** - * @license - * Copyright 2006 Dan Vanderkam (danvdk@gmail.com) - * MIT-licensed (http://opensource.org/licenses/MIT) - */ - -/** - * @fileoverview Based on PlotKit.CanvasRenderer, but modified to meet the - * needs of dygraphs. - * - * In particular, support for: - * - grid overlays - * - error bars - * - dygraphs attribute system - */ - -/** - * The DygraphCanvasRenderer class does the actual rendering of the chart onto - * a canvas. It's based on PlotKit.CanvasRenderer. - * @param {Object} element The canvas to attach to - * @param {Object} elementContext The 2d context of the canvas (injected so it - * can be mocked for testing.) - * @param {Layout} layout The DygraphLayout object for this graph. - * @constructor - */ - -/*global Dygraph:false */ -"use strict"; - -Object.defineProperty(exports, '__esModule', { - value: true -}); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } - -function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } } - -var _dygraphUtils = require('./dygraph-utils'); - -var utils = _interopRequireWildcard(_dygraphUtils); - -var _dygraph = require('./dygraph'); - -var _dygraph2 = _interopRequireDefault(_dygraph); - -/** - * @constructor - * - * This gets called when there are "new points" to chart. This is generally the - * case when the underlying data being charted has changed. It is _not_ called - * in the common case that the user has zoomed or is panning the view. - * - * The chart canvas has already been created by the Dygraph object. The - * renderer simply gets a drawing context. - * - * @param {Dygraph} dygraph The chart to which this renderer belongs. - * @param {HTMLCanvasElement} element The <canvas> DOM element on which to draw. - * @param {CanvasRenderingContext2D} elementContext The drawing context. - * @param {DygraphLayout} layout The chart's DygraphLayout object. - * - * TODO(danvk): remove the elementContext property. - */ -var DygraphCanvasRenderer = function DygraphCanvasRenderer(dygraph, element, elementContext, layout) { - this.dygraph_ = dygraph; - - this.layout = layout; - this.element = element; - this.elementContext = elementContext; - - this.height = dygraph.height_; - this.width = dygraph.width_; - - // --- check whether everything is ok before we return - if (!utils.isCanvasSupported(this.element)) { - throw "Canvas is not supported."; - } - - // internal state - this.area = layout.getPlotArea(); - - // Set up a clipping area for the canvas (and the interaction canvas). - // This ensures that we don't overdraw. - var ctx = this.dygraph_.canvas_ctx_; - ctx.beginPath(); - ctx.rect(this.area.x, this.area.y, this.area.w, this.area.h); - ctx.clip(); - - ctx = this.dygraph_.hidden_ctx_; - ctx.beginPath(); - ctx.rect(this.area.x, this.area.y, this.area.w, this.area.h); - ctx.clip(); -}; - -/** - * Clears out all chart content and DOM elements. - * This is called immediately before render() on every frame, including - * during zooms and pans. - * @private - */ -DygraphCanvasRenderer.prototype.clear = function () { - this.elementContext.clearRect(0, 0, this.width, this.height); -}; - -/** - * This method is responsible for drawing everything on the chart, including - * lines, error bars, fills and axes. - * It is called immediately after clear() on every frame, including during pans - * and zooms. - * @private - */ -DygraphCanvasRenderer.prototype.render = function () { - // attaches point.canvas{x,y} - this._updatePoints(); - - // actually draws the chart. - this._renderLineChart(); -}; - -/** - * Returns a predicate to be used with an iterator, which will - * iterate over points appropriately, depending on whether - * connectSeparatedPoints is true. When it's false, the predicate will - * skip over points with missing yVals. - */ -DygraphCanvasRenderer._getIteratorPredicate = function (connectSeparatedPoints) { - return connectSeparatedPoints ? DygraphCanvasRenderer._predicateThatSkipsEmptyPoints : null; -}; - -DygraphCanvasRenderer._predicateThatSkipsEmptyPoints = function (array, idx) { - return array[idx].yval !== null; -}; - -/** - * Draws a line with the styles passed in and calls all the drawPointCallbacks. - * @param {Object} e The dictionary passed to the plotter function. - * @private - */ -DygraphCanvasRenderer._drawStyledLine = function (e, color, strokeWidth, strokePattern, drawPoints, drawPointCallback, pointSize) { - var g = e.dygraph; - // TODO(konigsberg): Compute attributes outside this method call. - var stepPlot = g.getBooleanOption("stepPlot", e.setName); - - if (!utils.isArrayLike(strokePattern)) { - strokePattern = null; - } - - var drawGapPoints = g.getBooleanOption('drawGapEdgePoints', e.setName); - - var points = e.points; - var setName = e.setName; - var iter = utils.createIterator(points, 0, points.length, DygraphCanvasRenderer._getIteratorPredicate(g.getBooleanOption("connectSeparatedPoints", setName))); - - var stroking = strokePattern && strokePattern.length >= 2; - - var ctx = e.drawingContext; - ctx.save(); - if (stroking) { - if (ctx.setLineDash) ctx.setLineDash(strokePattern); - } - - var pointsOnLine = DygraphCanvasRenderer._drawSeries(e, iter, strokeWidth, pointSize, drawPoints, drawGapPoints, stepPlot, color); - DygraphCanvasRenderer._drawPointsOnLine(e, pointsOnLine, drawPointCallback, color, pointSize); - - if (stroking) { - if (ctx.setLineDash) ctx.setLineDash([]); - } - - ctx.restore(); -}; - -/** - * This does the actual drawing of lines on the canvas, for just one series. - * Returns a list of [canvasx, canvasy] pairs for points for which a - * drawPointCallback should be fired. These include isolated points, or all - * points if drawPoints=true. - * @param {Object} e The dictionary passed to the plotter function. - * @private - */ -DygraphCanvasRenderer._drawSeries = function (e, iter, strokeWidth, pointSize, drawPoints, drawGapPoints, stepPlot, color) { - - var prevCanvasX = null; - var prevCanvasY = null; - var nextCanvasY = null; - var isIsolated; // true if this point is isolated (no line segments) - var point; // the point being processed in the while loop - var pointsOnLine = []; // Array of [canvasx, canvasy] pairs. - var first = true; // the first cycle through the while loop - - var ctx = e.drawingContext; - ctx.beginPath(); - ctx.strokeStyle = color; - ctx.lineWidth = strokeWidth; - - // NOTE: we break the iterator's encapsulation here for about a 25% speedup. - var arr = iter.array_; - var limit = iter.end_; - var predicate = iter.predicate_; - - for (var i = iter.start_; i < limit; i++) { - point = arr[i]; - if (predicate) { - while (i < limit && !predicate(arr, i)) { - i++; - } - if (i == limit) break; - point = arr[i]; - } - - // FIXME: The 'canvasy != canvasy' test here catches NaN values but the test - // doesn't catch Infinity values. Could change this to - // !isFinite(point.canvasy), but I assume it avoids isNaN for performance? - if (point.canvasy === null || point.canvasy != point.canvasy) { - if (stepPlot && prevCanvasX !== null) { - // Draw a horizontal line to the start of the missing data - ctx.moveTo(prevCanvasX, prevCanvasY); - ctx.lineTo(point.canvasx, prevCanvasY); - } - prevCanvasX = prevCanvasY = null; - } else { - isIsolated = false; - if (drawGapPoints || prevCanvasX === null) { - iter.nextIdx_ = i; - iter.next(); - nextCanvasY = iter.hasNext ? iter.peek.canvasy : null; - - var isNextCanvasYNullOrNaN = nextCanvasY === null || nextCanvasY != nextCanvasY; - isIsolated = prevCanvasX === null && isNextCanvasYNullOrNaN; - if (drawGapPoints) { - // Also consider a point to be "isolated" if it's adjacent to a - // null point, excluding the graph edges. - if (!first && prevCanvasX === null || iter.hasNext && isNextCanvasYNullOrNaN) { - isIsolated = true; - } - } - } - - if (prevCanvasX !== null) { - if (strokeWidth) { - if (stepPlot) { - ctx.moveTo(prevCanvasX, prevCanvasY); - ctx.lineTo(point.canvasx, prevCanvasY); - } - - ctx.lineTo(point.canvasx, point.canvasy); - } - } else { - ctx.moveTo(point.canvasx, point.canvasy); - } - if (drawPoints || isIsolated) { - pointsOnLine.push([point.canvasx, point.canvasy, point.idx]); - } - prevCanvasX = point.canvasx; - prevCanvasY = point.canvasy; - } - first = false; - } - ctx.stroke(); - return pointsOnLine; -}; - -/** - * This fires the drawPointCallback functions, which draw dots on the points by - * default. This gets used when the "drawPoints" option is set, or when there - * are isolated points. - * @param {Object} e The dictionary passed to the plotter function. - * @private - */ -DygraphCanvasRenderer._drawPointsOnLine = function (e, pointsOnLine, drawPointCallback, color, pointSize) { - var ctx = e.drawingContext; - for (var idx = 0; idx < pointsOnLine.length; idx++) { - var cb = pointsOnLine[idx]; - ctx.save(); - drawPointCallback.call(e.dygraph, e.dygraph, e.setName, ctx, cb[0], cb[1], color, pointSize, cb[2]); - ctx.restore(); - } -}; - -/** - * Attaches canvas coordinates to the points array. - * @private - */ -DygraphCanvasRenderer.prototype._updatePoints = function () { - // Update Points - // TODO(danvk): here - // - // TODO(bhs): this loop is a hot-spot for high-point-count charts. These - // transformations can be pushed into the canvas via linear transformation - // matrices. - // NOTE(danvk): this is trickier than it sounds at first. The transformation - // needs to be done before the .moveTo() and .lineTo() calls, but must be - // undone before the .stroke() call to ensure that the stroke width is - // unaffected. An alternative is to reduce the stroke width in the - // transformed coordinate space, but you can't specify different values for - // each dimension (as you can with .scale()). The speedup here is ~12%. - var sets = this.layout.points; - for (var i = sets.length; i--;) { - var points = sets[i]; - for (var j = points.length; j--;) { - var point = points[j]; - point.canvasx = this.area.w * point.x + this.area.x; - point.canvasy = this.area.h * point.y + this.area.y; - } - } -}; - -/** - * Add canvas Actually draw the lines chart, including error bars. - * - * This function can only be called if DygraphLayout's points array has been - * updated with canvas{x,y} attributes, i.e. by - * DygraphCanvasRenderer._updatePoints. - * - * @param {string=} opt_seriesName when specified, only that series will - * be drawn. (This is used for expedited redrawing with highlightSeriesOpts) - * @param {CanvasRenderingContext2D} opt_ctx when specified, the drawing - * context. However, lines are typically drawn on the object's - * elementContext. - * @private - */ -DygraphCanvasRenderer.prototype._renderLineChart = function (opt_seriesName, opt_ctx) { - var ctx = opt_ctx || this.elementContext; - var i; - - var sets = this.layout.points; - var setNames = this.layout.setNames; - var setName; - - this.colors = this.dygraph_.colorsMap_; - - // Determine which series have specialized plotters. - var plotter_attr = this.dygraph_.getOption("plotter"); - var plotters = plotter_attr; - if (!utils.isArrayLike(plotters)) { - plotters = [plotters]; - } - - var setPlotters = {}; // series name -> plotter fn. - for (i = 0; i < setNames.length; i++) { - setName = setNames[i]; - var setPlotter = this.dygraph_.getOption("plotter", setName); - if (setPlotter == plotter_attr) continue; // not specialized. - - setPlotters[setName] = setPlotter; - } - - for (i = 0; i < plotters.length; i++) { - var plotter = plotters[i]; - var is_last = i == plotters.length - 1; - - for (var j = 0; j < sets.length; j++) { - setName = setNames[j]; - if (opt_seriesName && setName != opt_seriesName) continue; - var plotterFinishedCallback = this.dygraph_.getOption("plotterFinishedCallback", setName); - - var points = sets[j]; - - // Only throw in the specialized plotters on the last iteration. - var p = plotter; - if (setName in setPlotters) { - if (is_last) { - p = setPlotters[setName]; - } else { - // Don't use the standard plotters in this case. - continue; - } - } - - var color = this.colors[setName]; - var strokeWidth = this.dygraph_.getOption("strokeWidth", setName); - - ctx.save(); - ctx.strokeStyle = color; - ctx.lineWidth = strokeWidth; - p({ - points: points, - setName: setName, - drawingContext: ctx, - color: color, - strokeWidth: strokeWidth, - dygraph: this.dygraph_, - axis: this.dygraph_.axisPropertiesForSeries(setName), - plotArea: this.area, - seriesIndex: j, - seriesCount: sets.length, - singleSeriesName: opt_seriesName, - allSeriesPoints: sets - }); - ctx.restore(); - if (plotterFinishedCallback) { - plotterFinishedCallback(ctx); - } - } - } -}; - -/** - * Standard plotters. These may be used by clients via Dygraph.Plotters. - * See comments there for more details. - */ -DygraphCanvasRenderer._Plotters = { - linePlotter: function linePlotter(e) { - DygraphCanvasRenderer._linePlotter(e); - }, - - fillPlotter: function fillPlotter(e) { - DygraphCanvasRenderer._fillPlotter(e); - }, - - errorPlotter: function errorPlotter(e) { - DygraphCanvasRenderer._errorPlotter(e); - }, - - pointPlotter: function pointPlotter(e) { - DygraphCanvasRenderer._pointPlotter(e); - } -}; - -/** - * Plotter that only renders points for a series without doing any - * work to compute lines connecting them. - * @private - */ -DygraphCanvasRenderer._pointPlotter = function (e) { - var g = e.dygraph; - var setName = e.setName; - - if (!g.getBooleanOption("drawPoints", setName)) { - return; - } - - var drawPointCallback = g.getOption("drawPointCallback", setName) || utils.Circles.DEFAULT; - var pointSize = g.getNumericOption("pointSize", setName); - var color = e.color; - var points = e.points; - var iter = utils.createIterator(points, 0, points.length, null); - - var ctx = e.drawingContext; - var point; - - // This performance hack is lifted from DygraphCanvasRenderer._drawSeries - var arr = iter.array_; - var limit = iter.end_; - - ctx.save(); - for (var i = iter.start_; i < limit; i++) { - point = arr[i]; - // We compare point.canvasy against itself to discard NaNs; this is lifted - // from DygraphCanvasRenderer._drawSeries - if (point.canvasy !== null && point.canvasy == point.canvasy) { - drawPointCallback.call(e.dygraph, e.dygraph, e.setName, ctx, point.canvasx, point.canvasy, color, pointSize, point.idx); - } - } - ctx.restore(); -}; - -/** - * Plotter which draws the central lines for a series. - * @private - */ -DygraphCanvasRenderer._linePlotter = function (e) { - var g = e.dygraph; - var setName = e.setName; - var strokeWidth = e.strokeWidth; - - // TODO(danvk): Check if there's any performance impact of just calling - // getOption() inside of _drawStyledLine. Passing in so many parameters makes - // this code a bit nasty. - var borderWidth = g.getNumericOption("strokeBorderWidth", setName); - var drawPointCallback = g.getOption("drawPointCallback", setName) || utils.Circles.DEFAULT; - var strokePattern = g.getOption("strokePattern", setName); - var drawPoints = g.getBooleanOption("drawPoints", setName); - var pointSize = g.getNumericOption("pointSize", setName); - - if (borderWidth && strokeWidth) { - DygraphCanvasRenderer._drawStyledLine(e, g.getOption("strokeBorderColor", setName), strokeWidth + 2 * borderWidth, strokePattern, drawPoints, drawPointCallback, pointSize); - } - - DygraphCanvasRenderer._drawStyledLine(e, e.color, strokeWidth, strokePattern, drawPoints, drawPointCallback, pointSize); -}; - -/** - * Draws the shaded error bars/confidence intervals for each series. - * This happens before the center lines are drawn, since the center lines - * need to be drawn on top of the error bars for all series. - * @private - */ -DygraphCanvasRenderer._errorPlotter = function (e) { - var g = e.dygraph; - var setName = e.setName; - var errorBars = g.getBooleanOption("errorBars") || g.getBooleanOption("customBars"); - if (!errorBars) return; - - var fillGraph = g.getBooleanOption("fillGraph", setName); - if (fillGraph) { - console.warn("Can't use fillGraph option with error bars"); - } - - var ctx = e.drawingContext; - var color = e.color; - var fillAlpha = g.getNumericOption('fillAlpha', setName); - var stepPlot = g.getBooleanOption("stepPlot", setName); - var points = e.points; - - var iter = utils.createIterator(points, 0, points.length, DygraphCanvasRenderer._getIteratorPredicate(g.getBooleanOption("connectSeparatedPoints", setName))); - - var newYs; - - // setup graphics context - var prevX = NaN; - var prevY = NaN; - var prevYs = [-1, -1]; - // should be same color as the lines but only 15% opaque. - var rgb = utils.toRGB_(color); - var err_color = 'rgba(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + fillAlpha + ')'; - ctx.fillStyle = err_color; - ctx.beginPath(); - - var isNullUndefinedOrNaN = function isNullUndefinedOrNaN(x) { - return x === null || x === undefined || isNaN(x); - }; - - while (iter.hasNext) { - var point = iter.next(); - if (!stepPlot && isNullUndefinedOrNaN(point.y) || stepPlot && !isNaN(prevY) && isNullUndefinedOrNaN(prevY)) { - prevX = NaN; - continue; - } - - newYs = [point.y_bottom, point.y_top]; - if (stepPlot) { - prevY = point.y; - } - - // The documentation specifically disallows nulls inside the point arrays, - // but in case it happens we should do something sensible. - if (isNaN(newYs[0])) newYs[0] = point.y; - if (isNaN(newYs[1])) newYs[1] = point.y; - - newYs[0] = e.plotArea.h * newYs[0] + e.plotArea.y; - newYs[1] = e.plotArea.h * newYs[1] + e.plotArea.y; - if (!isNaN(prevX)) { - if (stepPlot) { - ctx.moveTo(prevX, prevYs[0]); - ctx.lineTo(point.canvasx, prevYs[0]); - ctx.lineTo(point.canvasx, prevYs[1]); - } else { - ctx.moveTo(prevX, prevYs[0]); - ctx.lineTo(point.canvasx, newYs[0]); - ctx.lineTo(point.canvasx, newYs[1]); - } - ctx.lineTo(prevX, prevYs[1]); - ctx.closePath(); - } - prevYs = newYs; - prevX = point.canvasx; - } - ctx.fill(); -}; - -/** - * Proxy for CanvasRenderingContext2D which drops moveTo/lineTo calls which are - * superfluous. It accumulates all movements which haven't changed the x-value - * and only applies the two with the most extreme y-values. - * - * Calls to lineTo/moveTo must have non-decreasing x-values. - */ -DygraphCanvasRenderer._fastCanvasProxy = function (context) { - var pendingActions = []; // array of [type, x, y] tuples - var lastRoundedX = null; - var lastFlushedX = null; - - var LINE_TO = 1, - MOVE_TO = 2; - - var actionCount = 0; // number of moveTos and lineTos passed to context. - - // Drop superfluous motions - // Assumes all pendingActions have the same (rounded) x-value. - var compressActions = function compressActions(opt_losslessOnly) { - if (pendingActions.length <= 1) return; - - // Lossless compression: drop inconsequential moveTos. - for (var i = pendingActions.length - 1; i > 0; i--) { - var action = pendingActions[i]; - if (action[0] == MOVE_TO) { - var prevAction = pendingActions[i - 1]; - if (prevAction[1] == action[1] && prevAction[2] == action[2]) { - pendingActions.splice(i, 1); - } - } - } - - // Lossless compression: ... drop consecutive moveTos ... - for (var i = 0; i < pendingActions.length - 1;) /* incremented internally */{ - var action = pendingActions[i]; - if (action[0] == MOVE_TO && pendingActions[i + 1][0] == MOVE_TO) { - pendingActions.splice(i, 1); - } else { - i++; - } - } - - // Lossy compression: ... drop all but the extreme y-values ... - if (pendingActions.length > 2 && !opt_losslessOnly) { - // keep an initial moveTo, but drop all others. - var startIdx = 0; - if (pendingActions[0][0] == MOVE_TO) startIdx++; - var minIdx = null, - maxIdx = null; - for (var i = startIdx; i < pendingActions.length; i++) { - var action = pendingActions[i]; - if (action[0] != LINE_TO) continue; - if (minIdx === null && maxIdx === null) { - minIdx = i; - maxIdx = i; - } else { - var y = action[2]; - if (y < pendingActions[minIdx][2]) { - minIdx = i; - } else if (y > pendingActions[maxIdx][2]) { - maxIdx = i; - } - } - } - var minAction = pendingActions[minIdx], - maxAction = pendingActions[maxIdx]; - pendingActions.splice(startIdx, pendingActions.length - startIdx); - if (minIdx < maxIdx) { - pendingActions.push(minAction); - pendingActions.push(maxAction); - } else if (minIdx > maxIdx) { - pendingActions.push(maxAction); - pendingActions.push(minAction); - } else { - pendingActions.push(minAction); - } - } - }; - - var flushActions = function flushActions(opt_noLossyCompression) { - compressActions(opt_noLossyCompression); - for (var i = 0, len = pendingActions.length; i < len; i++) { - var action = pendingActions[i]; - if (action[0] == LINE_TO) { - context.lineTo(action[1], action[2]); - } else if (action[0] == MOVE_TO) { - context.moveTo(action[1], action[2]); - } - } - if (pendingActions.length) { - lastFlushedX = pendingActions[pendingActions.length - 1][1]; - } - actionCount += pendingActions.length; - pendingActions = []; - }; - - var addAction = function addAction(action, x, y) { - var rx = Math.round(x); - if (lastRoundedX === null || rx != lastRoundedX) { - // if there are large gaps on the x-axis, it's essential to keep the - // first and last point as well. - var hasGapOnLeft = lastRoundedX - lastFlushedX > 1, - hasGapOnRight = rx - lastRoundedX > 1, - hasGap = hasGapOnLeft || hasGapOnRight; - flushActions(hasGap); - lastRoundedX = rx; - } - pendingActions.push([action, x, y]); - }; - - return { - moveTo: function moveTo(x, y) { - addAction(MOVE_TO, x, y); - }, - lineTo: function lineTo(x, y) { - addAction(LINE_TO, x, y); - }, - - // for major operations like stroke/fill, we skip compression to ensure - // that there are no artifacts at the right edge. - stroke: function stroke() { - flushActions(true);context.stroke(); - }, - fill: function fill() { - flushActions(true);context.fill(); - }, - beginPath: function beginPath() { - flushActions(true);context.beginPath(); - }, - closePath: function closePath() { - flushActions(true);context.closePath(); - }, - - _count: function _count() { - return actionCount; - } - }; -}; - -/** - * Draws the shaded regions when "fillGraph" is set. Not to be confused with - * error bars. - * - * For stacked charts, it's more convenient to handle all the series - * simultaneously. So this plotter plots all the points on the first series - * it's asked to draw, then ignores all the other series. - * - * @private - */ -DygraphCanvasRenderer._fillPlotter = function (e) { - // Skip if we're drawing a single series for interactive highlight overlay. - if (e.singleSeriesName) return; - - // We'll handle all the series at once, not one-by-one. - if (e.seriesIndex !== 0) return; - - var g = e.dygraph; - var setNames = g.getLabels().slice(1); // remove x-axis - - // getLabels() includes names for invisible series, which are not included in - // allSeriesPoints. We remove those to make the two match. - // TODO(danvk): provide a simpler way to get this information. - for (var i = setNames.length; i >= 0; i--) { - if (!g.visibility()[i]) setNames.splice(i, 1); - } - - var anySeriesFilled = (function () { - for (var i = 0; i < setNames.length; i++) { - if (g.getBooleanOption("fillGraph", setNames[i])) return true; - } - return false; - })(); - - if (!anySeriesFilled) return; - - var area = e.plotArea; - var sets = e.allSeriesPoints; - var setCount = sets.length; - - var stackedGraph = g.getBooleanOption("stackedGraph"); - var colors = g.getColors(); - - // For stacked graphs, track the baseline for filling. - // - // The filled areas below graph lines are trapezoids with two - // vertical edges. The top edge is the line segment being drawn, and - // the baseline is the bottom edge. Each baseline corresponds to the - // top line segment from the previous stacked line. In the case of - // step plots, the trapezoids are rectangles. - var baseline = {}; - var currBaseline; - var prevStepPlot; // for different line drawing modes (line/step) per series - - // Helper function to trace a line back along the baseline. - var traceBackPath = function traceBackPath(ctx, baselineX, baselineY, pathBack) { - ctx.lineTo(baselineX, baselineY); - if (stackedGraph) { - for (var i = pathBack.length - 1; i >= 0; i--) { - var pt = pathBack[i]; - ctx.lineTo(pt[0], pt[1]); - } - } - }; - - // process sets in reverse order (needed for stacked graphs) - for (var setIdx = setCount - 1; setIdx >= 0; setIdx--) { - var ctx = e.drawingContext; - var setName = setNames[setIdx]; - if (!g.getBooleanOption('fillGraph', setName)) continue; - - var fillAlpha = g.getNumericOption('fillAlpha', setName); - var stepPlot = g.getBooleanOption('stepPlot', setName); - var color = colors[setIdx]; - var axis = g.axisPropertiesForSeries(setName); - var axisY = 1.0 + axis.minyval * axis.yscale; - if (axisY < 0.0) axisY = 0.0;else if (axisY > 1.0) axisY = 1.0; - axisY = area.h * axisY + area.y; - - var points = sets[setIdx]; - var iter = utils.createIterator(points, 0, points.length, DygraphCanvasRenderer._getIteratorPredicate(g.getBooleanOption("connectSeparatedPoints", setName))); - - // setup graphics context - var prevX = NaN; - var prevYs = [-1, -1]; - var newYs; - // should be same color as the lines but only 15% opaque. - var rgb = utils.toRGB_(color); - var err_color = 'rgba(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + fillAlpha + ')'; - ctx.fillStyle = err_color; - ctx.beginPath(); - var last_x, - is_first = true; - - // If the point density is high enough, dropping segments on their way to - // the canvas justifies the overhead of doing so. - if (points.length > 2 * g.width_ || _dygraph2['default'].FORCE_FAST_PROXY) { - ctx = DygraphCanvasRenderer._fastCanvasProxy(ctx); - } - - // For filled charts, we draw points from left to right, then back along - // the x-axis to complete a shape for filling. - // For stacked plots, this "back path" is a more complex shape. This array - // stores the [x, y] values needed to trace that shape. - var pathBack = []; - - // TODO(danvk): there are a lot of options at play in this loop. - // The logic would be much clearer if some (e.g. stackGraph and - // stepPlot) were split off into separate sub-plotters. - var point; - while (iter.hasNext) { - point = iter.next(); - if (!utils.isOK(point.y) && !stepPlot) { - traceBackPath(ctx, prevX, prevYs[1], pathBack); - pathBack = []; - prevX = NaN; - if (point.y_stacked !== null && !isNaN(point.y_stacked)) { - baseline[point.canvasx] = area.h * point.y_stacked + area.y; - } - continue; - } - if (stackedGraph) { - if (!is_first && last_x == point.xval) { - continue; - } else { - is_first = false; - last_x = point.xval; - } - - currBaseline = baseline[point.canvasx]; - var lastY; - if (currBaseline === undefined) { - lastY = axisY; - } else { - if (prevStepPlot) { - lastY = currBaseline[0]; - } else { - lastY = currBaseline; - } - } - newYs = [point.canvasy, lastY]; - - if (stepPlot) { - // Step plots must keep track of the top and bottom of - // the baseline at each point. - if (prevYs[0] === -1) { - baseline[point.canvasx] = [point.canvasy, axisY]; - } else { - baseline[point.canvasx] = [point.canvasy, prevYs[0]]; - } - } else { - baseline[point.canvasx] = point.canvasy; - } - } else { - if (isNaN(point.canvasy) && stepPlot) { - newYs = [area.y + area.h, axisY]; - } else { - newYs = [point.canvasy, axisY]; - } - } - if (!isNaN(prevX)) { - // Move to top fill point - if (stepPlot) { - ctx.lineTo(point.canvasx, prevYs[0]); - ctx.lineTo(point.canvasx, newYs[0]); - } else { - ctx.lineTo(point.canvasx, newYs[0]); - } - - // Record the baseline for the reverse path. - if (stackedGraph) { - pathBack.push([prevX, prevYs[1]]); - if (prevStepPlot && currBaseline) { - // Draw to the bottom of the baseline - pathBack.push([point.canvasx, currBaseline[1]]); - } else { - pathBack.push([point.canvasx, newYs[1]]); - } - } - } else { - ctx.moveTo(point.canvasx, newYs[1]); - ctx.lineTo(point.canvasx, newYs[0]); - } - prevYs = newYs; - prevX = point.canvasx; - } - prevStepPlot = stepPlot; - if (newYs && point) { - traceBackPath(ctx, point.canvasx, newYs[1], pathBack); - pathBack = []; - } - ctx.fill(); - } -}; - -exports['default'] = DygraphCanvasRenderer; -module.exports = exports['default']; - -},{"./dygraph":18,"./dygraph-utils":17}],10:[function(require,module,exports){ -'use strict'; - -Object.defineProperty(exports, '__esModule', { - value: true -}); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } - -function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } } - -var _dygraphTickers = require('./dygraph-tickers'); - -var DygraphTickers = _interopRequireWildcard(_dygraphTickers); - -var _dygraphInteractionModel = require('./dygraph-interaction-model'); - -var _dygraphInteractionModel2 = _interopRequireDefault(_dygraphInteractionModel); - -var _dygraphCanvas = require('./dygraph-canvas'); - -var _dygraphCanvas2 = _interopRequireDefault(_dygraphCanvas); - -var _dygraphUtils = require('./dygraph-utils'); - -var utils = _interopRequireWildcard(_dygraphUtils); - -// Default attribute values. -var DEFAULT_ATTRS = { - highlightCircleSize: 3, - highlightSeriesOpts: null, - highlightSeriesBackgroundAlpha: 0.5, - highlightSeriesBackgroundColor: 'rgb(255, 255, 255)', - - labelsSeparateLines: false, - labelsShowZeroValues: true, - labelsKMB: false, - labelsKMG2: false, - showLabelsOnHighlight: true, - - digitsAfterDecimal: 2, - maxNumberWidth: 6, - sigFigs: null, - - strokeWidth: 1.0, - strokeBorderWidth: 0, - strokeBorderColor: "white", - - axisTickSize: 3, - axisLabelFontSize: 14, - rightGap: 5, - - showRoller: false, - xValueParser: undefined, - - delimiter: ',', - - sigma: 2.0, - errorBars: false, - fractions: false, - wilsonInterval: true, // only relevant if fractions is true - customBars: false, - fillGraph: false, - fillAlpha: 0.15, - connectSeparatedPoints: false, - - stackedGraph: false, - stackedGraphNaNFill: 'all', - hideOverlayOnMouseOut: true, - - legend: 'onmouseover', - stepPlot: false, - xRangePad: 0, - yRangePad: null, - drawAxesAtZero: false, - - // Sizes of the various chart labels. - titleHeight: 28, - xLabelHeight: 18, - yLabelWidth: 18, - - axisLineColor: "black", - axisLineWidth: 0.3, - gridLineWidth: 0.3, - axisLabelWidth: 50, - gridLineColor: "rgb(128,128,128)", - - interactionModel: _dygraphInteractionModel2['default'].defaultModel, - animatedZooms: false, // (for now) - - // Range selector options - showRangeSelector: false, - rangeSelectorHeight: 40, - rangeSelectorPlotStrokeColor: "#808FAB", - rangeSelectorPlotFillGradientColor: "white", - rangeSelectorPlotFillColor: "#A7B1C4", - rangeSelectorBackgroundStrokeColor: "gray", - rangeSelectorBackgroundLineWidth: 1, - rangeSelectorPlotLineWidth: 1.5, - rangeSelectorForegroundStrokeColor: "black", - rangeSelectorForegroundLineWidth: 1, - rangeSelectorAlpha: 0.6, - showInRangeSelector: null, - - // The ordering here ensures that central lines always appear above any - // fill bars/error bars. - plotter: [_dygraphCanvas2['default']._fillPlotter, _dygraphCanvas2['default']._errorPlotter, _dygraphCanvas2['default']._linePlotter], - - plugins: [], - - // per-axis options - axes: { - x: { - pixelsPerLabel: 70, - axisLabelWidth: 60, - axisLabelFormatter: utils.dateAxisLabelFormatter, - valueFormatter: utils.dateValueFormatter, - drawGrid: true, - drawAxis: true, - independentTicks: true, - ticker: DygraphTickers.dateTicker - }, - y: { - axisLabelWidth: 50, - pixelsPerLabel: 30, - valueFormatter: utils.numberValueFormatter, - axisLabelFormatter: utils.numberAxisLabelFormatter, - drawGrid: true, - drawAxis: true, - independentTicks: true, - ticker: DygraphTickers.numericTicks - }, - y2: { - axisLabelWidth: 50, - pixelsPerLabel: 30, - valueFormatter: utils.numberValueFormatter, - axisLabelFormatter: utils.numberAxisLabelFormatter, - drawAxis: true, // only applies when there are two axes of data. - drawGrid: false, - independentTicks: false, - ticker: DygraphTickers.numericTicks - } - } -}; - -exports['default'] = DEFAULT_ATTRS; -module.exports = exports['default']; - -},{"./dygraph-canvas":9,"./dygraph-interaction-model":12,"./dygraph-tickers":16,"./dygraph-utils":17}],11:[function(require,module,exports){ -/** - * @license - * Copyright 2011 Dan Vanderkam (danvdk@gmail.com) - * MIT-licensed (http://opensource.org/licenses/MIT) - */ - -/** - * @fileoverview A wrapper around the Dygraph class which implements the - * interface for a GViz (aka Google Visualization API) visualization. - * It is designed to be a drop-in replacement for Google's AnnotatedTimeline, - * so the documentation at - * http://code.google.com/apis/chart/interactive/docs/gallery/annotatedtimeline.html - * translates over directly. - * - * For a full demo, see: - * - http://dygraphs.com/tests/gviz.html - * - http://dygraphs.com/tests/annotation-gviz.html - */ - -/*global Dygraph:false */ -"use strict"; - -Object.defineProperty(exports, '__esModule', { - value: true -}); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } - -var _dygraph = require('./dygraph'); - -var _dygraph2 = _interopRequireDefault(_dygraph); - -/** - * A wrapper around Dygraph that implements the gviz API. - * @param {!HTMLDivElement} container The DOM object the visualization should - * live in. - * @constructor - */ -var GVizChart = function GVizChart(container) { - this.container = container; -}; - -/** - * @param {GVizDataTable} data - * @param {Object.<*>} options - */ -GVizChart.prototype.draw = function (data, options) { - // Clear out any existing dygraph. - // TODO(danvk): would it make more sense to simply redraw using the current - // date_graph object? - this.container.innerHTML = ''; - if (typeof this.date_graph != 'undefined') { - this.date_graph.destroy(); - } - - this.date_graph = new _dygraph2['default'](this.container, data, options); -}; - -/** - * Google charts compatible setSelection - * Only row selection is supported, all points in the row will be highlighted - * @param {Array.<{row:number}>} selection_array array of the selected cells - * @public - */ -GVizChart.prototype.setSelection = function (selection_array) { - var row = false; - if (selection_array.length) { - row = selection_array[0].row; - } - this.date_graph.setSelection(row); -}; - -/** - * Google charts compatible getSelection implementation - * @return {Array.<{row:number,column:number}>} array of the selected cells - * @public - */ -GVizChart.prototype.getSelection = function () { - var selection = []; - - var row = this.date_graph.getSelection(); - - if (row < 0) return selection; - - var points = this.date_graph.layout_.points; - for (var setIdx = 0; setIdx < points.length; ++setIdx) { - selection.push({ row: row, column: setIdx + 1 }); - } - - return selection; -}; - -exports['default'] = GVizChart; -module.exports = exports['default']; - -},{"./dygraph":18}],12:[function(require,module,exports){ -/** - * @license - * Copyright 2011 Robert Konigsberg (konigsberg@google.com) - * MIT-licensed (http://opensource.org/licenses/MIT) - */ - -/** - * @fileoverview The default interaction model for Dygraphs. This is kept out - * of dygraph.js for better navigability. - * @author Robert Konigsberg (konigsberg@google.com) - */ - -/*global Dygraph:false */ -"use strict"; - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj["default"] = obj; return newObj; } } - -var _dygraphUtils = require('./dygraph-utils'); - -var utils = _interopRequireWildcard(_dygraphUtils); - -/** - * You can drag this many pixels past the edge of the chart and still have it - * be considered a zoom. This makes it easier to zoom to the exact edge of the - * chart, a fairly common operation. - */ -var DRAG_EDGE_MARGIN = 100; - -/** - * A collection of functions to facilitate build custom interaction models. - * @class - */ -var DygraphInteraction = {}; - -/** - * Checks whether the beginning & ending of an event were close enough that it - * should be considered a click. If it should, dispatch appropriate events. - * Returns true if the event was treated as a click. - * - * @param {Event} event - * @param {Dygraph} g - * @param {Object} context - */ -DygraphInteraction.maybeTreatMouseOpAsClick = function (event, g, context) { - context.dragEndX = utils.dragGetX_(event, context); - context.dragEndY = utils.dragGetY_(event, context); - var regionWidth = Math.abs(context.dragEndX - context.dragStartX); - var regionHeight = Math.abs(context.dragEndY - context.dragStartY); - - if (regionWidth < 2 && regionHeight < 2 && g.lastx_ !== undefined && g.lastx_ != -1) { - DygraphInteraction.treatMouseOpAsClick(g, event, context); - } - - context.regionWidth = regionWidth; - context.regionHeight = regionHeight; -}; - -/** - * Called in response to an interaction model operation that - * should start the default panning behavior. - * - * It's used in the default callback for "mousedown" operations. - * Custom interaction model builders can use it to provide the default - * panning behavior. - * - * @param {Event} event the event object which led to the startPan call. - * @param {Dygraph} g The dygraph on which to act. - * @param {Object} context The dragging context object (with - * dragStartX/dragStartY/etc. properties). This function modifies the - * context. - */ -DygraphInteraction.startPan = function (event, g, context) { - var i, axis; - context.isPanning = true; - var xRange = g.xAxisRange(); - - if (g.getOptionForAxis("logscale", "x")) { - context.initialLeftmostDate = utils.log10(xRange[0]); - context.dateRange = utils.log10(xRange[1]) - utils.log10(xRange[0]); - } else { - context.initialLeftmostDate = xRange[0]; - context.dateRange = xRange[1] - xRange[0]; - } - context.xUnitsPerPixel = context.dateRange / (g.plotter_.area.w - 1); - - if (g.getNumericOption("panEdgeFraction")) { - var maxXPixelsToDraw = g.width_ * g.getNumericOption("panEdgeFraction"); - var xExtremes = g.xAxisExtremes(); // I REALLY WANT TO CALL THIS xTremes! - - var boundedLeftX = g.toDomXCoord(xExtremes[0]) - maxXPixelsToDraw; - var boundedRightX = g.toDomXCoord(xExtremes[1]) + maxXPixelsToDraw; - - var boundedLeftDate = g.toDataXCoord(boundedLeftX); - var boundedRightDate = g.toDataXCoord(boundedRightX); - context.boundedDates = [boundedLeftDate, boundedRightDate]; - - var boundedValues = []; - var maxYPixelsToDraw = g.height_ * g.getNumericOption("panEdgeFraction"); - - for (i = 0; i < g.axes_.length; i++) { - axis = g.axes_[i]; - var yExtremes = axis.extremeRange; - - var boundedTopY = g.toDomYCoord(yExtremes[0], i) + maxYPixelsToDraw; - var boundedBottomY = g.toDomYCoord(yExtremes[1], i) - maxYPixelsToDraw; - - var boundedTopValue = g.toDataYCoord(boundedTopY, i); - var boundedBottomValue = g.toDataYCoord(boundedBottomY, i); - - boundedValues[i] = [boundedTopValue, boundedBottomValue]; - } - context.boundedValues = boundedValues; - } - - // Record the range of each y-axis at the start of the drag. - // If any axis has a valueRange, then we want a 2D pan. - // We can't store data directly in g.axes_, because it does not belong to us - // and could change out from under us during a pan (say if there's a data - // update). - context.is2DPan = false; - context.axes = []; - for (i = 0; i < g.axes_.length; i++) { - axis = g.axes_[i]; - var axis_data = {}; - var yRange = g.yAxisRange(i); - // TODO(konigsberg): These values should be in |context|. - // In log scale, initialTopValue, dragValueRange and unitsPerPixel are log scale. - var logscale = g.attributes_.getForAxis("logscale", i); - if (logscale) { - axis_data.initialTopValue = utils.log10(yRange[1]); - axis_data.dragValueRange = utils.log10(yRange[1]) - utils.log10(yRange[0]); - } else { - axis_data.initialTopValue = yRange[1]; - axis_data.dragValueRange = yRange[1] - yRange[0]; - } - axis_data.unitsPerPixel = axis_data.dragValueRange / (g.plotter_.area.h - 1); - context.axes.push(axis_data); - - // While calculating axes, set 2dpan. - if (axis.valueRange) context.is2DPan = true; - } -}; - -/** - * Called in response to an interaction model operation that - * responds to an event that pans the view. - * - * It's used in the default callback for "mousemove" operations. - * Custom interaction model builders can use it to provide the default - * panning behavior. - * - * @param {Event} event the event object which led to the movePan call. - * @param {Dygraph} g The dygraph on which to act. - * @param {Object} context The dragging context object (with - * dragStartX/dragStartY/etc. properties). This function modifies the - * context. - */ -DygraphInteraction.movePan = function (event, g, context) { - context.dragEndX = utils.dragGetX_(event, context); - context.dragEndY = utils.dragGetY_(event, context); - - var minDate = context.initialLeftmostDate - (context.dragEndX - context.dragStartX) * context.xUnitsPerPixel; - if (context.boundedDates) { - minDate = Math.max(minDate, context.boundedDates[0]); - } - var maxDate = minDate + context.dateRange; - if (context.boundedDates) { - if (maxDate > context.boundedDates[1]) { - // Adjust minDate, and recompute maxDate. - minDate = minDate - (maxDate - context.boundedDates[1]); - maxDate = minDate + context.dateRange; - } - } - - if (g.getOptionForAxis("logscale", "x")) { - g.dateWindow_ = [Math.pow(utils.LOG_SCALE, minDate), Math.pow(utils.LOG_SCALE, maxDate)]; - } else { - g.dateWindow_ = [minDate, maxDate]; - } - - // y-axis scaling is automatic unless this is a full 2D pan. - if (context.is2DPan) { - - var pixelsDragged = context.dragEndY - context.dragStartY; - - // Adjust each axis appropriately. - for (var i = 0; i < g.axes_.length; i++) { - var axis = g.axes_[i]; - var axis_data = context.axes[i]; - var unitsDragged = pixelsDragged * axis_data.unitsPerPixel; - - var boundedValue = context.boundedValues ? context.boundedValues[i] : null; - - // In log scale, maxValue and minValue are the logs of those values. - var maxValue = axis_data.initialTopValue + unitsDragged; - if (boundedValue) { - maxValue = Math.min(maxValue, boundedValue[1]); - } - var minValue = maxValue - axis_data.dragValueRange; - if (boundedValue) { - if (minValue < boundedValue[0]) { - // Adjust maxValue, and recompute minValue. - maxValue = maxValue - (minValue - boundedValue[0]); - minValue = maxValue - axis_data.dragValueRange; - } - } - if (g.attributes_.getForAxis("logscale", i)) { - axis.valueRange = [Math.pow(utils.LOG_SCALE, minValue), Math.pow(utils.LOG_SCALE, maxValue)]; - } else { - axis.valueRange = [minValue, maxValue]; - } - } - } - - g.drawGraph_(false); -}; - -/** - * Called in response to an interaction model operation that - * responds to an event that ends panning. - * - * It's used in the default callback for "mouseup" operations. - * Custom interaction model builders can use it to provide the default - * panning behavior. - * - * @param {Event} event the event object which led to the endPan call. - * @param {Dygraph} g The dygraph on which to act. - * @param {Object} context The dragging context object (with - * dragStartX/dragStartY/etc. properties). This function modifies the - * context. - */ -DygraphInteraction.endPan = DygraphInteraction.maybeTreatMouseOpAsClick; - -/** - * Called in response to an interaction model operation that - * responds to an event that starts zooming. - * - * It's used in the default callback for "mousedown" operations. - * Custom interaction model builders can use it to provide the default - * zooming behavior. - * - * @param {Event} event the event object which led to the startZoom call. - * @param {Dygraph} g The dygraph on which to act. - * @param {Object} context The dragging context object (with - * dragStartX/dragStartY/etc. properties). This function modifies the - * context. - */ -DygraphInteraction.startZoom = function (event, g, context) { - context.isZooming = true; - context.zoomMoved = false; -}; - -/** - * Called in response to an interaction model operation that - * responds to an event that defines zoom boundaries. - * - * It's used in the default callback for "mousemove" operations. - * Custom interaction model builders can use it to provide the default - * zooming behavior. - * - * @param {Event} event the event object which led to the moveZoom call. - * @param {Dygraph} g The dygraph on which to act. - * @param {Object} context The dragging context object (with - * dragStartX/dragStartY/etc. properties). This function modifies the - * context. - */ -DygraphInteraction.moveZoom = function (event, g, context) { - context.zoomMoved = true; - context.dragEndX = utils.dragGetX_(event, context); - context.dragEndY = utils.dragGetY_(event, context); - - var xDelta = Math.abs(context.dragStartX - context.dragEndX); - var yDelta = Math.abs(context.dragStartY - context.dragEndY); - - // drag direction threshold for y axis is twice as large as x axis - context.dragDirection = xDelta < yDelta / 2 ? utils.VERTICAL : utils.HORIZONTAL; - - g.drawZoomRect_(context.dragDirection, context.dragStartX, context.dragEndX, context.dragStartY, context.dragEndY, context.prevDragDirection, context.prevEndX, context.prevEndY); - - context.prevEndX = context.dragEndX; - context.prevEndY = context.dragEndY; - context.prevDragDirection = context.dragDirection; -}; - -/** - * TODO(danvk): move this logic into dygraph.js - * @param {Dygraph} g - * @param {Event} event - * @param {Object} context - */ -DygraphInteraction.treatMouseOpAsClick = function (g, event, context) { - var clickCallback = g.getFunctionOption('clickCallback'); - var pointClickCallback = g.getFunctionOption('pointClickCallback'); - - var selectedPoint = null; - - // Find out if the click occurs on a point. - var closestIdx = -1; - var closestDistance = Number.MAX_VALUE; - - // check if the click was on a particular point. - for (var i = 0; i < g.selPoints_.length; i++) { - var p = g.selPoints_[i]; - var distance = Math.pow(p.canvasx - context.dragEndX, 2) + Math.pow(p.canvasy - context.dragEndY, 2); - if (!isNaN(distance) && (closestIdx == -1 || distance < closestDistance)) { - closestDistance = distance; - closestIdx = i; - } - } - - // Allow any click within two pixels of the dot. - var radius = g.getNumericOption('highlightCircleSize') + 2; - if (closestDistance <= radius * radius) { - selectedPoint = g.selPoints_[closestIdx]; - } - - if (selectedPoint) { - var e = { - cancelable: true, - point: selectedPoint, - canvasx: context.dragEndX, - canvasy: context.dragEndY - }; - var defaultPrevented = g.cascadeEvents_('pointClick', e); - if (defaultPrevented) { - // Note: this also prevents click / clickCallback from firing. - return; - } - if (pointClickCallback) { - pointClickCallback.call(g, event, selectedPoint); - } - } - - var e = { - cancelable: true, - xval: g.lastx_, // closest point by x value - pts: g.selPoints_, - canvasx: context.dragEndX, - canvasy: context.dragEndY - }; - if (!g.cascadeEvents_('click', e)) { - if (clickCallback) { - // TODO(danvk): pass along more info about the points, e.g. 'x' - clickCallback.call(g, event, g.lastx_, g.selPoints_); - } - } -}; - -/** - * Called in response to an interaction model operation that - * responds to an event that performs a zoom based on previously defined - * bounds.. - * - * It's used in the default callback for "mouseup" operations. - * Custom interaction model builders can use it to provide the default - * zooming behavior. - * - * @param {Event} event the event object which led to the endZoom call. - * @param {Dygraph} g The dygraph on which to end the zoom. - * @param {Object} context The dragging context object (with - * dragStartX/dragStartY/etc. properties). This function modifies the - * context. - */ -DygraphInteraction.endZoom = function (event, g, context) { - g.clearZoomRect_(); - context.isZooming = false; - DygraphInteraction.maybeTreatMouseOpAsClick(event, g, context); - - // The zoom rectangle is visibly clipped to the plot area, so its behavior - // should be as well. - // See http://code.google.com/p/dygraphs/issues/detail?id=280 - var plotArea = g.getArea(); - if (context.regionWidth >= 10 && context.dragDirection == utils.HORIZONTAL) { - var left = Math.min(context.dragStartX, context.dragEndX), - right = Math.max(context.dragStartX, context.dragEndX); - left = Math.max(left, plotArea.x); - right = Math.min(right, plotArea.x + plotArea.w); - if (left < right) { - g.doZoomX_(left, right); - } - context.cancelNextDblclick = true; - } else if (context.regionHeight >= 10 && context.dragDirection == utils.VERTICAL) { - var top = Math.min(context.dragStartY, context.dragEndY), - bottom = Math.max(context.dragStartY, context.dragEndY); - top = Math.max(top, plotArea.y); - bottom = Math.min(bottom, plotArea.y + plotArea.h); - if (top < bottom) { - g.doZoomY_(top, bottom); - } - context.cancelNextDblclick = true; - } - context.dragStartX = null; - context.dragStartY = null; -}; - -/** - * @private - */ -DygraphInteraction.startTouch = function (event, g, context) { - event.preventDefault(); // touch browsers are all nice. - if (event.touches.length > 1) { - // If the user ever puts two fingers down, it's not a double tap. - context.startTimeForDoubleTapMs = null; - } - - var touches = []; - for (var i = 0; i < event.touches.length; i++) { - var t = event.touches[i]; - // we dispense with 'dragGetX_' because all touchBrowsers support pageX - touches.push({ - pageX: t.pageX, - pageY: t.pageY, - dataX: g.toDataXCoord(t.pageX), - dataY: g.toDataYCoord(t.pageY) - // identifier: t.identifier - }); - } - context.initialTouches = touches; - - if (touches.length == 1) { - // This is just a swipe. - context.initialPinchCenter = touches[0]; - context.touchDirections = { x: true, y: true }; - } else if (touches.length >= 2) { - // It's become a pinch! - // In case there are 3+ touches, we ignore all but the "first" two. - - // only screen coordinates can be averaged (data coords could be log scale). - context.initialPinchCenter = { - pageX: 0.5 * (touches[0].pageX + touches[1].pageX), - pageY: 0.5 * (touches[0].pageY + touches[1].pageY), - - // TODO(danvk): remove - dataX: 0.5 * (touches[0].dataX + touches[1].dataX), - dataY: 0.5 * (touches[0].dataY + touches[1].dataY) - }; - - // Make pinches in a 45-degree swath around either axis 1-dimensional zooms. - var initialAngle = 180 / Math.PI * Math.atan2(context.initialPinchCenter.pageY - touches[0].pageY, touches[0].pageX - context.initialPinchCenter.pageX); - - // use symmetry to get it into the first quadrant. - initialAngle = Math.abs(initialAngle); - if (initialAngle > 90) initialAngle = 90 - initialAngle; - - context.touchDirections = { - x: initialAngle < 90 - 45 / 2, - y: initialAngle > 45 / 2 - }; - } - - // save the full x & y ranges. - context.initialRange = { - x: g.xAxisRange(), - y: g.yAxisRange() - }; -}; - -/** - * @private - */ -DygraphInteraction.moveTouch = function (event, g, context) { - // If the tap moves, then it's definitely not part of a double-tap. - context.startTimeForDoubleTapMs = null; - - var i, - touches = []; - for (i = 0; i < event.touches.length; i++) { - var t = event.touches[i]; - touches.push({ - pageX: t.pageX, - pageY: t.pageY - }); - } - var initialTouches = context.initialTouches; - - var c_now; - - // old and new centers. - var c_init = context.initialPinchCenter; - if (touches.length == 1) { - c_now = touches[0]; - } else { - c_now = { - pageX: 0.5 * (touches[0].pageX + touches[1].pageX), - pageY: 0.5 * (touches[0].pageY + touches[1].pageY) - }; - } - - // this is the "swipe" component - // we toss it out for now, but could use it in the future. - var swipe = { - pageX: c_now.pageX - c_init.pageX, - pageY: c_now.pageY - c_init.pageY - }; - var dataWidth = context.initialRange.x[1] - context.initialRange.x[0]; - var dataHeight = context.initialRange.y[0] - context.initialRange.y[1]; - swipe.dataX = swipe.pageX / g.plotter_.area.w * dataWidth; - swipe.dataY = swipe.pageY / g.plotter_.area.h * dataHeight; - var xScale, yScale; - - // The residual bits are usually split into scale & rotate bits, but we split - // them into x-scale and y-scale bits. - if (touches.length == 1) { - xScale = 1.0; - yScale = 1.0; - } else if (touches.length >= 2) { - var initHalfWidth = initialTouches[1].pageX - c_init.pageX; - xScale = (touches[1].pageX - c_now.pageX) / initHalfWidth; - - var initHalfHeight = initialTouches[1].pageY - c_init.pageY; - yScale = (touches[1].pageY - c_now.pageY) / initHalfHeight; - } - - // Clip scaling to [1/8, 8] to prevent too much blowup. - xScale = Math.min(8, Math.max(0.125, xScale)); - yScale = Math.min(8, Math.max(0.125, yScale)); - - var didZoom = false; - if (context.touchDirections.x) { - g.dateWindow_ = [c_init.dataX - swipe.dataX + (context.initialRange.x[0] - c_init.dataX) / xScale, c_init.dataX - swipe.dataX + (context.initialRange.x[1] - c_init.dataX) / xScale]; - didZoom = true; - } - - if (context.touchDirections.y) { - for (i = 0; i < 1 /*g.axes_.length*/; i++) { - var axis = g.axes_[i]; - var logscale = g.attributes_.getForAxis("logscale", i); - if (logscale) { - // TODO(danvk): implement - } else { - axis.valueRange = [c_init.dataY - swipe.dataY + (context.initialRange.y[0] - c_init.dataY) / yScale, c_init.dataY - swipe.dataY + (context.initialRange.y[1] - c_init.dataY) / yScale]; - didZoom = true; - } - } - } - - g.drawGraph_(false); - - // We only call zoomCallback on zooms, not pans, to mirror desktop behavior. - if (didZoom && touches.length > 1 && g.getFunctionOption('zoomCallback')) { - var viewWindow = g.xAxisRange(); - g.getFunctionOption("zoomCallback").call(g, viewWindow[0], viewWindow[1], g.yAxisRanges()); - } -}; - -/** - * @private - */ -DygraphInteraction.endTouch = function (event, g, context) { - if (event.touches.length !== 0) { - // this is effectively a "reset" - DygraphInteraction.startTouch(event, g, context); - } else if (event.changedTouches.length == 1) { - // Could be part of a "double tap" - // The heuristic here is that it's a double-tap if the two touchend events - // occur within 500ms and within a 50x50 pixel box. - var now = new Date().getTime(); - var t = event.changedTouches[0]; - if (context.startTimeForDoubleTapMs && now - context.startTimeForDoubleTapMs < 500 && context.doubleTapX && Math.abs(context.doubleTapX - t.screenX) < 50 && context.doubleTapY && Math.abs(context.doubleTapY - t.screenY) < 50) { - g.resetZoom(); - } else { - context.startTimeForDoubleTapMs = now; - context.doubleTapX = t.screenX; - context.doubleTapY = t.screenY; - } - } -}; - -// Determine the distance from x to [left, right]. -var distanceFromInterval = function distanceFromInterval(x, left, right) { - if (x < left) { - return left - x; - } else if (x > right) { - return x - right; - } else { - return 0; - } -}; - -/** - * Returns the number of pixels by which the event happens from the nearest - * edge of the chart. For events in the interior of the chart, this returns zero. - */ -var distanceFromChart = function distanceFromChart(event, g) { - var chartPos = utils.findPos(g.canvas_); - var box = { - left: chartPos.x, - right: chartPos.x + g.canvas_.offsetWidth, - top: chartPos.y, - bottom: chartPos.y + g.canvas_.offsetHeight - }; - - var pt = { - x: utils.pageX(event), - y: utils.pageY(event) - }; - - var dx = distanceFromInterval(pt.x, box.left, box.right), - dy = distanceFromInterval(pt.y, box.top, box.bottom); - return Math.max(dx, dy); -}; - -/** - * Default interation model for dygraphs. You can refer to specific elements of - * this when constructing your own interaction model, e.g.: - * g.updateOptions( { - * interactionModel: { - * mousedown: DygraphInteraction.defaultInteractionModel.mousedown - * } - * } ); - */ -DygraphInteraction.defaultModel = { - // Track the beginning of drag events - mousedown: function mousedown(event, g, context) { - // Right-click should not initiate a zoom. - if (event.button && event.button == 2) return; - - context.initializeMouseDown(event, g, context); - - if (event.altKey || event.shiftKey) { - DygraphInteraction.startPan(event, g, context); - } else { - DygraphInteraction.startZoom(event, g, context); - } - - // Note: we register mousemove/mouseup on document to allow some leeway for - // events to move outside of the chart. Interaction model events get - // registered on the canvas, which is too small to allow this. - var mousemove = function mousemove(event) { - if (context.isZooming) { - // When the mouse moves >200px from the chart edge, cancel the zoom. - var d = distanceFromChart(event, g); - if (d < DRAG_EDGE_MARGIN) { - DygraphInteraction.moveZoom(event, g, context); - } else { - if (context.dragEndX !== null) { - context.dragEndX = null; - context.dragEndY = null; - g.clearZoomRect_(); - } - } - } else if (context.isPanning) { - DygraphInteraction.movePan(event, g, context); - } - }; - var mouseup = function mouseup(event) { - if (context.isZooming) { - if (context.dragEndX !== null) { - DygraphInteraction.endZoom(event, g, context); - } else { - DygraphInteraction.maybeTreatMouseOpAsClick(event, g, context); - } - } else if (context.isPanning) { - DygraphInteraction.endPan(event, g, context); - } - - utils.removeEvent(document, 'mousemove', mousemove); - utils.removeEvent(document, 'mouseup', mouseup); - context.destroy(); - }; - - g.addAndTrackEvent(document, 'mousemove', mousemove); - g.addAndTrackEvent(document, 'mouseup', mouseup); - }, - willDestroyContextMyself: true, - - touchstart: function touchstart(event, g, context) { - DygraphInteraction.startTouch(event, g, context); - }, - touchmove: function touchmove(event, g, context) { - DygraphInteraction.moveTouch(event, g, context); - }, - touchend: function touchend(event, g, context) { - DygraphInteraction.endTouch(event, g, context); - }, - - // Disable zooming out if panning. - dblclick: function dblclick(event, g, context) { - if (context.cancelNextDblclick) { - context.cancelNextDblclick = false; - return; - } - - // Give plugins a chance to grab this event. - var e = { - canvasx: context.dragEndX, - canvasy: context.dragEndY, - cancelable: true - }; - if (g.cascadeEvents_('dblclick', e)) { - return; - } - - if (event.altKey || event.shiftKey) { - return; - } - g.resetZoom(); - } -}; - -/* -Dygraph.DEFAULT_ATTRS.interactionModel = DygraphInteraction.defaultModel; - -// old ways of accessing these methods/properties -Dygraph.defaultInteractionModel = DygraphInteraction.defaultModel; -Dygraph.endZoom = DygraphInteraction.endZoom; -Dygraph.moveZoom = DygraphInteraction.moveZoom; -Dygraph.startZoom = DygraphInteraction.startZoom; -Dygraph.endPan = DygraphInteraction.endPan; -Dygraph.movePan = DygraphInteraction.movePan; -Dygraph.startPan = DygraphInteraction.startPan; -*/ - -DygraphInteraction.nonInteractiveModel_ = { - mousedown: function mousedown(event, g, context) { - context.initializeMouseDown(event, g, context); - }, - mouseup: DygraphInteraction.maybeTreatMouseOpAsClick -}; - -// Default interaction model when using the range selector. -DygraphInteraction.dragIsPanInteractionModel = { - mousedown: function mousedown(event, g, context) { - context.initializeMouseDown(event, g, context); - DygraphInteraction.startPan(event, g, context); - }, - mousemove: function mousemove(event, g, context) { - if (context.isPanning) { - DygraphInteraction.movePan(event, g, context); - } - }, - mouseup: function mouseup(event, g, context) { - if (context.isPanning) { - DygraphInteraction.endPan(event, g, context); - } - } -}; - -exports["default"] = DygraphInteraction; -module.exports = exports["default"]; - -},{"./dygraph-utils":17}],13:[function(require,module,exports){ -/** - * @license - * Copyright 2011 Dan Vanderkam (danvdk@gmail.com) - * MIT-licensed (http://opensource.org/licenses/MIT) - */ - -/** - * @fileoverview Based on PlotKitLayout, but modified to meet the needs of - * dygraphs. - */ - -/*global Dygraph:false */ -"use strict"; - -Object.defineProperty(exports, '__esModule', { - value: true -}); - -function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } } - -var _dygraphUtils = require('./dygraph-utils'); - -var utils = _interopRequireWildcard(_dygraphUtils); - -/** - * Creates a new DygraphLayout object. - * - * This class contains all the data to be charted. - * It uses data coordinates, but also records the chart range (in data - * coordinates) and hence is able to calculate percentage positions ('In this - * view, Point A lies 25% down the x-axis.') - * - * Two things that it does not do are: - * 1. Record pixel coordinates for anything. - * 2. (oddly) determine anything about the layout of chart elements. - * - * The naming is a vestige of Dygraph's original PlotKit roots. - * - * @constructor - */ -var DygraphLayout = function DygraphLayout(dygraph) { - this.dygraph_ = dygraph; - /** - * Array of points for each series. - * - * [series index][row index in series] = |Point| structure, - * where series index refers to visible series only, and the - * point index is for the reduced set of points for the current - * zoom region (including one point just outside the window). - * All points in the same row index share the same X value. - * - * @type {Array.>} - */ - this.points = []; - this.setNames = []; - this.annotations = []; - this.yAxes_ = null; - - // TODO(danvk): it's odd that xTicks_ and yTicks_ are inputs, but xticks and - // yticks are outputs. Clean this up. - this.xTicks_ = null; - this.yTicks_ = null; -}; - -/** - * Add points for a single series. - * - * @param {string} setname Name of the series. - * @param {Array.} set_xy Points for the series. - */ -DygraphLayout.prototype.addDataset = function (setname, set_xy) { - this.points.push(set_xy); - this.setNames.push(setname); -}; - -/** - * Returns the box which the chart should be drawn in. This is the canvas's - * box, less space needed for the axis and chart labels. - * - * @return {{x: number, y: number, w: number, h: number}} - */ -DygraphLayout.prototype.getPlotArea = function () { - return this.area_; -}; - -// Compute the box which the chart should be drawn in. This is the canvas's -// box, less space needed for axis, chart labels, and other plug-ins. -// NOTE: This should only be called by Dygraph.predraw_(). -DygraphLayout.prototype.computePlotArea = function () { - var area = { - // TODO(danvk): per-axis setting. - x: 0, - y: 0 - }; - - area.w = this.dygraph_.width_ - area.x - this.dygraph_.getOption('rightGap'); - area.h = this.dygraph_.height_; - - // Let plugins reserve space. - var e = { - chart_div: this.dygraph_.graphDiv, - reserveSpaceLeft: function reserveSpaceLeft(px) { - var r = { - x: area.x, - y: area.y, - w: px, - h: area.h - }; - area.x += px; - area.w -= px; - return r; - }, - reserveSpaceRight: function reserveSpaceRight(px) { - var r = { - x: area.x + area.w - px, - y: area.y, - w: px, - h: area.h - }; - area.w -= px; - return r; - }, - reserveSpaceTop: function reserveSpaceTop(px) { - var r = { - x: area.x, - y: area.y, - w: area.w, - h: px - }; - area.y += px; - area.h -= px; - return r; - }, - reserveSpaceBottom: function reserveSpaceBottom(px) { - var r = { - x: area.x, - y: area.y + area.h - px, - w: area.w, - h: px - }; - area.h -= px; - return r; - }, - chartRect: function chartRect() { - return { x: area.x, y: area.y, w: area.w, h: area.h }; - } - }; - this.dygraph_.cascadeEvents_('layout', e); - - this.area_ = area; -}; - -DygraphLayout.prototype.setAnnotations = function (ann) { - // The Dygraph object's annotations aren't parsed. We parse them here and - // save a copy. If there is no parser, then the user must be using raw format. - this.annotations = []; - var parse = this.dygraph_.getOption('xValueParser') || function (x) { - return x; - }; - for (var i = 0; i < ann.length; i++) { - var a = {}; - if (!ann[i].xval && ann[i].x === undefined) { - console.error("Annotations must have an 'x' property"); - return; - } - if (ann[i].icon && !(ann[i].hasOwnProperty('width') && ann[i].hasOwnProperty('height'))) { - console.error("Must set width and height when setting " + "annotation.icon property"); - return; - } - utils.update(a, ann[i]); - if (!a.xval) a.xval = parse(a.x); - this.annotations.push(a); - } -}; - -DygraphLayout.prototype.setXTicks = function (xTicks) { - this.xTicks_ = xTicks; -}; - -// TODO(danvk): add this to the Dygraph object's API or move it into Layout. -DygraphLayout.prototype.setYAxes = function (yAxes) { - this.yAxes_ = yAxes; -}; - -DygraphLayout.prototype.evaluate = function () { - this._xAxis = {}; - this._evaluateLimits(); - this._evaluateLineCharts(); - this._evaluateLineTicks(); - this._evaluateAnnotations(); -}; - -DygraphLayout.prototype._evaluateLimits = function () { - var xlimits = this.dygraph_.xAxisRange(); - this._xAxis.minval = xlimits[0]; - this._xAxis.maxval = xlimits[1]; - var xrange = xlimits[1] - xlimits[0]; - this._xAxis.scale = xrange !== 0 ? 1 / xrange : 1.0; - - if (this.dygraph_.getOptionForAxis("logscale", 'x')) { - this._xAxis.xlogrange = utils.log10(this._xAxis.maxval) - utils.log10(this._xAxis.minval); - this._xAxis.xlogscale = this._xAxis.xlogrange !== 0 ? 1.0 / this._xAxis.xlogrange : 1.0; - } - for (var i = 0; i < this.yAxes_.length; i++) { - var axis = this.yAxes_[i]; - axis.minyval = axis.computedValueRange[0]; - axis.maxyval = axis.computedValueRange[1]; - axis.yrange = axis.maxyval - axis.minyval; - axis.yscale = axis.yrange !== 0 ? 1.0 / axis.yrange : 1.0; - - if (this.dygraph_.getOption("logscale")) { - axis.ylogrange = utils.log10(axis.maxyval) - utils.log10(axis.minyval); - axis.ylogscale = axis.ylogrange !== 0 ? 1.0 / axis.ylogrange : 1.0; - if (!isFinite(axis.ylogrange) || isNaN(axis.ylogrange)) { - console.error('axis ' + i + ' of graph at ' + axis.g + ' can\'t be displayed in log scale for range [' + axis.minyval + ' - ' + axis.maxyval + ']'); - } - } - } -}; - -DygraphLayout.calcXNormal_ = function (value, xAxis, logscale) { - if (logscale) { - return (utils.log10(value) - utils.log10(xAxis.minval)) * xAxis.xlogscale; - } else { - return (value - xAxis.minval) * xAxis.scale; - } -}; - -/** - * @param {DygraphAxisType} axis - * @param {number} value - * @param {boolean} logscale - * @return {number} - */ -DygraphLayout.calcYNormal_ = function (axis, value, logscale) { - if (logscale) { - var x = 1.0 - (utils.log10(value) - utils.log10(axis.minyval)) * axis.ylogscale; - return isFinite(x) ? x : NaN; // shim for v8 issue; see pull request 276 - } else { - return 1.0 - (value - axis.minyval) * axis.yscale; - } -}; - -DygraphLayout.prototype._evaluateLineCharts = function () { - var isStacked = this.dygraph_.getOption("stackedGraph"); - var isLogscaleForX = this.dygraph_.getOptionForAxis("logscale", 'x'); - - for (var setIdx = 0; setIdx < this.points.length; setIdx++) { - var points = this.points[setIdx]; - var setName = this.setNames[setIdx]; - var connectSeparated = this.dygraph_.getOption('connectSeparatedPoints', setName); - var axis = this.dygraph_.axisPropertiesForSeries(setName); - // TODO (konigsberg): use optionsForAxis instead. - var logscale = this.dygraph_.attributes_.getForSeries("logscale", setName); - - for (var j = 0; j < points.length; j++) { - var point = points[j]; - - // Range from 0-1 where 0 represents left and 1 represents right. - point.x = DygraphLayout.calcXNormal_(point.xval, this._xAxis, isLogscaleForX); - // Range from 0-1 where 0 represents top and 1 represents bottom - var yval = point.yval; - if (isStacked) { - point.y_stacked = DygraphLayout.calcYNormal_(axis, point.yval_stacked, logscale); - if (yval !== null && !isNaN(yval)) { - yval = point.yval_stacked; - } - } - if (yval === null) { - yval = NaN; - if (!connectSeparated) { - point.yval = NaN; - } - } - point.y = DygraphLayout.calcYNormal_(axis, yval, logscale); - } - - this.dygraph_.dataHandler_.onLineEvaluated(points, axis, logscale); - } -}; - -DygraphLayout.prototype._evaluateLineTicks = function () { - var i, tick, label, pos, v, has_tick; - this.xticks = []; - for (i = 0; i < this.xTicks_.length; i++) { - tick = this.xTicks_[i]; - label = tick.label; - has_tick = !('label_v' in tick); - v = has_tick ? tick.v : tick.label_v; - pos = this.dygraph_.toPercentXCoord(v); - if (pos >= 0.0 && pos < 1.0) { - this.xticks.push({ pos: pos, label: label, has_tick: has_tick }); - } - } - - this.yticks = []; - for (i = 0; i < this.yAxes_.length; i++) { - var axis = this.yAxes_[i]; - for (var j = 0; j < axis.ticks.length; j++) { - tick = axis.ticks[j]; - label = tick.label; - has_tick = !('label_v' in tick); - v = has_tick ? tick.v : tick.label_v; - pos = this.dygraph_.toPercentYCoord(v, i); - if (pos > 0.0 && pos <= 1.0) { - this.yticks.push({ axis: i, pos: pos, label: label, has_tick: has_tick }); - } - } - } -}; - -DygraphLayout.prototype._evaluateAnnotations = function () { - // Add the annotations to the point to which they belong. - // Make a map from (setName, xval) to annotation for quick lookups. - var i; - var annotations = {}; - for (i = 0; i < this.annotations.length; i++) { - var a = this.annotations[i]; - annotations[a.xval + "," + a.series] = a; - } - - this.annotated_points = []; - - // Exit the function early if there are no annotations. - if (!this.annotations || !this.annotations.length) { - return; - } - - // TODO(antrob): loop through annotations not points. - for (var setIdx = 0; setIdx < this.points.length; setIdx++) { - var points = this.points[setIdx]; - for (i = 0; i < points.length; i++) { - var p = points[i]; - var k = p.xval + "," + p.name; - if (k in annotations) { - p.annotation = annotations[k]; - this.annotated_points.push(p); - } - } - } -}; - -/** - * Convenience function to remove all the data sets from a graph - */ -DygraphLayout.prototype.removeAllDatasets = function () { - delete this.points; - delete this.setNames; - delete this.setPointsLengths; - delete this.setPointsOffsets; - this.points = []; - this.setNames = []; - this.setPointsLengths = []; - this.setPointsOffsets = []; -}; - -exports['default'] = DygraphLayout; -module.exports = exports['default']; - -},{"./dygraph-utils":17}],14:[function(require,module,exports){ -(function (process){ -/** - * @license - * Copyright 2011 Dan Vanderkam (danvdk@gmail.com) - * MIT-licensed (http://opensource.org/licenses/MIT) - */ - -"use strict"; - -Object.defineProperty(exports, '__esModule', { - value: true -}); -var OPTIONS_REFERENCE = null; - -// For "production" code, this gets removed by uglifyjs. -if (typeof process !== 'undefined') { - if ("development" != 'production') { - - // NOTE: in addition to parsing as JS, this snippet is expected to be valid - // JSON. This assumption cannot be checked in JS, but it will be checked when - // documentation is generated by the generate-documentation.py script. For the - // most part, this just means that you should always use double quotes. - OPTIONS_REFERENCE = // - { - "xValueParser": { - "default": "parseFloat() or Date.parse()*", - "labels": ["CSV parsing"], - "type": "function(str) -> number", - "description": "A function which parses x-values (i.e. the dependent series). Must return a number, even when the values are dates. In this case, millis since epoch are used. This is used primarily for parsing CSV data. *=Dygraphs is slightly more accepting in the dates which it will parse. See code for details." - }, - "stackedGraph": { - "default": "false", - "labels": ["Data Line display"], - "type": "boolean", - "description": "If set, stack series on top of one another rather than drawing them independently. The first series specified in the input data will wind up on top of the chart and the last will be on bottom. NaN values are drawn as white areas without a line on top, see stackedGraphNaNFill for details." - }, - "stackedGraphNaNFill": { - "default": "all", - "labels": ["Data Line display"], - "type": "string", - "description": "Controls handling of NaN values inside a stacked graph. NaN values are interpolated/extended for stacking purposes, but the actual point value remains NaN in the legend display. Valid option values are \"all\" (interpolate internally, repeat leftmost and rightmost value as needed), \"inside\" (interpolate internally only, use zero outside leftmost and rightmost value), and \"none\" (treat NaN as zero everywhere)." - }, - "pointSize": { - "default": "1", - "labels": ["Data Line display"], - "type": "integer", - "description": "The size of the dot to draw on each point in pixels (see drawPoints). A dot is always drawn when a point is \"isolated\", i.e. there is a missing point on either side of it. This also controls the size of those dots." - }, - "drawPoints": { - "default": "false", - "labels": ["Data Line display"], - "type": "boolean", - "description": "Draw a small dot at each point, in addition to a line going through the point. This makes the individual data points easier to see, but can increase visual clutter in the chart. The small dot can be replaced with a custom rendering by supplying a drawPointCallback." - }, - "drawGapEdgePoints": { - "default": "false", - "labels": ["Data Line display"], - "type": "boolean", - "description": "Draw points at the edges of gaps in the data. This improves visibility of small data segments or other data irregularities." - }, - "plotterFinishedCallback": { - "default": "null", - "labels": [], - "type": "function(canvasContext)", - "parameters": [["canvasContext", "the canvas that was drawn to"]], - "description": "Jane Street extension" - }, - "drawPointCallback": { - "default": "null", - "labels": ["Data Line display"], - "type": "function(g, seriesName, canvasContext, cx, cy, color, pointSize)", - "parameters": [["g", "the reference graph"], ["seriesName", "the name of the series"], ["canvasContext", "the canvas to draw on"], ["cx", "center x coordinate"], ["cy", "center y coordinate"], ["color", "series color"], ["pointSize", "the radius of the image."], ["idx", "the row-index of the point in the data."]], - "description": "Draw a custom item when drawPoints is enabled. Default is a small dot matching the series color. This method should constrain drawing to within pointSize pixels from (cx, cy). Also see drawHighlightPointCallback" - }, - "height": { - "default": "320", - "labels": ["Overall display"], - "type": "integer", - "description": "Height, in pixels, of the chart. If the container div has been explicitly sized, this will be ignored." - }, - "zoomCallback": { - "default": "null", - "labels": ["Callbacks"], - "type": "function(minDate, maxDate, yRanges)", - "parameters": [["minDate", "milliseconds since epoch"], ["maxDate", "milliseconds since epoch."], ["yRanges", "is an array of [bottom, top] pairs, one for each y-axis."]], - "description": "A function to call when the zoom window is changed (either by zooming in or out). When animatedZooms is set, zoomCallback is called once at the end of the transition (it will not be called for intermediate frames)." - }, - "pointClickCallback": { - "snippet": "function(e, point){
  alert(point);
}", - "default": "null", - "labels": ["Callbacks", "Interactive Elements"], - "type": "function(e, point)", - "parameters": [["e", "the event object for the click"], ["point", "the point that was clicked See Point properties for details"]], - "description": "A function to call when a data point is clicked. and the point that was clicked." - }, - "color": { - "default": "(see description)", - "labels": ["Data Series Colors"], - "type": "string", - "example": "red", - "description": "A per-series color definition. Used in conjunction with, and overrides, the colors option." - }, - "colors": { - "default": "(see description)", - "labels": ["Data Series Colors"], - "type": "array", - "example": "['red', '#00FF00']", - "description": "List of colors for the data series. These can be of the form \"#AABBCC\" or \"rgb(255,100,200)\" or \"yellow\", etc. If not specified, equally-spaced points around a color wheel are used. Overridden by the 'color' option." - }, - "connectSeparatedPoints": { - "default": "false", - "labels": ["Data Line display"], - "type": "boolean", - "description": "Usually, when Dygraphs encounters a missing value in a data series, it interprets this as a gap and draws it as such. If, instead, the missing values represents an x-value for which only a different series has data, then you'll want to connect the dots by setting this to true. To explicitly include a gap with this option set, use a value of NaN." - }, - "highlightCallback": { - "default": "null", - "labels": ["Callbacks"], - "type": "function(event, x, points, row, seriesName)", - "description": "When set, this callback gets called every time a new point is highlighted.", - "parameters": [["event", "the JavaScript mousemove event"], ["x", "the x-coordinate of the highlighted points"], ["points", "an array of highlighted points: [ {name: 'series', yval: y-value}, … ]"], ["row", "integer index of the highlighted row in the data table, starting from 0"], ["seriesName", "name of the highlighted series, only present if highlightSeriesOpts is set."]] - }, - "drawHighlightPointCallback": { - "default": "null", - "labels": ["Data Line display"], - "type": "function(g, seriesName, canvasContext, cx, cy, color, pointSize)", - "parameters": [["g", "the reference graph"], ["seriesName", "the name of the series"], ["canvasContext", "the canvas to draw on"], ["cx", "center x coordinate"], ["cy", "center y coordinate"], ["color", "series color"], ["pointSize", "the radius of the image."], ["idx", "the row-index of the point in the data."]], - "description": "Draw a custom item when a point is highlighted. Default is a small dot matching the series color. This method should constrain drawing to within pointSize pixels from (cx, cy) Also see drawPointCallback" - }, - "highlightSeriesOpts": { - "default": "null", - "labels": ["Interactive Elements"], - "type": "Object", - "description": "When set, the options from this object are applied to the timeseries closest to the mouse pointer for interactive highlighting. See also 'highlightCallback'. Example: highlightSeriesOpts: { strokeWidth: 3 }." - }, - "highlightSeriesBackgroundAlpha": { - "default": "0.5", - "labels": ["Interactive Elements"], - "type": "float", - "description": "Fade the background while highlighting series. 1=fully visible background (disable fading), 0=hiddden background (show highlighted series only)." - }, - "highlightSeriesBackgroundColor": { - "default": "rgb(255, 255, 255)", - "labels": ["Interactive Elements"], - "type": "string", - "description": "Sets the background color used to fade out the series in conjunction with 'highlightSeriesBackgroundAlpha'." - }, - "includeZero": { - "default": "false", - "labels": ["Axis display"], - "type": "boolean", - "description": "Usually, dygraphs will use the range of the data plus some padding to set the range of the y-axis. If this option is set, the y-axis will always include zero, typically as the lowest value. This can be used to avoid exaggerating the variance in the data" - }, - "rollPeriod": { - "default": "1", - "labels": ["Error Bars", "Rolling Averages"], - "type": "integer >= 1", - "description": "Number of days over which to average data. Discussed extensively above." - }, - "unhighlightCallback": { - "default": "null", - "labels": ["Callbacks"], - "type": "function(event)", - "parameters": [["event", "the mouse event"]], - "description": "When set, this callback gets called every time the user stops highlighting any point by mousing out of the graph." - }, - "axisTickSize": { - "default": "3.0", - "labels": ["Axis display"], - "type": "number", - "description": "The size of the line to display next to each tick mark on x- or y-axes." - }, - "labelsSeparateLines": { - "default": "false", - "labels": ["Legend"], - "type": "boolean", - "description": "Put <br/> between lines in the label string. Often used in conjunction with labelsDiv." - }, - "valueFormatter": { - "default": "Depends on the type of your data.", - "labels": ["Legend", "Value display/formatting"], - "type": "function(num or millis, opts, seriesName, dygraph, row, col)", - "description": "Function to provide a custom display format for the values displayed on mouseover. This does not affect the values that appear on tick marks next to the axes. To format those, see axisLabelFormatter. This is usually set on a per-axis basis. .", - "parameters": [["num_or_millis", "The value to be formatted. This is always a number. For date axes, it's millis since epoch. You can call new Date(millis) to get a Date object."], ["opts", "This is a function you can call to access various options (e.g. opts('labelsKMB')). It returns per-axis values for the option when available."], ["seriesName", "The name of the series from which the point came, e.g. 'X', 'Y', 'A', etc."], ["dygraph", "The dygraph object for which the formatting is being done"], ["row", "The row of the data from which this point comes. g.getValue(row, 0) will return the x-value for this point."], ["col", "The column of the data from which this point comes. g.getValue(row, col) will return the original y-value for this point. This can be used to get the full confidence interval for the point, or access un-rolled values for the point."]] - }, - "annotationMouseOverHandler": { - "default": "null", - "labels": ["Annotations"], - "type": "function(annotation, point, dygraph, event)", - "description": "If provided, this function is called whenever the user mouses over an annotation." - }, - "annotationMouseOutHandler": { - "default": "null", - "labels": ["Annotations"], - "type": "function(annotation, point, dygraph, event)", - "parameters": [["annotation", "the annotation left"], ["point", "the point associated with the annotation"], ["dygraph", "the reference graph"], ["event", "the mouse event"]], - "description": "If provided, this function is called whenever the user mouses out of an annotation." - }, - "annotationClickHandler": { - "default": "null", - "labels": ["Annotations"], - "type": "function(annotation, point, dygraph, event)", - "parameters": [["annotation", "the annotation left"], ["point", "the point associated with the annotation"], ["dygraph", "the reference graph"], ["event", "the mouse event"]], - "description": "If provided, this function is called whenever the user clicks on an annotation." - }, - "annotationDblClickHandler": { - "default": "null", - "labels": ["Annotations"], - "type": "function(annotation, point, dygraph, event)", - "parameters": [["annotation", "the annotation left"], ["point", "the point associated with the annotation"], ["dygraph", "the reference graph"], ["event", "the mouse event"]], - "description": "If provided, this function is called whenever the user double-clicks on an annotation." - }, - "drawCallback": { - "default": "null", - "labels": ["Callbacks"], - "type": "function(dygraph, is_initial)", - "parameters": [["dygraph", "The graph being drawn"], ["is_initial", "True if this is the initial draw, false for subsequent draws."]], - "description": "When set, this callback gets called every time the dygraph is drawn. This includes the initial draw, after zooming and repeatedly while panning." - }, - "labelsKMG2": { - "default": "false", - "labels": ["Value display/formatting"], - "type": "boolean", - "description": "Show k/M/G for kilo/Mega/Giga on y-axis. This is different than labelsKMB in that it uses base 2, not 10." - }, - "delimiter": { - "default": ",", - "labels": ["CSV parsing"], - "type": "string", - "description": "The delimiter to look for when separating fields of a CSV file. Setting this to a tab is not usually necessary, since tab-delimited data is auto-detected." - }, - "axisLabelFontSize": { - "default": "14", - "labels": ["Axis display"], - "type": "integer", - "description": "Size of the font (in pixels) to use in the axis labels, both x- and y-axis." - }, - "underlayCallback": { - "default": "null", - "labels": ["Callbacks"], - "type": "function(context, area, dygraph)", - "parameters": [["context", "the canvas drawing context on which to draw"], ["area", "An object with {x,y,w,h} properties describing the drawing area."], ["dygraph", "the reference graph"]], - "description": "When set, this callback gets called before the chart is drawn. It details on how to use this." - }, - "width": { - "default": "480", - "labels": ["Overall display"], - "type": "integer", - "description": "Width, in pixels, of the chart. If the container div has been explicitly sized, this will be ignored." - }, - "pixelRatio": { - "default": "(devicePixelRatio / context.backingStoreRatio)", - "labels": ["Overall display"], - "type": "float", - "description": "Overrides the pixel ratio scaling factor for the canvas's 2d context. Ordinarily, this is set to the devicePixelRatio / (context.backingStoreRatio || 1), so on mobile devices, where the devicePixelRatio can be somewhere around 3, performance can be improved by overriding this value to something less precise, like 1, at the expense of resolution." - }, - "interactionModel": { - "default": "...", - "labels": ["Interactive Elements"], - "type": "Object", - "description": "TODO(konigsberg): document this" - }, - "ticker": { - "default": "Dygraph.dateTicker or Dygraph.numericTicks", - "labels": ["Axis display"], - "type": "function(min, max, pixels, opts, dygraph, vals) -> [{v: ..., label: ...}, ...]", - "parameters": [["min", ""], ["max", ""], ["pixels", ""], ["opts", ""], ["dygraph", "the reference graph"], ["vals", ""]], - "description": "This lets you specify an arbitrary function to generate tick marks on an axis. The tick marks are an array of (value, label) pairs. The built-in functions go to great lengths to choose good tick marks so, if you set this option, you'll most likely want to call one of them and modify the result. See dygraph-tickers.js for an extensive discussion. This is set on a per-axis basis." - }, - "xAxisHeight": { - "default": "(null)", - "labels": ["Axis display"], - "type": "integer", - "description": "Height, in pixels, of the x-axis. If not set explicitly, this is computed based on axisLabelFontSize and axisTickSize." - }, - "showLabelsOnHighlight": { - "default": "true", - "labels": ["Interactive Elements", "Legend"], - "type": "boolean", - "description": "Whether to show the legend upon mouseover." - }, - "axis": { - "default": "(none)", - "labels": ["Axis display"], - "type": "string", - "description": "Set to either 'y1' or 'y2' to assign a series to a y-axis (primary or secondary). Must be set per-series." - }, - "pixelsPerLabel": { - "default": "70 (x-axis) or 30 (y-axes)", - "labels": ["Axis display", "Grid"], - "type": "integer", - "description": "Number of pixels to require between each x- and y-label. Larger values will yield a sparser axis with fewer ticks. This is set on a per-axis basis." - }, - "labelsDiv": { - "default": "null", - "labels": ["Legend"], - "type": "DOM element or string", - "example": "document.getElementById('foo')or'foo'", - "description": "Show data labels in an external div, rather than on the graph. This value can either be a div element or a div id." - }, - "fractions": { - "default": "false", - "labels": ["CSV parsing", "Error Bars"], - "type": "boolean", - "description": "When set, attempt to parse each cell in the CSV file as \"a/b\", where a and b are integers. The ratio will be plotted. This allows computation of Wilson confidence intervals (see below)." - }, - "logscale": { - "default": "false", - "labels": ["Axis display"], - "type": "boolean", - "description": "When set for the y-axis or x-axis, the graph shows that axis in log scale. Any values less than or equal to zero are not displayed. Showing log scale with ranges that go below zero will result in an unviewable graph.\n\n Not compatible with showZero. connectSeparatedPoints is ignored. This is ignored for date-based x-axes." - }, - "strokeWidth": { - "default": "1.0", - "labels": ["Data Line display"], - "type": "float", - "example": "0.5, 2.0", - "description": "The width of the lines connecting data points. This can be used to increase the contrast or some graphs." - }, - "strokePattern": { - "default": "null", - "labels": ["Data Line display"], - "type": "array", - "example": "[10, 2, 5, 2]", - "description": "A custom pattern array where the even index is a draw and odd is a space in pixels. If null then it draws a solid line. The array should have a even length as any odd lengthed array could be expressed as a smaller even length array. This is used to create dashed lines." - }, - "strokeBorderWidth": { - "default": "null", - "labels": ["Data Line display"], - "type": "float", - "example": "1.0", - "description": "Draw a border around graph lines to make crossing lines more easily distinguishable. Useful for graphs with many lines." - }, - "strokeBorderColor": { - "default": "white", - "labels": ["Data Line display"], - "type": "string", - "example": "red, #ccffdd", - "description": "Color for the line border used if strokeBorderWidth is set." - }, - "wilsonInterval": { - "default": "true", - "labels": ["Error Bars"], - "type": "boolean", - "description": "Use in conjunction with the \"fractions\" option. Instead of plotting +/- N standard deviations, dygraphs will compute a Wilson confidence interval and plot that. This has more reasonable behavior for ratios close to 0 or 1." - }, - "fillGraph": { - "default": "false", - "labels": ["Data Line display"], - "type": "boolean", - "description": "Should the area underneath the graph be filled? This option is not compatible with error bars. This may be set on a per-series basis." - }, - "highlightCircleSize": { - "default": "3", - "labels": ["Interactive Elements"], - "type": "integer", - "description": "The size in pixels of the dot drawn over highlighted points." - }, - "gridLineColor": { - "default": "rgb(128,128,128)", - "labels": ["Grid"], - "type": "red, blue", - "description": "The color of the gridlines. This may be set on a per-axis basis to define each axis' grid separately." - }, - "gridLinePattern": { - "default": "null", - "labels": ["Grid"], - "type": "array", - "example": "[10, 2, 5, 2]", - "description": "A custom pattern array where the even index is a draw and odd is a space in pixels. If null then it draws a solid line. The array should have a even length as any odd lengthed array could be expressed as a smaller even length array. This is used to create dashed gridlines." - }, - "visibility": { - "default": "[true, true, ...]", - "labels": ["Data Line display"], - "type": "Array of booleans", - "description": "Which series should initially be visible? Once the Dygraph has been constructed, you can access and modify the visibility of each series using the visibility and setVisibility methods." - }, - "valueRange": { - "default": "Full range of the input is shown", - "labels": ["Axis display"], - "type": "Array of two numbers", - "example": "[10, 110]", - "description": "Explicitly set the vertical range of the graph to [low, high]. This may be set on a per-axis basis to define each y-axis separately. If either limit is unspecified, it will be calculated automatically (e.g. [null, 30] to automatically calculate just the lower bound)" - }, - "colorSaturation": { - "default": "1.0", - "labels": ["Data Series Colors"], - "type": "float (0.0 - 1.0)", - "description": "If colors is not specified, saturation of the automatically-generated data series colors." - }, - "hideOverlayOnMouseOut": { - "default": "true", - "labels": ["Interactive Elements", "Legend"], - "type": "boolean", - "description": "Whether to hide the legend when the mouse leaves the chart area." - }, - "legend": { - "default": "onmouseover", - "labels": ["Legend"], - "type": "string", - "description": "When to display the legend. By default, it only appears when a user mouses over the chart. Set it to \"always\" to always display a legend of some sort. When set to \"follow\", legend follows highlighted points." - }, - "legendFormatter": { - "default": "null", - "labels": ["Legend"], - "type": "function(data): string", - "params": [["data", "An object containing information about the selection (or lack of a selection). This includes formatted values and series information. See here for sample values."]], - "description": "Set this to supply a custom formatter for the legend. See this comment and the legendFormatter demo for usage." - }, - "labelsShowZeroValues": { - "default": "true", - "labels": ["Legend"], - "type": "boolean", - "description": "Show zero value labels in the labelsDiv." - }, - "stepPlot": { - "default": "false", - "labels": ["Data Line display"], - "type": "boolean", - "description": "When set, display the graph as a step plot instead of a line plot. This option may either be set for the whole graph or for single series." - }, - "labelsUTC": { - "default": "false", - "labels": ["Value display/formatting", "Axis display"], - "type": "boolean", - "description": "Show date/time labels according to UTC (instead of local time)." - }, - "labelsKMB": { - "default": "false", - "labels": ["Value display/formatting"], - "type": "boolean", - "description": "Show K/M/B for thousands/millions/billions on y-axis." - }, - "rightGap": { - "default": "5", - "labels": ["Overall display"], - "type": "integer", - "description": "Number of pixels to leave blank at the right edge of the Dygraph. This makes it easier to highlight the right-most data point." - }, - "drawAxesAtZero": { - "default": "false", - "labels": ["Axis display"], - "type": "boolean", - "description": "When set, draw the X axis at the Y=0 position and the Y axis at the X=0 position if those positions are inside the graph's visible area. Otherwise, draw the axes at the bottom or left graph edge as usual." - }, - "xRangePad": { - "default": "0", - "labels": ["Axis display"], - "type": "float", - "description": "Add the specified amount of extra space (in pixels) around the X-axis value range to ensure points at the edges remain visible." - }, - "yRangePad": { - "default": "null", - "labels": ["Axis display"], - "type": "float", - "description": "If set, add the specified amount of extra space (in pixels) around the Y-axis value range to ensure points at the edges remain visible. If unset, use the traditional Y padding algorithm." - }, - "axisLabelFormatter": { - "default": "Depends on the data type", - "labels": ["Axis display"], - "type": "function(number or Date, granularity, opts, dygraph)", - "parameters": [["number or date", "Either a number (for a numeric axis) or a Date object (for a date axis)"], ["granularity", "specifies how fine-grained the axis is. For date axes, this is a reference to the time granularity enumeration, defined in dygraph-tickers.js, e.g. Dygraph.WEEKLY."], ["opts", "a function which provides access to various options on the dygraph, e.g. opts('labelsKMB')."], ["dygraph", "the referenced graph"]], - "description": "Function to call to format the tick values that appear along an axis. This is usually set on a per-axis basis." - }, - "clickCallback": { - "snippet": "function(e, date_millis){
  alert(new Date(date_millis));
}", - "default": "null", - "labels": ["Callbacks"], - "type": "function(e, x, points)", - "parameters": [["e", "The event object for the click"], ["x", "The x value that was clicked (for dates, this is milliseconds since epoch)"], ["points", "The closest points along that date. See Point properties for details."]], - "description": "A function to call when the canvas is clicked." - }, - "labels": { - "default": "[\"X\", \"Y1\", \"Y2\", ...]*", - "labels": ["Legend"], - "type": "array", - "description": "A name for each data series, including the independent (X) series. For CSV files and DataTable objections, this is determined by context. For raw data, this must be specified. If it is not, default values are supplied and a warning is logged." - }, - "dateWindow": { - "default": "Full range of the input is shown", - "labels": ["Axis display"], - "type": "Array of two numbers", - "example": "[
  Date.parse('2006-01-01'),
  (new Date()).valueOf()
]", - "description": "Initially zoom in on a section of the graph. Is of the form [earliest, latest], where earliest/latest are milliseconds since epoch. If the data for the x-axis is numeric, the values in dateWindow must also be numbers." - }, - "showRoller": { - "default": "false", - "labels": ["Interactive Elements", "Rolling Averages"], - "type": "boolean", - "description": "If the rolling average period text box should be shown." - }, - "sigma": { - "default": "2.0", - "labels": ["Error Bars"], - "type": "float", - "description": "When errorBars is set, shade this many standard deviations above/below each point." - }, - "customBars": { - "default": "false", - "labels": ["CSV parsing", "Error Bars"], - "type": "boolean", - "description": "When set, parse each CSV cell as \"low;middle;high\". Error bars will be drawn for each point between low and high, with the series itself going through middle." - }, - "colorValue": { - "default": "1.0", - "labels": ["Data Series Colors"], - "type": "float (0.0 - 1.0)", - "description": "If colors is not specified, value of the data series colors, as in hue/saturation/value. (0.0-1.0, default 0.5)" - }, - "errorBars": { - "default": "false", - "labels": ["CSV parsing", "Error Bars"], - "type": "boolean", - "description": "Does the data contain standard deviations? Setting this to true alters the input format (see above)." - }, - "displayAnnotations": { - "default": "false", - "labels": ["Annotations"], - "type": "boolean", - "description": "Only applies when Dygraphs is used as a GViz chart. Causes string columns following a data series to be interpreted as annotations on points in that series. This is the same format used by Google's AnnotatedTimeLine chart." - }, - "panEdgeFraction": { - "default": "null", - "labels": ["Axis display", "Interactive Elements"], - "type": "float", - "description": "A value representing the farthest a graph may be panned, in percent of the display. For example, a value of 0.1 means that the graph can only be panned 10% passed the edges of the displayed values. null means no bounds." - }, - "title": { - "labels": ["Chart labels"], - "type": "string", - "default": "null", - "description": "Text to display above the chart. You can supply any HTML for this value, not just text. If you wish to style it using CSS, use the 'dygraph-label' or 'dygraph-title' classes." - }, - "titleHeight": { - "default": "18", - "labels": ["Chart labels"], - "type": "integer", - "description": "Height of the chart title, in pixels. This also controls the default font size of the title. If you style the title on your own, this controls how much space is set aside above the chart for the title's div." - }, - "xlabel": { - "labels": ["Chart labels"], - "type": "string", - "default": "null", - "description": "Text to display below the chart's x-axis. You can supply any HTML for this value, not just text. If you wish to style it using CSS, use the 'dygraph-label' or 'dygraph-xlabel' classes." - }, - "xLabelHeight": { - "labels": ["Chart labels"], - "type": "integer", - "default": "18", - "description": "Height of the x-axis label, in pixels. This also controls the default font size of the x-axis label. If you style the label on your own, this controls how much space is set aside below the chart for the x-axis label's div." - }, - "ylabel": { - "labels": ["Chart labels"], - "type": "string", - "default": "null", - "description": "Text to display to the left of the chart's y-axis. You can supply any HTML for this value, not just text. If you wish to style it using CSS, use the 'dygraph-label' or 'dygraph-ylabel' classes. The text will be rotated 90 degrees by default, so CSS rules may behave in unintuitive ways. No additional space is set aside for a y-axis label. If you need more space, increase the width of the y-axis tick labels using the yAxisLabelWidth option. If you need a wider div for the y-axis label, either style it that way with CSS (but remember that it's rotated, so width is controlled by the 'height' property) or set the yLabelWidth option." - }, - "y2label": { - "labels": ["Chart labels"], - "type": "string", - "default": "null", - "description": "Text to display to the right of the chart's secondary y-axis. This label is only displayed if a secondary y-axis is present. See this test for an example of how to do this. The comments for the 'ylabel' option generally apply here as well. This label gets a 'dygraph-y2label' instead of a 'dygraph-ylabel' class." - }, - "yLabelWidth": { - "labels": ["Chart labels"], - "type": "integer", - "default": "18", - "description": "Width of the div which contains the y-axis label. Since the y-axis label appears rotated 90 degrees, this actually affects the height of its div." - }, - "drawGrid": { - "default": "true for x and y, false for y2", - "labels": ["Grid"], - "type": "boolean", - "description": "Whether to display gridlines in the chart. This may be set on a per-axis basis to define the visibility of each axis' grid separately." - }, - "independentTicks": { - "default": "true for y, false for y2", - "labels": ["Axis display", "Grid"], - "type": "boolean", - "description": "Only valid for y and y2, has no effect on x: This option defines whether the y axes should align their ticks or if they should be independent. Possible combinations: 1.) y=true, y2=false (default): y is the primary axis and the y2 ticks are aligned to the the ones of y. (only 1 grid) 2.) y=false, y2=true: y2 is the primary axis and the y ticks are aligned to the the ones of y2. (only 1 grid) 3.) y=true, y2=true: Both axis are independent and have their own ticks. (2 grids) 4.) y=false, y2=false: Invalid configuration causes an error." - }, - "drawAxis": { - "default": "true for x and y, false for y2", - "labels": ["Axis display"], - "type": "boolean", - "description": "Whether to draw the specified axis. This may be set on a per-axis basis to define the visibility of each axis separately. Setting this to false also prevents axis ticks from being drawn and reclaims the space for the chart grid/lines." - }, - "gridLineWidth": { - "default": "0.3", - "labels": ["Grid"], - "type": "float", - "description": "Thickness (in pixels) of the gridlines drawn under the chart. The vertical/horizontal gridlines can be turned off entirely by using the drawGrid option. This may be set on a per-axis basis to define each axis' grid separately." - }, - "axisLineWidth": { - "default": "0.3", - "labels": ["Axis display"], - "type": "float", - "description": "Thickness (in pixels) of the x- and y-axis lines." - }, - "axisLineColor": { - "default": "black", - "labels": ["Axis display"], - "type": "string", - "description": "Color of the x- and y-axis lines. Accepts any value which the HTML canvas strokeStyle attribute understands, e.g. 'black' or 'rgb(0, 100, 255)'." - }, - "fillAlpha": { - "default": "0.15", - "labels": ["Error Bars", "Data Series Colors"], - "type": "float (0.0 - 1.0)", - "description": "Error bars (or custom bars) for each series are drawn in the same color as the series, but with partial transparency. This sets the transparency. A value of 0.0 means that the error bars will not be drawn, whereas a value of 1.0 means that the error bars will be as dark as the line for the series itself. This can be used to produce chart lines whose thickness varies at each point." - }, - "axisLabelWidth": { - "default": "50 (y-axis), 60 (x-axis)", - "labels": ["Axis display", "Chart labels"], - "type": "integer", - "description": "Width (in pixels) of the containing divs for x- and y-axis labels. For the y-axis, this also controls the width of the y-axis. Note that for the x-axis, this is independent from pixelsPerLabel, which controls the spacing between labels." - }, - "sigFigs": { - "default": "null", - "labels": ["Value display/formatting"], - "type": "integer", - "description": "By default, dygraphs displays numbers with a fixed number of digits after the decimal point. If you'd prefer to have a fixed number of significant figures, set this option to that number of sig figs. A value of 2, for instance, would cause 1 to be display as 1.0 and 1234 to be displayed as 1.23e+3." - }, - "digitsAfterDecimal": { - "default": "2", - "labels": ["Value display/formatting"], - "type": "integer", - "description": "Unless it's run in scientific mode (see the sigFigs option), dygraphs displays numbers with digitsAfterDecimal digits after the decimal point. Trailing zeros are not displayed, so with a value of 2 you'll get '0', '0.1', '0.12', '123.45' but not '123.456' (it will be rounded to '123.46'). Numbers with absolute value less than 0.1^digitsAfterDecimal (i.e. those which would show up as '0.00') will be displayed in scientific notation." - }, - "maxNumberWidth": { - "default": "6", - "labels": ["Value display/formatting"], - "type": "integer", - "description": "When displaying numbers in normal (not scientific) mode, large numbers will be displayed with many trailing zeros (e.g. 100000000 instead of 1e9). This can lead to unwieldy y-axis labels. If there are more than maxNumberWidth digits to the left of the decimal in a number, dygraphs will switch to scientific notation, even when not operating in scientific mode. If you'd like to see all those digits, set this to something large, like 20 or 30." - }, - "file": { - "default": "(set when constructed)", - "labels": ["Data"], - "type": "string (URL of CSV or CSV), GViz DataTable or 2D Array", - "description": "Sets the data being displayed in the chart. This can only be set when calling updateOptions; it cannot be set from the constructor. For a full description of valid data formats, see the Data Formats page." - }, - "timingName": { - "default": "null", - "labels": ["Debugging", "Deprecated"], - "type": "string", - "description": "Set this option to log timing information. The value of the option will be logged along with the timimg, so that you can distinguish multiple dygraphs on the same page." - }, - "showRangeSelector": { - "default": "false", - "labels": ["Range Selector"], - "type": "boolean", - "description": "Show or hide the range selector widget." - }, - "rangeSelectorHeight": { - "default": "40", - "labels": ["Range Selector"], - "type": "integer", - "description": "Height, in pixels, of the range selector widget. This option can only be specified at Dygraph creation time." - }, - "rangeSelectorPlotStrokeColor": { - "default": "#808FAB", - "labels": ["Range Selector"], - "type": "string", - "description": "The range selector mini plot stroke color. This can be of the form \"#AABBCC\" or \"rgb(255,100,200)\" or \"yellow\". You can also specify null or \"\" to turn off stroke." - }, - "rangeSelectorPlotFillColor": { - "default": "#A7B1C4", - "labels": ["Range Selector"], - "type": "string", - "description": "The range selector mini plot fill color. This can be of the form \"#AABBCC\" or \"rgb(255,100,200)\" or \"yellow\". You can also specify null or \"\" to turn off fill." - }, - "rangeSelectorPlotFillGradientColor": { - "default": "white", - "labels": ["Range Selector"], - "type": "string", - "description": "The top color for the range selector mini plot fill color gradient. This can be of the form \"#AABBCC\" or \"rgb(255,100,200)\" or \"rgba(255,100,200,42)\" or \"yellow\". You can also specify null or \"\" to disable the gradient and fill with one single color." - }, - "rangeSelectorBackgroundStrokeColor": { - "default": "gray", - "labels": ["Range Selector"], - "type": "string", - "description": "The color of the lines below and on both sides of the range selector mini plot. This can be of the form \"#AABBCC\" or \"rgb(255,100,200)\" or \"yellow\"." - }, - "rangeSelectorBackgroundLineWidth": { - "default": "1", - "labels": ["Range Selector"], - "type": "float", - "description": "The width of the lines below and on both sides of the range selector mini plot." - }, - "rangeSelectorPlotLineWidth": { - "default": "1.5", - "labels": ["Range Selector"], - "type": "float", - "description": "The width of the range selector mini plot line." - }, - "rangeSelectorForegroundStrokeColor": { - "default": "black", - "labels": ["Range Selector"], - "type": "string", - "description": "The color of the lines in the interactive layer of the range selector. This can be of the form \"#AABBCC\" or \"rgb(255,100,200)\" or \"yellow\"." - }, - "rangeSelectorForegroundLineWidth": { - "default": "1", - "labels": ["Range Selector"], - "type": "float", - "description": "The width the lines in the interactive layer of the range selector." - }, - "rangeSelectorAlpha": { - "default": "0.6", - "labels": ["Range Selector"], - "type": "float (0.0 - 1.0)", - "description": "The transparency of the veil that is drawn over the unselected portions of the range selector mini plot. A value of 0 represents full transparency and the unselected portions of the mini plot will appear as normal. A value of 1 represents full opacity and the unselected portions of the mini plot will be hidden." - }, - "showInRangeSelector": { - "default": "null", - "labels": ["Range Selector"], - "type": "boolean", - "description": "Mark this series for inclusion in the range selector. The mini plot curve will be an average of all such series. If this is not specified for any series, the default behavior is to average all the visible series. Setting it for one series will result in that series being charted alone in the range selector. Once it's set for a single series, it needs to be set for all series which should be included (regardless of visibility)." - }, - "animatedZooms": { - "default": "false", - "labels": ["Interactive Elements"], - "type": "boolean", - "description": "Set this option to animate the transition between zoom windows. Applies to programmatic and interactive zooms. Note that if you also set a drawCallback, it will be called several times on each zoom. If you set a zoomCallback, it will only be called after the animation is complete." - }, - "plotter": { - "default": "[DygraphCanvasRenderer.Plotters.fillPlotter, DygraphCanvasRenderer.Plotters.errorPlotter, DygraphCanvasRenderer.Plotters.linePlotter]", - "labels": ["Data Line display"], - "type": "array or function", - "description": "A function (or array of functions) which plot each data series on the chart. TODO(danvk): more details! May be set per-series." - }, - "axes": { - "default": "null", - "labels": ["Configuration"], - "type": "Object", - "description": "Defines per-axis options. Valid keys are 'x', 'y' and 'y2'. Only some options may be set on a per-axis basis. If an option may be set in this way, it will be noted on this page. See also documentation on per-series and per-axis options." - }, - "series": { - "default": "null", - "labels": ["Series"], - "type": "Object", - "description": "Defines per-series options. Its keys match the y-axis label names, and the values are dictionaries themselves that contain options specific to that series." - }, - "plugins": { - "default": "[]", - "labels": ["Configuration"], - "type": "Array", - "description": "Defines per-graph plugins. Useful for per-graph customization" - }, - "dataHandler": { - "default": "(depends on data)", - "labels": ["Data"], - "type": "Dygraph.DataHandler", - "description": "Custom DataHandler. This is an advanced customization. See http://bit.ly/151E7Aq." - } - }; //
- // NOTE: in addition to parsing as JS, this snippet is expected to be valid - // JSON. This assumption cannot be checked in JS, but it will be checked when - // documentation is generated by the generate-documentation.py script. For the - // most part, this just means that you should always use double quotes. - - // Do a quick sanity check on the options reference. - var warn = function warn(msg) { - if (window.console) window.console.warn(msg); - }; - var flds = ['type', 'default', 'description']; - var valid_cats = ['Annotations', 'Axis display', 'Chart labels', 'CSV parsing', 'Callbacks', 'Data', 'Data Line display', 'Data Series Colors', 'Error Bars', 'Grid', 'Interactive Elements', 'Range Selector', 'Legend', 'Overall display', 'Rolling Averages', 'Series', 'Value display/formatting', 'Zooming', 'Debugging', 'Configuration', 'Deprecated']; - var i; - var cats = {}; - for (i = 0; i < valid_cats.length; i++) cats[valid_cats[i]] = true; - - for (var k in OPTIONS_REFERENCE) { - if (!OPTIONS_REFERENCE.hasOwnProperty(k)) continue; - var op = OPTIONS_REFERENCE[k]; - for (i = 0; i < flds.length; i++) { - if (!op.hasOwnProperty(flds[i])) { - warn('Option ' + k + ' missing "' + flds[i] + '" property'); - } else if (typeof op[flds[i]] != 'string') { - warn(k + '.' + flds[i] + ' must be of type string'); - } - } - var labels = op.labels; - if (typeof labels !== 'object') { - warn('Option "' + k + '" is missing a "labels": [...] option'); - } else { - for (i = 0; i < labels.length; i++) { - if (!cats.hasOwnProperty(labels[i])) { - warn('Option "' + k + '" has label "' + labels[i] + '", which is invalid.'); - } - } - } - } - } -} - -exports['default'] = OPTIONS_REFERENCE; -module.exports = exports['default']; - -}).call(this,require('_process')) - -},{"_process":1}],15:[function(require,module,exports){ -(function (process){ -/** - * @license - * Copyright 2011 Dan Vanderkam (danvdk@gmail.com) - * MIT-licensed (http://opensource.org/licenses/MIT) - */ - -/** - * @fileoverview DygraphOptions is responsible for parsing and returning - * information about options. - */ - -// TODO: remove this jshint directive & fix the warnings. -/*jshint sub:true */ -"use strict"; - -Object.defineProperty(exports, '__esModule', { - value: true -}); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } - -function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } } - -var _dygraphUtils = require('./dygraph-utils'); - -var utils = _interopRequireWildcard(_dygraphUtils); - -var _dygraphDefaultAttrs = require('./dygraph-default-attrs'); - -var _dygraphDefaultAttrs2 = _interopRequireDefault(_dygraphDefaultAttrs); - -var _dygraphOptionsReference = require('./dygraph-options-reference'); - -var _dygraphOptionsReference2 = _interopRequireDefault(_dygraphOptionsReference); - -/* - * Interesting member variables: (REMOVING THIS LIST AS I CLOSURIZE) - * global_ - global attributes (common among all graphs, AIUI) - * user - attributes set by the user - * series_ - { seriesName -> { idx, yAxis, options }} - */ - -/** - * This parses attributes into an object that can be easily queried. - * - * It doesn't necessarily mean that all options are available, specifically - * if labels are not yet available, since those drive details of the per-series - * and per-axis options. - * - * @param {Dygraph} dygraph The chart to which these options belong. - * @constructor - */ -var DygraphOptions = function DygraphOptions(dygraph) { - /** - * The dygraph. - * @type {!Dygraph} - */ - this.dygraph_ = dygraph; - - /** - * Array of axis index to { series : [ series names ] , options : { axis-specific options. } - * @type {Array.<{series : Array., options : Object}>} @private - */ - this.yAxes_ = []; - - /** - * Contains x-axis specific options, which are stored in the options key. - * This matches the yAxes_ object structure (by being a dictionary with an - * options element) allowing for shared code. - * @type {options: Object} @private - */ - this.xAxis_ = {}; - this.series_ = {}; - - // Once these two objects are initialized, you can call get(); - this.global_ = this.dygraph_.attrs_; - this.user_ = this.dygraph_.user_attrs_ || {}; - - /** - * A list of series in columnar order. - * @type {Array.} - */ - this.labels_ = []; - - this.highlightSeries_ = this.get("highlightSeriesOpts") || {}; - this.reparseSeries(); -}; - -/** - * Not optimal, but does the trick when you're only using two axes. - * If we move to more axes, this can just become a function. - * - * @type {Object.} - * @private - */ -DygraphOptions.AXIS_STRING_MAPPINGS_ = { - 'y': 0, - 'Y': 0, - 'y1': 0, - 'Y1': 0, - 'y2': 1, - 'Y2': 1 -}; - -/** - * @param {string|number} axis - * @private - */ -DygraphOptions.axisToIndex_ = function (axis) { - if (typeof axis == "string") { - if (DygraphOptions.AXIS_STRING_MAPPINGS_.hasOwnProperty(axis)) { - return DygraphOptions.AXIS_STRING_MAPPINGS_[axis]; - } - throw "Unknown axis : " + axis; - } - if (typeof axis == "number") { - if (axis === 0 || axis === 1) { - return axis; - } - throw "Dygraphs only supports two y-axes, indexed from 0-1."; - } - if (axis) { - throw "Unknown axis : " + axis; - } - // No axis specification means axis 0. - return 0; -}; - -/** - * Reparses options that are all related to series. This typically occurs when - * options are either updated, or source data has been made available. - * - * TODO(konigsberg): The method name is kind of weak; fix. - */ -DygraphOptions.prototype.reparseSeries = function () { - var labels = this.get("labels"); - if (!labels) { - return; // -- can't do more for now, will parse after getting the labels. - } - - this.labels_ = labels.slice(1); - - this.yAxes_ = [{ series: [], options: {} }]; // Always one axis at least. - this.xAxis_ = { options: {} }; - this.series_ = {}; - - // Series are specified in the series element: - // - // { - // labels: [ "X", "foo", "bar" ], - // pointSize: 3, - // series : { - // foo : {}, // options for foo - // bar : {} // options for bar - // } - // } - // - // So, if series is found, it's expected to contain per-series data, otherwise set a - // default. - var seriesDict = this.user_.series || {}; - for (var idx = 0; idx < this.labels_.length; idx++) { - var seriesName = this.labels_[idx]; - var optionsForSeries = seriesDict[seriesName] || {}; - var yAxis = DygraphOptions.axisToIndex_(optionsForSeries["axis"]); - - this.series_[seriesName] = { - idx: idx, - yAxis: yAxis, - options: optionsForSeries }; - - if (!this.yAxes_[yAxis]) { - this.yAxes_[yAxis] = { series: [seriesName], options: {} }; - } else { - this.yAxes_[yAxis].series.push(seriesName); - } - } - - var axis_opts = this.user_["axes"] || {}; - utils.update(this.yAxes_[0].options, axis_opts["y"] || {}); - if (this.yAxes_.length > 1) { - utils.update(this.yAxes_[1].options, axis_opts["y2"] || {}); - } - utils.update(this.xAxis_.options, axis_opts["x"] || {}); - - // For "production" code, this gets removed by uglifyjs. - if (typeof process !== 'undefined') { - if ("development" != 'production') { - this.validateOptions_(); - } - } -}; - -/** - * Get a global value. - * - * @param {string} name the name of the option. - */ -DygraphOptions.prototype.get = function (name) { - var result = this.getGlobalUser_(name); - if (result !== null) { - return result; - } - return this.getGlobalDefault_(name); -}; - -DygraphOptions.prototype.getGlobalUser_ = function (name) { - if (this.user_.hasOwnProperty(name)) { - return this.user_[name]; - } - return null; -}; - -DygraphOptions.prototype.getGlobalDefault_ = function (name) { - if (this.global_.hasOwnProperty(name)) { - return this.global_[name]; - } - if (_dygraphDefaultAttrs2['default'].hasOwnProperty(name)) { - return _dygraphDefaultAttrs2['default'][name]; - } - return null; -}; - -/** - * Get a value for a specific axis. If there is no specific value for the axis, - * the global value is returned. - * - * @param {string} name the name of the option. - * @param {string|number} axis the axis to search. Can be the string representation - * ("y", "y2") or the axis number (0, 1). - */ -DygraphOptions.prototype.getForAxis = function (name, axis) { - var axisIdx; - var axisString; - - // Since axis can be a number or a string, straighten everything out here. - if (typeof axis == 'number') { - axisIdx = axis; - axisString = axisIdx === 0 ? "y" : "y2"; - } else { - if (axis == "y1") { - axis = "y"; - } // Standardize on 'y'. Is this bad? I think so. - if (axis == "y") { - axisIdx = 0; - } else if (axis == "y2") { - axisIdx = 1; - } else if (axis == "x") { - axisIdx = -1; // simply a placeholder for below. - } else { - throw "Unknown axis " + axis; - } - axisString = axis; - } - - var userAxis = axisIdx == -1 ? this.xAxis_ : this.yAxes_[axisIdx]; - - // Search the user-specified axis option first. - if (userAxis) { - // This condition could be removed if we always set up this.yAxes_ for y2. - var axisOptions = userAxis.options; - if (axisOptions.hasOwnProperty(name)) { - return axisOptions[name]; - } - } - - // User-specified global options second. - // But, hack, ignore globally-specified 'logscale' for 'x' axis declaration. - if (!(axis === 'x' && name === 'logscale')) { - var result = this.getGlobalUser_(name); - if (result !== null) { - return result; - } - } - // Default axis options third. - var defaultAxisOptions = _dygraphDefaultAttrs2['default'].axes[axisString]; - if (defaultAxisOptions.hasOwnProperty(name)) { - return defaultAxisOptions[name]; - } - - // Default global options last. - return this.getGlobalDefault_(name); -}; - -/** - * Get a value for a specific series. If there is no specific value for the series, - * the value for the axis is returned (and afterwards, the global value.) - * - * @param {string} name the name of the option. - * @param {string} series the series to search. - */ -DygraphOptions.prototype.getForSeries = function (name, series) { - // Honors indexes as series. - if (series === this.dygraph_.getHighlightSeries()) { - if (this.highlightSeries_.hasOwnProperty(name)) { - return this.highlightSeries_[name]; - } - } - - if (!this.series_.hasOwnProperty(series)) { - throw "Unknown series: " + series; - } - - var seriesObj = this.series_[series]; - var seriesOptions = seriesObj["options"]; - if (seriesOptions.hasOwnProperty(name)) { - return seriesOptions[name]; - } - - return this.getForAxis(name, seriesObj["yAxis"]); -}; - -/** - * Returns the number of y-axes on the chart. - * @return {number} the number of axes. - */ -DygraphOptions.prototype.numAxes = function () { - return this.yAxes_.length; -}; - -/** - * Return the y-axis for a given series, specified by name. - */ -DygraphOptions.prototype.axisForSeries = function (series) { - return this.series_[series].yAxis; -}; - -/** - * Returns the options for the specified axis. - */ -// TODO(konigsberg): this is y-axis specific. Support the x axis. -DygraphOptions.prototype.axisOptions = function (yAxis) { - return this.yAxes_[yAxis].options; -}; - -/** - * Return the series associated with an axis. - */ -DygraphOptions.prototype.seriesForAxis = function (yAxis) { - return this.yAxes_[yAxis].series; -}; - -/** - * Return the list of all series, in their columnar order. - */ -DygraphOptions.prototype.seriesNames = function () { - return this.labels_; -}; - -// For "production" code, this gets removed by uglifyjs. -if (typeof process !== 'undefined') { - if ("development" != 'production') { - - /** - * Validate all options. - * This requires OPTIONS_REFERENCE, which is only available in debug builds. - * @private - */ - DygraphOptions.prototype.validateOptions_ = function () { - if (typeof _dygraphOptionsReference2['default'] === 'undefined') { - throw 'Called validateOptions_ in prod build.'; - } - - var that = this; - var validateOption = function validateOption(optionName) { - if (!_dygraphOptionsReference2['default'][optionName]) { - that.warnInvalidOption_(optionName); - } - }; - - var optionsDicts = [this.xAxis_.options, this.yAxes_[0].options, this.yAxes_[1] && this.yAxes_[1].options, this.global_, this.user_, this.highlightSeries_]; - var names = this.seriesNames(); - for (var i = 0; i < names.length; i++) { - var name = names[i]; - if (this.series_.hasOwnProperty(name)) { - optionsDicts.push(this.series_[name].options); - } - } - for (var i = 0; i < optionsDicts.length; i++) { - var dict = optionsDicts[i]; - if (!dict) continue; - for (var optionName in dict) { - if (dict.hasOwnProperty(optionName)) { - validateOption(optionName); - } - } - } - }; - - var WARNINGS = {}; // Only show any particular warning once. - - /** - * Logs a warning about invalid options. - * TODO: make this throw for testing - * @private - */ - DygraphOptions.prototype.warnInvalidOption_ = function (optionName) { - if (!WARNINGS[optionName]) { - WARNINGS[optionName] = true; - var isSeries = this.labels_.indexOf(optionName) >= 0; - if (isSeries) { - console.warn('Use new-style per-series options (saw ' + optionName + ' as top-level options key). See http://bit.ly/1tceaJs'); - } else { - console.warn('Unknown option ' + optionName + ' (full list of options at dygraphs.com/options.html'); - } - throw "invalid option " + optionName; - } - }; - - // Reset list of previously-shown warnings. Used for testing. - DygraphOptions.resetWarnings_ = function () { - WARNINGS = {}; - }; - } -} - -exports['default'] = DygraphOptions; -module.exports = exports['default']; - -}).call(this,require('_process')) - -},{"./dygraph-default-attrs":10,"./dygraph-options-reference":14,"./dygraph-utils":17,"_process":1}],16:[function(require,module,exports){ -/** - * @license - * Copyright 2011 Dan Vanderkam (danvdk@gmail.com) - * MIT-licensed (http://opensource.org/licenses/MIT) - */ - -/** - * @fileoverview Description of this file. - * @author danvk@google.com (Dan Vanderkam) - * - * A ticker is a function with the following interface: - * - * function(a, b, pixels, options_view, dygraph, forced_values); - * -> [ { v: tick1_v, label: tick1_label[, label_v: label_v1] }, - * { v: tick2_v, label: tick2_label[, label_v: label_v2] }, - * ... - * ] - * - * The returned value is called a "tick list". - * - * Arguments - * --------- - * - * [a, b] is the range of the axis for which ticks are being generated. For a - * numeric axis, these will simply be numbers. For a date axis, these will be - * millis since epoch (convertable to Date objects using "new Date(a)" and "new - * Date(b)"). - * - * opts provides access to chart- and axis-specific options. It can be used to - * access number/date formatting code/options, check for a log scale, etc. - * - * pixels is the length of the axis in pixels. opts('pixelsPerLabel') is the - * minimum amount of space to be allotted to each label. For instance, if - * pixels=400 and opts('pixelsPerLabel')=40 then the ticker should return - * between zero and ten (400/40) ticks. - * - * dygraph is the Dygraph object for which an axis is being constructed. - * - * forced_values is used for secondary y-axes. The tick positions are typically - * set by the primary y-axis, so the secondary y-axis has no choice in where to - * put these. It simply has to generate labels for these data values. - * - * Tick lists - * ---------- - * Typically a tick will have both a grid/tick line and a label at one end of - * that line (at the bottom for an x-axis, at left or right for the y-axis). - * - * A tick may be missing one of these two components: - * - If "label_v" is specified instead of "v", then there will be no tick or - * gridline, just a label. - * - Similarly, if "label" is not specified, then there will be a gridline - * without a label. - * - * This flexibility is useful in a few situations: - * - For log scales, some of the tick lines may be too close to all have labels. - * - For date scales where years are being displayed, it is desirable to display - * tick marks at the beginnings of years but labels (e.g. "2006") in the - * middle of the years. - */ - -/*jshint sub:true */ -/*global Dygraph:false */ -"use strict"; - -Object.defineProperty(exports, '__esModule', { - value: true -}); - -function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } } - -var _dygraphUtils = require('./dygraph-utils'); - -var utils = _interopRequireWildcard(_dygraphUtils); - -/** @typedef {Array.<{v:number, label:string, label_v:(string|undefined)}>} */ -var TickList = undefined; // the ' = undefined' keeps jshint happy. - -/** @typedef {function( - * number, - * number, - * number, - * function(string):*, - * Dygraph=, - * Array.= - * ): TickList} - */ -var Ticker = undefined; // the ' = undefined' keeps jshint happy. - -/** @type {Ticker} */ -var numericLinearTicks = function numericLinearTicks(a, b, pixels, opts, dygraph, vals) { - var nonLogscaleOpts = function nonLogscaleOpts(opt) { - if (opt === 'logscale') return false; - return opts(opt); - }; - return numericTicks(a, b, pixels, nonLogscaleOpts, dygraph, vals); -}; - -exports.numericLinearTicks = numericLinearTicks; -/** @type {Ticker} */ -var numericTicks = function numericTicks(a, b, pixels, opts, dygraph, vals) { - var pixels_per_tick = /** @type{number} */opts('pixelsPerLabel'); - var ticks = []; - var i, j, tickV, nTicks; - if (vals) { - for (i = 0; i < vals.length; i++) { - ticks.push({ v: vals[i] }); - } - } else { - // TODO(danvk): factor this log-scale block out into a separate function. - if (opts("logscale")) { - nTicks = Math.floor(pixels / pixels_per_tick); - var minIdx = utils.binarySearch(a, PREFERRED_LOG_TICK_VALUES, 1); - var maxIdx = utils.binarySearch(b, PREFERRED_LOG_TICK_VALUES, -1); - if (minIdx == -1) { - minIdx = 0; - } - if (maxIdx == -1) { - maxIdx = PREFERRED_LOG_TICK_VALUES.length - 1; - } - // Count the number of tick values would appear, if we can get at least - // nTicks / 4 accept them. - var lastDisplayed = null; - if (maxIdx - minIdx >= nTicks / 4) { - for (var idx = maxIdx; idx >= minIdx; idx--) { - var tickValue = PREFERRED_LOG_TICK_VALUES[idx]; - var pixel_coord = Math.log(tickValue / a) / Math.log(b / a) * pixels; - var tick = { v: tickValue }; - if (lastDisplayed === null) { - lastDisplayed = { - tickValue: tickValue, - pixel_coord: pixel_coord - }; - } else { - if (Math.abs(pixel_coord - lastDisplayed.pixel_coord) >= pixels_per_tick) { - lastDisplayed = { - tickValue: tickValue, - pixel_coord: pixel_coord - }; - } else { - tick.label = ""; - } - } - ticks.push(tick); - } - // Since we went in backwards order. - ticks.reverse(); - } - } - - // ticks.length won't be 0 if the log scale function finds values to insert. - if (ticks.length === 0) { - // Basic idea: - // Try labels every 1, 2, 5, 10, 20, 50, 100, etc. - // Calculate the resulting tick spacing (i.e. this.height_ / nTicks). - // The first spacing greater than pixelsPerYLabel is what we use. - // TODO(danvk): version that works on a log scale. - var kmg2 = opts("labelsKMG2"); - var mults, base; - if (kmg2) { - mults = [1, 2, 4, 8, 16, 32, 64, 128, 256]; - base = 16; - } else { - mults = [1, 2, 5, 10, 20, 50, 100]; - base = 10; - } - - // Get the maximum number of permitted ticks based on the - // graph's pixel size and pixels_per_tick setting. - var max_ticks = Math.ceil(pixels / pixels_per_tick); - - // Now calculate the data unit equivalent of this tick spacing. - // Use abs() since graphs may have a reversed Y axis. - var units_per_tick = Math.abs(b - a) / max_ticks; - - // Based on this, get a starting scale which is the largest - // integer power of the chosen base (10 or 16) that still remains - // below the requested pixels_per_tick spacing. - var base_power = Math.floor(Math.log(units_per_tick) / Math.log(base)); - var base_scale = Math.pow(base, base_power); - - // Now try multiples of the starting scale until we find one - // that results in tick marks spaced sufficiently far apart. - // The "mults" array should cover the range 1 .. base^2 to - // adjust for rounding and edge effects. - var scale, low_val, high_val, spacing; - for (j = 0; j < mults.length; j++) { - scale = base_scale * mults[j]; - low_val = Math.floor(a / scale) * scale; - high_val = Math.ceil(b / scale) * scale; - nTicks = Math.abs(high_val - low_val) / scale; - spacing = pixels / nTicks; - if (spacing > pixels_per_tick) break; - } - - // Construct the set of ticks. - // Allow reverse y-axis if it's explicitly requested. - if (low_val > high_val) scale *= -1; - for (i = 0; i <= nTicks; i++) { - tickV = low_val + i * scale; - ticks.push({ v: tickV }); - } - } - } - - var formatter = /**@type{AxisLabelFormatter}*/opts('axisLabelFormatter'); - - // Add labels to the ticks. - for (i = 0; i < ticks.length; i++) { - if (ticks[i].label !== undefined) continue; // Use current label. - // TODO(danvk): set granularity to something appropriate here. - ticks[i].label = formatter.call(dygraph, ticks[i].v, 0, opts, dygraph); - } - - return ticks; -}; - -exports.numericTicks = numericTicks; -/** @type {Ticker} */ -var dateTicker = function dateTicker(a, b, pixels, opts, dygraph, vals) { - var chosen = pickDateTickGranularity(a, b, pixels, opts); - - if (chosen >= 0) { - return getDateAxis(a, b, chosen, opts, dygraph); - } else { - // this can happen if self.width_ is zero. - return []; - } -}; - -exports.dateTicker = dateTicker; -// Time granularity enumeration -var Granularity = { - MILLISECONDLY: 0, - TWO_MILLISECONDLY: 1, - FIVE_MILLISECONDLY: 2, - TEN_MILLISECONDLY: 3, - FIFTY_MILLISECONDLY: 4, - HUNDRED_MILLISECONDLY: 5, - FIVE_HUNDRED_MILLISECONDLY: 6, - SECONDLY: 7, - TWO_SECONDLY: 8, - FIVE_SECONDLY: 9, - TEN_SECONDLY: 10, - THIRTY_SECONDLY: 11, - MINUTELY: 12, - TWO_MINUTELY: 13, - FIVE_MINUTELY: 14, - TEN_MINUTELY: 15, - THIRTY_MINUTELY: 16, - HOURLY: 17, - TWO_HOURLY: 18, - SIX_HOURLY: 19, - DAILY: 20, - TWO_DAILY: 21, - WEEKLY: 22, - MONTHLY: 23, - QUARTERLY: 24, - BIANNUAL: 25, - ANNUAL: 26, - DECADAL: 27, - CENTENNIAL: 28, - NUM_GRANULARITIES: 29 -}; - -exports.Granularity = Granularity; -// Date components enumeration (in the order of the arguments in Date) -// TODO: make this an @enum -var DateField = { - DATEFIELD_Y: 0, - DATEFIELD_M: 1, - DATEFIELD_D: 2, - DATEFIELD_HH: 3, - DATEFIELD_MM: 4, - DATEFIELD_SS: 5, - DATEFIELD_MS: 6, - NUM_DATEFIELDS: 7 -}; - -/** - * The value of datefield will start at an even multiple of "step", i.e. - * if datefield=SS and step=5 then the first tick will be on a multiple of 5s. - * - * For granularities <= HOURLY, ticks are generated every `spacing` ms. - * - * At coarser granularities, ticks are generated by incrementing `datefield` by - * `step`. In this case, the `spacing` value is only used to estimate the - * number of ticks. It should roughly correspond to the spacing between - * adjacent ticks. - * - * @type {Array.<{datefield:number, step:number, spacing:number}>} - */ -var TICK_PLACEMENT = []; -TICK_PLACEMENT[Granularity.MILLISECONDLY] = { datefield: DateField.DATEFIELD_MS, step: 1, spacing: 1 }; -TICK_PLACEMENT[Granularity.TWO_MILLISECONDLY] = { datefield: DateField.DATEFIELD_MS, step: 2, spacing: 2 }; -TICK_PLACEMENT[Granularity.FIVE_MILLISECONDLY] = { datefield: DateField.DATEFIELD_MS, step: 5, spacing: 5 }; -TICK_PLACEMENT[Granularity.TEN_MILLISECONDLY] = { datefield: DateField.DATEFIELD_MS, step: 10, spacing: 10 }; -TICK_PLACEMENT[Granularity.FIFTY_MILLISECONDLY] = { datefield: DateField.DATEFIELD_MS, step: 50, spacing: 50 }; -TICK_PLACEMENT[Granularity.HUNDRED_MILLISECONDLY] = { datefield: DateField.DATEFIELD_MS, step: 100, spacing: 100 }; -TICK_PLACEMENT[Granularity.FIVE_HUNDRED_MILLISECONDLY] = { datefield: DateField.DATEFIELD_MS, step: 500, spacing: 500 }; -TICK_PLACEMENT[Granularity.SECONDLY] = { datefield: DateField.DATEFIELD_SS, step: 1, spacing: 1000 * 1 }; -TICK_PLACEMENT[Granularity.TWO_SECONDLY] = { datefield: DateField.DATEFIELD_SS, step: 2, spacing: 1000 * 2 }; -TICK_PLACEMENT[Granularity.FIVE_SECONDLY] = { datefield: DateField.DATEFIELD_SS, step: 5, spacing: 1000 * 5 }; -TICK_PLACEMENT[Granularity.TEN_SECONDLY] = { datefield: DateField.DATEFIELD_SS, step: 10, spacing: 1000 * 10 }; -TICK_PLACEMENT[Granularity.THIRTY_SECONDLY] = { datefield: DateField.DATEFIELD_SS, step: 30, spacing: 1000 * 30 }; -TICK_PLACEMENT[Granularity.MINUTELY] = { datefield: DateField.DATEFIELD_MM, step: 1, spacing: 1000 * 60 }; -TICK_PLACEMENT[Granularity.TWO_MINUTELY] = { datefield: DateField.DATEFIELD_MM, step: 2, spacing: 1000 * 60 * 2 }; -TICK_PLACEMENT[Granularity.FIVE_MINUTELY] = { datefield: DateField.DATEFIELD_MM, step: 5, spacing: 1000 * 60 * 5 }; -TICK_PLACEMENT[Granularity.TEN_MINUTELY] = { datefield: DateField.DATEFIELD_MM, step: 10, spacing: 1000 * 60 * 10 }; -TICK_PLACEMENT[Granularity.THIRTY_MINUTELY] = { datefield: DateField.DATEFIELD_MM, step: 30, spacing: 1000 * 60 * 30 }; -TICK_PLACEMENT[Granularity.HOURLY] = { datefield: DateField.DATEFIELD_HH, step: 1, spacing: 1000 * 3600 }; -TICK_PLACEMENT[Granularity.TWO_HOURLY] = { datefield: DateField.DATEFIELD_HH, step: 2, spacing: 1000 * 3600 * 2 }; -TICK_PLACEMENT[Granularity.SIX_HOURLY] = { datefield: DateField.DATEFIELD_HH, step: 6, spacing: 1000 * 3600 * 6 }; -TICK_PLACEMENT[Granularity.DAILY] = { datefield: DateField.DATEFIELD_D, step: 1, spacing: 1000 * 86400 }; -TICK_PLACEMENT[Granularity.TWO_DAILY] = { datefield: DateField.DATEFIELD_D, step: 2, spacing: 1000 * 86400 * 2 }; -TICK_PLACEMENT[Granularity.WEEKLY] = { datefield: DateField.DATEFIELD_D, step: 7, spacing: 1000 * 604800 }; -TICK_PLACEMENT[Granularity.MONTHLY] = { datefield: DateField.DATEFIELD_M, step: 1, spacing: 1000 * 7200 * 365.2524 }; // 1e3 * 60 * 60 * 24 * 365.2524 / 12 -TICK_PLACEMENT[Granularity.QUARTERLY] = { datefield: DateField.DATEFIELD_M, step: 3, spacing: 1000 * 21600 * 365.2524 }; // 1e3 * 60 * 60 * 24 * 365.2524 / 4 -TICK_PLACEMENT[Granularity.BIANNUAL] = { datefield: DateField.DATEFIELD_M, step: 6, spacing: 1000 * 43200 * 365.2524 }; // 1e3 * 60 * 60 * 24 * 365.2524 / 2 -TICK_PLACEMENT[Granularity.ANNUAL] = { datefield: DateField.DATEFIELD_Y, step: 1, spacing: 1000 * 86400 * 365.2524 }; // 1e3 * 60 * 60 * 24 * 365.2524 * 1 -TICK_PLACEMENT[Granularity.DECADAL] = { datefield: DateField.DATEFIELD_Y, step: 10, spacing: 1000 * 864000 * 365.2524 }; // 1e3 * 60 * 60 * 24 * 365.2524 * 10 -TICK_PLACEMENT[Granularity.CENTENNIAL] = { datefield: DateField.DATEFIELD_Y, step: 100, spacing: 1000 * 8640000 * 365.2524 }; // 1e3 * 60 * 60 * 24 * 365.2524 * 100 - -/** - * This is a list of human-friendly values at which to show tick marks on a log - * scale. It is k * 10^n, where k=1..9 and n=-39..+39, so: - * ..., 1, 2, 3, 4, 5, ..., 9, 10, 20, 30, ..., 90, 100, 200, 300, ... - * NOTE: this assumes that utils.LOG_SCALE = 10. - * @type {Array.} - */ -var PREFERRED_LOG_TICK_VALUES = (function () { - var vals = []; - for (var power = -39; power <= 39; power++) { - var range = Math.pow(10, power); - for (var mult = 1; mult <= 9; mult++) { - var val = range * mult; - vals.push(val); - } - } - return vals; -})(); - -/** - * Determine the correct granularity of ticks on a date axis. - * - * @param {number} a Left edge of the chart (ms) - * @param {number} b Right edge of the chart (ms) - * @param {number} pixels Size of the chart in the relevant dimension (width). - * @param {function(string):*} opts Function mapping from option name -> value. - * @return {number} The appropriate axis granularity for this chart. See the - * enumeration of possible values in dygraph-tickers.js. - */ -var pickDateTickGranularity = function pickDateTickGranularity(a, b, pixels, opts) { - var pixels_per_tick = /** @type{number} */opts('pixelsPerLabel'); - for (var i = 0; i < Granularity.NUM_GRANULARITIES; i++) { - var num_ticks = numDateTicks(a, b, i); - if (pixels / num_ticks >= pixels_per_tick) { - return i; - } - } - return -1; -}; - -/** - * Compute the number of ticks on a date axis for a given granularity. - * @param {number} start_time - * @param {number} end_time - * @param {number} granularity (one of the granularities enumerated above) - * @return {number} (Approximate) number of ticks that would result. - */ -var numDateTicks = function numDateTicks(start_time, end_time, granularity) { - var spacing = TICK_PLACEMENT[granularity].spacing; - return Math.round(1.0 * (end_time - start_time) / spacing); -}; - -/** - * Compute the positions and labels of ticks on a date axis for a given granularity. - * @param {number} start_time - * @param {number} end_time - * @param {number} granularity (one of the granularities enumerated above) - * @param {function(string):*} opts Function mapping from option name -> value. - * @param {Dygraph=} dg - * @return {!TickList} - */ -var getDateAxis = function getDateAxis(start_time, end_time, granularity, opts, dg) { - var formatter = /** @type{AxisLabelFormatter} */opts("axisLabelFormatter"); - var utc = opts("labelsUTC"); - var accessors = utc ? utils.DateAccessorsUTC : utils.DateAccessorsLocal; - - var datefield = TICK_PLACEMENT[granularity].datefield; - var step = TICK_PLACEMENT[granularity].step; - var spacing = TICK_PLACEMENT[granularity].spacing; - - // Choose a nice tick position before the initial instant. - // Currently, this code deals properly with the existent daily granularities: - // DAILY (with step of 1) and WEEKLY (with step of 7 but specially handled). - // Other daily granularities (say TWO_DAILY) should also be handled specially - // by setting the start_date_offset to 0. - var start_date = new Date(start_time); - var date_array = []; - date_array[DateField.DATEFIELD_Y] = accessors.getFullYear(start_date); - date_array[DateField.DATEFIELD_M] = accessors.getMonth(start_date); - date_array[DateField.DATEFIELD_D] = accessors.getDate(start_date); - date_array[DateField.DATEFIELD_HH] = accessors.getHours(start_date); - date_array[DateField.DATEFIELD_MM] = accessors.getMinutes(start_date); - date_array[DateField.DATEFIELD_SS] = accessors.getSeconds(start_date); - date_array[DateField.DATEFIELD_MS] = accessors.getMilliseconds(start_date); - - var start_date_offset = date_array[datefield] % step; - if (granularity == Granularity.WEEKLY) { - // This will put the ticks on Sundays. - start_date_offset = accessors.getDay(start_date); - } - - date_array[datefield] -= start_date_offset; - for (var df = datefield + 1; df < DateField.NUM_DATEFIELDS; df++) { - // The minimum value is 1 for the day of month, and 0 for all other fields. - date_array[df] = df === DateField.DATEFIELD_D ? 1 : 0; - } - - // Generate the ticks. - // For granularities not coarser than HOURLY we use the fact that: - // the number of milliseconds between ticks is constant - // and equal to the defined spacing. - // Otherwise we rely on the 'roll over' property of the Date functions: - // when some date field is set to a value outside of its logical range, - // the excess 'rolls over' the next (more significant) field. - // However, when using local time with DST transitions, - // there are dates that do not represent any time value at all - // (those in the hour skipped at the 'spring forward'), - // and the JavaScript engines usually return an equivalent value. - // Hence we have to check that the date is properly increased at each step, - // returning a date at a nice tick position. - var ticks = []; - var tick_date = accessors.makeDate.apply(null, date_array); - var tick_time = tick_date.getTime(); - if (granularity <= Granularity.HOURLY) { - if (tick_time < start_time) { - tick_time += spacing; - tick_date = new Date(tick_time); - } - while (tick_time <= end_time) { - ticks.push({ v: tick_time, - label: formatter.call(dg, tick_date, granularity, opts, dg) - }); - tick_time += spacing; - tick_date = new Date(tick_time); - } - } else { - if (tick_time < start_time) { - date_array[datefield] += step; - tick_date = accessors.makeDate.apply(null, date_array); - tick_time = tick_date.getTime(); - } - while (tick_time <= end_time) { - if (granularity >= Granularity.DAILY || accessors.getHours(tick_date) % step === 0) { - ticks.push({ v: tick_time, - label: formatter.call(dg, tick_date, granularity, opts, dg) - }); - } - date_array[datefield] += step; - tick_date = accessors.makeDate.apply(null, date_array); - tick_time = tick_date.getTime(); - } - } - return ticks; -}; -exports.getDateAxis = getDateAxis; - -},{"./dygraph-utils":17}],17:[function(require,module,exports){ -/** - * @license - * Copyright 2011 Dan Vanderkam (danvdk@gmail.com) - * MIT-licensed (http://opensource.org/licenses/MIT) - */ - -/** - * @fileoverview This file contains utility functions used by dygraphs. These - * are typically static (i.e. not related to any particular dygraph). Examples - * include date/time formatting functions, basic algorithms (e.g. binary - * search) and generic DOM-manipulation functions. - */ - -/*global Dygraph:false, Node:false */ -"use strict"; - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.removeEvent = removeEvent; -exports.cancelEvent = cancelEvent; -exports.hsvToRGB = hsvToRGB; -exports.findPos = findPos; -exports.pageX = pageX; -exports.pageY = pageY; -exports.dragGetX_ = dragGetX_; -exports.dragGetY_ = dragGetY_; -exports.isOK = isOK; -exports.isValidPoint = isValidPoint; -exports.floatFormat = floatFormat; -exports.zeropad = zeropad; -exports.hmsString_ = hmsString_; -exports.dateString_ = dateString_; -exports.round_ = round_; -exports.binarySearch = binarySearch; -exports.dateParser = dateParser; -exports.dateStrToMillis = dateStrToMillis; -exports.update = update; -exports.updateDeep = updateDeep; -exports.isArrayLike = isArrayLike; -exports.isDateLike = isDateLike; -exports.clone = clone; -exports.createCanvas = createCanvas; -exports.getContextPixelRatio = getContextPixelRatio; -exports.Iterator = Iterator; -exports.createIterator = createIterator; -exports.repeatAndCleanup = repeatAndCleanup; -exports.isPixelChangingOptionList = isPixelChangingOptionList; -exports.detectLineDelimiter = detectLineDelimiter; -exports.isNodeContainedBy = isNodeContainedBy; -exports.pow = pow; -exports.toRGB_ = toRGB_; -exports.isCanvasSupported = isCanvasSupported; -exports.parseFloat_ = parseFloat_; -exports.numberValueFormatter = numberValueFormatter; -exports.numberAxisLabelFormatter = numberAxisLabelFormatter; -exports.dateAxisLabelFormatter = dateAxisLabelFormatter; -exports.dateValueFormatter = dateValueFormatter; - -function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj["default"] = obj; return newObj; } } - -var _dygraphTickers = require('./dygraph-tickers'); - -var DygraphTickers = _interopRequireWildcard(_dygraphTickers); - -var LOG_SCALE = 10; -exports.LOG_SCALE = LOG_SCALE; -var LN_TEN = Math.log(LOG_SCALE); - -exports.LN_TEN = LN_TEN; -/** - * @private - * @param {number} x - * @return {number} - */ -var log10 = function log10(x) { - return Math.log(x) / LN_TEN; -}; - -exports.log10 = log10; -/** - * @private - * @param {number} r0 - * @param {number} r1 - * @param {number} pct - * @return {number} - */ -var logRangeFraction = function logRangeFraction(r0, r1, pct) { - // Computing the inverse of toPercentXCoord. The function was arrived at with - // the following steps: - // - // Original calcuation: - // pct = (log(x) - log(xRange[0])) / (log(xRange[1]) - log(xRange[0]))); - // - // Multiply both sides by the right-side denominator. - // pct * (log(xRange[1] - log(xRange[0]))) = log(x) - log(xRange[0]) - // - // add log(xRange[0]) to both sides - // log(xRange[0]) + (pct * (log(xRange[1]) - log(xRange[0])) = log(x); - // - // Swap both sides of the equation, - // log(x) = log(xRange[0]) + (pct * (log(xRange[1]) - log(xRange[0])) - // - // Use both sides as the exponent in 10^exp and we're done. - // x = 10 ^ (log(xRange[0]) + (pct * (log(xRange[1]) - log(xRange[0]))) - - var logr0 = log10(r0); - var logr1 = log10(r1); - var exponent = logr0 + pct * (logr1 - logr0); - var value = Math.pow(LOG_SCALE, exponent); - return value; -}; - -exports.logRangeFraction = logRangeFraction; -/** A dotted line stroke pattern. */ -var DOTTED_LINE = [2, 2]; -exports.DOTTED_LINE = DOTTED_LINE; -/** A dashed line stroke pattern. */ -var DASHED_LINE = [7, 3]; -exports.DASHED_LINE = DASHED_LINE; -/** A dot dash stroke pattern. */ -var DOT_DASH_LINE = [7, 2, 2, 2]; - -exports.DOT_DASH_LINE = DOT_DASH_LINE; -// Directions for panning and zooming. Use bit operations when combined -// values are possible. -var HORIZONTAL = 1; -exports.HORIZONTAL = HORIZONTAL; -var VERTICAL = 2; - -exports.VERTICAL = VERTICAL; -/** - * Return the 2d context for a dygraph canvas. - * - * This method is only exposed for the sake of replacing the function in - * automated tests. - * - * @param {!HTMLCanvasElement} canvas - * @return {!CanvasRenderingContext2D} - * @private - */ -var getContext = function getContext(canvas) { - return (/** @type{!CanvasRenderingContext2D}*/canvas.getContext("2d") - ); -}; - -exports.getContext = getContext; -/** - * Add an event handler. - * @param {!Node} elem The element to add the event to. - * @param {string} type The type of the event, e.g. 'click' or 'mousemove'. - * @param {function(Event):(boolean|undefined)} fn The function to call - * on the event. The function takes one parameter: the event object. - * @private - */ -var addEvent = function addEvent(elem, type, fn) { - elem.addEventListener(type, fn, false); -}; - -exports.addEvent = addEvent; -/** - * Remove an event handler. - * @param {!Node} elem The element to remove the event from. - * @param {string} type The type of the event, e.g. 'click' or 'mousemove'. - * @param {function(Event):(boolean|undefined)} fn The function to call - * on the event. The function takes one parameter: the event object. - */ - -function removeEvent(elem, type, fn) { - elem.removeEventListener(type, fn, false); -} - -; - -/** - * Cancels further processing of an event. This is useful to prevent default - * browser actions, e.g. highlighting text on a double-click. - * Based on the article at - * http://www.switchonthecode.com/tutorials/javascript-tutorial-the-scroll-wheel - * @param {!Event} e The event whose normal behavior should be canceled. - * @private - */ - -function cancelEvent(e) { - e = e ? e : window.event; - if (e.stopPropagation) { - e.stopPropagation(); - } - if (e.preventDefault) { - e.preventDefault(); - } - e.cancelBubble = true; - e.cancel = true; - e.returnValue = false; - return false; -} - -; - -/** - * Convert hsv values to an rgb(r,g,b) string. Taken from MochiKit.Color. This - * is used to generate default series colors which are evenly spaced on the - * color wheel. - * @param { number } hue Range is 0.0-1.0. - * @param { number } saturation Range is 0.0-1.0. - * @param { number } value Range is 0.0-1.0. - * @return { string } "rgb(r,g,b)" where r, g and b range from 0-255. - * @private - */ - -function hsvToRGB(hue, saturation, value) { - var red; - var green; - var blue; - if (saturation === 0) { - red = value; - green = value; - blue = value; - } else { - var i = Math.floor(hue * 6); - var f = hue * 6 - i; - var p = value * (1 - saturation); - var q = value * (1 - saturation * f); - var t = value * (1 - saturation * (1 - f)); - switch (i) { - case 1: - red = q;green = value;blue = p;break; - case 2: - red = p;green = value;blue = t;break; - case 3: - red = p;green = q;blue = value;break; - case 4: - red = t;green = p;blue = value;break; - case 5: - red = value;green = p;blue = q;break; - case 6: // fall through - case 0: - red = value;green = t;blue = p;break; - } - } - red = Math.floor(255 * red + 0.5); - green = Math.floor(255 * green + 0.5); - blue = Math.floor(255 * blue + 0.5); - return 'rgb(' + red + ',' + green + ',' + blue + ')'; -} - -; - -/** - * Find the coordinates of an object relative to the top left of the page. - * - * @param {Node} obj - * @return {{x:number,y:number}} - * @private - */ - -function findPos(obj) { - var p = obj.getBoundingClientRect(), - w = window, - d = document.documentElement; - - return { - x: p.left + (w.pageXOffset || d.scrollLeft), - y: p.top + (w.pageYOffset || d.scrollTop) - }; -} - -; - -/** - * Returns the x-coordinate of the event in a coordinate system where the - * top-left corner of the page (not the window) is (0,0). - * Taken from MochiKit.Signal - * @param {!Event} e - * @return {number} - * @private - */ - -function pageX(e) { - return !e.pageX || e.pageX < 0 ? 0 : e.pageX; -} - -; - -/** - * Returns the y-coordinate of the event in a coordinate system where the - * top-left corner of the page (not the window) is (0,0). - * Taken from MochiKit.Signal - * @param {!Event} e - * @return {number} - * @private - */ - -function pageY(e) { - return !e.pageY || e.pageY < 0 ? 0 : e.pageY; -} - -; - -/** - * Converts page the x-coordinate of the event to pixel x-coordinates on the - * canvas (i.e. DOM Coords). - * @param {!Event} e Drag event. - * @param {!DygraphInteractionContext} context Interaction context object. - * @return {number} The amount by which the drag has moved to the right. - */ - -function dragGetX_(e, context) { - return pageX(e) - context.px; -} - -; - -/** - * Converts page the y-coordinate of the event to pixel y-coordinates on the - * canvas (i.e. DOM Coords). - * @param {!Event} e Drag event. - * @param {!DygraphInteractionContext} context Interaction context object. - * @return {number} The amount by which the drag has moved down. - */ - -function dragGetY_(e, context) { - return pageY(e) - context.py; -} - -; - -/** - * This returns true unless the parameter is 0, null, undefined or NaN. - * TODO(danvk): rename this function to something like 'isNonZeroNan'. - * - * @param {number} x The number to consider. - * @return {boolean} Whether the number is zero or NaN. - * @private - */ - -function isOK(x) { - return !!x && !isNaN(x); -} - -; - -/** - * @param {{x:?number,y:?number,yval:?number}} p The point to consider, valid - * points are {x, y} objects - * @param {boolean=} opt_allowNaNY Treat point with y=NaN as valid - * @return {boolean} Whether the point has numeric x and y. - * @private - */ - -function isValidPoint(p, opt_allowNaNY) { - if (!p) return false; // null or undefined object - if (p.yval === null) return false; // missing point - if (p.x === null || p.x === undefined) return false; - if (p.y === null || p.y === undefined) return false; - if (isNaN(p.x) || !opt_allowNaNY && isNaN(p.y)) return false; - return true; -} - -; - -/** - * Number formatting function which mimics the behavior of %g in printf, i.e. - * either exponential or fixed format (without trailing 0s) is used depending on - * the length of the generated string. The advantage of this format is that - * there is a predictable upper bound on the resulting string length, - * significant figures are not dropped, and normal numbers are not displayed in - * exponential notation. - * - * NOTE: JavaScript's native toPrecision() is NOT a drop-in replacement for %g. - * It creates strings which are too long for absolute values between 10^-4 and - * 10^-6, e.g. '0.00001' instead of '1e-5'. See tests/number-format.html for - * output examples. - * - * @param {number} x The number to format - * @param {number=} opt_precision The precision to use, default 2. - * @return {string} A string formatted like %g in printf. The max generated - * string length should be precision + 6 (e.g 1.123e+300). - */ - -function floatFormat(x, opt_precision) { - // Avoid invalid precision values; [1, 21] is the valid range. - var p = Math.min(Math.max(1, opt_precision || 2), 21); - - // This is deceptively simple. The actual algorithm comes from: - // - // Max allowed length = p + 4 - // where 4 comes from 'e+n' and '.'. - // - // Length of fixed format = 2 + y + p - // where 2 comes from '0.' and y = # of leading zeroes. - // - // Equating the two and solving for y yields y = 2, or 0.00xxxx which is - // 1.0e-3. - // - // Since the behavior of toPrecision() is identical for larger numbers, we - // don't have to worry about the other bound. - // - // Finally, the argument for toExponential() is the number of trailing digits, - // so we take off 1 for the value before the '.'. - return Math.abs(x) < 1.0e-3 && x !== 0.0 ? x.toExponential(p - 1) : x.toPrecision(p); -} - -; - -/** - * Converts '9' to '09' (useful for dates) - * @param {number} x - * @return {string} - * @private - */ - -function zeropad(x) { - if (x < 10) return "0" + x;else return "" + x; -} - -; - -/** - * Date accessors to get the parts of a calendar date (year, month, - * day, hour, minute, second and millisecond) according to local time, - * and factory method to call the Date constructor with an array of arguments. - */ -var DateAccessorsLocal = { - getFullYear: function getFullYear(d) { - return d.getFullYear(); - }, - getMonth: function getMonth(d) { - return d.getMonth(); - }, - getDate: function getDate(d) { - return d.getDate(); - }, - getHours: function getHours(d) { - return d.getHours(); - }, - getMinutes: function getMinutes(d) { - return d.getMinutes(); - }, - getSeconds: function getSeconds(d) { - return d.getSeconds(); - }, - getMilliseconds: function getMilliseconds(d) { - return d.getMilliseconds(); - }, - getDay: function getDay(d) { - return d.getDay(); - }, - makeDate: function makeDate(y, m, d, hh, mm, ss, ms) { - return new Date(y, m, d, hh, mm, ss, ms); - } -}; - -exports.DateAccessorsLocal = DateAccessorsLocal; -/** - * Date accessors to get the parts of a calendar date (year, month, - * day of month, hour, minute, second and millisecond) according to UTC time, - * and factory method to call the Date constructor with an array of arguments. - */ -var DateAccessorsUTC = { - getFullYear: function getFullYear(d) { - return d.getUTCFullYear(); - }, - getMonth: function getMonth(d) { - return d.getUTCMonth(); - }, - getDate: function getDate(d) { - return d.getUTCDate(); - }, - getHours: function getHours(d) { - return d.getUTCHours(); - }, - getMinutes: function getMinutes(d) { - return d.getUTCMinutes(); - }, - getSeconds: function getSeconds(d) { - return d.getUTCSeconds(); - }, - getMilliseconds: function getMilliseconds(d) { - return d.getUTCMilliseconds(); - }, - getDay: function getDay(d) { - return d.getUTCDay(); - }, - makeDate: function makeDate(y, m, d, hh, mm, ss, ms) { - return new Date(Date.UTC(y, m, d, hh, mm, ss, ms)); - } -}; - -exports.DateAccessorsUTC = DateAccessorsUTC; -/** - * Return a string version of the hours, minutes and seconds portion of a date. - * @param {number} hh The hours (from 0-23) - * @param {number} mm The minutes (from 0-59) - * @param {number} ss The seconds (from 0-59) - * @return {string} A time of the form "HH:MM" or "HH:MM:SS" - * @private - */ - -function hmsString_(hh, mm, ss, ms) { - var ret = zeropad(hh) + ":" + zeropad(mm); - if (ss) { - ret += ":" + zeropad(ss); - if (ms) { - var str = "" + ms; - ret += "." + ('000' + str).substring(str.length); - } - } - return ret; -} - -; - -/** - * Convert a JS date (millis since epoch) to a formatted string. - * @param {number} time The JavaScript time value (ms since epoch) - * @param {boolean} utc Whether output UTC or local time - * @return {string} A date of one of these forms: - * "YYYY/MM/DD", "YYYY/MM/DD HH:MM" or "YYYY/MM/DD HH:MM:SS" - * @private - */ - -function dateString_(time, utc) { - var accessors = utc ? DateAccessorsUTC : DateAccessorsLocal; - var date = new Date(time); - var y = accessors.getFullYear(date); - var m = accessors.getMonth(date); - var d = accessors.getDate(date); - var hh = accessors.getHours(date); - var mm = accessors.getMinutes(date); - var ss = accessors.getSeconds(date); - var ms = accessors.getMilliseconds(date); - // Get a year string: - var year = "" + y; - // Get a 0 padded month string - var month = zeropad(m + 1); //months are 0-offset, sigh - // Get a 0 padded day string - var day = zeropad(d); - var frac = hh * 3600 + mm * 60 + ss + 1e-3 * ms; - var ret = year + "/" + month + "/" + day; - if (frac) { - ret += " " + hmsString_(hh, mm, ss, ms); - } - return ret; -} - -; - -/** - * Round a number to the specified number of digits past the decimal point. - * @param {number} num The number to round - * @param {number} places The number of decimals to which to round - * @return {number} The rounded number - * @private - */ - -function round_(num, places) { - var shift = Math.pow(10, places); - return Math.round(num * shift) / shift; -} - -; - -/** - * Implementation of binary search over an array. - * Currently does not work when val is outside the range of arry's values. - * @param {number} val the value to search for - * @param {Array.} arry is the value over which to search - * @param {number} abs If abs > 0, find the lowest entry greater than val - * If abs < 0, find the highest entry less than val. - * If abs == 0, find the entry that equals val. - * @param {number=} low The first index in arry to consider (optional) - * @param {number=} high The last index in arry to consider (optional) - * @return {number} Index of the element, or -1 if it isn't found. - * @private - */ - -function binarySearch(_x, _x2, _x3, _x4, _x5) { - var _again = true; - - _function: while (_again) { - var val = _x, - arry = _x2, - abs = _x3, - low = _x4, - high = _x5; - _again = false; - - if (low === null || low === undefined || high === null || high === undefined) { - low = 0; - high = arry.length - 1; - } - if (low > high) { - return -1; - } - if (abs === null || abs === undefined) { - abs = 0; - } - var validIndex = function validIndex(idx) { - return idx >= 0 && idx < arry.length; - }; - var mid = parseInt((low + high) / 2, 10); - var element = arry[mid]; - var idx; - if (element == val) { - return mid; - } else if (element > val) { - if (abs > 0) { - // Accept if element > val, but also if prior element < val. - idx = mid - 1; - if (validIndex(idx) && arry[idx] < val) { - return mid; - } - } - _x = val; - _x2 = arry; - _x3 = abs; - _x4 = low; - _x5 = mid - 1; - _again = true; - validIndex = mid = element = idx = undefined; - continue _function; - } else if (element < val) { - if (abs < 0) { - // Accept if element < val, but also if prior element > val. - idx = mid + 1; - if (validIndex(idx) && arry[idx] > val) { - return mid; - } - } - _x = val; - _x2 = arry; - _x3 = abs; - _x4 = mid + 1; - _x5 = high; - _again = true; - validIndex = mid = element = idx = undefined; - continue _function; - } - return -1; // can't actually happen, but makes closure compiler happy - } -} - -; - -/** - * Parses a date, returning the number of milliseconds since epoch. This can be - * passed in as an xValueParser in the Dygraph constructor. - * TODO(danvk): enumerate formats that this understands. - * - * @param {string} dateStr A date in a variety of possible string formats. - * @return {number} Milliseconds since epoch. - * @private - */ - -function dateParser(dateStr) { - var dateStrSlashed; - var d; - - // Let the system try the format first, with one caveat: - // YYYY-MM-DD[ HH:MM:SS] is interpreted as UTC by a variety of browsers. - // dygraphs displays dates in local time, so this will result in surprising - // inconsistencies. But if you specify "T" or "Z" (i.e. YYYY-MM-DDTHH:MM:SS), - // then you probably know what you're doing, so we'll let you go ahead. - // Issue: http://code.google.com/p/dygraphs/issues/detail?id=255 - if (dateStr.search("-") == -1 || dateStr.search("T") != -1 || dateStr.search("Z") != -1) { - d = dateStrToMillis(dateStr); - if (d && !isNaN(d)) return d; - } - - if (dateStr.search("-") != -1) { - // e.g. '2009-7-12' or '2009-07-12' - dateStrSlashed = dateStr.replace("-", "/", "g"); - while (dateStrSlashed.search("-") != -1) { - dateStrSlashed = dateStrSlashed.replace("-", "/"); - } - d = dateStrToMillis(dateStrSlashed); - } else if (dateStr.length == 8) { - // e.g. '20090712' - // TODO(danvk): remove support for this format. It's confusing. - dateStrSlashed = dateStr.substr(0, 4) + "/" + dateStr.substr(4, 2) + "/" + dateStr.substr(6, 2); - d = dateStrToMillis(dateStrSlashed); - } else { - // Any format that Date.parse will accept, e.g. "2009/07/12" or - // "2009/07/12 12:34:56" - d = dateStrToMillis(dateStr); - } - - if (!d || isNaN(d)) { - console.error("Couldn't parse " + dateStr + " as a date"); - } - return d; -} - -; - -/** - * This is identical to JavaScript's built-in Date.parse() method, except that - * it doesn't get replaced with an incompatible method by aggressive JS - * libraries like MooTools or Joomla. - * @param {string} str The date string, e.g. "2011/05/06" - * @return {number} millis since epoch - * @private - */ - -function dateStrToMillis(str) { - return new Date(str).getTime(); -} - -; - -// These functions are all based on MochiKit. -/** - * Copies all the properties from o to self. - * - * @param {!Object} self - * @param {!Object} o - * @return {!Object} - */ - -function update(self, o) { - if (typeof o != 'undefined' && o !== null) { - for (var k in o) { - if (o.hasOwnProperty(k)) { - self[k] = o[k]; - } - } - } - return self; -} - -; - -/** - * Copies all the properties from o to self. - * - * @param {!Object} self - * @param {!Object} o - * @return {!Object} - * @private - */ - -function updateDeep(self, o) { - // Taken from http://stackoverflow.com/questions/384286/javascript-isdom-how-do-you-check-if-a-javascript-object-is-a-dom-object - function isNode(o) { - return typeof Node === "object" ? o instanceof Node : typeof o === "object" && typeof o.nodeType === "number" && typeof o.nodeName === "string"; - } - - if (typeof o != 'undefined' && o !== null) { - for (var k in o) { - if (o.hasOwnProperty(k)) { - if (o[k] === null) { - self[k] = null; - } else if (isArrayLike(o[k])) { - self[k] = o[k].slice(); - } else if (isNode(o[k])) { - // DOM objects are shallowly-copied. - self[k] = o[k]; - } else if (typeof o[k] == 'object') { - if (typeof self[k] != 'object' || self[k] === null) { - self[k] = {}; - } - updateDeep(self[k], o[k]); - } else { - self[k] = o[k]; - } - } - } - } - return self; -} - -; - -/** - * @param {*} o - * @return {boolean} - * @private - */ - -function isArrayLike(o) { - var typ = typeof o; - if (typ != 'object' && !(typ == 'function' && typeof o.item == 'function') || o === null || typeof o.length != 'number' || o.nodeType === 3) { - return false; - } - return true; -} - -; - -/** - * @param {Object} o - * @return {boolean} - * @private - */ - -function isDateLike(o) { - if (typeof o != "object" || o === null || typeof o.getTime != 'function') { - return false; - } - return true; -} - -; - -/** - * Note: this only seems to work for arrays. - * @param {!Array} o - * @return {!Array} - * @private - */ - -function clone(o) { - // TODO(danvk): figure out how MochiKit's version works - var r = []; - for (var i = 0; i < o.length; i++) { - if (isArrayLike(o[i])) { - r.push(clone(o[i])); - } else { - r.push(o[i]); - } - } - return r; -} - -; - -/** - * Create a new canvas element. - * - * @return {!HTMLCanvasElement} - * @private - */ - -function createCanvas() { - return document.createElement('canvas'); -} - -; - -/** - * Returns the context's pixel ratio, which is the ratio between the device - * pixel ratio and the backing store ratio. Typically this is 1 for conventional - * displays, and > 1 for HiDPI displays (such as the Retina MBP). - * See http://www.html5rocks.com/en/tutorials/canvas/hidpi/ for more details. - * - * @param {!CanvasRenderingContext2D} context The canvas's 2d context. - * @return {number} The ratio of the device pixel ratio and the backing store - * ratio for the specified context. - */ - -function getContextPixelRatio(context) { - try { - var devicePixelRatio = window.devicePixelRatio; - var backingStoreRatio = context.webkitBackingStorePixelRatio || context.mozBackingStorePixelRatio || context.msBackingStorePixelRatio || context.oBackingStorePixelRatio || context.backingStorePixelRatio || 1; - if (devicePixelRatio !== undefined) { - return devicePixelRatio / backingStoreRatio; - } else { - // At least devicePixelRatio must be defined for this ratio to make sense. - // We default backingStoreRatio to 1: this does not exist on some browsers - // (i.e. desktop Chrome). - return 1; - } - } catch (e) { - return 1; - } -} - -; - -/** - * TODO(danvk): use @template here when it's better supported for classes. - * @param {!Array} array - * @param {number} start - * @param {number} length - * @param {function(!Array,?):boolean=} predicate - * @constructor - */ - -function Iterator(array, start, length, predicate) { - start = start || 0; - length = length || array.length; - this.hasNext = true; // Use to identify if there's another element. - this.peek = null; // Use for look-ahead - this.start_ = start; - this.array_ = array; - this.predicate_ = predicate; - this.end_ = Math.min(array.length, start + length); - this.nextIdx_ = start - 1; // use -1 so initial advance works. - this.next(); // ignoring result. -} - -; - -/** - * @return {Object} - */ -Iterator.prototype.next = function () { - if (!this.hasNext) { - return null; - } - var obj = this.peek; - - var nextIdx = this.nextIdx_ + 1; - var found = false; - while (nextIdx < this.end_) { - if (!this.predicate_ || this.predicate_(this.array_, nextIdx)) { - this.peek = this.array_[nextIdx]; - found = true; - break; - } - nextIdx++; - } - this.nextIdx_ = nextIdx; - if (!found) { - this.hasNext = false; - this.peek = null; - } - return obj; -}; - -/** - * Returns a new iterator over array, between indexes start and - * start + length, and only returns entries that pass the accept function - * - * @param {!Array} array the array to iterate over. - * @param {number} start the first index to iterate over, 0 if absent. - * @param {number} length the number of elements in the array to iterate over. - * This, along with start, defines a slice of the array, and so length - * doesn't imply the number of elements in the iterator when accept doesn't - * always accept all values. array.length when absent. - * @param {function(?):boolean=} opt_predicate a function that takes - * parameters array and idx, which returns true when the element should be - * returned. If omitted, all elements are accepted. - * @private - */ - -function createIterator(array, start, length, opt_predicate) { - return new Iterator(array, start, length, opt_predicate); -} - -; - -// Shim layer with setTimeout fallback. -// From: http://paulirish.com/2011/requestanimationframe-for-smart-animating/ -// Should be called with the window context: -// Dygraph.requestAnimFrame.call(window, function() {}) -var requestAnimFrame = (function () { - return typeof window !== "undefined" && (window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame) || function (callback) { - setTimeout(callback, 1000 / 60); - }; -})(); - -exports.requestAnimFrame = requestAnimFrame; -/** - * Call a function at most maxFrames times at an attempted interval of - * framePeriodInMillis, then call a cleanup function once. repeatFn is called - * once immediately, then at most (maxFrames - 1) times asynchronously. If - * maxFrames==1, then cleanup_fn() is also called synchronously. This function - * is used to sequence animation. - * @param {function(number)} repeatFn Called repeatedly -- takes the frame - * number (from 0 to maxFrames-1) as an argument. - * @param {number} maxFrames The max number of times to call repeatFn - * @param {number} framePeriodInMillis Max requested time between frames. - * @param {function()} cleanupFn A function to call after all repeatFn calls. - * @private - */ - -function repeatAndCleanup(repeatFn, maxFrames, framePeriodInMillis, cleanupFn) { - var frameNumber = 0; - var previousFrameNumber; - var startTime = new Date().getTime(); - repeatFn(frameNumber); - if (maxFrames == 1) { - cleanupFn(); - return; - } - var maxFrameArg = maxFrames - 1; - - (function loop() { - if (frameNumber >= maxFrames) return; - requestAnimFrame.call(window, function () { - // Determine which frame to draw based on the delay so far. Will skip - // frames if necessary. - var currentTime = new Date().getTime(); - var delayInMillis = currentTime - startTime; - previousFrameNumber = frameNumber; - frameNumber = Math.floor(delayInMillis / framePeriodInMillis); - var frameDelta = frameNumber - previousFrameNumber; - // If we predict that the subsequent repeatFn call will overshoot our - // total frame target, so our last call will cause a stutter, then jump to - // the last call immediately. If we're going to cause a stutter, better - // to do it faster than slower. - var predictOvershootStutter = frameNumber + frameDelta > maxFrameArg; - if (predictOvershootStutter || frameNumber >= maxFrameArg) { - repeatFn(maxFrameArg); // Ensure final call with maxFrameArg. - cleanupFn(); - } else { - if (frameDelta !== 0) { - // Don't call repeatFn with duplicate frames. - repeatFn(frameNumber); - } - loop(); - } - }); - })(); -} - -; - -// A whitelist of options that do not change pixel positions. -var pixelSafeOptions = { - 'annotationClickHandler': true, - 'annotationDblClickHandler': true, - 'annotationMouseOutHandler': true, - 'annotationMouseOverHandler': true, - 'axisLineColor': true, - 'axisLineWidth': true, - 'clickCallback': true, - 'drawCallback': true, - 'drawHighlightPointCallback': true, - 'drawPoints': true, - 'drawPointCallback': true, - 'drawGrid': true, - 'fillAlpha': true, - 'gridLineColor': true, - 'gridLineWidth': true, - 'hideOverlayOnMouseOut': true, - 'highlightCallback': true, - 'highlightCircleSize': true, - 'interactionModel': true, - 'labelsDiv': true, - 'labelsKMB': true, - 'labelsKMG2': true, - 'labelsSeparateLines': true, - 'labelsShowZeroValues': true, - 'legend': true, - 'panEdgeFraction': true, - 'pixelsPerYLabel': true, - 'pointClickCallback': true, - 'pointSize': true, - 'rangeSelectorPlotFillColor': true, - 'rangeSelectorPlotFillGradientColor': true, - 'rangeSelectorPlotStrokeColor': true, - 'rangeSelectorBackgroundStrokeColor': true, - 'rangeSelectorBackgroundLineWidth': true, - 'rangeSelectorPlotLineWidth': true, - 'rangeSelectorForegroundStrokeColor': true, - 'rangeSelectorForegroundLineWidth': true, - 'rangeSelectorAlpha': true, - 'showLabelsOnHighlight': true, - 'showRoller': true, - 'strokeWidth': true, - 'underlayCallback': true, - 'unhighlightCallback': true, - 'zoomCallback': true -}; - -/** - * This function will scan the option list and determine if they - * require us to recalculate the pixel positions of each point. - * TODO: move this into dygraph-options.js - * @param {!Array.} labels a list of options to check. - * @param {!Object} attrs - * @return {boolean} true if the graph needs new points else false. - * @private - */ - -function isPixelChangingOptionList(labels, attrs) { - // Assume that we do not require new points. - // This will change to true if we actually do need new points. - - // Create a dictionary of series names for faster lookup. - // If there are no labels, then the dictionary stays empty. - var seriesNamesDictionary = {}; - if (labels) { - for (var i = 1; i < labels.length; i++) { - seriesNamesDictionary[labels[i]] = true; - } - } - - // Scan through a flat (i.e. non-nested) object of options. - // Returns true/false depending on whether new points are needed. - var scanFlatOptions = function scanFlatOptions(options) { - for (var property in options) { - if (options.hasOwnProperty(property) && !pixelSafeOptions[property]) { - return true; - } - } - return false; - }; - - // Iterate through the list of updated options. - for (var property in attrs) { - if (!attrs.hasOwnProperty(property)) continue; - - // Find out of this field is actually a series specific options list. - if (property == 'highlightSeriesOpts' || seriesNamesDictionary[property] && !attrs.series) { - // This property value is a list of options for this series. - if (scanFlatOptions(attrs[property])) return true; - } else if (property == 'series' || property == 'axes') { - // This is twice-nested options list. - var perSeries = attrs[property]; - for (var series in perSeries) { - if (perSeries.hasOwnProperty(series) && scanFlatOptions(perSeries[series])) { - return true; - } - } - } else { - // If this was not a series specific option list, check if it's a pixel - // changing property. - if (!pixelSafeOptions[property]) return true; - } - } - - return false; -} - -; - -var Circles = { - DEFAULT: function DEFAULT(g, name, ctx, canvasx, canvasy, color, radius) { - ctx.beginPath(); - ctx.fillStyle = color; - ctx.arc(canvasx, canvasy, radius, 0, 2 * Math.PI, false); - ctx.fill(); - } - // For more shapes, include extras/shapes.js -}; - -exports.Circles = Circles; -/** - * Determine whether |data| is delimited by CR, CRLF, LF, LFCR. - * @param {string} data - * @return {?string} the delimiter that was detected (or null on failure). - */ - -function detectLineDelimiter(data) { - for (var i = 0; i < data.length; i++) { - var code = data.charAt(i); - if (code === '\r') { - // Might actually be "\r\n". - if (i + 1 < data.length && data.charAt(i + 1) === '\n') { - return '\r\n'; - } - return code; - } - if (code === '\n') { - // Might actually be "\n\r". - if (i + 1 < data.length && data.charAt(i + 1) === '\r') { - return '\n\r'; - } - return code; - } - } - - return null; -} - -; - -/** - * Is one node contained by another? - * @param {Node} containee The contained node. - * @param {Node} container The container node. - * @return {boolean} Whether containee is inside (or equal to) container. - * @private - */ - -function isNodeContainedBy(containee, container) { - if (container === null || containee === null) { - return false; - } - var containeeNode = /** @type {Node} */containee; - while (containeeNode && containeeNode !== container) { - containeeNode = containeeNode.parentNode; - } - return containeeNode === container; -} - -; - -// This masks some numeric issues in older versions of Firefox, -// where 1.0/Math.pow(10,2) != Math.pow(10,-2). -/** @type {function(number,number):number} */ - -function pow(base, exp) { - if (exp < 0) { - return 1.0 / Math.pow(base, -exp); - } - return Math.pow(base, exp); -} - -; - -var RGBA_RE = /^rgba?\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})(?:,\s*([01](?:\.\d+)?))?\)$/; - -/** - * Helper for toRGB_ which parses strings of the form: - * rgb(123, 45, 67) - * rgba(123, 45, 67, 0.5) - * @return parsed {r,g,b,a?} tuple or null. - */ -function parseRGBA(rgbStr) { - var bits = RGBA_RE.exec(rgbStr); - if (!bits) return null; - var r = parseInt(bits[1], 10), - g = parseInt(bits[2], 10), - b = parseInt(bits[3], 10); - if (bits[4]) { - return { r: r, g: g, b: b, a: parseFloat(bits[4]) }; - } else { - return { r: r, g: g, b: b }; - } -} - -/** - * Converts any valid CSS color (hex, rgb(), named color) to an RGB tuple. - * - * @param {!string} colorStr Any valid CSS color string. - * @return {{r:number,g:number,b:number,a:number?}} Parsed RGB tuple. - * @private - */ - -function toRGB_(colorStr) { - // Strategy: First try to parse colorStr directly. This is fast & avoids DOM - // manipulation. If that fails (e.g. for named colors like 'red'), then - // create a hidden DOM element and parse its computed color. - var rgb = parseRGBA(colorStr); - if (rgb) return rgb; - - var div = document.createElement('div'); - div.style.backgroundColor = colorStr; - div.style.visibility = 'hidden'; - document.body.appendChild(div); - var rgbStr = window.getComputedStyle(div, null).backgroundColor; - document.body.removeChild(div); - return parseRGBA(rgbStr); -} - -; - -/** - * Checks whether the browser supports the <canvas> tag. - * @param {HTMLCanvasElement=} opt_canvasElement Pass a canvas element as an - * optimization if you have one. - * @return {boolean} Whether the browser supports canvas. - */ - -function isCanvasSupported(opt_canvasElement) { - try { - var canvas = opt_canvasElement || document.createElement("canvas"); - canvas.getContext("2d"); - } catch (e) { - return false; - } - return true; -} - -; - -/** - * Parses the value as a floating point number. This is like the parseFloat() - * built-in, but with a few differences: - * - the empty string is parsed as null, rather than NaN. - * - if the string cannot be parsed at all, an error is logged. - * If the string can't be parsed, this method returns null. - * @param {string} x The string to be parsed - * @param {number=} opt_line_no The line number from which the string comes. - * @param {string=} opt_line The text of the line from which the string comes. - */ - -function parseFloat_(x, opt_line_no, opt_line) { - var val = parseFloat(x); - if (!isNaN(val)) return val; - - // Try to figure out what happeend. - // If the value is the empty string, parse it as null. - if (/^ *$/.test(x)) return null; - - // If it was actually "NaN", return it as NaN. - if (/^ *nan *$/i.test(x)) return NaN; - - // Looks like a parsing error. - var msg = "Unable to parse '" + x + "' as a number"; - if (opt_line !== undefined && opt_line_no !== undefined) { - msg += " on line " + (1 + (opt_line_no || 0)) + " ('" + opt_line + "') of CSV."; - } - console.error(msg); - - return null; -} - -; - -// Label constants for the labelsKMB and labelsKMG2 options. -// (i.e. '100000' -> '100K') -var KMB_LABELS = ['K', 'M', 'B', 'T', 'Q']; -var KMG2_BIG_LABELS = ['k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y']; -var KMG2_SMALL_LABELS = ['m', 'u', 'n', 'p', 'f', 'a', 'z', 'y']; - -/** - * @private - * Return a string version of a number. This respects the digitsAfterDecimal - * and maxNumberWidth options. - * @param {number} x The number to be formatted - * @param {Dygraph} opts An options view - */ - -function numberValueFormatter(x, opts) { - var sigFigs = opts('sigFigs'); - - if (sigFigs !== null) { - // User has opted for a fixed number of significant figures. - return floatFormat(x, sigFigs); - } - - var digits = opts('digitsAfterDecimal'); - var maxNumberWidth = opts('maxNumberWidth'); - - var kmb = opts('labelsKMB'); - var kmg2 = opts('labelsKMG2'); - - var label; - - // switch to scientific notation if we underflow or overflow fixed display. - if (x !== 0.0 && (Math.abs(x) >= Math.pow(10, maxNumberWidth) || Math.abs(x) < Math.pow(10, -digits))) { - label = x.toExponential(digits); - } else { - label = '' + round_(x, digits); - } - - if (kmb || kmg2) { - var k; - var k_labels = []; - var m_labels = []; - if (kmb) { - k = 1000; - k_labels = KMB_LABELS; - } - if (kmg2) { - if (kmb) console.warn("Setting both labelsKMB and labelsKMG2. Pick one!"); - k = 1024; - k_labels = KMG2_BIG_LABELS; - m_labels = KMG2_SMALL_LABELS; - } - - var absx = Math.abs(x); - var n = pow(k, k_labels.length); - for (var j = k_labels.length - 1; j >= 0; j--, n /= k) { - if (absx >= n) { - label = round_(x / n, digits) + k_labels[j]; - break; - } - } - if (kmg2) { - // TODO(danvk): clean up this logic. Why so different than kmb? - var x_parts = String(x.toExponential()).split('e-'); - if (x_parts.length === 2 && x_parts[1] >= 3 && x_parts[1] <= 24) { - if (x_parts[1] % 3 > 0) { - label = round_(x_parts[0] / pow(10, x_parts[1] % 3), digits); - } else { - label = Number(x_parts[0]).toFixed(2); - } - label += m_labels[Math.floor(x_parts[1] / 3) - 1]; - } - } - } - - return label; -} - -; - -/** - * variant for use as an axisLabelFormatter. - * @private - */ - -function numberAxisLabelFormatter(x, granularity, opts) { - return numberValueFormatter.call(this, x, opts); -} - -; - -/** - * @type {!Array.} - * @private - * @constant - */ -var SHORT_MONTH_NAMES_ = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; - -/** - * Convert a JS date to a string appropriate to display on an axis that - * is displaying values at the stated granularity. This respects the - * labelsUTC option. - * @param {Date} date The date to format - * @param {number} granularity One of the Dygraph granularity constants - * @param {Dygraph} opts An options view - * @return {string} The date formatted as local time - * @private - */ - -function dateAxisLabelFormatter(date, granularity, opts) { - var utc = opts('labelsUTC'); - var accessors = utc ? DateAccessorsUTC : DateAccessorsLocal; - - var year = accessors.getFullYear(date), - month = accessors.getMonth(date), - day = accessors.getDate(date), - hours = accessors.getHours(date), - mins = accessors.getMinutes(date), - secs = accessors.getSeconds(date), - millis = accessors.getMilliseconds(date); - - if (granularity >= DygraphTickers.Granularity.DECADAL) { - return '' + year; - } else if (granularity >= DygraphTickers.Granularity.MONTHLY) { - return SHORT_MONTH_NAMES_[month] + ' ' + year; - } else { - var frac = hours * 3600 + mins * 60 + secs + 1e-3 * millis; - if (frac === 0 || granularity >= DygraphTickers.Granularity.DAILY) { - // e.g. '21 Jan' (%d%b) - return zeropad(day) + ' ' + SHORT_MONTH_NAMES_[month]; - } else if (granularity < DygraphTickers.Granularity.SECONDLY) { - // e.g. 40.310 (meaning 40 seconds and 310 milliseconds) - var str = "" + millis; - return zeropad(secs) + "." + ('000' + str).substring(str.length); - } else if (granularity > DygraphTickers.Granularity.MINUTELY) { - return hmsString_(hours, mins, secs, 0); - } else { - return hmsString_(hours, mins, secs, millis); - } - } -} - -; -// alias in case anyone is referencing the old method. -// Dygraph.dateAxisFormatter = Dygraph.dateAxisLabelFormatter; - -/** - * Return a string version of a JS date for a value label. This respects the - * labelsUTC option. - * @param {Date} date The date to be formatted - * @param {Dygraph} opts An options view - * @private - */ - -function dateValueFormatter(d, opts) { - return dateString_(d, opts('labelsUTC')); -} - -; - -},{"./dygraph-tickers":16}],18:[function(require,module,exports){ -(function (process){ -/** - * @license - * Copyright 2006 Dan Vanderkam (danvdk@gmail.com) - * MIT-licensed (http://opensource.org/licenses/MIT) - */ /** - * @fileoverview Creates an interactive, zoomable graph based on a CSV file or - * string. Dygraph can handle multiple series with or without error bars. The - * date/value ranges will be automatically set. Dygraph uses the - * <canvas> tag, so it only works in FF1.5+. - * @author danvdk@gmail.com (Dan Vanderkam) - - Usage: -
- - - The CSV file is of the form - - Date,SeriesA,SeriesB,SeriesC - YYYYMMDD,A1,B1,C1 - YYYYMMDD,A2,B2,C2 - - If the 'errorBars' option is set in the constructor, the input should be of - the form - Date,SeriesA,SeriesB,... - YYYYMMDD,A1,sigmaA1,B1,sigmaB1,... - YYYYMMDD,A2,sigmaA2,B2,sigmaB2,... - - If the 'fractions' option is set, the input should be of the form: - - Date,SeriesA,SeriesB,... - YYYYMMDD,A1/B1,A2/B2,... - YYYYMMDD,A1/B1,A2/B2,... - - And error bars will be calculated automatically using a binomial distribution. - - For further documentation and examples, see http://dygraphs.com/ - */'use strict';Object.defineProperty(exports,'__esModule',{value:true});var _slicedToArray=(function(){function sliceIterator(arr,i){var _arr=[];var _n=true;var _d=false;var _e=undefined;try{for(var _i=arr[Symbol.iterator](),_s;!(_n = (_s = _i.next()).done);_n = true) {_arr.push(_s.value);if(i && _arr.length === i)break;}}catch(err) {_d = true;_e = err;}finally {try{if(!_n && _i['return'])_i['return']();}finally {if(_d)throw _e;}}return _arr;}return function(arr,i){if(Array.isArray(arr)){return arr;}else if(Symbol.iterator in Object(arr)){return sliceIterator(arr,i);}else {throw new TypeError('Invalid attempt to destructure non-iterable instance');}};})();function _interopRequireWildcard(obj){if(obj && obj.__esModule){return obj;}else {var newObj={};if(obj != null){for(var key in obj) {if(Object.prototype.hasOwnProperty.call(obj,key))newObj[key] = obj[key];}}newObj['default'] = obj;return newObj;}}function _interopRequireDefault(obj){return obj && obj.__esModule?obj:{'default':obj};}var _dygraphLayout=require('./dygraph-layout');var _dygraphLayout2=_interopRequireDefault(_dygraphLayout);var _dygraphCanvas=require('./dygraph-canvas');var _dygraphCanvas2=_interopRequireDefault(_dygraphCanvas);var _dygraphOptions=require('./dygraph-options');var _dygraphOptions2=_interopRequireDefault(_dygraphOptions);var _dygraphInteractionModel=require('./dygraph-interaction-model');var _dygraphInteractionModel2=_interopRequireDefault(_dygraphInteractionModel);var _dygraphTickers=require('./dygraph-tickers');var DygraphTickers=_interopRequireWildcard(_dygraphTickers);var _dygraphUtils=require('./dygraph-utils');var utils=_interopRequireWildcard(_dygraphUtils);var _dygraphDefaultAttrs=require('./dygraph-default-attrs');var _dygraphDefaultAttrs2=_interopRequireDefault(_dygraphDefaultAttrs);var _dygraphOptionsReference=require('./dygraph-options-reference');var _dygraphOptionsReference2=_interopRequireDefault(_dygraphOptionsReference);var _iframeTarp=require('./iframe-tarp');var _iframeTarp2=_interopRequireDefault(_iframeTarp);var _datahandlerDefault=require('./datahandler/default');var _datahandlerDefault2=_interopRequireDefault(_datahandlerDefault);var _datahandlerBarsError=require('./datahandler/bars-error');var _datahandlerBarsError2=_interopRequireDefault(_datahandlerBarsError);var _datahandlerBarsCustom=require('./datahandler/bars-custom');var _datahandlerBarsCustom2=_interopRequireDefault(_datahandlerBarsCustom);var _datahandlerDefaultFractions=require('./datahandler/default-fractions');var _datahandlerDefaultFractions2=_interopRequireDefault(_datahandlerDefaultFractions);var _datahandlerBarsFractions=require('./datahandler/bars-fractions');var _datahandlerBarsFractions2=_interopRequireDefault(_datahandlerBarsFractions);var _datahandlerBars=require('./datahandler/bars');var _datahandlerBars2=_interopRequireDefault(_datahandlerBars);var _pluginsAnnotations=require('./plugins/annotations');var _pluginsAnnotations2=_interopRequireDefault(_pluginsAnnotations);var _pluginsAxes=require('./plugins/axes');var _pluginsAxes2=_interopRequireDefault(_pluginsAxes);var _pluginsChartLabels=require('./plugins/chart-labels');var _pluginsChartLabels2=_interopRequireDefault(_pluginsChartLabels);var _pluginsGrid=require('./plugins/grid');var _pluginsGrid2=_interopRequireDefault(_pluginsGrid);var _pluginsLegend=require('./plugins/legend');var _pluginsLegend2=_interopRequireDefault(_pluginsLegend);var _pluginsRangeSelector=require('./plugins/range-selector');var _pluginsRangeSelector2=_interopRequireDefault(_pluginsRangeSelector);var _dygraphGviz=require('./dygraph-gviz');var _dygraphGviz2=_interopRequireDefault(_dygraphGviz);"use strict"; /** - * Creates an interactive, zoomable chart. - * - * @constructor - * @param {div | String} div A div or the id of a div into which to construct - * the chart. - * @param {String | Function} file A file containing CSV data or a function - * that returns this data. The most basic expected format for each line is - * "YYYY/MM/DD,val1,val2,...". For more information, see - * http://dygraphs.com/data.html. - * @param {Object} attrs Various other attributes, e.g. errorBars determines - * whether the input data contains error ranges. For a complete list of - * options, see http://dygraphs.com/options.html. - */var Dygraph=function Dygraph(div,data,opts){this.__init__(div,data,opts);};Dygraph.NAME = "Dygraph";Dygraph.VERSION = "2.1.0"; // Various default values -Dygraph.DEFAULT_ROLL_PERIOD = 1;Dygraph.DEFAULT_WIDTH = 480;Dygraph.DEFAULT_HEIGHT = 320; // For max 60 Hz. animation: -Dygraph.ANIMATION_STEPS = 12;Dygraph.ANIMATION_DURATION = 200; /** - * Standard plotters. These may be used by clients. - * Available plotters are: - * - Dygraph.Plotters.linePlotter: draws central lines (most common) - * - Dygraph.Plotters.errorPlotter: draws error bars - * - Dygraph.Plotters.fillPlotter: draws fills under lines (used with fillGraph) - * - * By default, the plotter is [fillPlotter, errorPlotter, linePlotter]. - * This causes all the lines to be drawn over all the fills/error bars. - */Dygraph.Plotters = _dygraphCanvas2['default']._Plotters; // Used for initializing annotation CSS rules only once. -Dygraph.addedAnnotationCSS = false; /** - * Initializes the Dygraph. This creates a new DIV and constructs the PlotKit - * and context <canvas> inside of it. See the constructor for details. - * on the parameters. - * @param {Element} div the Element to render the graph into. - * @param {string | Function} file Source data - * @param {Object} attrs Miscellaneous other options - * @private - */Dygraph.prototype.__init__ = function(div,file,attrs){this.is_initial_draw_ = true;this.readyFns_ = []; // Support two-argument constructor -if(attrs === null || attrs === undefined){attrs = {};}attrs = Dygraph.copyUserAttrs_(attrs);if(typeof div == 'string'){div = document.getElementById(div);}if(!div){throw new Error('Constructing dygraph with a non-existent div!');} // Copy the important bits into the object -// TODO(danvk): most of these should just stay in the attrs_ dictionary. -this.maindiv_ = div;this.file_ = file;this.rollPeriod_ = attrs.rollPeriod || Dygraph.DEFAULT_ROLL_PERIOD;this.previousVerticalX_ = -1;this.fractions_ = attrs.fractions || false;this.dateWindow_ = attrs.dateWindow || null;this.annotations_ = []; // Clear the div. This ensure that, if multiple dygraphs are passed the same -// div, then only one will be drawn. -div.innerHTML = ""; // For historical reasons, the 'width' and 'height' options trump all CSS -// rules _except_ for an explicit 'width' or 'height' on the div. -// As an added convenience, if the div has zero height (like
does -// without any styles), then we use a default height/width. -if(div.style.width === '' && attrs.width){div.style.width = attrs.width + "px";}if(div.style.height === '' && attrs.height){div.style.height = attrs.height + "px";}if(div.style.height === '' && div.clientHeight === 0){div.style.height = Dygraph.DEFAULT_HEIGHT + "px";if(div.style.width === ''){div.style.width = Dygraph.DEFAULT_WIDTH + "px";}} // These will be zero if the dygraph's div is hidden. In that case, -// use the user-specified attributes if present. If not, use zero -// and assume the user will call resize to fix things later. -this.width_ = div.clientWidth || attrs.width || 0;this.height_ = div.clientHeight || attrs.height || 0; // TODO(danvk): set fillGraph to be part of attrs_ here, not user_attrs_. -if(attrs.stackedGraph){attrs.fillGraph = true; // TODO(nikhilk): Add any other stackedGraph checks here. -} // DEPRECATION WARNING: All option processing should be moved from -// attrs_ and user_attrs_ to options_, which holds all this information. -// -// Dygraphs has many options, some of which interact with one another. -// To keep track of everything, we maintain two sets of options: -// -// this.user_attrs_ only options explicitly set by the user. -// this.attrs_ defaults, options derived from user_attrs_, data. -// -// Options are then accessed this.attr_('attr'), which first looks at -// user_attrs_ and then computed attrs_. This way Dygraphs can set intelligent -// defaults without overriding behavior that the user specifically asks for. -this.user_attrs_ = {};utils.update(this.user_attrs_,attrs); // This sequence ensures that Dygraph.DEFAULT_ATTRS is never modified. -this.attrs_ = {};utils.updateDeep(this.attrs_,_dygraphDefaultAttrs2['default']);this.boundaryIds_ = [];this.setIndexByName_ = {};this.datasetIndex_ = [];this.registeredEvents_ = [];this.eventListeners_ = {};this.attributes_ = new _dygraphOptions2['default'](this); // Create the containing DIV and other interactive elements -this.createInterface_(); // Activate plugins. -this.plugins_ = [];var plugins=Dygraph.PLUGINS.concat(this.getOption('plugins'));for(var i=0;i < plugins.length;i++) { // the plugins option may contain either plugin classes or instances. -// Plugin instances contain an activate method. -var Plugin=plugins[i]; // either a constructor or an instance. -var pluginInstance;if(typeof Plugin.activate !== 'undefined'){pluginInstance = Plugin;}else {pluginInstance = new Plugin();}var pluginDict={plugin:pluginInstance,events:{},options:{},pluginOptions:{}};var handlers=pluginInstance.activate(this);for(var eventName in handlers) {if(!handlers.hasOwnProperty(eventName))continue; // TODO(danvk): validate eventName. -pluginDict.events[eventName] = handlers[eventName];}this.plugins_.push(pluginDict);} // At this point, plugins can no longer register event handlers. -// Construct a map from event -> ordered list of [callback, plugin]. -for(var i=0;i < this.plugins_.length;i++) {var plugin_dict=this.plugins_[i];for(var eventName in plugin_dict.events) {if(!plugin_dict.events.hasOwnProperty(eventName))continue;var callback=plugin_dict.events[eventName];var pair=[plugin_dict.plugin,callback];if(!(eventName in this.eventListeners_)){this.eventListeners_[eventName] = [pair];}else {this.eventListeners_[eventName].push(pair);}}}this.createDragInterface_();this.start_();}; /** - * Triggers a cascade of events to the various plugins which are interested in them. - * Returns true if the "default behavior" should be prevented, i.e. if one - * of the event listeners called event.preventDefault(). - * @private - */Dygraph.prototype.cascadeEvents_ = function(name,extra_props){if(!(name in this.eventListeners_))return false; // QUESTION: can we use objects & prototypes to speed this up? -var e={dygraph:this,cancelable:false,defaultPrevented:false,preventDefault:function preventDefault(){if(!e.cancelable)throw "Cannot call preventDefault on non-cancelable event.";e.defaultPrevented = true;},propagationStopped:false,stopPropagation:function stopPropagation(){e.propagationStopped = true;}};utils.update(e,extra_props);var callback_plugin_pairs=this.eventListeners_[name];if(callback_plugin_pairs){for(var i=callback_plugin_pairs.length - 1;i >= 0;i--) {var plugin=callback_plugin_pairs[i][0];var callback=callback_plugin_pairs[i][1];callback.call(plugin,e);if(e.propagationStopped)break;}}return e.defaultPrevented;}; /** - * Fetch a plugin instance of a particular class. Only for testing. - * @private - * @param {!Class} type The type of the plugin. - * @return {Object} Instance of the plugin, or null if there is none. - */Dygraph.prototype.getPluginInstance_ = function(type){for(var i=0;i < this.plugins_.length;i++) {var p=this.plugins_[i];if(p.plugin instanceof type){return p.plugin;}}return null;}; /** - * Returns the zoomed status of the chart for one or both axes. - * - * Axis is an optional parameter. Can be set to 'x' or 'y'. - * - * The zoomed status for an axis is set whenever a user zooms using the mouse - * or when the dateWindow or valueRange are updated. Double-clicking or calling - * resetZoom() resets the zoom status for the chart. - */Dygraph.prototype.isZoomed = function(axis){var isZoomedX=!!this.dateWindow_;if(axis === 'x')return isZoomedX;var isZoomedY=this.axes_.map(function(axis){return !!axis.valueRange;}).indexOf(true) >= 0;if(axis === null || axis === undefined){return isZoomedX || isZoomedY;}if(axis === 'y')return isZoomedY;throw new Error('axis parameter is [' + axis + '] must be null, \'x\' or \'y\'.');}; /** - * Returns information about the Dygraph object, including its containing ID. - */Dygraph.prototype.toString = function(){var maindiv=this.maindiv_;var id=maindiv && maindiv.id?maindiv.id:maindiv;return "[Dygraph " + id + "]";}; /** - * @private - * Returns the value of an option. This may be set by the user (either in the - * constructor or by calling updateOptions) or by dygraphs, and may be set to a - * per-series value. - * @param {string} name The name of the option, e.g. 'rollPeriod'. - * @param {string} [seriesName] The name of the series to which the option - * will be applied. If no per-series value of this option is available, then - * the global value is returned. This is optional. - * @return { ... } The value of the option. - */Dygraph.prototype.attr_ = function(name,seriesName){ // For "production" code, this gets removed by uglifyjs. -if(typeof process !== 'undefined'){if("development" != 'production'){if(typeof _dygraphOptionsReference2['default'] === 'undefined'){console.error('Must include options reference JS for testing');}else if(!_dygraphOptionsReference2['default'].hasOwnProperty(name)){console.error('Dygraphs is using property ' + name + ', which has no ' + 'entry in the Dygraphs.OPTIONS_REFERENCE listing.'); // Only log this error once. -_dygraphOptionsReference2['default'][name] = true;}}}return seriesName?this.attributes_.getForSeries(name,seriesName):this.attributes_.get(name);}; /** - * Returns the current value for an option, as set in the constructor or via - * updateOptions. You may pass in an (optional) series name to get per-series - * values for the option. - * - * All values returned by this method should be considered immutable. If you - * modify them, there is no guarantee that the changes will be honored or that - * dygraphs will remain in a consistent state. If you want to modify an option, - * use updateOptions() instead. - * - * @param {string} name The name of the option (e.g. 'strokeWidth') - * @param {string=} opt_seriesName Series name to get per-series values. - * @return {*} The value of the option. - */Dygraph.prototype.getOption = function(name,opt_seriesName){return this.attr_(name,opt_seriesName);}; /** - * Like getOption(), but specifically returns a number. - * This is a convenience function for working with the Closure Compiler. - * @param {string} name The name of the option (e.g. 'strokeWidth') - * @param {string=} opt_seriesName Series name to get per-series values. - * @return {number} The value of the option. - * @private - */Dygraph.prototype.getNumericOption = function(name,opt_seriesName){return (/** @type{number} */this.getOption(name,opt_seriesName));}; /** - * Like getOption(), but specifically returns a string. - * This is a convenience function for working with the Closure Compiler. - * @param {string} name The name of the option (e.g. 'strokeWidth') - * @param {string=} opt_seriesName Series name to get per-series values. - * @return {string} The value of the option. - * @private - */Dygraph.prototype.getStringOption = function(name,opt_seriesName){return (/** @type{string} */this.getOption(name,opt_seriesName));}; /** - * Like getOption(), but specifically returns a boolean. - * This is a convenience function for working with the Closure Compiler. - * @param {string} name The name of the option (e.g. 'strokeWidth') - * @param {string=} opt_seriesName Series name to get per-series values. - * @return {boolean} The value of the option. - * @private - */Dygraph.prototype.getBooleanOption = function(name,opt_seriesName){return (/** @type{boolean} */this.getOption(name,opt_seriesName));}; /** - * Like getOption(), but specifically returns a function. - * This is a convenience function for working with the Closure Compiler. - * @param {string} name The name of the option (e.g. 'strokeWidth') - * @param {string=} opt_seriesName Series name to get per-series values. - * @return {function(...)} The value of the option. - * @private - */Dygraph.prototype.getFunctionOption = function(name,opt_seriesName){return (/** @type{function(...)} */this.getOption(name,opt_seriesName));};Dygraph.prototype.getOptionForAxis = function(name,axis){return this.attributes_.getForAxis(name,axis);}; /** - * @private - * @param {string} axis The name of the axis (i.e. 'x', 'y' or 'y2') - * @return { ... } A function mapping string -> option value - */Dygraph.prototype.optionsViewForAxis_ = function(axis){var self=this;return function(opt){var axis_opts=self.user_attrs_.axes;if(axis_opts && axis_opts[axis] && axis_opts[axis].hasOwnProperty(opt)){return axis_opts[axis][opt];} // I don't like that this is in a second spot. -if(axis === 'x' && opt === 'logscale'){ // return the default value. -// TODO(konigsberg): pull the default from a global default. -return false;} // user-specified attributes always trump defaults, even if they're less -// specific. -if(typeof self.user_attrs_[opt] != 'undefined'){return self.user_attrs_[opt];}axis_opts = self.attrs_.axes;if(axis_opts && axis_opts[axis] && axis_opts[axis].hasOwnProperty(opt)){return axis_opts[axis][opt];} // check old-style axis options -// TODO(danvk): add a deprecation warning if either of these match. -if(axis == 'y' && self.axes_[0].hasOwnProperty(opt)){return self.axes_[0][opt];}else if(axis == 'y2' && self.axes_[1].hasOwnProperty(opt)){return self.axes_[1][opt];}return self.attr_(opt);};}; /** - * Returns the current rolling period, as set by the user or an option. - * @return {number} The number of points in the rolling window - */Dygraph.prototype.rollPeriod = function(){return this.rollPeriod_;}; /** - * Returns the currently-visible x-range. This can be affected by zooming, - * panning or a call to updateOptions. - * Returns a two-element array: [left, right]. - * If the Dygraph has dates on the x-axis, these will be millis since epoch. - */Dygraph.prototype.xAxisRange = function(){return this.dateWindow_?this.dateWindow_:this.xAxisExtremes();}; /** - * Returns the lower- and upper-bound x-axis values of the data set. - */Dygraph.prototype.xAxisExtremes = function(){var pad=this.getNumericOption('xRangePad') / this.plotter_.area.w;if(this.numRows() === 0){return [0 - pad,1 + pad];}var left=this.rawData_[0][0];var right=this.rawData_[this.rawData_.length - 1][0];if(pad){ // Must keep this in sync with dygraph-layout _evaluateLimits() -var range=right - left;left -= range * pad;right += range * pad;}return [left,right];}; /** - * Returns the lower- and upper-bound y-axis values for each axis. These are - * the ranges you'll get if you double-click to zoom out or call resetZoom(). - * The return value is an array of [low, high] tuples, one for each y-axis. - */Dygraph.prototype.yAxisExtremes = function(){ // TODO(danvk): this is pretty inefficient -var packed=this.gatherDatasets_(this.rolledSeries_,null);var extremes=packed.extremes;var saveAxes=this.axes_;this.computeYAxisRanges_(extremes);var newAxes=this.axes_;this.axes_ = saveAxes;return newAxes.map(function(axis){return axis.extremeRange;});}; /** - * Returns the currently-visible y-range for an axis. This can be affected by - * zooming, panning or a call to updateOptions. Axis indices are zero-based. If - * called with no arguments, returns the range of the first axis. - * Returns a two-element array: [bottom, top]. - */Dygraph.prototype.yAxisRange = function(idx){if(typeof idx == "undefined")idx = 0;if(idx < 0 || idx >= this.axes_.length){return null;}var axis=this.axes_[idx];return [axis.computedValueRange[0],axis.computedValueRange[1]];}; /** - * Returns the currently-visible y-ranges for each axis. This can be affected by - * zooming, panning, calls to updateOptions, etc. - * Returns an array of [bottom, top] pairs, one for each y-axis. - */Dygraph.prototype.yAxisRanges = function(){var ret=[];for(var i=0;i < this.axes_.length;i++) {ret.push(this.yAxisRange(i));}return ret;}; // TODO(danvk): use these functions throughout dygraphs. -/** - * Convert from data coordinates to canvas/div X/Y coordinates. - * If specified, do this conversion for the coordinate system of a particular - * axis. Uses the first axis by default. - * Returns a two-element array: [X, Y] - * - * Note: use toDomXCoord instead of toDomCoords(x, null) and use toDomYCoord - * instead of toDomCoords(null, y, axis). - */Dygraph.prototype.toDomCoords = function(x,y,axis){return [this.toDomXCoord(x),this.toDomYCoord(y,axis)];}; /** - * Convert from data x coordinates to canvas/div X coordinate. - * If specified, do this conversion for the coordinate system of a particular - * axis. - * Returns a single value or null if x is null. - */Dygraph.prototype.toDomXCoord = function(x){if(x === null){return null;}var area=this.plotter_.area;var xRange=this.xAxisRange();return area.x + (x - xRange[0]) / (xRange[1] - xRange[0]) * area.w;}; /** - * Convert from data x coordinates to canvas/div Y coordinate and optional - * axis. Uses the first axis by default. - * - * returns a single value or null if y is null. - */Dygraph.prototype.toDomYCoord = function(y,axis){var pct=this.toPercentYCoord(y,axis);if(pct === null){return null;}var area=this.plotter_.area;return area.y + pct * area.h;}; /** - * Convert from canvas/div coords to data coordinates. - * If specified, do this conversion for the coordinate system of a particular - * axis. Uses the first axis by default. - * Returns a two-element array: [X, Y]. - * - * Note: use toDataXCoord instead of toDataCoords(x, null) and use toDataYCoord - * instead of toDataCoords(null, y, axis). - */Dygraph.prototype.toDataCoords = function(x,y,axis){return [this.toDataXCoord(x),this.toDataYCoord(y,axis)];}; /** - * Convert from canvas/div x coordinate to data coordinate. - * - * If x is null, this returns null. - */Dygraph.prototype.toDataXCoord = function(x){if(x === null){return null;}var area=this.plotter_.area;var xRange=this.xAxisRange();if(!this.attributes_.getForAxis("logscale",'x')){return xRange[0] + (x - area.x) / area.w * (xRange[1] - xRange[0]);}else {var pct=(x - area.x) / area.w;return utils.logRangeFraction(xRange[0],xRange[1],pct);}}; /** - * Convert from canvas/div y coord to value. - * - * If y is null, this returns null. - * if axis is null, this uses the first axis. - */Dygraph.prototype.toDataYCoord = function(y,axis){if(y === null){return null;}var area=this.plotter_.area;var yRange=this.yAxisRange(axis);if(typeof axis == "undefined")axis = 0;if(!this.attributes_.getForAxis("logscale",axis)){return yRange[0] + (area.y + area.h - y) / area.h * (yRange[1] - yRange[0]);}else { // Computing the inverse of toDomCoord. -var pct=(y - area.y) / area.h; // Note reversed yRange, y1 is on top with pct==0. -return utils.logRangeFraction(yRange[1],yRange[0],pct);}}; /** - * Converts a y for an axis to a percentage from the top to the - * bottom of the drawing area. - * - * If the coordinate represents a value visible on the canvas, then - * the value will be between 0 and 1, where 0 is the top of the canvas. - * However, this method will return values outside the range, as - * values can fall outside the canvas. - * - * If y is null, this returns null. - * if axis is null, this uses the first axis. - * - * @param {number} y The data y-coordinate. - * @param {number} [axis] The axis number on which the data coordinate lives. - * @return {number} A fraction in [0, 1] where 0 = the top edge. - */Dygraph.prototype.toPercentYCoord = function(y,axis){if(y === null){return null;}if(typeof axis == "undefined")axis = 0;var yRange=this.yAxisRange(axis);var pct;var logscale=this.attributes_.getForAxis("logscale",axis);if(logscale){var logr0=utils.log10(yRange[0]);var logr1=utils.log10(yRange[1]);pct = (logr1 - utils.log10(y)) / (logr1 - logr0);}else { // yRange[1] - y is unit distance from the bottom. -// yRange[1] - yRange[0] is the scale of the range. -// (yRange[1] - y) / (yRange[1] - yRange[0]) is the % from the bottom. -pct = (yRange[1] - y) / (yRange[1] - yRange[0]);}return pct;}; /** - * Converts an x value to a percentage from the left to the right of - * the drawing area. - * - * If the coordinate represents a value visible on the canvas, then - * the value will be between 0 and 1, where 0 is the left of the canvas. - * However, this method will return values outside the range, as - * values can fall outside the canvas. - * - * If x is null, this returns null. - * @param {number} x The data x-coordinate. - * @return {number} A fraction in [0, 1] where 0 = the left edge. - */Dygraph.prototype.toPercentXCoord = function(x){if(x === null){return null;}var xRange=this.xAxisRange();var pct;var logscale=this.attributes_.getForAxis("logscale",'x');if(logscale === true){ // logscale can be null so we test for true explicitly. -var logr0=utils.log10(xRange[0]);var logr1=utils.log10(xRange[1]);pct = (utils.log10(x) - logr0) / (logr1 - logr0);}else { // x - xRange[0] is unit distance from the left. -// xRange[1] - xRange[0] is the scale of the range. -// The full expression below is the % from the left. -pct = (x - xRange[0]) / (xRange[1] - xRange[0]);}return pct;}; /** - * Returns the number of columns (including the independent variable). - * @return {number} The number of columns. - */Dygraph.prototype.numColumns = function(){if(!this.rawData_)return 0;return this.rawData_[0]?this.rawData_[0].length:this.attr_("labels").length;}; /** - * Returns the number of rows (excluding any header/label row). - * @return {number} The number of rows, less any header. - */Dygraph.prototype.numRows = function(){if(!this.rawData_)return 0;return this.rawData_.length;}; /** - * Returns the value in the given row and column. If the row and column exceed - * the bounds on the data, returns null. Also returns null if the value is - * missing. - * @param {number} row The row number of the data (0-based). Row 0 is the - * first row of data, not a header row. - * @param {number} col The column number of the data (0-based) - * @return {number} The value in the specified cell or null if the row/col - * were out of range. - */Dygraph.prototype.getValue = function(row,col){if(row < 0 || row > this.rawData_.length)return null;if(col < 0 || col > this.rawData_[row].length)return null;return this.rawData_[row][col];}; /** - * Generates interface elements for the Dygraph: a containing div, a div to - * display the current point, and a textbox to adjust the rolling average - * period. Also creates the Renderer/Layout elements. - * @private - */Dygraph.prototype.createInterface_ = function(){ // Create the all-enclosing graph div -var enclosing=this.maindiv_;this.graphDiv = document.createElement("div"); // TODO(danvk): any other styles that are useful to set here? -this.graphDiv.style.textAlign = 'left'; // This is a CSS "reset" -this.graphDiv.style.position = 'relative';enclosing.appendChild(this.graphDiv); // Create the canvas for interactive parts of the chart. -this.canvas_ = utils.createCanvas();this.canvas_.style.position = "absolute"; // ... and for static parts of the chart. -this.hidden_ = this.createPlotKitCanvas_(this.canvas_);this.canvas_ctx_ = utils.getContext(this.canvas_);this.hidden_ctx_ = utils.getContext(this.hidden_);this.resizeElements_(); // The interactive parts of the graph are drawn on top of the chart. -this.graphDiv.appendChild(this.hidden_);this.graphDiv.appendChild(this.canvas_);this.mouseEventElement_ = this.createMouseEventElement_(); // Create the grapher -this.layout_ = new _dygraphLayout2['default'](this);var dygraph=this;this.mouseMoveHandler_ = function(e){dygraph.mouseMove_(e);};this.mouseOutHandler_ = function(e){ // The mouse has left the chart if: -// 1. e.target is inside the chart -// 2. e.relatedTarget is outside the chart -var target=e.target || e.fromElement;var relatedTarget=e.relatedTarget || e.toElement;if(utils.isNodeContainedBy(target,dygraph.graphDiv) && !utils.isNodeContainedBy(relatedTarget,dygraph.graphDiv)){dygraph.mouseOut_(e);}};this.addAndTrackEvent(window,'mouseout',this.mouseOutHandler_);this.addAndTrackEvent(this.mouseEventElement_,'mousemove',this.mouseMoveHandler_); // Don't recreate and register the resize handler on subsequent calls. -// This happens when the graph is resized. -if(!this.resizeHandler_){this.resizeHandler_ = function(e){dygraph.resize();}; // Update when the window is resized. -// TODO(danvk): drop frames depending on complexity of the chart. -this.addAndTrackEvent(window,'resize',this.resizeHandler_);}};Dygraph.prototype.resizeElements_ = function(){this.graphDiv.style.width = this.width_ + "px";this.graphDiv.style.height = this.height_ + "px";var pixelRatioOption=this.getNumericOption('pixelRatio');var canvasScale=pixelRatioOption || utils.getContextPixelRatio(this.canvas_ctx_);this.canvas_.width = this.width_ * canvasScale;this.canvas_.height = this.height_ * canvasScale;this.canvas_.style.width = this.width_ + "px"; // for IE -this.canvas_.style.height = this.height_ + "px"; // for IE -if(canvasScale !== 1){this.canvas_ctx_.scale(canvasScale,canvasScale);}var hiddenScale=pixelRatioOption || utils.getContextPixelRatio(this.hidden_ctx_);this.hidden_.width = this.width_ * hiddenScale;this.hidden_.height = this.height_ * hiddenScale;this.hidden_.style.width = this.width_ + "px"; // for IE -this.hidden_.style.height = this.height_ + "px"; // for IE -if(hiddenScale !== 1){this.hidden_ctx_.scale(hiddenScale,hiddenScale);}}; /** - * Detach DOM elements in the dygraph and null out all data references. - * Calling this when you're done with a dygraph can dramatically reduce memory - * usage. See, e.g., the tests/perf.html example. - */Dygraph.prototype.destroy = function(){this.canvas_ctx_.restore();this.hidden_ctx_.restore(); // Destroy any plugins, in the reverse order that they were registered. -for(var i=this.plugins_.length - 1;i >= 0;i--) {var p=this.plugins_.pop();if(p.plugin.destroy)p.plugin.destroy();}var removeRecursive=function removeRecursive(node){while(node.hasChildNodes()) {removeRecursive(node.firstChild);node.removeChild(node.firstChild);}};this.removeTrackedEvents_(); // remove mouse event handlers (This may not be necessary anymore) -utils.removeEvent(window,'mouseout',this.mouseOutHandler_);utils.removeEvent(this.mouseEventElement_,'mousemove',this.mouseMoveHandler_); // remove window handlers -utils.removeEvent(window,'resize',this.resizeHandler_);this.resizeHandler_ = null;removeRecursive(this.maindiv_);var nullOut=function nullOut(obj){for(var n in obj) {if(typeof obj[n] === 'object'){obj[n] = null;}}}; // These may not all be necessary, but it can't hurt... -nullOut(this.layout_);nullOut(this.plotter_);nullOut(this);}; /** - * Creates the canvas on which the chart will be drawn. Only the Renderer ever - * draws on this particular canvas. All Dygraph work (i.e. drawing hover dots - * or the zoom rectangles) is done on this.canvas_. - * @param {Object} canvas The Dygraph canvas over which to overlay the plot - * @return {Object} The newly-created canvas - * @private - */Dygraph.prototype.createPlotKitCanvas_ = function(canvas){var h=utils.createCanvas();h.style.position = "absolute"; // TODO(danvk): h should be offset from canvas. canvas needs to include -// some extra area to make it easier to zoom in on the far left and far -// right. h needs to be precisely the plot area, so that clipping occurs. -h.style.top = canvas.style.top;h.style.left = canvas.style.left;h.width = this.width_;h.height = this.height_;h.style.width = this.width_ + "px"; // for IE -h.style.height = this.height_ + "px"; // for IE -return h;}; /** - * Creates an overlay element used to handle mouse events. - * @return {Object} The mouse event element. - * @private - */Dygraph.prototype.createMouseEventElement_ = function(){return this.canvas_;}; /** - * Generate a set of distinct colors for the data series. This is done with a - * color wheel. Saturation/Value are customizable, and the hue is - * equally-spaced around the color wheel. If a custom set of colors is - * specified, that is used instead. - * @private - */Dygraph.prototype.setColors_ = function(){var labels=this.getLabels();var num=labels.length - 1;this.colors_ = [];this.colorsMap_ = {}; // These are used for when no custom colors are specified. -var sat=this.getNumericOption('colorSaturation') || 1.0;var val=this.getNumericOption('colorValue') || 0.5;var half=Math.ceil(num / 2);var colors=this.getOption('colors');var visibility=this.visibility();for(var i=0;i < num;i++) {if(!visibility[i]){continue;}var label=labels[i + 1];var colorStr=this.attributes_.getForSeries('color',label);if(!colorStr){if(colors){colorStr = colors[i % colors.length];}else { // alternate colors for high contrast. -var idx=i % 2?half + (i + 1) / 2:Math.ceil((i + 1) / 2);var hue=1.0 * idx / (1 + num);colorStr = utils.hsvToRGB(hue,sat,val);}}this.colors_.push(colorStr);this.colorsMap_[label] = colorStr;}}; /** - * Return the list of colors. This is either the list of colors passed in the - * attributes or the autogenerated list of rgb(r,g,b) strings. - * This does not return colors for invisible series. - * @return {Array.} The list of colors. - */Dygraph.prototype.getColors = function(){return this.colors_;}; /** - * Returns a few attributes of a series, i.e. its color, its visibility, which - * axis it's assigned to, and its column in the original data. - * Returns null if the series does not exist. - * Otherwise, returns an object with column, visibility, color and axis properties. - * The "axis" property will be set to 1 for y1 and 2 for y2. - * The "column" property can be fed back into getValue(row, column) to get - * values for this series. - */Dygraph.prototype.getPropertiesForSeries = function(series_name){var idx=-1;var labels=this.getLabels();for(var i=1;i < labels.length;i++) {if(labels[i] == series_name){idx = i;break;}}if(idx == -1)return null;return {name:series_name,column:idx,visible:this.visibility()[idx - 1],color:this.colorsMap_[series_name],axis:1 + this.attributes_.axisForSeries(series_name)};}; /** - * Create the text box to adjust the averaging period - * @private - */Dygraph.prototype.createRollInterface_ = function(){var _this=this; // Create a roller if one doesn't exist already. -var roller=this.roller_;if(!roller){this.roller_ = roller = document.createElement("input");roller.type = "text";roller.style.display = "none";roller.className = 'dygraph-roller';this.graphDiv.appendChild(roller);}var display=this.getBooleanOption('showRoller')?'block':'none';var area=this.getArea();var textAttr={"top":area.y + area.h - 25 + "px","left":area.x + 1 + "px","display":display};roller.size = "2";roller.value = this.rollPeriod_;utils.update(roller.style,textAttr);roller.onchange = function(){return _this.adjustRoll(roller.value);};}; /** - * Set up all the mouse handlers needed to capture dragging behavior for zoom - * events. - * @private - */Dygraph.prototype.createDragInterface_ = function(){var context={ // Tracks whether the mouse is down right now -isZooming:false,isPanning:false, // is this drag part of a pan? -is2DPan:false, // if so, is that pan 1- or 2-dimensional? -dragStartX:null, // pixel coordinates -dragStartY:null, // pixel coordinates -dragEndX:null, // pixel coordinates -dragEndY:null, // pixel coordinates -dragDirection:null,prevEndX:null, // pixel coordinates -prevEndY:null, // pixel coordinates -prevDragDirection:null,cancelNextDblclick:false, // see comment in dygraph-interaction-model.js -// The value on the left side of the graph when a pan operation starts. -initialLeftmostDate:null, // The number of units each pixel spans. (This won't be valid for log -// scales) -xUnitsPerPixel:null, // TODO(danvk): update this comment -// The range in second/value units that the viewport encompasses during a -// panning operation. -dateRange:null, // Top-left corner of the canvas, in DOM coords -// TODO(konigsberg): Rename topLeftCanvasX, topLeftCanvasY. -px:0,py:0, // Values for use with panEdgeFraction, which limit how far outside the -// graph's data boundaries it can be panned. -boundedDates:null, // [minDate, maxDate] -boundedValues:null, // [[minValue, maxValue] ...] -// We cover iframes during mouse interactions. See comments in -// dygraph-utils.js for more info on why this is a good idea. -tarp:new _iframeTarp2['default'](), // contextB is the same thing as this context object but renamed. -initializeMouseDown:function initializeMouseDown(event,g,contextB){ // prevents mouse drags from selecting page text. -if(event.preventDefault){event.preventDefault(); // Firefox, Chrome, etc. -}else {event.returnValue = false; // IE -event.cancelBubble = true;}var canvasPos=utils.findPos(g.canvas_);contextB.px = canvasPos.x;contextB.py = canvasPos.y;contextB.dragStartX = utils.dragGetX_(event,contextB);contextB.dragStartY = utils.dragGetY_(event,contextB);contextB.cancelNextDblclick = false;contextB.tarp.cover();},destroy:function destroy(){var context=this;if(context.isZooming || context.isPanning){context.isZooming = false;context.dragStartX = null;context.dragStartY = null;}if(context.isPanning){context.isPanning = false;context.draggingDate = null;context.dateRange = null;for(var i=0;i < self.axes_.length;i++) {delete self.axes_[i].draggingValue;delete self.axes_[i].dragValueRange;}}context.tarp.uncover();}};var interactionModel=this.getOption("interactionModel"); // Self is the graph. -var self=this; // Function that binds the graph and context to the handler. -var bindHandler=function bindHandler(handler){return function(event){handler(event,self,context);};};for(var eventName in interactionModel) {if(!interactionModel.hasOwnProperty(eventName))continue;this.addAndTrackEvent(this.mouseEventElement_,eventName,bindHandler(interactionModel[eventName]));} // If the user releases the mouse button during a drag, but not over the -// canvas, then it doesn't count as a zooming action. -if(!interactionModel.willDestroyContextMyself){var mouseUpHandler=function mouseUpHandler(event){context.destroy();};this.addAndTrackEvent(document,'mouseup',mouseUpHandler);}}; /** - * Draw a gray zoom rectangle over the desired area of the canvas. Also clears - * up any previous zoom rectangles that were drawn. This could be optimized to - * avoid extra redrawing, but it's tricky to avoid interactions with the status - * dots. - * - * @param {number} direction the direction of the zoom rectangle. Acceptable - * values are utils.HORIZONTAL and utils.VERTICAL. - * @param {number} startX The X position where the drag started, in canvas - * coordinates. - * @param {number} endX The current X position of the drag, in canvas coords. - * @param {number} startY The Y position where the drag started, in canvas - * coordinates. - * @param {number} endY The current Y position of the drag, in canvas coords. - * @param {number} prevDirection the value of direction on the previous call to - * this function. Used to avoid excess redrawing - * @param {number} prevEndX The value of endX on the previous call to this - * function. Used to avoid excess redrawing - * @param {number} prevEndY The value of endY on the previous call to this - * function. Used to avoid excess redrawing - * @private - */Dygraph.prototype.drawZoomRect_ = function(direction,startX,endX,startY,endY,prevDirection,prevEndX,prevEndY){var ctx=this.canvas_ctx_; // Clean up from the previous rect if necessary -if(prevDirection == utils.HORIZONTAL){ctx.clearRect(Math.min(startX,prevEndX),this.layout_.getPlotArea().y,Math.abs(startX - prevEndX),this.layout_.getPlotArea().h);}else if(prevDirection == utils.VERTICAL){ctx.clearRect(this.layout_.getPlotArea().x,Math.min(startY,prevEndY),this.layout_.getPlotArea().w,Math.abs(startY - prevEndY));} // Draw a light-grey rectangle to show the new viewing area -if(direction == utils.HORIZONTAL){if(endX && startX){ctx.fillStyle = "rgba(128,128,128,0.33)";ctx.fillRect(Math.min(startX,endX),this.layout_.getPlotArea().y,Math.abs(endX - startX),this.layout_.getPlotArea().h);}}else if(direction == utils.VERTICAL){if(endY && startY){ctx.fillStyle = "rgba(128,128,128,0.33)";ctx.fillRect(this.layout_.getPlotArea().x,Math.min(startY,endY),this.layout_.getPlotArea().w,Math.abs(endY - startY));}}}; /** - * Clear the zoom rectangle (and perform no zoom). - * @private - */Dygraph.prototype.clearZoomRect_ = function(){this.currentZoomRectArgs_ = null;this.canvas_ctx_.clearRect(0,0,this.width_,this.height_);}; /** - * Zoom to something containing [lowX, highX]. These are pixel coordinates in - * the canvas. The exact zoom window may be slightly larger if there are no data - * points near lowX or highX. Don't confuse this function with doZoomXDates, - * which accepts dates that match the raw data. This function redraws the graph. - * - * @param {number} lowX The leftmost pixel value that should be visible. - * @param {number} highX The rightmost pixel value that should be visible. - * @private - */Dygraph.prototype.doZoomX_ = function(lowX,highX){this.currentZoomRectArgs_ = null; // Find the earliest and latest dates contained in this canvasx range. -// Convert the call to date ranges of the raw data. -var minDate=this.toDataXCoord(lowX);var maxDate=this.toDataXCoord(highX);this.doZoomXDates_(minDate,maxDate);}; /** - * Zoom to something containing [minDate, maxDate] values. Don't confuse this - * method with doZoomX which accepts pixel coordinates. This function redraws - * the graph. - * - * @param {number} minDate The minimum date that should be visible. - * @param {number} maxDate The maximum date that should be visible. - * @private - */Dygraph.prototype.doZoomXDates_ = function(minDate,maxDate){var _this2=this; // TODO(danvk): when xAxisRange is null (i.e. "fit to data", the animation -// can produce strange effects. Rather than the x-axis transitioning slowly -// between values, it can jerk around.) -var old_window=this.xAxisRange();var new_window=[minDate,maxDate];var zoomCallback=this.getFunctionOption('zoomCallback');this.doAnimatedZoom(old_window,new_window,null,null,function(){if(zoomCallback){zoomCallback.call(_this2,minDate,maxDate,_this2.yAxisRanges());}});}; /** - * Zoom to something containing [lowY, highY]. These are pixel coordinates in - * the canvas. This function redraws the graph. - * - * @param {number} lowY The topmost pixel value that should be visible. - * @param {number} highY The lowest pixel value that should be visible. - * @private - */Dygraph.prototype.doZoomY_ = function(lowY,highY){var _this3=this;this.currentZoomRectArgs_ = null; // Find the highest and lowest values in pixel range for each axis. -// Note that lowY (in pixels) corresponds to the max Value (in data coords). -// This is because pixels increase as you go down on the screen, whereas data -// coordinates increase as you go up the screen. -var oldValueRanges=this.yAxisRanges();var newValueRanges=[];for(var i=0;i < this.axes_.length;i++) {var hi=this.toDataYCoord(lowY,i);var low=this.toDataYCoord(highY,i);newValueRanges.push([low,hi]);}var zoomCallback=this.getFunctionOption('zoomCallback');this.doAnimatedZoom(null,null,oldValueRanges,newValueRanges,function(){if(zoomCallback){var _xAxisRange=_this3.xAxisRange();var _xAxisRange2=_slicedToArray(_xAxisRange,2);var minX=_xAxisRange2[0];var maxX=_xAxisRange2[1];zoomCallback.call(_this3,minX,maxX,_this3.yAxisRanges());}});}; /** - * Transition function to use in animations. Returns values between 0.0 - * (totally old values) and 1.0 (totally new values) for each frame. - * @private - */Dygraph.zoomAnimationFunction = function(frame,numFrames){var k=1.5;return (1.0 - Math.pow(k,-frame)) / (1.0 - Math.pow(k,-numFrames));}; /** - * Reset the zoom to the original view coordinates. This is the same as - * double-clicking on the graph. - */Dygraph.prototype.resetZoom = function(){var _this4=this;var dirtyX=this.isZoomed('x');var dirtyY=this.isZoomed('y');var dirty=dirtyX || dirtyY; // Clear any selection, since it's likely to be drawn in the wrong place. -this.clearSelection();if(!dirty)return; // Calculate extremes to avoid lack of padding on reset. -var _xAxisExtremes=this.xAxisExtremes();var _xAxisExtremes2=_slicedToArray(_xAxisExtremes,2);var minDate=_xAxisExtremes2[0];var maxDate=_xAxisExtremes2[1];var animatedZooms=this.getBooleanOption('animatedZooms');var zoomCallback=this.getFunctionOption('zoomCallback'); // TODO(danvk): merge this block w/ the code below. -// TODO(danvk): factor out a generic, public zoomTo method. -if(!animatedZooms){this.dateWindow_ = null;this.axes_.forEach(function(axis){if(axis.valueRange)delete axis.valueRange;});this.drawGraph_();if(zoomCallback){zoomCallback.call(this,minDate,maxDate,this.yAxisRanges());}return;}var oldWindow=null,newWindow=null,oldValueRanges=null,newValueRanges=null;if(dirtyX){oldWindow = this.xAxisRange();newWindow = [minDate,maxDate];}if(dirtyY){oldValueRanges = this.yAxisRanges();newValueRanges = this.yAxisExtremes();}this.doAnimatedZoom(oldWindow,newWindow,oldValueRanges,newValueRanges,function(){_this4.dateWindow_ = null;_this4.axes_.forEach(function(axis){if(axis.valueRange)delete axis.valueRange;});if(zoomCallback){zoomCallback.call(_this4,minDate,maxDate,_this4.yAxisRanges());}});}; /** - * Combined animation logic for all zoom functions. - * either the x parameters or y parameters may be null. - * @private - */Dygraph.prototype.doAnimatedZoom = function(oldXRange,newXRange,oldYRanges,newYRanges,callback){var _this5=this;var steps=this.getBooleanOption("animatedZooms")?Dygraph.ANIMATION_STEPS:1;var windows=[];var valueRanges=[];var step,frac;if(oldXRange !== null && newXRange !== null){for(step = 1;step <= steps;step++) {frac = Dygraph.zoomAnimationFunction(step,steps);windows[step - 1] = [oldXRange[0] * (1 - frac) + frac * newXRange[0],oldXRange[1] * (1 - frac) + frac * newXRange[1]];}}if(oldYRanges !== null && newYRanges !== null){for(step = 1;step <= steps;step++) {frac = Dygraph.zoomAnimationFunction(step,steps);var thisRange=[];for(var j=0;j < this.axes_.length;j++) {thisRange.push([oldYRanges[j][0] * (1 - frac) + frac * newYRanges[j][0],oldYRanges[j][1] * (1 - frac) + frac * newYRanges[j][1]]);}valueRanges[step - 1] = thisRange;}}utils.repeatAndCleanup(function(step){if(valueRanges.length){for(var i=0;i < _this5.axes_.length;i++) {var w=valueRanges[step][i];_this5.axes_[i].valueRange = [w[0],w[1]];}}if(windows.length){_this5.dateWindow_ = windows[step];}_this5.drawGraph_();},steps,Dygraph.ANIMATION_DURATION / steps,callback);}; /** - * Get the current graph's area object. - * - * Returns: {x, y, w, h} - */Dygraph.prototype.getArea = function(){return this.plotter_.area;}; /** - * Convert a mouse event to DOM coordinates relative to the graph origin. - * - * Returns a two-element array: [X, Y]. - */Dygraph.prototype.eventToDomCoords = function(event){if(event.offsetX && event.offsetY){return [event.offsetX,event.offsetY];}else {var eventElementPos=utils.findPos(this.mouseEventElement_);var canvasx=utils.pageX(event) - eventElementPos.x;var canvasy=utils.pageY(event) - eventElementPos.y;return [canvasx,canvasy];}}; /** - * Given a canvas X coordinate, find the closest row. - * @param {number} domX graph-relative DOM X coordinate - * Returns {number} row number. - * @private - */Dygraph.prototype.findClosestRow = function(domX){var minDistX=Infinity;var closestRow=-1;var sets=this.layout_.points;for(var i=0;i < sets.length;i++) {var points=sets[i];var len=points.length;for(var j=0;j < len;j++) {var point=points[j];if(!utils.isValidPoint(point,true))continue;var dist=Math.abs(point.canvasx - domX);if(dist < minDistX){minDistX = dist;closestRow = point.idx;}}}return closestRow;}; /** - * Given canvas X,Y coordinates, find the closest point. - * - * This finds the individual data point across all visible series - * that's closest to the supplied DOM coordinates using the standard - * Euclidean X,Y distance. - * - * @param {number} domX graph-relative DOM X coordinate - * @param {number} domY graph-relative DOM Y coordinate - * Returns: {row, seriesName, point} - * @private - */Dygraph.prototype.findClosestPoint = function(domX,domY){var minDist=Infinity;var dist,dx,dy,point,closestPoint,closestSeries,closestRow;for(var setIdx=this.layout_.points.length - 1;setIdx >= 0;--setIdx) {var points=this.layout_.points[setIdx];for(var i=0;i < points.length;++i) {point = points[i];if(!utils.isValidPoint(point))continue;dx = point.canvasx - domX;dy = point.canvasy - domY;dist = dx * dx + dy * dy;if(dist < minDist){minDist = dist;closestPoint = point;closestSeries = setIdx;closestRow = point.idx;}}}var name=this.layout_.setNames[closestSeries];return {row:closestRow,seriesName:name,point:closestPoint};}; /** - * Given canvas X,Y coordinates, find the touched area in a stacked graph. - * - * This first finds the X data point closest to the supplied DOM X coordinate, - * then finds the series which puts the Y coordinate on top of its filled area, - * using linear interpolation between adjacent point pairs. - * - * @param {number} domX graph-relative DOM X coordinate - * @param {number} domY graph-relative DOM Y coordinate - * Returns: {row, seriesName, point} - * @private - */Dygraph.prototype.findStackedPoint = function(domX,domY){var row=this.findClosestRow(domX);var closestPoint,closestSeries;for(var setIdx=0;setIdx < this.layout_.points.length;++setIdx) {var boundary=this.getLeftBoundary_(setIdx);var rowIdx=row - boundary;var points=this.layout_.points[setIdx];if(rowIdx >= points.length)continue;var p1=points[rowIdx];if(!utils.isValidPoint(p1))continue;var py=p1.canvasy;if(domX > p1.canvasx && rowIdx + 1 < points.length){ // interpolate series Y value using next point -var p2=points[rowIdx + 1];if(utils.isValidPoint(p2)){var dx=p2.canvasx - p1.canvasx;if(dx > 0){var r=(domX - p1.canvasx) / dx;py += r * (p2.canvasy - p1.canvasy);}}}else if(domX < p1.canvasx && rowIdx > 0){ // interpolate series Y value using previous point -var p0=points[rowIdx - 1];if(utils.isValidPoint(p0)){var dx=p1.canvasx - p0.canvasx;if(dx > 0){var r=(p1.canvasx - domX) / dx;py += r * (p0.canvasy - p1.canvasy);}}} // Stop if the point (domX, py) is above this series' upper edge -if(setIdx === 0 || py < domY){closestPoint = p1;closestSeries = setIdx;}}var name=this.layout_.setNames[closestSeries];return {row:row,seriesName:name,point:closestPoint};}; /** - * When the mouse moves in the canvas, display information about a nearby data - * point and draw dots over those points in the data series. This function - * takes care of cleanup of previously-drawn dots. - * @param {Object} event The mousemove event from the browser. - * @private - */Dygraph.prototype.mouseMove_ = function(event){ // This prevents JS errors when mousing over the canvas before data loads. -var points=this.layout_.points;if(points === undefined || points === null)return;var canvasCoords=this.eventToDomCoords(event);var canvasx=canvasCoords[0];var canvasy=canvasCoords[1];var highlightSeriesOpts=this.getOption("highlightSeriesOpts");var selectionChanged=false;if(highlightSeriesOpts && !this.isSeriesLocked()){var closest;if(this.getBooleanOption("stackedGraph")){closest = this.findStackedPoint(canvasx,canvasy);}else {closest = this.findClosestPoint(canvasx,canvasy);}selectionChanged = this.setSelection(closest.row,closest.seriesName);}else {var idx=this.findClosestRow(canvasx);selectionChanged = this.setSelection(idx);}var callback=this.getFunctionOption("highlightCallback");if(callback && selectionChanged){callback.call(this,event,this.lastx_,this.selPoints_,this.lastRow_,this.highlightSet_);}}; /** - * Fetch left offset from the specified set index or if not passed, the - * first defined boundaryIds record (see bug #236). - * @private - */Dygraph.prototype.getLeftBoundary_ = function(setIdx){if(this.boundaryIds_[setIdx]){return this.boundaryIds_[setIdx][0];}else {for(var i=0;i < this.boundaryIds_.length;i++) {if(this.boundaryIds_[i] !== undefined){return this.boundaryIds_[i][0];}}return 0;}};Dygraph.prototype.animateSelection_ = function(direction){var totalSteps=10;var millis=30;if(this.fadeLevel === undefined)this.fadeLevel = 0;if(this.animateId === undefined)this.animateId = 0;var start=this.fadeLevel;var steps=direction < 0?start:totalSteps - start;if(steps <= 0){if(this.fadeLevel){this.updateSelection_(1.0);}return;}var thisId=++this.animateId;var that=this;var cleanupIfClearing=function cleanupIfClearing(){ // if we haven't reached fadeLevel 0 in the max frame time, -// ensure that the clear happens and just go to 0 -if(that.fadeLevel !== 0 && direction < 0){that.fadeLevel = 0;that.clearSelection();}};utils.repeatAndCleanup(function(n){ // ignore simultaneous animations -if(that.animateId != thisId)return;that.fadeLevel += direction;if(that.fadeLevel === 0){that.clearSelection();}else {that.updateSelection_(that.fadeLevel / totalSteps);}},steps,millis,cleanupIfClearing);}; /** - * Draw dots over the selectied points in the data series. This function - * takes care of cleanup of previously-drawn dots. - * @private - */Dygraph.prototype.updateSelection_ = function(opt_animFraction){ /*var defaultPrevented = */this.cascadeEvents_('select',{selectedRow:this.lastRow_ === -1?undefined:this.lastRow_,selectedX:this.lastx_ === -1?undefined:this.lastx_,selectedPoints:this.selPoints_}); // TODO(danvk): use defaultPrevented here? -// Clear the previously drawn vertical, if there is one -var i;var ctx=this.canvas_ctx_;if(this.getOption('highlightSeriesOpts')){ctx.clearRect(0,0,this.width_,this.height_);var alpha=1.0 - this.getNumericOption('highlightSeriesBackgroundAlpha');var backgroundColor=utils.toRGB_(this.getOption('highlightSeriesBackgroundColor'));if(alpha){ // Activating background fade includes an animation effect for a gradual -// fade. TODO(klausw): make this independently configurable if it causes -// issues? Use a shared preference to control animations? -var animateBackgroundFade=true;if(animateBackgroundFade){if(opt_animFraction === undefined){ // start a new animation -this.animateSelection_(1);return;}alpha *= opt_animFraction;}ctx.fillStyle = 'rgba(' + backgroundColor.r + ',' + backgroundColor.g + ',' + backgroundColor.b + ',' + alpha + ')';ctx.fillRect(0,0,this.width_,this.height_);} // Redraw only the highlighted series in the interactive canvas (not the -// static plot canvas, which is where series are usually drawn). -this.plotter_._renderLineChart(this.highlightSet_,ctx);}else if(this.previousVerticalX_ >= 0){ // Determine the maximum highlight circle size. -var maxCircleSize=0;var labels=this.attr_('labels');for(i = 1;i < labels.length;i++) {var r=this.getNumericOption('highlightCircleSize',labels[i]);if(r > maxCircleSize)maxCircleSize = r;}var px=this.previousVerticalX_;ctx.clearRect(px - maxCircleSize - 1,0,2 * maxCircleSize + 2,this.height_);}if(this.selPoints_.length > 0){ // Draw colored circles over the center of each selected point -var canvasx=this.selPoints_[0].canvasx;ctx.save();for(i = 0;i < this.selPoints_.length;i++) {var pt=this.selPoints_[i];if(isNaN(pt.canvasy))continue;var circleSize=this.getNumericOption('highlightCircleSize',pt.name);var callback=this.getFunctionOption("drawHighlightPointCallback",pt.name);var color=this.plotter_.colors[pt.name];if(!callback){callback = utils.Circles.DEFAULT;}ctx.lineWidth = this.getNumericOption('strokeWidth',pt.name);ctx.strokeStyle = color;ctx.fillStyle = color;callback.call(this,this,pt.name,ctx,canvasx,pt.canvasy,color,circleSize,pt.idx);}ctx.restore();this.previousVerticalX_ = canvasx;}}; /** - * Manually set the selected points and display information about them in the - * legend. The selection can be cleared using clearSelection() and queried - * using getSelection(). - * - * To set a selected series but not a selected point, call setSelection with - * row=false and the selected series name. - * - * @param {number} row Row number that should be highlighted (i.e. appear with - * hover dots on the chart). - * @param {seriesName} optional series name to highlight that series with the - * the highlightSeriesOpts setting. - * @param { locked } optional If true, keep seriesName selected when mousing - * over the graph, disabling closest-series highlighting. Call clearSelection() - * to unlock it. - */Dygraph.prototype.setSelection = function(row,opt_seriesName,opt_locked){ // Extract the points we've selected -this.selPoints_ = [];var changed=false;if(row !== false && row >= 0){if(row != this.lastRow_)changed = true;this.lastRow_ = row;for(var setIdx=0;setIdx < this.layout_.points.length;++setIdx) {var points=this.layout_.points[setIdx]; // Check if the point at the appropriate index is the point we're looking -// for. If it is, just use it, otherwise search the array for a point -// in the proper place. -var setRow=row - this.getLeftBoundary_(setIdx);if(setRow >= 0 && setRow < points.length && points[setRow].idx == row){var point=points[setRow];if(point.yval !== null)this.selPoints_.push(point);}else {for(var pointIdx=0;pointIdx < points.length;++pointIdx) {var point=points[pointIdx];if(point.idx == row){if(point.yval !== null){this.selPoints_.push(point);}break;}}}}}else {if(this.lastRow_ >= 0)changed = true;this.lastRow_ = -1;}if(this.selPoints_.length){this.lastx_ = this.selPoints_[0].xval;}else {this.lastx_ = -1;}if(opt_seriesName !== undefined){if(this.highlightSet_ !== opt_seriesName)changed = true;this.highlightSet_ = opt_seriesName;}if(opt_locked !== undefined){this.lockedSet_ = opt_locked;}if(changed){this.updateSelection_(undefined);}return changed;}; /** - * The mouse has left the canvas. Clear out whatever artifacts remain - * @param {Object} event the mouseout event from the browser. - * @private - */Dygraph.prototype.mouseOut_ = function(event){if(this.getFunctionOption("unhighlightCallback")){this.getFunctionOption("unhighlightCallback").call(this,event);}if(this.getBooleanOption("hideOverlayOnMouseOut") && !this.lockedSet_){this.clearSelection();}}; /** - * Clears the current selection (i.e. points that were highlighted by moving - * the mouse over the chart). - */Dygraph.prototype.clearSelection = function(){this.cascadeEvents_('deselect',{});this.lockedSet_ = false; // Get rid of the overlay data -if(this.fadeLevel){this.animateSelection_(-1);return;}this.canvas_ctx_.clearRect(0,0,this.width_,this.height_);this.fadeLevel = 0;this.selPoints_ = [];this.lastx_ = -1;this.lastRow_ = -1;this.highlightSet_ = null;}; /** - * Returns the number of the currently selected row. To get data for this row, - * you can use the getValue method. - * @return {number} row number, or -1 if nothing is selected - */Dygraph.prototype.getSelection = function(){if(!this.selPoints_ || this.selPoints_.length < 1){return -1;}for(var setIdx=0;setIdx < this.layout_.points.length;setIdx++) {var points=this.layout_.points[setIdx];for(var row=0;row < points.length;row++) {if(points[row].x == this.selPoints_[0].x){return points[row].idx;}}}return -1;}; /** - * Returns the name of the currently-highlighted series. - * Only available when the highlightSeriesOpts option is in use. - */Dygraph.prototype.getHighlightSeries = function(){return this.highlightSet_;}; /** - * Returns true if the currently-highlighted series was locked - * via setSelection(..., seriesName, true). - */Dygraph.prototype.isSeriesLocked = function(){return this.lockedSet_;}; /** - * Fires when there's data available to be graphed. - * @param {string} data Raw CSV data to be plotted - * @private - */Dygraph.prototype.loadedEvent_ = function(data){this.rawData_ = this.parseCSV_(data);this.cascadeDataDidUpdateEvent_();this.predraw_();}; /** - * Add ticks on the x-axis representing years, months, quarters, weeks, or days - * @private - */Dygraph.prototype.addXTicks_ = function(){ // Determine the correct ticks scale on the x-axis: quarterly, monthly, ... -var range;if(this.dateWindow_){range = [this.dateWindow_[0],this.dateWindow_[1]];}else {range = this.xAxisExtremes();}var xAxisOptionsView=this.optionsViewForAxis_('x');var xTicks=xAxisOptionsView('ticker')(range[0],range[1],this.plotter_.area.w, // TODO(danvk): should be area.width -xAxisOptionsView,this); // var msg = 'ticker(' + range[0] + ', ' + range[1] + ', ' + this.width_ + ', ' + this.attr_('pixelsPerXLabel') + ') -> ' + JSON.stringify(xTicks); -// console.log(msg); -this.layout_.setXTicks(xTicks);}; /** - * Returns the correct handler class for the currently set options. - * @private - */Dygraph.prototype.getHandlerClass_ = function(){var handlerClass;if(this.attr_('dataHandler')){handlerClass = this.attr_('dataHandler');}else if(this.fractions_){if(this.getBooleanOption('errorBars')){handlerClass = _datahandlerBarsFractions2['default'];}else {handlerClass = _datahandlerDefaultFractions2['default'];}}else if(this.getBooleanOption('customBars')){handlerClass = _datahandlerBarsCustom2['default'];}else if(this.getBooleanOption('errorBars')){handlerClass = _datahandlerBarsError2['default'];}else {handlerClass = _datahandlerDefault2['default'];}return handlerClass;}; /** - * @private - * This function is called once when the chart's data is changed or the options - * dictionary is updated. It is _not_ called when the user pans or zooms. The - * idea is that values derived from the chart's data can be computed here, - * rather than every time the chart is drawn. This includes things like the - * number of axes, rolling averages, etc. - */Dygraph.prototype.predraw_ = function(){var start=new Date(); // Create the correct dataHandler -this.dataHandler_ = new (this.getHandlerClass_())();this.layout_.computePlotArea(); // TODO(danvk): move more computations out of drawGraph_ and into here. -this.computeYAxes_();if(!this.is_initial_draw_){this.canvas_ctx_.restore();this.hidden_ctx_.restore();}this.canvas_ctx_.save();this.hidden_ctx_.save(); // Create a new plotter. -this.plotter_ = new _dygraphCanvas2['default'](this,this.hidden_,this.hidden_ctx_,this.layout_); // The roller sits in the bottom left corner of the chart. We don't know where -// this will be until the options are available, so it's positioned here. -this.createRollInterface_();this.cascadeEvents_('predraw'); // Convert the raw data (a 2D array) into the internal format and compute -// rolling averages. -this.rolledSeries_ = [null]; // x-axis is the first series and it's special -for(var i=1;i < this.numColumns();i++) { // var logScale = this.attr_('logscale', i); // TODO(klausw): this looks wrong // konigsberg thinks so too. -var series=this.dataHandler_.extractSeries(this.rawData_,i,this.attributes_);if(this.rollPeriod_ > 1){series = this.dataHandler_.rollingAverage(series,this.rollPeriod_,this.attributes_);}this.rolledSeries_.push(series);} // If the data or options have changed, then we'd better redraw. -this.drawGraph_(); // This is used to determine whether to do various animations. -var end=new Date();this.drawingTimeMs_ = end - start;}; /** - * Point structure. - * - * xval_* and yval_* are the original unscaled data values, - * while x_* and y_* are scaled to the range (0.0-1.0) for plotting. - * yval_stacked is the cumulative Y value used for stacking graphs, - * and bottom/top/minus/plus are used for error bar graphs. - * - * @typedef {{ - * idx: number, - * name: string, - * x: ?number, - * xval: ?number, - * y_bottom: ?number, - * y: ?number, - * y_stacked: ?number, - * y_top: ?number, - * yval_minus: ?number, - * yval: ?number, - * yval_plus: ?number, - * yval_stacked - * }} - */Dygraph.PointType = undefined; /** - * Calculates point stacking for stackedGraph=true. - * - * For stacking purposes, interpolate or extend neighboring data across - * NaN values based on stackedGraphNaNFill settings. This is for display - * only, the underlying data value as shown in the legend remains NaN. - * - * @param {Array.} points Point array for a single series. - * Updates each Point's yval_stacked property. - * @param {Array.} cumulativeYval Accumulated top-of-graph stacked Y - * values for the series seen so far. Index is the row number. Updated - * based on the current series's values. - * @param {Array.} seriesExtremes Min and max values, updated - * to reflect the stacked values. - * @param {string} fillMethod Interpolation method, one of 'all', 'inside', or - * 'none'. - * @private - */Dygraph.stackPoints_ = function(points,cumulativeYval,seriesExtremes,fillMethod){var lastXval=null;var prevPoint=null;var nextPoint=null;var nextPointIdx=-1; // Find the next stackable point starting from the given index. -var updateNextPoint=function updateNextPoint(idx){ // If we've previously found a non-NaN point and haven't gone past it yet, -// just use that. -if(nextPointIdx >= idx)return; // We haven't found a non-NaN point yet or have moved past it, -// look towards the right to find a non-NaN point. -for(var j=idx;j < points.length;++j) { // Clear out a previously-found point (if any) since it's no longer -// valid, we shouldn't use it for interpolation anymore. -nextPoint = null;if(!isNaN(points[j].yval) && points[j].yval !== null){nextPointIdx = j;nextPoint = points[j];break;}}};for(var i=0;i < points.length;++i) {var point=points[i];var xval=point.xval;if(cumulativeYval[xval] === undefined){cumulativeYval[xval] = 0;}var actualYval=point.yval;if(isNaN(actualYval) || actualYval === null){if(fillMethod == 'none'){actualYval = 0;}else { // Interpolate/extend for stacking purposes if possible. -updateNextPoint(i);if(prevPoint && nextPoint && fillMethod != 'none'){ // Use linear interpolation between prevPoint and nextPoint. -actualYval = prevPoint.yval + (nextPoint.yval - prevPoint.yval) * ((xval - prevPoint.xval) / (nextPoint.xval - prevPoint.xval));}else if(prevPoint && fillMethod == 'all'){actualYval = prevPoint.yval;}else if(nextPoint && fillMethod == 'all'){actualYval = nextPoint.yval;}else {actualYval = 0;}}}else {prevPoint = point;}var stackedYval=cumulativeYval[xval];if(lastXval != xval){ // If an x-value is repeated, we ignore the duplicates. -stackedYval += actualYval;cumulativeYval[xval] = stackedYval;}lastXval = xval;point.yval_stacked = stackedYval;if(stackedYval > seriesExtremes[1]){seriesExtremes[1] = stackedYval;}if(stackedYval < seriesExtremes[0]){seriesExtremes[0] = stackedYval;}}}; /** - * Loop over all fields and create datasets, calculating extreme y-values for - * each series and extreme x-indices as we go. - * - * dateWindow is passed in as an explicit parameter so that we can compute - * extreme values "speculatively", i.e. without actually setting state on the - * dygraph. - * - * @param {Array.)>>} rolledSeries, where - * rolledSeries[seriesIndex][row] = raw point, where - * seriesIndex is the column number starting with 1, and - * rawPoint is [x,y] or [x, [y, err]] or [x, [y, yminus, yplus]]. - * @param {?Array.} dateWindow [xmin, xmax] pair, or null. - * @return {{ - * points: Array.>, - * seriesExtremes: Array.>, - * boundaryIds: Array.}} - * @private - */Dygraph.prototype.gatherDatasets_ = function(rolledSeries,dateWindow){var boundaryIds=[];var points=[];var cumulativeYval=[]; // For stacked series. -var extremes={}; // series name -> [low, high] -var seriesIdx,sampleIdx;var firstIdx,lastIdx;var axisIdx; // Loop over the fields (series). Go from the last to the first, -// because if they're stacked that's how we accumulate the values. -var num_series=rolledSeries.length - 1;var series;for(seriesIdx = num_series;seriesIdx >= 1;seriesIdx--) {if(!this.visibility()[seriesIdx - 1])continue; // Prune down to the desired range, if necessary (for zooming) -// Because there can be lines going to points outside of the visible area, -// we actually prune to visible points, plus one on either side. -if(dateWindow){series = rolledSeries[seriesIdx];var low=dateWindow[0];var high=dateWindow[1]; // TODO(danvk): do binary search instead of linear search. -// TODO(danvk): pass firstIdx and lastIdx directly to the renderer. -firstIdx = null;lastIdx = null;for(sampleIdx = 0;sampleIdx < series.length;sampleIdx++) {if(series[sampleIdx][0] >= low && firstIdx === null){firstIdx = sampleIdx;}if(series[sampleIdx][0] <= high){lastIdx = sampleIdx;}}if(firstIdx === null)firstIdx = 0;var correctedFirstIdx=firstIdx;var isInvalidValue=true;while(isInvalidValue && correctedFirstIdx > 0) {correctedFirstIdx--; // check if the y value is null. -isInvalidValue = series[correctedFirstIdx][1] === null;}if(lastIdx === null)lastIdx = series.length - 1;var correctedLastIdx=lastIdx;isInvalidValue = true;while(isInvalidValue && correctedLastIdx < series.length - 1) {correctedLastIdx++;isInvalidValue = series[correctedLastIdx][1] === null;}if(correctedFirstIdx !== firstIdx){firstIdx = correctedFirstIdx;}if(correctedLastIdx !== lastIdx){lastIdx = correctedLastIdx;}boundaryIds[seriesIdx - 1] = [firstIdx,lastIdx]; // .slice's end is exclusive, we want to include lastIdx. -series = series.slice(firstIdx,lastIdx + 1);}else {series = rolledSeries[seriesIdx];boundaryIds[seriesIdx - 1] = [0,series.length - 1];}var seriesName=this.attr_("labels")[seriesIdx];var seriesExtremes=this.dataHandler_.getExtremeYValues(series,dateWindow,this.getBooleanOption("stepPlot",seriesName));var seriesPoints=this.dataHandler_.seriesToPoints(series,seriesName,boundaryIds[seriesIdx - 1][0]);if(this.getBooleanOption("stackedGraph")){axisIdx = this.attributes_.axisForSeries(seriesName);if(cumulativeYval[axisIdx] === undefined){cumulativeYval[axisIdx] = [];}Dygraph.stackPoints_(seriesPoints,cumulativeYval[axisIdx],seriesExtremes,this.getBooleanOption("stackedGraphNaNFill"));}extremes[seriesName] = seriesExtremes;points[seriesIdx] = seriesPoints;}return {points:points,extremes:extremes,boundaryIds:boundaryIds};}; /** - * Update the graph with new data. This method is called when the viewing area - * has changed. If the underlying data or options have changed, predraw_ will - * be called before drawGraph_ is called. - * - * @private - */Dygraph.prototype.drawGraph_ = function(){var start=new Date(); // This is used to set the second parameter to drawCallback, below. -var is_initial_draw=this.is_initial_draw_;this.is_initial_draw_ = false;this.layout_.removeAllDatasets();this.setColors_();this.attrs_.pointSize = 0.5 * this.getNumericOption('highlightCircleSize');var packed=this.gatherDatasets_(this.rolledSeries_,this.dateWindow_);var points=packed.points;var extremes=packed.extremes;this.boundaryIds_ = packed.boundaryIds;this.setIndexByName_ = {};var labels=this.attr_("labels");var dataIdx=0;for(var i=1;i < points.length;i++) {if(!this.visibility()[i - 1])continue;this.layout_.addDataset(labels[i],points[i]);this.datasetIndex_[i] = dataIdx++;}for(var i=0;i < labels.length;i++) {this.setIndexByName_[labels[i]] = i;}this.computeYAxisRanges_(extremes);this.layout_.setYAxes(this.axes_);this.addXTicks_(); // Tell PlotKit to use this new data and render itself -this.layout_.evaluate();this.renderGraph_(is_initial_draw);if(this.getStringOption("timingName")){var end=new Date();console.log(this.getStringOption("timingName") + " - drawGraph: " + (end - start) + "ms");}}; /** - * This does the work of drawing the chart. It assumes that the layout and axis - * scales have already been set (e.g. by predraw_). - * - * @private - */Dygraph.prototype.renderGraph_ = function(is_initial_draw){this.cascadeEvents_('clearChart');this.plotter_.clear();var underlayCallback=this.getFunctionOption('underlayCallback');if(underlayCallback){ // NOTE: we pass the dygraph object to this callback twice to avoid breaking -// users who expect a deprecated form of this callback. -underlayCallback.call(this,this.hidden_ctx_,this.layout_.getPlotArea(),this,this);}var e={canvas:this.hidden_,drawingContext:this.hidden_ctx_};this.cascadeEvents_('willDrawChart',e);this.plotter_.render();this.cascadeEvents_('didDrawChart',e);this.lastRow_ = -1; // because plugins/legend.js clears the legend -// TODO(danvk): is this a performance bottleneck when panning? -// The interaction canvas should already be empty in that situation. -this.canvas_.getContext('2d').clearRect(0,0,this.width_,this.height_);var drawCallback=this.getFunctionOption("drawCallback");if(drawCallback !== null){drawCallback.call(this,this,is_initial_draw);}if(is_initial_draw){this.readyFired_ = true;while(this.readyFns_.length > 0) {var fn=this.readyFns_.pop();fn(this);}}}; /** - * @private - * Determine properties of the y-axes which are independent of the data - * currently being displayed. This includes things like the number of axes and - * the style of the axes. It does not include the range of each axis and its - * tick marks. - * This fills in this.axes_. - * axes_ = [ { options } ] - * indices are into the axes_ array. - */Dygraph.prototype.computeYAxes_ = function(){var axis,index,opts,v; // this.axes_ doesn't match this.attributes_.axes_.options. It's used for -// data computation as well as options storage. -// Go through once and add all the axes. -this.axes_ = [];for(axis = 0;axis < this.attributes_.numAxes();axis++) { // Add a new axis, making a copy of its per-axis options. -opts = {g:this};utils.update(opts,this.attributes_.axisOptions(axis));this.axes_[axis] = opts;}for(axis = 0;axis < this.axes_.length;axis++) {if(axis === 0){opts = this.optionsViewForAxis_('y' + (axis?'2':''));v = opts("valueRange");if(v)this.axes_[axis].valueRange = v;}else { // To keep old behavior -var axes=this.user_attrs_.axes;if(axes && axes.y2){v = axes.y2.valueRange;if(v)this.axes_[axis].valueRange = v;}}}}; /** - * Returns the number of y-axes on the chart. - * @return {number} the number of axes. - */Dygraph.prototype.numAxes = function(){return this.attributes_.numAxes();}; /** - * @private - * Returns axis properties for the given series. - * @param {string} setName The name of the series for which to get axis - * properties, e.g. 'Y1'. - * @return {Object} The axis properties. - */Dygraph.prototype.axisPropertiesForSeries = function(series){ // TODO(danvk): handle errors. -return this.axes_[this.attributes_.axisForSeries(series)];}; /** - * @private - * Determine the value range and tick marks for each axis. - * @param {Object} extremes A mapping from seriesName -> [low, high] - * This fills in the valueRange and ticks fields in each entry of this.axes_. - */Dygraph.prototype.computeYAxisRanges_ = function(extremes){var isNullUndefinedOrNaN=function isNullUndefinedOrNaN(num){return isNaN(parseFloat(num));};var numAxes=this.attributes_.numAxes();var ypadCompat,span,series,ypad;var p_axis; // Compute extreme values, a span and tick marks for each axis. -for(var i=0;i < numAxes;i++) {var axis=this.axes_[i];var logscale=this.attributes_.getForAxis("logscale",i);var includeZero=this.attributes_.getForAxis("includeZero",i);var independentTicks=this.attributes_.getForAxis("independentTicks",i);series = this.attributes_.seriesForAxis(i); // Add some padding. This supports two Y padding operation modes: -// -// - backwards compatible (yRangePad not set): -// 10% padding for automatic Y ranges, but not for user-supplied -// ranges, and move a close-to-zero edge to zero, since drawing at the edge -// results in invisible lines. Unfortunately lines drawn at the edge of a -// user-supplied range will still be invisible. If logscale is -// set, add a variable amount of padding at the top but -// none at the bottom. -// -// - new-style (yRangePad set by the user): -// always add the specified Y padding. -// -ypadCompat = true;ypad = 0.1; // add 10% -var yRangePad=this.getNumericOption('yRangePad');if(yRangePad !== null){ypadCompat = false; // Convert pixel padding to ratio -ypad = yRangePad / this.plotter_.area.h;}if(series.length === 0){ // If no series are defined or visible then use a reasonable default -axis.extremeRange = [0,1];}else { // Calculate the extremes of extremes. -var minY=Infinity; // extremes[series[0]][0]; -var maxY=-Infinity; // extremes[series[0]][1]; -var extremeMinY,extremeMaxY;for(var j=0;j < series.length;j++) { // this skips invisible series -if(!extremes.hasOwnProperty(series[j]))continue; // Only use valid extremes to stop null data series' from corrupting the scale. -extremeMinY = extremes[series[j]][0];if(extremeMinY !== null){minY = Math.min(extremeMinY,minY);}extremeMaxY = extremes[series[j]][1];if(extremeMaxY !== null){maxY = Math.max(extremeMaxY,maxY);}} // Include zero if requested by the user. -if(includeZero && !logscale){if(minY > 0)minY = 0;if(maxY < 0)maxY = 0;} // Ensure we have a valid scale, otherwise default to [0, 1] for safety. -if(minY == Infinity)minY = 0;if(maxY == -Infinity)maxY = 1;span = maxY - minY; // special case: if we have no sense of scale, center on the sole value. -if(span === 0){if(maxY !== 0){span = Math.abs(maxY);}else { // ... and if the sole value is zero, use range 0-1. -maxY = 1;span = 1;}}var maxAxisY=maxY,minAxisY=minY;if(ypadCompat){if(logscale){maxAxisY = maxY + ypad * span;minAxisY = minY;}else {maxAxisY = maxY + ypad * span;minAxisY = minY - ypad * span; // Backwards-compatible behavior: Move the span to start or end at zero if it's -// close to zero. -if(minAxisY < 0 && minY >= 0)minAxisY = 0;if(maxAxisY > 0 && maxY <= 0)maxAxisY = 0;}}axis.extremeRange = [minAxisY,maxAxisY];}if(axis.valueRange){ // This is a user-set value range for this axis. -var y0=isNullUndefinedOrNaN(axis.valueRange[0])?axis.extremeRange[0]:axis.valueRange[0];var y1=isNullUndefinedOrNaN(axis.valueRange[1])?axis.extremeRange[1]:axis.valueRange[1];axis.computedValueRange = [y0,y1];}else {axis.computedValueRange = axis.extremeRange;}if(!ypadCompat){ // When using yRangePad, adjust the upper/lower bounds to add -// padding unless the user has zoomed/panned the Y axis range. -if(logscale){y0 = axis.computedValueRange[0];y1 = axis.computedValueRange[1];var y0pct=ypad / (2 * ypad - 1);var y1pct=(ypad - 1) / (2 * ypad - 1);axis.computedValueRange[0] = utils.logRangeFraction(y0,y1,y0pct);axis.computedValueRange[1] = utils.logRangeFraction(y0,y1,y1pct);}else {y0 = axis.computedValueRange[0];y1 = axis.computedValueRange[1];span = y1 - y0;axis.computedValueRange[0] = y0 - span * ypad;axis.computedValueRange[1] = y1 + span * ypad;}}if(independentTicks){axis.independentTicks = independentTicks;var opts=this.optionsViewForAxis_('y' + (i?'2':''));var ticker=opts('ticker');axis.ticks = ticker(axis.computedValueRange[0],axis.computedValueRange[1],this.plotter_.area.h,opts,this); // Define the first independent axis as primary axis. -if(!p_axis)p_axis = axis;}}if(p_axis === undefined){throw "Configuration Error: At least one axis has to have the \"independentTicks\" option activated.";} // Add ticks. By default, all axes inherit the tick positions of the -// primary axis. However, if an axis is specifically marked as having -// independent ticks, then that is permissible as well. -for(var i=0;i < numAxes;i++) {var axis=this.axes_[i];if(!axis.independentTicks){var opts=this.optionsViewForAxis_('y' + (i?'2':''));var ticker=opts('ticker');var p_ticks=p_axis.ticks;var p_scale=p_axis.computedValueRange[1] - p_axis.computedValueRange[0];var scale=axis.computedValueRange[1] - axis.computedValueRange[0];var tick_values=[];for(var k=0;k < p_ticks.length;k++) {var y_frac=(p_ticks[k].v - p_axis.computedValueRange[0]) / p_scale;var y_val=axis.computedValueRange[0] + y_frac * scale;tick_values.push(y_val);}axis.ticks = ticker(axis.computedValueRange[0],axis.computedValueRange[1],this.plotter_.area.h,opts,this,tick_values);}}}; /** - * Detects the type of the str (date or numeric) and sets the various - * formatting attributes in this.attrs_ based on this type. - * @param {string} str An x value. - * @private - */Dygraph.prototype.detectTypeFromString_ = function(str){var isDate=false;var dashPos=str.indexOf('-'); // could be 2006-01-01 _or_ 1.0e-2 -if(dashPos > 0 && str[dashPos - 1] != 'e' && str[dashPos - 1] != 'E' || str.indexOf('/') >= 0 || isNaN(parseFloat(str))){isDate = true;}else if(str.length == 8 && str > '19700101' && str < '20371231'){ // TODO(danvk): remove support for this format. -isDate = true;}this.setXAxisOptions_(isDate);};Dygraph.prototype.setXAxisOptions_ = function(isDate){if(isDate){this.attrs_.xValueParser = utils.dateParser;this.attrs_.axes.x.valueFormatter = utils.dateValueFormatter;this.attrs_.axes.x.ticker = DygraphTickers.dateTicker;this.attrs_.axes.x.axisLabelFormatter = utils.dateAxisLabelFormatter;}else { /** @private (shut up, jsdoc!) */this.attrs_.xValueParser = function(x){return parseFloat(x);}; // TODO(danvk): use Dygraph.numberValueFormatter here? -/** @private (shut up, jsdoc!) */this.attrs_.axes.x.valueFormatter = function(x){return x;};this.attrs_.axes.x.ticker = DygraphTickers.numericTicks;this.attrs_.axes.x.axisLabelFormatter = this.attrs_.axes.x.valueFormatter;}}; /** - * @private - * Parses a string in a special csv format. We expect a csv file where each - * line is a date point, and the first field in each line is the date string. - * We also expect that all remaining fields represent series. - * if the errorBars attribute is set, then interpret the fields as: - * date, series1, stddev1, series2, stddev2, ... - * @param {[Object]} data See above. - * - * @return [Object] An array with one entry for each row. These entries - * are an array of cells in that row. The first entry is the parsed x-value for - * the row. The second, third, etc. are the y-values. These can take on one of - * three forms, depending on the CSV and constructor parameters: - * 1. numeric value - * 2. [ value, stddev ] - * 3. [ low value, center value, high value ] - */Dygraph.prototype.parseCSV_ = function(data){var ret=[];var line_delimiter=utils.detectLineDelimiter(data);var lines=data.split(line_delimiter || "\n");var vals,j; // Use the default delimiter or fall back to a tab if that makes sense. -var delim=this.getStringOption('delimiter');if(lines[0].indexOf(delim) == -1 && lines[0].indexOf('\t') >= 0){delim = '\t';}var start=0;if(!('labels' in this.user_attrs_)){ // User hasn't explicitly set labels, so they're (presumably) in the CSV. -start = 1;this.attrs_.labels = lines[0].split(delim); // NOTE: _not_ user_attrs_. -this.attributes_.reparseSeries();}var line_no=0;var xParser;var defaultParserSet=false; // attempt to auto-detect x value type -var expectedCols=this.attr_("labels").length;var outOfOrder=false;for(var i=start;i < lines.length;i++) {var line=lines[i];line_no = i;if(line.length === 0)continue; // skip blank lines -if(line[0] == '#')continue; // skip comment lines -var inFields=line.split(delim);if(inFields.length < 2)continue;var fields=[];if(!defaultParserSet){this.detectTypeFromString_(inFields[0]);xParser = this.getFunctionOption("xValueParser");defaultParserSet = true;}fields[0] = xParser(inFields[0],this); // If fractions are expected, parse the numbers as "A/B" -if(this.fractions_){for(j = 1;j < inFields.length;j++) { // TODO(danvk): figure out an appropriate way to flag parse errors. -vals = inFields[j].split("/");if(vals.length != 2){console.error('Expected fractional "num/den" values in CSV data ' + "but found a value '" + inFields[j] + "' on line " + (1 + i) + " ('" + line + "') which is not of this form.");fields[j] = [0,0];}else {fields[j] = [utils.parseFloat_(vals[0],i,line),utils.parseFloat_(vals[1],i,line)];}}}else if(this.getBooleanOption("errorBars")){ // If there are error bars, values are (value, stddev) pairs -if(inFields.length % 2 != 1){console.error('Expected alternating (value, stdev.) pairs in CSV data ' + 'but line ' + (1 + i) + ' has an odd number of values (' + (inFields.length - 1) + "): '" + line + "'");}for(j = 1;j < inFields.length;j += 2) {fields[(j + 1) / 2] = [utils.parseFloat_(inFields[j],i,line),utils.parseFloat_(inFields[j + 1],i,line)];}}else if(this.getBooleanOption("customBars")){ // Bars are a low;center;high tuple -for(j = 1;j < inFields.length;j++) {var val=inFields[j];if(/^ *$/.test(val)){fields[j] = [null,null,null];}else {vals = val.split(";");if(vals.length == 3){fields[j] = [utils.parseFloat_(vals[0],i,line),utils.parseFloat_(vals[1],i,line),utils.parseFloat_(vals[2],i,line)];}else {console.warn('When using customBars, values must be either blank ' + 'or "low;center;high" tuples (got "' + val + '" on line ' + (1 + i));}}}}else { // Values are just numbers -for(j = 1;j < inFields.length;j++) {fields[j] = utils.parseFloat_(inFields[j],i,line);}}if(ret.length > 0 && fields[0] < ret[ret.length - 1][0]){outOfOrder = true;}if(fields.length != expectedCols){console.error("Number of columns in line " + i + " (" + fields.length + ") does not agree with number of labels (" + expectedCols + ") " + line);} // If the user specified the 'labels' option and none of the cells of the -// first row parsed correctly, then they probably double-specified the -// labels. We go with the values set in the option, discard this row and -// log a warning to the JS console. -if(i === 0 && this.attr_('labels')){var all_null=true;for(j = 0;all_null && j < fields.length;j++) {if(fields[j])all_null = false;}if(all_null){console.warn("The dygraphs 'labels' option is set, but the first row " + "of CSV data ('" + line + "') appears to also contain " + "labels. Will drop the CSV labels and use the option " + "labels.");continue;}}ret.push(fields);}if(outOfOrder){console.warn("CSV is out of order; order it correctly to speed loading.");ret.sort(function(a,b){return a[0] - b[0];});}return ret;}; // In native format, all values must be dates or numbers. -// This check isn't perfect but will catch most mistaken uses of strings. -function validateNativeFormat(data){var firstRow=data[0];var firstX=firstRow[0];if(typeof firstX !== 'number' && !utils.isDateLike(firstX)){throw new Error('Expected number or date but got ' + typeof firstX + ': ' + firstX + '.');}for(var i=1;i < firstRow.length;i++) {var val=firstRow[i];if(val === null || val === undefined)continue;if(typeof val === 'number')continue;if(utils.isArrayLike(val))continue; // e.g. error bars or custom bars. -throw new Error('Expected number or array but got ' + typeof val + ': ' + val + '.');}} /** - * The user has provided their data as a pre-packaged JS array. If the x values - * are numeric, this is the same as dygraphs' internal format. If the x values - * are dates, we need to convert them from Date objects to ms since epoch. - * @param {!Array} data - * @return {Object} data with numeric x values. - * @private - */Dygraph.prototype.parseArray_ = function(data){ // Peek at the first x value to see if it's numeric. -if(data.length === 0){console.error("Can't plot empty data set");return null;}if(data[0].length === 0){console.error("Data set cannot contain an empty row");return null;}validateNativeFormat(data);var i;if(this.attr_("labels") === null){console.warn("Using default labels. Set labels explicitly via 'labels' " + "in the options parameter");this.attrs_.labels = ["X"];for(i = 1;i < data[0].length;i++) {this.attrs_.labels.push("Y" + i); // Not user_attrs_. -}this.attributes_.reparseSeries();}else {var num_labels=this.attr_("labels");if(num_labels.length != data[0].length){console.error("Mismatch between number of labels (" + num_labels + ")" + " and number of columns in array (" + data[0].length + ")");return null;}}if(utils.isDateLike(data[0][0])){ // Some intelligent defaults for a date x-axis. -this.attrs_.axes.x.valueFormatter = utils.dateValueFormatter;this.attrs_.axes.x.ticker = DygraphTickers.dateTicker;this.attrs_.axes.x.axisLabelFormatter = utils.dateAxisLabelFormatter; // Assume they're all dates. -var parsedData=utils.clone(data);for(i = 0;i < data.length;i++) {if(parsedData[i].length === 0){console.error("Row " + (1 + i) + " of data is empty");return null;}if(parsedData[i][0] === null || typeof parsedData[i][0].getTime != 'function' || isNaN(parsedData[i][0].getTime())){console.error("x value in row " + (1 + i) + " is not a Date");return null;}parsedData[i][0] = parsedData[i][0].getTime();}return parsedData;}else { // Some intelligent defaults for a numeric x-axis. -/** @private (shut up, jsdoc!) */this.attrs_.axes.x.valueFormatter = function(x){return x;};this.attrs_.axes.x.ticker = DygraphTickers.numericTicks;this.attrs_.axes.x.axisLabelFormatter = utils.numberAxisLabelFormatter;return data;}}; /** - * Parses a DataTable object from gviz. - * The data is expected to have a first column that is either a date or a - * number. All subsequent columns must be numbers. If there is a clear mismatch - * between this.xValueParser_ and the type of the first column, it will be - * fixed. Fills out rawData_. - * @param {!google.visualization.DataTable} data See above. - * @private - */Dygraph.prototype.parseDataTable_ = function(data){var shortTextForAnnotationNum=function shortTextForAnnotationNum(num){ // converts [0-9]+ [A-Z][a-z]* -// example: 0=A, 1=B, 25=Z, 26=Aa, 27=Ab -// and continues like.. Ba Bb .. Za .. Zz..Aaa...Zzz Aaaa Zzzz -var shortText=String.fromCharCode(65 /* A */ + num % 26);num = Math.floor(num / 26);while(num > 0) {shortText = String.fromCharCode(65 /* A */ + (num - 1) % 26) + shortText.toLowerCase();num = Math.floor((num - 1) / 26);}return shortText;};var cols=data.getNumberOfColumns();var rows=data.getNumberOfRows();var indepType=data.getColumnType(0);if(indepType == 'date' || indepType == 'datetime'){this.attrs_.xValueParser = utils.dateParser;this.attrs_.axes.x.valueFormatter = utils.dateValueFormatter;this.attrs_.axes.x.ticker = DygraphTickers.dateTicker;this.attrs_.axes.x.axisLabelFormatter = utils.dateAxisLabelFormatter;}else if(indepType == 'number'){this.attrs_.xValueParser = function(x){return parseFloat(x);};this.attrs_.axes.x.valueFormatter = function(x){return x;};this.attrs_.axes.x.ticker = DygraphTickers.numericTicks;this.attrs_.axes.x.axisLabelFormatter = this.attrs_.axes.x.valueFormatter;}else {throw new Error("only 'date', 'datetime' and 'number' types are supported " + "for column 1 of DataTable input (Got '" + indepType + "')");} // Array of the column indices which contain data (and not annotations). -var colIdx=[];var annotationCols={}; // data index -> [annotation cols] -var hasAnnotations=false;var i,j;for(i = 1;i < cols;i++) {var type=data.getColumnType(i);if(type == 'number'){colIdx.push(i);}else if(type == 'string' && this.getBooleanOption('displayAnnotations')){ // This is OK -- it's an annotation column. -var dataIdx=colIdx[colIdx.length - 1];if(!annotationCols.hasOwnProperty(dataIdx)){annotationCols[dataIdx] = [i];}else {annotationCols[dataIdx].push(i);}hasAnnotations = true;}else {throw new Error("Only 'number' is supported as a dependent type with Gviz." + " 'string' is only supported if displayAnnotations is true");}} // Read column labels -// TODO(danvk): add support back for errorBars -var labels=[data.getColumnLabel(0)];for(i = 0;i < colIdx.length;i++) {labels.push(data.getColumnLabel(colIdx[i]));if(this.getBooleanOption("errorBars"))i += 1;}this.attrs_.labels = labels;cols = labels.length;var ret=[];var outOfOrder=false;var annotations=[];for(i = 0;i < rows;i++) {var row=[];if(typeof data.getValue(i,0) === 'undefined' || data.getValue(i,0) === null){console.warn("Ignoring row " + i + " of DataTable because of undefined or null first column.");continue;}if(indepType == 'date' || indepType == 'datetime'){row.push(data.getValue(i,0).getTime());}else {row.push(data.getValue(i,0));}if(!this.getBooleanOption("errorBars")){for(j = 0;j < colIdx.length;j++) {var col=colIdx[j];row.push(data.getValue(i,col));if(hasAnnotations && annotationCols.hasOwnProperty(col) && data.getValue(i,annotationCols[col][0]) !== null){var ann={};ann.series = data.getColumnLabel(col);ann.xval = row[0];ann.shortText = shortTextForAnnotationNum(annotations.length);ann.text = '';for(var k=0;k < annotationCols[col].length;k++) {if(k)ann.text += "\n";ann.text += data.getValue(i,annotationCols[col][k]);}annotations.push(ann);}} // Strip out infinities, which give dygraphs problems later on. -for(j = 0;j < row.length;j++) {if(!isFinite(row[j]))row[j] = null;}}else {for(j = 0;j < cols - 1;j++) {row.push([data.getValue(i,1 + 2 * j),data.getValue(i,2 + 2 * j)]);}}if(ret.length > 0 && row[0] < ret[ret.length - 1][0]){outOfOrder = true;}ret.push(row);}if(outOfOrder){console.warn("DataTable is out of order; order it correctly to speed loading.");ret.sort(function(a,b){return a[0] - b[0];});}this.rawData_ = ret;if(annotations.length > 0){this.setAnnotations(annotations,true);}this.attributes_.reparseSeries();}; /** - * Signals to plugins that the chart data has updated. - * This happens after the data has updated but before the chart has redrawn. - * @private - */Dygraph.prototype.cascadeDataDidUpdateEvent_ = function(){ // TODO(danvk): there are some issues checking xAxisRange() and using -// toDomCoords from handlers of this event. The visible range should be set -// when the chart is drawn, not derived from the data. -this.cascadeEvents_('dataDidUpdate',{});}; /** - * Get the CSV data. If it's in a function, call that function. If it's in a - * file, do an XMLHttpRequest to get it. - * @private - */Dygraph.prototype.start_ = function(){var data=this.file_; // Functions can return references of all other types. -if(typeof data == 'function'){data = data();}if(utils.isArrayLike(data)){this.rawData_ = this.parseArray_(data);this.cascadeDataDidUpdateEvent_();this.predraw_();}else if(typeof data == 'object' && typeof data.getColumnRange == 'function'){ // must be a DataTable from gviz. -this.parseDataTable_(data);this.cascadeDataDidUpdateEvent_();this.predraw_();}else if(typeof data == 'string'){ // Heuristic: a newline means it's CSV data. Otherwise it's an URL. -var line_delimiter=utils.detectLineDelimiter(data);if(line_delimiter){this.loadedEvent_(data);}else { // REMOVE_FOR_IE -var req;if(window.XMLHttpRequest){ // Firefox, Opera, IE7, and other browsers will use the native object -req = new XMLHttpRequest();}else { // IE 5 and 6 will use the ActiveX control -req = new ActiveXObject("Microsoft.XMLHTTP");}var caller=this;req.onreadystatechange = function(){if(req.readyState == 4){if(req.status === 200 || // Normal http -req.status === 0){ // Chrome w/ --allow-file-access-from-files -caller.loadedEvent_(req.responseText);}}};req.open("GET",data,true);req.send(null);}}else {console.error("Unknown data format: " + typeof data);}}; /** - * Changes various properties of the graph. These can include: - *
    - *
  • file: changes the source data for the graph
  • - *
  • errorBars: changes whether the data contains stddev
  • - *
- * - * There's a huge variety of options that can be passed to this method. For a - * full list, see http://dygraphs.com/options.html. - * - * @param {Object} input_attrs The new properties and values - * @param {boolean} block_redraw Usually the chart is redrawn after every - * call to updateOptions(). If you know better, you can pass true to - * explicitly block the redraw. This can be useful for chaining - * updateOptions() calls, avoiding the occasional infinite loop and - * preventing redraws when it's not necessary (e.g. when updating a - * callback). - */Dygraph.prototype.updateOptions = function(input_attrs,block_redraw){if(typeof block_redraw == 'undefined')block_redraw = false; // copyUserAttrs_ drops the "file" parameter as a convenience to us. -var file=input_attrs.file;var attrs=Dygraph.copyUserAttrs_(input_attrs); // TODO(danvk): this is a mess. Move these options into attr_. -if('rollPeriod' in attrs){this.rollPeriod_ = attrs.rollPeriod;}if('dateWindow' in attrs){this.dateWindow_ = attrs.dateWindow;} // TODO(danvk): validate per-series options. -// Supported: -// strokeWidth -// pointSize -// drawPoints -// highlightCircleSize -// Check if this set options will require new points. -var requiresNewPoints=utils.isPixelChangingOptionList(this.attr_("labels"),attrs);utils.updateDeep(this.user_attrs_,attrs);this.attributes_.reparseSeries();if(file){ // This event indicates that the data is about to change, but hasn't yet. -// TODO(danvk): support cancellation of the update via this event. -this.cascadeEvents_('dataWillUpdate',{});this.file_ = file;if(!block_redraw)this.start_();}else {if(!block_redraw){if(requiresNewPoints){this.predraw_();}else {this.renderGraph_(false);}}}}; /** - * Make a copy of input attributes, removing file as a convenience. - * @private - */Dygraph.copyUserAttrs_ = function(attrs){var my_attrs={};for(var k in attrs) {if(!attrs.hasOwnProperty(k))continue;if(k == 'file')continue;if(attrs.hasOwnProperty(k))my_attrs[k] = attrs[k];}return my_attrs;}; /** - * Resizes the dygraph. If no parameters are specified, resizes to fill the - * containing div (which has presumably changed size since the dygraph was - * instantiated. If the width/height are specified, the div will be resized. - * - * This is far more efficient than destroying and re-instantiating a - * Dygraph, since it doesn't have to reparse the underlying data. - * - * @param {number} width Width (in pixels) - * @param {number} height Height (in pixels) - */Dygraph.prototype.resize = function(width,height){if(this.resize_lock){return;}this.resize_lock = true;if(width === null != (height === null)){console.warn("Dygraph.resize() should be called with zero parameters or " + "two non-NULL parameters. Pretending it was zero.");width = height = null;}var old_width=this.width_;var old_height=this.height_;if(width){this.maindiv_.style.width = width + "px";this.maindiv_.style.height = height + "px";this.width_ = width;this.height_ = height;}else {this.width_ = this.maindiv_.clientWidth;this.height_ = this.maindiv_.clientHeight;}if(old_width != this.width_ || old_height != this.height_){ // Resizing a canvas erases it, even when the size doesn't change, so -// any resize needs to be followed by a redraw. -this.resizeElements_();this.predraw_();}this.resize_lock = false;}; /** - * Adjusts the number of points in the rolling average. Updates the graph to - * reflect the new averaging period. - * @param {number} length Number of points over which to average the data. - */Dygraph.prototype.adjustRoll = function(length){this.rollPeriod_ = length;this.predraw_();}; /** - * Returns a boolean array of visibility statuses. - */Dygraph.prototype.visibility = function(){ // Do lazy-initialization, so that this happens after we know the number of -// data series. -if(!this.getOption("visibility")){this.attrs_.visibility = [];} // TODO(danvk): it looks like this could go into an infinite loop w/ user_attrs. -while(this.getOption("visibility").length < this.numColumns() - 1) {this.attrs_.visibility.push(true);}return this.getOption("visibility");}; /** - * Changes the visibility of one or more series. - * - * @param {number|number[]|object} num the series index or an array of series indices - * or a boolean array of visibility states by index - * or an object mapping series numbers, as keys, to - * visibility state (boolean values) - * @param {boolean} value the visibility state expressed as a boolean - */Dygraph.prototype.setVisibility = function(num,value){var x=this.visibility();var numIsObject=false;if(!Array.isArray(num)){if(num !== null && typeof num === 'object'){numIsObject = true;}else {num = [num];}}if(numIsObject){for(var i in num) {if(num.hasOwnProperty(i)){if(i < 0 || i >= x.length){console.warn("Invalid series number in setVisibility: " + i);}else {x[i] = num[i];}}}}else {for(var i=0;i < num.length;i++) {if(typeof num[i] === 'boolean'){if(i >= x.length){console.warn("Invalid series number in setVisibility: " + i);}else {x[i] = num[i];}}else {if(num[i] < 0 || num[i] >= x.length){console.warn("Invalid series number in setVisibility: " + num[i]);}else {x[num[i]] = value;}}}}this.predraw_();}; /** - * How large of an area will the dygraph render itself in? - * This is used for testing. - * @return A {width: w, height: h} object. - * @private - */Dygraph.prototype.size = function(){return {width:this.width_,height:this.height_};}; /** - * Update the list of annotations and redraw the chart. - * See dygraphs.com/annotations.html for more info on how to use annotations. - * @param ann {Array} An array of annotation objects. - * @param suppressDraw {Boolean} Set to "true" to block chart redraw (optional). - */Dygraph.prototype.setAnnotations = function(ann,suppressDraw){ // Only add the annotation CSS rule once we know it will be used. -this.annotations_ = ann;if(!this.layout_){console.warn("Tried to setAnnotations before dygraph was ready. " + "Try setting them in a ready() block. See " + "dygraphs.com/tests/annotation.html");return;}this.layout_.setAnnotations(this.annotations_);if(!suppressDraw){this.predraw_();}}; /** - * Return the list of annotations. - */Dygraph.prototype.annotations = function(){return this.annotations_;}; /** - * Get the list of label names for this graph. The first column is the - * x-axis, so the data series names start at index 1. - * - * Returns null when labels have not yet been defined. - */Dygraph.prototype.getLabels = function(){var labels=this.attr_("labels");return labels?labels.slice():null;}; /** - * Get the index of a series (column) given its name. The first column is the - * x-axis, so the data series start with index 1. - */Dygraph.prototype.indexFromSetName = function(name){return this.setIndexByName_[name];}; /** - * Find the row number corresponding to the given x-value. - * Returns null if there is no such x-value in the data. - * If there are multiple rows with the same x-value, this will return the - * first one. - * @param {number} xVal The x-value to look for (e.g. millis since epoch). - * @return {?number} The row number, which you can pass to getValue(), or null. - */Dygraph.prototype.getRowForX = function(xVal){var low=0,high=this.numRows() - 1;while(low <= high) {var idx=high + low >> 1;var x=this.getValue(idx,0);if(x < xVal){low = idx + 1;}else if(x > xVal){high = idx - 1;}else if(low != idx){ // equal, but there may be an earlier match. -high = idx;}else {return idx;}}return null;}; /** - * Trigger a callback when the dygraph has drawn itself and is ready to be - * manipulated. This is primarily useful when dygraphs has to do an XHR for the - * data (i.e. a URL is passed as the data source) and the chart is drawn - * asynchronously. If the chart has already drawn, the callback will fire - * immediately. - * - * This is a good place to call setAnnotation(). - * - * @param {function(!Dygraph)} callback The callback to trigger when the chart - * is ready. - */Dygraph.prototype.ready = function(callback){if(this.is_initial_draw_){this.readyFns_.push(callback);}else {callback.call(this,this);}}; /** - * Add an event handler. This event handler is kept until the graph is - * destroyed with a call to graph.destroy(). - * - * @param {!Node} elem The element to add the event to. - * @param {string} type The type of the event, e.g. 'click' or 'mousemove'. - * @param {function(Event):(boolean|undefined)} fn The function to call - * on the event. The function takes one parameter: the event object. - * @private - */Dygraph.prototype.addAndTrackEvent = function(elem,type,fn){utils.addEvent(elem,type,fn);this.registeredEvents_.push({elem:elem,type:type,fn:fn});};Dygraph.prototype.removeTrackedEvents_ = function(){if(this.registeredEvents_){for(var idx=0;idx < this.registeredEvents_.length;idx++) {var reg=this.registeredEvents_[idx];utils.removeEvent(reg.elem,reg.type,reg.fn);}}this.registeredEvents_ = [];}; // Installed plugins, in order of precedence (most-general to most-specific). -Dygraph.PLUGINS = [_pluginsLegend2['default'],_pluginsAxes2['default'],_pluginsRangeSelector2['default'], // Has to be before ChartLabels so that its callbacks are called after ChartLabels' callbacks. -_pluginsChartLabels2['default'],_pluginsAnnotations2['default'],_pluginsGrid2['default']]; // There are many symbols which have historically been available through the -// Dygraph class. These are exported here for backwards compatibility. -Dygraph.GVizChart = _dygraphGviz2['default'];Dygraph.DASHED_LINE = utils.DASHED_LINE;Dygraph.DOT_DASH_LINE = utils.DOT_DASH_LINE;Dygraph.dateAxisLabelFormatter = utils.dateAxisLabelFormatter;Dygraph.toRGB_ = utils.toRGB_;Dygraph.findPos = utils.findPos;Dygraph.pageX = utils.pageX;Dygraph.pageY = utils.pageY;Dygraph.dateString_ = utils.dateString_;Dygraph.defaultInteractionModel = _dygraphInteractionModel2['default'].defaultModel;Dygraph.nonInteractiveModel = Dygraph.nonInteractiveModel_ = _dygraphInteractionModel2['default'].nonInteractiveModel_;Dygraph.Circles = utils.Circles;Dygraph.Plugins = {Legend:_pluginsLegend2['default'],Axes:_pluginsAxes2['default'],Annotations:_pluginsAnnotations2['default'],ChartLabels:_pluginsChartLabels2['default'],Grid:_pluginsGrid2['default'],RangeSelector:_pluginsRangeSelector2['default']};Dygraph.DataHandlers = {DefaultHandler:_datahandlerDefault2['default'],BarsHandler:_datahandlerBars2['default'],CustomBarsHandler:_datahandlerBarsCustom2['default'],DefaultFractionHandler:_datahandlerDefaultFractions2['default'],ErrorBarsHandler:_datahandlerBarsError2['default'],FractionsBarsHandler:_datahandlerBarsFractions2['default']};Dygraph.startPan = _dygraphInteractionModel2['default'].startPan;Dygraph.startZoom = _dygraphInteractionModel2['default'].startZoom;Dygraph.movePan = _dygraphInteractionModel2['default'].movePan;Dygraph.moveZoom = _dygraphInteractionModel2['default'].moveZoom;Dygraph.endPan = _dygraphInteractionModel2['default'].endPan;Dygraph.endZoom = _dygraphInteractionModel2['default'].endZoom;Dygraph.numericLinearTicks = DygraphTickers.numericLinearTicks;Dygraph.numericTicks = DygraphTickers.numericTicks;Dygraph.dateTicker = DygraphTickers.dateTicker;Dygraph.Granularity = DygraphTickers.Granularity;Dygraph.getDateAxis = DygraphTickers.getDateAxis;Dygraph.floatFormat = utils.floatFormat;exports['default'] = Dygraph;module.exports = exports['default']; - -}).call(this,require('_process')) - -},{"./datahandler/bars":5,"./datahandler/bars-custom":2,"./datahandler/bars-error":3,"./datahandler/bars-fractions":4,"./datahandler/default":8,"./datahandler/default-fractions":7,"./dygraph-canvas":9,"./dygraph-default-attrs":10,"./dygraph-gviz":11,"./dygraph-interaction-model":12,"./dygraph-layout":13,"./dygraph-options":15,"./dygraph-options-reference":14,"./dygraph-tickers":16,"./dygraph-utils":17,"./iframe-tarp":19,"./plugins/annotations":20,"./plugins/axes":21,"./plugins/chart-labels":22,"./plugins/grid":23,"./plugins/legend":24,"./plugins/range-selector":25,"_process":1}],19:[function(require,module,exports){ -/** - * To create a "drag" interaction, you typically register a mousedown event - * handler on the element where the drag begins. In that handler, you register a - * mouseup handler on the window to determine when the mouse is released, - * wherever that release happens. This works well, except when the user releases - * the mouse over an off-domain iframe. In that case, the mouseup event is - * handled by the iframe and never bubbles up to the window handler. - * - * To deal with this issue, we cover iframes with high z-index divs to make sure - * they don't capture mouseup. - * - * Usage: - * element.addEventListener('mousedown', function() { - * var tarper = new IFrameTarp(); - * tarper.cover(); - * var mouseUpHandler = function() { - * ... - * window.removeEventListener(mouseUpHandler); - * tarper.uncover(); - * }; - * window.addEventListener('mouseup', mouseUpHandler); - * }; - * - * @constructor - */ -"use strict"; - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj["default"] = obj; return newObj; } } - -var _dygraphUtils = require('./dygraph-utils'); - -var utils = _interopRequireWildcard(_dygraphUtils); - -function IFrameTarp() { - /** @type {Array.} */ - this.tarps = []; -}; - -/** - * Find all the iframes in the document and cover them with high z-index - * transparent divs. - */ -IFrameTarp.prototype.cover = function () { - var iframes = document.getElementsByTagName("iframe"); - for (var i = 0; i < iframes.length; i++) { - var iframe = iframes[i]; - var pos = utils.findPos(iframe), - x = pos.x, - y = pos.y, - width = iframe.offsetWidth, - height = iframe.offsetHeight; - - var div = document.createElement("div"); - div.style.position = "absolute"; - div.style.left = x + 'px'; - div.style.top = y + 'px'; - div.style.width = width + 'px'; - div.style.height = height + 'px'; - div.style.zIndex = 999; - document.body.appendChild(div); - this.tarps.push(div); - } -}; - -/** - * Remove all the iframe covers. You should call this in a mouseup handler. - */ -IFrameTarp.prototype.uncover = function () { - for (var i = 0; i < this.tarps.length; i++) { - this.tarps[i].parentNode.removeChild(this.tarps[i]); - } - this.tarps = []; -}; - -exports["default"] = IFrameTarp; -module.exports = exports["default"]; - -},{"./dygraph-utils":17}],20:[function(require,module,exports){ -/** - * @license - * Copyright 2012 Dan Vanderkam (danvdk@gmail.com) - * MIT-licensed (http://opensource.org/licenses/MIT) - */ - -/*global Dygraph:false */ - -"use strict"; - -/** -Current bits of jankiness: -- Uses dygraph.layout_ to get the parsed annotations. -- Uses dygraph.plotter_.area - -It would be nice if the plugin didn't require so much special support inside -the core dygraphs classes, but annotations involve quite a bit of parsing and -layout. - -TODO(danvk): cache DOM elements. -*/ - -Object.defineProperty(exports, "__esModule", { - value: true -}); -var annotations = function annotations() { - this.annotations_ = []; -}; - -annotations.prototype.toString = function () { - return "Annotations Plugin"; -}; - -annotations.prototype.activate = function (g) { - return { - clearChart: this.clearChart, - didDrawChart: this.didDrawChart - }; -}; - -annotations.prototype.detachLabels = function () { - for (var i = 0; i < this.annotations_.length; i++) { - var a = this.annotations_[i]; - if (a.parentNode) a.parentNode.removeChild(a); - this.annotations_[i] = null; - } - this.annotations_ = []; -}; - -annotations.prototype.clearChart = function (e) { - this.detachLabels(); -}; - -annotations.prototype.didDrawChart = function (e) { - var g = e.dygraph; - - // Early out in the (common) case of zero annotations. - var points = g.layout_.annotated_points; - if (!points || points.length === 0) return; - - var containerDiv = e.canvas.parentNode; - - var bindEvt = function bindEvt(eventName, classEventName, pt) { - return function (annotation_event) { - var a = pt.annotation; - if (a.hasOwnProperty(eventName)) { - a[eventName](a, pt, g, annotation_event); - } else if (g.getOption(classEventName)) { - g.getOption(classEventName)(a, pt, g, annotation_event); - } - }; - }; - - // Add the annotations one-by-one. - var area = e.dygraph.getArea(); - - // x-coord to sum of previous annotation's heights (used for stacking). - var xToUsedHeight = {}; - - for (var i = 0; i < points.length; i++) { - var p = points[i]; - if (p.canvasx < area.x || p.canvasx > area.x + area.w || p.canvasy < area.y || p.canvasy > area.y + area.h) { - continue; - } - - var a = p.annotation; - var tick_height = 6; - if (a.hasOwnProperty("tickHeight")) { - tick_height = a.tickHeight; - } - - // TODO: deprecate axisLabelFontSize in favor of CSS - var div = document.createElement("div"); - div.style['fontSize'] = g.getOption('axisLabelFontSize') + "px"; - var className = 'dygraph-annotation'; - if (!a.hasOwnProperty('icon')) { - // camelCase class names are deprecated. - className += ' dygraphDefaultAnnotation dygraph-default-annotation'; - } - if (a.hasOwnProperty('cssClass')) { - className += " " + a.cssClass; - } - div.className = className; - - var width = a.hasOwnProperty('width') ? a.width : 16; - var height = a.hasOwnProperty('height') ? a.height : 16; - if (a.hasOwnProperty('icon')) { - var img = document.createElement("img"); - img.src = a.icon; - img.width = width; - img.height = height; - div.appendChild(img); - } else if (p.annotation.hasOwnProperty('shortText')) { - div.appendChild(document.createTextNode(p.annotation.shortText)); - } - var left = p.canvasx - width / 2; - div.style.left = left + "px"; - var divTop = 0; - if (a.attachAtBottom) { - var y = area.y + area.h - height - tick_height; - if (xToUsedHeight[left]) { - y -= xToUsedHeight[left]; - } else { - xToUsedHeight[left] = 0; - } - xToUsedHeight[left] += tick_height + height; - divTop = y; - } else { - divTop = p.canvasy - height - tick_height; - } - div.style.top = divTop + "px"; - div.style.width = width + "px"; - div.style.height = height + "px"; - div.title = p.annotation.text; - div.style.color = g.colorsMap_[p.name]; - div.style.borderColor = g.colorsMap_[p.name]; - a.div = div; - - g.addAndTrackEvent(div, 'click', bindEvt('clickHandler', 'annotationClickHandler', p, this)); - g.addAndTrackEvent(div, 'mouseover', bindEvt('mouseOverHandler', 'annotationMouseOverHandler', p, this)); - g.addAndTrackEvent(div, 'mouseout', bindEvt('mouseOutHandler', 'annotationMouseOutHandler', p, this)); - g.addAndTrackEvent(div, 'dblclick', bindEvt('dblClickHandler', 'annotationDblClickHandler', p, this)); - - containerDiv.appendChild(div); - this.annotations_.push(div); - - var ctx = e.drawingContext; - ctx.save(); - ctx.strokeStyle = a.hasOwnProperty('tickColor') ? a.tickColor : g.colorsMap_[p.name]; - ctx.lineWidth = a.hasOwnProperty('tickWidth') ? a.tickWidth : g.getOption('strokeWidth'); - ctx.beginPath(); - if (!a.attachAtBottom) { - ctx.moveTo(p.canvasx, p.canvasy); - ctx.lineTo(p.canvasx, p.canvasy - 2 - tick_height); - } else { - var y = divTop + height; - ctx.moveTo(p.canvasx, y); - ctx.lineTo(p.canvasx, y + tick_height); - } - ctx.closePath(); - ctx.stroke(); - ctx.restore(); - } -}; - -annotations.prototype.destroy = function () { - this.detachLabels(); -}; - -exports["default"] = annotations; -module.exports = exports["default"]; - -},{}],21:[function(require,module,exports){ -/** - * @license - * Copyright 2012 Dan Vanderkam (danvdk@gmail.com) - * MIT-licensed (http://opensource.org/licenses/MIT) - */ - -/*global Dygraph:false */ - -'use strict'; - -/* -Bits of jankiness: -- Direct layout access -- Direct area access -- Should include calculation of ticks, not just the drawing. - -Options left to make axis-friendly. - ('drawAxesAtZero') - ('xAxisHeight') -*/ - -Object.defineProperty(exports, '__esModule', { - value: true -}); - -function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } } - -var _dygraphUtils = require('../dygraph-utils'); - -var utils = _interopRequireWildcard(_dygraphUtils); - -/** - * Draws the axes. This includes the labels on the x- and y-axes, as well - * as the tick marks on the axes. - * It does _not_ draw the grid lines which span the entire chart. - */ -var axes = function axes() { - this.xlabels_ = []; - this.ylabels_ = []; -}; - -axes.prototype.toString = function () { - return 'Axes Plugin'; -}; - -axes.prototype.activate = function (g) { - return { - layout: this.layout, - clearChart: this.clearChart, - willDrawChart: this.willDrawChart - }; -}; - -axes.prototype.layout = function (e) { - var g = e.dygraph; - - if (g.getOptionForAxis('drawAxis', 'y')) { - var w = g.getOptionForAxis('axisLabelWidth', 'y') + 2 * g.getOptionForAxis('axisTickSize', 'y'); - e.reserveSpaceLeft(w); - } - - if (g.getOptionForAxis('drawAxis', 'x')) { - var h; - // NOTE: I think this is probably broken now, since g.getOption() now - // hits the dictionary. (That is, g.getOption('xAxisHeight') now always - // has a value.) - if (g.getOption('xAxisHeight')) { - h = g.getOption('xAxisHeight'); - } else { - h = g.getOptionForAxis('axisLabelFontSize', 'x') + 2 * g.getOptionForAxis('axisTickSize', 'x'); - } - e.reserveSpaceBottom(h); - } - - if (g.numAxes() == 2) { - if (g.getOptionForAxis('drawAxis', 'y2')) { - var w = g.getOptionForAxis('axisLabelWidth', 'y2') + 2 * g.getOptionForAxis('axisTickSize', 'y2'); - e.reserveSpaceRight(w); - } - } else if (g.numAxes() > 2) { - g.error('Only two y-axes are supported at this time. (Trying ' + 'to use ' + g.numAxes() + ')'); - } -}; - -axes.prototype.detachLabels = function () { - function removeArray(ary) { - for (var i = 0; i < ary.length; i++) { - var el = ary[i]; - if (el.parentNode) el.parentNode.removeChild(el); - } - } - - removeArray(this.xlabels_); - removeArray(this.ylabels_); - this.xlabels_ = []; - this.ylabels_ = []; -}; - -axes.prototype.clearChart = function (e) { - this.detachLabels(); -}; - -axes.prototype.willDrawChart = function (e) { - var _this = this; - - var g = e.dygraph; - - if (!g.getOptionForAxis('drawAxis', 'x') && !g.getOptionForAxis('drawAxis', 'y') && !g.getOptionForAxis('drawAxis', 'y2')) { - return; - } - - // Round pixels to half-integer boundaries for crisper drawing. - function halfUp(x) { - return Math.round(x) + 0.5; - } - function halfDown(y) { - return Math.round(y) - 0.5; - } - - var context = e.drawingContext; - var containerDiv = e.canvas.parentNode; - var canvasWidth = g.width_; // e.canvas.width is affected by pixel ratio. - var canvasHeight = g.height_; - - var label, x, y, tick, i; - - var makeLabelStyle = function makeLabelStyle(axis) { - return { - position: 'absolute', - fontSize: g.getOptionForAxis('axisLabelFontSize', axis) + 'px', - width: g.getOptionForAxis('axisLabelWidth', axis) + 'px' - }; - }; - - var labelStyles = { - x: makeLabelStyle('x'), - y: makeLabelStyle('y'), - y2: makeLabelStyle('y2') - }; - - var makeDiv = function makeDiv(txt, axis, prec_axis) { - /* - * This seems to be called with the following three sets of axis/prec_axis: - * x: undefined - * y: y1 - * y: y2 - */ - var div = document.createElement('div'); - var labelStyle = labelStyles[prec_axis == 'y2' ? 'y2' : axis]; - utils.update(div.style, labelStyle); - // TODO: combine outer & inner divs - var inner_div = document.createElement('div'); - inner_div.className = 'dygraph-axis-label' + ' dygraph-axis-label-' + axis + (prec_axis ? ' dygraph-axis-label-' + prec_axis : ''); - inner_div.innerHTML = txt; - div.appendChild(inner_div); - return div; - }; - - // axis lines - context.save(); - - var layout = g.layout_; - var area = e.dygraph.plotter_.area; - - // Helper for repeated axis-option accesses. - var makeOptionGetter = function makeOptionGetter(axis) { - return function (option) { - return g.getOptionForAxis(option, axis); - }; - }; - - if (g.getOptionForAxis('drawAxis', 'y')) { - if (layout.yticks && layout.yticks.length > 0) { - var num_axes = g.numAxes(); - var getOptions = [makeOptionGetter('y'), makeOptionGetter('y2')]; - layout.yticks.forEach(function (tick) { - if (tick.label === undefined) return; // this tick only has a grid line. - x = area.x; - var sgn = 1; - var prec_axis = 'y1'; - var getAxisOption = getOptions[0]; - if (tick.axis == 1) { - // right-side y-axis - x = area.x + area.w; - sgn = -1; - prec_axis = 'y2'; - getAxisOption = getOptions[1]; - } - var fontSize = getAxisOption('axisLabelFontSize'); - y = area.y + tick.pos * area.h; - - /* Tick marks are currently clipped, so don't bother drawing them. - context.beginPath(); - context.moveTo(halfUp(x), halfDown(y)); - context.lineTo(halfUp(x - sgn * this.attr_('axisTickSize')), halfDown(y)); - context.closePath(); - context.stroke(); - */ - - label = makeDiv(tick.label, 'y', num_axes == 2 ? prec_axis : null); - var top = y - fontSize / 2; - if (top < 0) top = 0; - - if (top + fontSize + 3 > canvasHeight) { - label.style.bottom = '0'; - } else { - label.style.top = top + 'px'; - } - // TODO: replace these with css classes? - if (tick.axis === 0) { - label.style.left = area.x - getAxisOption('axisLabelWidth') - getAxisOption('axisTickSize') + 'px'; - label.style.textAlign = 'right'; - } else if (tick.axis == 1) { - label.style.left = area.x + area.w + getAxisOption('axisTickSize') + 'px'; - label.style.textAlign = 'left'; - } - label.style.width = getAxisOption('axisLabelWidth') + 'px'; - containerDiv.appendChild(label); - _this.ylabels_.push(label); - }); - - // The lowest tick on the y-axis often overlaps with the leftmost - // tick on the x-axis. Shift the bottom tick up a little bit to - // compensate if necessary. - var bottomTick = this.ylabels_[0]; - // Interested in the y2 axis also? - var fontSize = g.getOptionForAxis('axisLabelFontSize', 'y'); - var bottom = parseInt(bottomTick.style.top, 10) + fontSize; - if (bottom > canvasHeight - fontSize) { - bottomTick.style.top = parseInt(bottomTick.style.top, 10) - fontSize / 2 + 'px'; - } - } - - // draw a vertical line on the left to separate the chart from the labels. - var axisX; - if (g.getOption('drawAxesAtZero')) { - var r = g.toPercentXCoord(0); - if (r > 1 || r < 0 || isNaN(r)) r = 0; - axisX = halfUp(area.x + r * area.w); - } else { - axisX = halfUp(area.x); - } - - context.strokeStyle = g.getOptionForAxis('axisLineColor', 'y'); - context.lineWidth = g.getOptionForAxis('axisLineWidth', 'y'); - - context.beginPath(); - context.moveTo(axisX, halfDown(area.y)); - context.lineTo(axisX, halfDown(area.y + area.h)); - context.closePath(); - context.stroke(); - - // if there's a secondary y-axis, draw a vertical line for that, too. - if (g.numAxes() == 2) { - context.strokeStyle = g.getOptionForAxis('axisLineColor', 'y2'); - context.lineWidth = g.getOptionForAxis('axisLineWidth', 'y2'); - context.beginPath(); - context.moveTo(halfDown(area.x + area.w), halfDown(area.y)); - context.lineTo(halfDown(area.x + area.w), halfDown(area.y + area.h)); - context.closePath(); - context.stroke(); - } - } - - if (g.getOptionForAxis('drawAxis', 'x')) { - if (layout.xticks) { - var getAxisOption = makeOptionGetter('x'); - layout.xticks.forEach(function (tick) { - if (tick.label === undefined) return; // this tick only has a grid line. - x = area.x + tick.pos * area.w; - y = area.y + area.h; - - /* Tick marks are currently clipped, so don't bother drawing them. - context.beginPath(); - context.moveTo(halfUp(x), halfDown(y)); - context.lineTo(halfUp(x), halfDown(y + this.attr_('axisTickSize'))); - context.closePath(); - context.stroke(); - */ - - label = makeDiv(tick.label, 'x'); - label.style.textAlign = 'center'; - label.style.top = y + getAxisOption('axisTickSize') + 'px'; - - var left = x - getAxisOption('axisLabelWidth') / 2; - if (left + getAxisOption('axisLabelWidth') > canvasWidth) { - left = canvasWidth - getAxisOption('axisLabelWidth'); - label.style.textAlign = 'right'; - } - if (left < 0) { - left = 0; - label.style.textAlign = 'left'; - } - - label.style.left = left + 'px'; - label.style.width = getAxisOption('axisLabelWidth') + 'px'; - containerDiv.appendChild(label); - _this.xlabels_.push(label); - }); - } - - context.strokeStyle = g.getOptionForAxis('axisLineColor', 'x'); - context.lineWidth = g.getOptionForAxis('axisLineWidth', 'x'); - context.beginPath(); - var axisY; - if (g.getOption('drawAxesAtZero')) { - var r = g.toPercentYCoord(0, 0); - if (r > 1 || r < 0) r = 1; - axisY = halfDown(area.y + r * area.h); - } else { - axisY = halfDown(area.y + area.h); - } - context.moveTo(halfUp(area.x), axisY); - context.lineTo(halfUp(area.x + area.w), axisY); - context.closePath(); - context.stroke(); - } - - context.restore(); -}; - -exports['default'] = axes; -module.exports = exports['default']; - -},{"../dygraph-utils":17}],22:[function(require,module,exports){ -/** - * @license - * Copyright 2012 Dan Vanderkam (danvdk@gmail.com) - * MIT-licensed (http://opensource.org/licenses/MIT) - */ -/*global Dygraph:false */ - -"use strict"; - -// TODO(danvk): move chart label options out of dygraphs and into the plugin. -// TODO(danvk): only tear down & rebuild the DIVs when it's necessary. - -Object.defineProperty(exports, "__esModule", { - value: true -}); -var chart_labels = function chart_labels() { - this.title_div_ = null; - this.xlabel_div_ = null; - this.ylabel_div_ = null; - this.y2label_div_ = null; -}; - -chart_labels.prototype.toString = function () { - return "ChartLabels Plugin"; -}; - -chart_labels.prototype.activate = function (g) { - return { - layout: this.layout, - // clearChart: this.clearChart, - didDrawChart: this.didDrawChart - }; -}; - -// QUESTION: should there be a plugin-utils.js? -var createDivInRect = function createDivInRect(r) { - var div = document.createElement('div'); - div.style.position = 'absolute'; - div.style.left = r.x + 'px'; - div.style.top = r.y + 'px'; - div.style.width = r.w + 'px'; - div.style.height = r.h + 'px'; - return div; -}; - -// Detach and null out any existing nodes. -chart_labels.prototype.detachLabels_ = function () { - var els = [this.title_div_, this.xlabel_div_, this.ylabel_div_, this.y2label_div_]; - for (var i = 0; i < els.length; i++) { - var el = els[i]; - if (!el) continue; - if (el.parentNode) el.parentNode.removeChild(el); - } - - this.title_div_ = null; - this.xlabel_div_ = null; - this.ylabel_div_ = null; - this.y2label_div_ = null; -}; - -var createRotatedDiv = function createRotatedDiv(g, box, axis, classes, html) { - // TODO(danvk): is this outer div actually necessary? - var div = document.createElement("div"); - div.style.position = 'absolute'; - if (axis == 1) { - // NOTE: this is cheating. Should be positioned relative to the box. - div.style.left = '0px'; - } else { - div.style.left = box.x + 'px'; - } - div.style.top = box.y + 'px'; - div.style.width = box.w + 'px'; - div.style.height = box.h + 'px'; - div.style.fontSize = g.getOption('yLabelWidth') - 2 + 'px'; - - var inner_div = document.createElement("div"); - inner_div.style.position = 'absolute'; - inner_div.style.width = box.h + 'px'; - inner_div.style.height = box.w + 'px'; - inner_div.style.top = box.h / 2 - box.w / 2 + 'px'; - inner_div.style.left = box.w / 2 - box.h / 2 + 'px'; - // TODO: combine inner_div and class_div. - inner_div.className = 'dygraph-label-rotate-' + (axis == 1 ? 'right' : 'left'); - - var class_div = document.createElement("div"); - class_div.className = classes; - class_div.innerHTML = html; - - inner_div.appendChild(class_div); - div.appendChild(inner_div); - return div; -}; - -chart_labels.prototype.layout = function (e) { - this.detachLabels_(); - - var g = e.dygraph; - var div = e.chart_div; - if (g.getOption('title')) { - // QUESTION: should this return an absolutely-positioned div instead? - var title_rect = e.reserveSpaceTop(g.getOption('titleHeight')); - this.title_div_ = createDivInRect(title_rect); - this.title_div_.style.fontSize = g.getOption('titleHeight') - 8 + 'px'; - - var class_div = document.createElement("div"); - class_div.className = 'dygraph-label dygraph-title'; - class_div.innerHTML = g.getOption('title'); - this.title_div_.appendChild(class_div); - div.appendChild(this.title_div_); - } - - if (g.getOption('xlabel')) { - var x_rect = e.reserveSpaceBottom(g.getOption('xLabelHeight')); - this.xlabel_div_ = createDivInRect(x_rect); - this.xlabel_div_.style.fontSize = g.getOption('xLabelHeight') - 2 + 'px'; - - var class_div = document.createElement("div"); - class_div.className = 'dygraph-label dygraph-xlabel'; - class_div.innerHTML = g.getOption('xlabel'); - this.xlabel_div_.appendChild(class_div); - div.appendChild(this.xlabel_div_); - } - - if (g.getOption('ylabel')) { - // It would make sense to shift the chart here to make room for the y-axis - // label, but the default yAxisLabelWidth is large enough that this results - // in overly-padded charts. The y-axis label should fit fine. If it - // doesn't, the yAxisLabelWidth option can be increased. - var y_rect = e.reserveSpaceLeft(0); - - this.ylabel_div_ = createRotatedDiv(g, y_rect, 1, // primary (left) y-axis - 'dygraph-label dygraph-ylabel', g.getOption('ylabel')); - div.appendChild(this.ylabel_div_); - } - - if (g.getOption('y2label') && g.numAxes() == 2) { - // same logic applies here as for ylabel. - var y2_rect = e.reserveSpaceRight(0); - this.y2label_div_ = createRotatedDiv(g, y2_rect, 2, // secondary (right) y-axis - 'dygraph-label dygraph-y2label', g.getOption('y2label')); - div.appendChild(this.y2label_div_); - } -}; - -chart_labels.prototype.didDrawChart = function (e) { - var g = e.dygraph; - if (this.title_div_) { - this.title_div_.children[0].innerHTML = g.getOption('title'); - } - if (this.xlabel_div_) { - this.xlabel_div_.children[0].innerHTML = g.getOption('xlabel'); - } - if (this.ylabel_div_) { - this.ylabel_div_.children[0].children[0].innerHTML = g.getOption('ylabel'); - } - if (this.y2label_div_) { - this.y2label_div_.children[0].children[0].innerHTML = g.getOption('y2label'); - } -}; - -chart_labels.prototype.clearChart = function () {}; - -chart_labels.prototype.destroy = function () { - this.detachLabels_(); -}; - -exports["default"] = chart_labels; -module.exports = exports["default"]; - -},{}],23:[function(require,module,exports){ -/** - * @license - * Copyright 2012 Dan Vanderkam (danvdk@gmail.com) - * MIT-licensed (http://opensource.org/licenses/MIT) - */ -/*global Dygraph:false */ - -/* - -Current bits of jankiness: -- Direct layout access -- Direct area access - -*/ - -"use strict"; - -/** - * Draws the gridlines, i.e. the gray horizontal & vertical lines running the - * length of the chart. - * - * @constructor - */ -Object.defineProperty(exports, "__esModule", { - value: true -}); -var grid = function grid() {}; - -grid.prototype.toString = function () { - return "Gridline Plugin"; -}; - -grid.prototype.activate = function (g) { - return { - willDrawChart: this.willDrawChart - }; -}; - -grid.prototype.willDrawChart = function (e) { - // Draw the new X/Y grid. Lines appear crisper when pixels are rounded to - // half-integers. This prevents them from drawing in two rows/cols. - var g = e.dygraph; - var ctx = e.drawingContext; - var layout = g.layout_; - var area = e.dygraph.plotter_.area; - - function halfUp(x) { - return Math.round(x) + 0.5; - } - function halfDown(y) { - return Math.round(y) - 0.5; - } - - var x, y, i, ticks; - if (g.getOptionForAxis('drawGrid', 'y')) { - var axes = ["y", "y2"]; - var strokeStyles = [], - lineWidths = [], - drawGrid = [], - stroking = [], - strokePattern = []; - for (var i = 0; i < axes.length; i++) { - drawGrid[i] = g.getOptionForAxis('drawGrid', axes[i]); - if (drawGrid[i]) { - strokeStyles[i] = g.getOptionForAxis('gridLineColor', axes[i]); - lineWidths[i] = g.getOptionForAxis('gridLineWidth', axes[i]); - strokePattern[i] = g.getOptionForAxis('gridLinePattern', axes[i]); - stroking[i] = strokePattern[i] && strokePattern[i].length >= 2; - } - } - ticks = layout.yticks; - ctx.save(); - // draw grids for the different y axes - ticks.forEach(function (tick) { - if (!tick.has_tick) return; - var axis = tick.axis; - if (drawGrid[axis]) { - ctx.save(); - if (stroking[axis]) { - if (ctx.setLineDash) ctx.setLineDash(strokePattern[axis]); - } - ctx.strokeStyle = strokeStyles[axis]; - ctx.lineWidth = lineWidths[axis]; - - x = halfUp(area.x); - y = halfDown(area.y + tick.pos * area.h); - ctx.beginPath(); - ctx.moveTo(x, y); - ctx.lineTo(x + area.w, y); - ctx.stroke(); - - ctx.restore(); - } - }); - ctx.restore(); - } - - // draw grid for x axis - if (g.getOptionForAxis('drawGrid', 'x')) { - ticks = layout.xticks; - ctx.save(); - var strokePattern = g.getOptionForAxis('gridLinePattern', 'x'); - var stroking = strokePattern && strokePattern.length >= 2; - if (stroking) { - if (ctx.setLineDash) ctx.setLineDash(strokePattern); - } - ctx.strokeStyle = g.getOptionForAxis('gridLineColor', 'x'); - ctx.lineWidth = g.getOptionForAxis('gridLineWidth', 'x'); - ticks.forEach(function (tick) { - if (!tick.has_tick) return; - x = halfUp(area.x + tick.pos * area.w); - y = halfDown(area.y + area.h); - ctx.beginPath(); - ctx.moveTo(x, y); - ctx.lineTo(x, area.y); - ctx.closePath(); - ctx.stroke(); - }); - if (stroking) { - if (ctx.setLineDash) ctx.setLineDash([]); - } - ctx.restore(); - } -}; - -grid.prototype.destroy = function () {}; - -exports["default"] = grid; -module.exports = exports["default"]; - -},{}],24:[function(require,module,exports){ -/** - * @license - * Copyright 2012 Dan Vanderkam (danvdk@gmail.com) - * MIT-licensed (http://opensource.org/licenses/MIT) - */ -/*global Dygraph:false */ - -/* -Current bits of jankiness: -- Uses two private APIs: - 1. Dygraph.optionsViewForAxis_ - 2. dygraph.plotter_.area -- Registers for a "predraw" event, which should be renamed. -- I call calculateEmWidthInDiv more often than needed. -*/ - -/*global Dygraph:false */ -"use strict"; - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj["default"] = obj; return newObj; } } - -var _dygraphUtils = require('../dygraph-utils'); - -var utils = _interopRequireWildcard(_dygraphUtils); - -/** - * Creates the legend, which appears when the user hovers over the chart. - * The legend can be either a user-specified or generated div. - * - * @constructor - */ -var Legend = function Legend() { - this.legend_div_ = null; - this.is_generated_div_ = false; // do we own this div, or was it user-specified? -}; - -Legend.prototype.toString = function () { - return "Legend Plugin"; -}; - -/** - * This is called during the dygraph constructor, after options have been set - * but before the data is available. - * - * Proper tasks to do here include: - * - Reading your own options - * - DOM manipulation - * - Registering event listeners - * - * @param {Dygraph} g Graph instance. - * @return {object.} Mapping of event names to callbacks. - */ -Legend.prototype.activate = function (g) { - var div; - - var userLabelsDiv = g.getOption('labelsDiv'); - if (userLabelsDiv && null !== userLabelsDiv) { - if (typeof userLabelsDiv == "string" || userLabelsDiv instanceof String) { - div = document.getElementById(userLabelsDiv); - } else { - div = userLabelsDiv; - } - } else { - div = document.createElement("div"); - div.className = "dygraph-legend"; - // TODO(danvk): come up with a cleaner way to expose this. - g.graphDiv.appendChild(div); - this.is_generated_div_ = true; - } - - this.legend_div_ = div; - this.one_em_width_ = 10; // just a guess, will be updated. - - return { - select: this.select, - deselect: this.deselect, - // TODO(danvk): rethink the name "predraw" before we commit to it in any API. - predraw: this.predraw, - didDrawChart: this.didDrawChart - }; -}; - -// Needed for dashed lines. -var calculateEmWidthInDiv = function calculateEmWidthInDiv(div) { - var sizeSpan = document.createElement('span'); - sizeSpan.setAttribute('style', 'margin: 0; padding: 0 0 0 1em; border: 0;'); - div.appendChild(sizeSpan); - var oneEmWidth = sizeSpan.offsetWidth; - div.removeChild(sizeSpan); - return oneEmWidth; -}; - -var escapeHTML = function escapeHTML(str) { - return str.replace(/&/g, "&").replace(/"/g, """).replace(//g, ">"); -}; - -Legend.prototype.select = function (e) { - var xValue = e.selectedX; - var points = e.selectedPoints; - var row = e.selectedRow; - - var legendMode = e.dygraph.getOption('legend'); - if (legendMode === 'never') { - this.legend_div_.style.display = 'none'; - return; - } - - if (legendMode === 'follow') { - // create floating legend div - var area = e.dygraph.plotter_.area; - var labelsDivWidth = this.legend_div_.offsetWidth; - var yAxisLabelWidth = e.dygraph.getOptionForAxis('axisLabelWidth', 'y'); - // determine floating [left, top] coordinates of the legend div - // within the plotter_ area - // offset 50 px to the right and down from the first selection point - // 50 px is guess based on mouse cursor size - var leftLegend = points[0].x * area.w + 50; - var topLegend = points[0].y * area.h - 50; - - // if legend floats to end of the chart area, it flips to the other - // side of the selection point - if (leftLegend + labelsDivWidth + 1 > area.w) { - leftLegend = leftLegend - 2 * 50 - labelsDivWidth - (yAxisLabelWidth - area.x); - } - - e.dygraph.graphDiv.appendChild(this.legend_div_); - this.legend_div_.style.left = yAxisLabelWidth + leftLegend + "px"; - this.legend_div_.style.top = topLegend + "px"; - } - - var html = Legend.generateLegendHTML(e.dygraph, xValue, points, this.one_em_width_, row); - this.legend_div_.innerHTML = html; - this.legend_div_.style.display = ''; -}; - -Legend.prototype.deselect = function (e) { - var legendMode = e.dygraph.getOption('legend'); - if (legendMode !== 'always') { - this.legend_div_.style.display = "none"; - } - - // Have to do this every time, since styles might have changed. - var oneEmWidth = calculateEmWidthInDiv(this.legend_div_); - this.one_em_width_ = oneEmWidth; - - var html = Legend.generateLegendHTML(e.dygraph, undefined, undefined, oneEmWidth, null); - this.legend_div_.innerHTML = html; -}; - -Legend.prototype.didDrawChart = function (e) { - this.deselect(e); -}; - -// Right edge should be flush with the right edge of the charting area (which -// may not be the same as the right edge of the div, if we have two y-axes. -// TODO(danvk): is any of this really necessary? Could just set "right" in "activate". -/** - * Position the labels div so that: - * - its right edge is flush with the right edge of the charting area - * - its top edge is flush with the top edge of the charting area - * @private - */ -Legend.prototype.predraw = function (e) { - // Don't touch a user-specified labelsDiv. - if (!this.is_generated_div_) return; - - // TODO(danvk): only use real APIs for this. - e.dygraph.graphDiv.appendChild(this.legend_div_); - var area = e.dygraph.getArea(); - var labelsDivWidth = this.legend_div_.offsetWidth; - this.legend_div_.style.left = area.x + area.w - labelsDivWidth - 1 + "px"; - this.legend_div_.style.top = area.y + "px"; -}; - -/** - * Called when dygraph.destroy() is called. - * You should null out any references and detach any DOM elements. - */ -Legend.prototype.destroy = function () { - this.legend_div_ = null; -}; - -/** - * Generates HTML for the legend which is displayed when hovering over the - * chart. If no selected points are specified, a default legend is returned - * (this may just be the empty string). - * @param {number} x The x-value of the selected points. - * @param {Object} sel_points List of selected points for the given - * x-value. Should have properties like 'name', 'yval' and 'canvasy'. - * @param {number} oneEmWidth The pixel width for 1em in the legend. Only - * relevant when displaying a legend with no selection (i.e. {legend: - * 'always'}) and with dashed lines. - * @param {number} row The selected row index. - * @private - */ -Legend.generateLegendHTML = function (g, x, sel_points, oneEmWidth, row) { - // Data about the selection to pass to legendFormatter - var data = { - dygraph: g, - x: x, - series: [] - }; - - var labelToSeries = {}; - var labels = g.getLabels(); - if (labels) { - for (var i = 1; i < labels.length; i++) { - var series = g.getPropertiesForSeries(labels[i]); - var strokePattern = g.getOption('strokePattern', labels[i]); - var seriesData = { - dashHTML: generateLegendDashHTML(strokePattern, series.color, oneEmWidth), - label: labels[i], - labelHTML: escapeHTML(labels[i]), - isVisible: series.visible, - color: series.color - }; - - data.series.push(seriesData); - labelToSeries[labels[i]] = seriesData; - } - } - - if (typeof x !== 'undefined') { - var xOptView = g.optionsViewForAxis_('x'); - var xvf = xOptView('valueFormatter'); - data.xHTML = xvf.call(g, x, xOptView, labels[0], g, row, 0); - - var yOptViews = []; - var num_axes = g.numAxes(); - for (var i = 0; i < num_axes; i++) { - // TODO(danvk): remove this use of a private API - yOptViews[i] = g.optionsViewForAxis_('y' + (i ? 1 + i : '')); - } - - var showZeros = g.getOption('labelsShowZeroValues'); - var highlightSeries = g.getHighlightSeries(); - for (i = 0; i < sel_points.length; i++) { - var pt = sel_points[i]; - var seriesData = labelToSeries[pt.name]; - seriesData.y = pt.yval; - - if (pt.yval === 0 && !showZeros || isNaN(pt.canvasy)) { - seriesData.isVisible = false; - continue; - } - - var series = g.getPropertiesForSeries(pt.name); - var yOptView = yOptViews[series.axis - 1]; - var fmtFunc = yOptView('valueFormatter'); - var yHTML = fmtFunc.call(g, pt.yval, yOptView, pt.name, g, row, labels.indexOf(pt.name)); - - utils.update(seriesData, { yHTML: yHTML }); - - if (pt.name == highlightSeries) { - seriesData.isHighlighted = true; - } - } - } - - var formatter = g.getOption('legendFormatter') || Legend.defaultFormatter; - return formatter.call(g, data); -}; - -Legend.defaultFormatter = function (data) { - var g = data.dygraph; - - // TODO(danvk): deprecate this option in place of {legend: 'never'} - if (g.getOption('showLabelsOnHighlight') !== true) return ''; - - var sepLines = g.getOption('labelsSeparateLines'); - var html; - - if (typeof data.x === 'undefined') { - // TODO: this check is duplicated in generateLegendHTML. Put it in one place. - if (g.getOption('legend') != 'always') { - return ''; - } - - html = ''; - for (var i = 0; i < data.series.length; i++) { - var series = data.series[i]; - if (!series.isVisible) continue; - - if (html !== '') html += sepLines ? '
' : ' '; - html += "" + series.dashHTML + " " + series.labelHTML + ""; - } - return html; - } - - html = data.xHTML + ':'; - for (var i = 0; i < data.series.length; i++) { - var series = data.series[i]; - if (!series.isVisible) continue; - if (sepLines) html += '
'; - var cls = series.isHighlighted ? ' class="highlight"' : ''; - html += " " + series.labelHTML + ": " + series.yHTML + ""; - } - return html; -}; - -/** - * Generates html for the "dash" displayed on the legend when using "legend: always". - * In particular, this works for dashed lines with any stroke pattern. It will - * try to scale the pattern to fit in 1em width. Or if small enough repeat the - * pattern for 1em width. - * - * @param strokePattern The pattern - * @param color The color of the series. - * @param oneEmWidth The width in pixels of 1em in the legend. - * @private - */ -// TODO(danvk): cache the results of this -function generateLegendDashHTML(strokePattern, color, oneEmWidth) { - // Easy, common case: a solid line - if (!strokePattern || strokePattern.length <= 1) { - return "
"; - } - - var i, j, paddingLeft, marginRight; - var strokePixelLength = 0, - segmentLoop = 0; - var normalizedPattern = []; - var loop; - - // Compute the length of the pixels including the first segment twice, - // since we repeat it. - for (i = 0; i <= strokePattern.length; i++) { - strokePixelLength += strokePattern[i % strokePattern.length]; - } - - // See if we can loop the pattern by itself at least twice. - loop = Math.floor(oneEmWidth / (strokePixelLength - strokePattern[0])); - if (loop > 1) { - // This pattern fits at least two times, no scaling just convert to em; - for (i = 0; i < strokePattern.length; i++) { - normalizedPattern[i] = strokePattern[i] / oneEmWidth; - } - // Since we are repeating the pattern, we don't worry about repeating the - // first segment in one draw. - segmentLoop = normalizedPattern.length; - } else { - // If the pattern doesn't fit in the legend we scale it to fit. - loop = 1; - for (i = 0; i < strokePattern.length; i++) { - normalizedPattern[i] = strokePattern[i] / strokePixelLength; - } - // For the scaled patterns we do redraw the first segment. - segmentLoop = normalizedPattern.length + 1; - } - - // Now make the pattern. - var dash = ""; - for (j = 0; j < loop; j++) { - for (i = 0; i < segmentLoop; i += 2) { - // The padding is the drawn segment. - paddingLeft = normalizedPattern[i % normalizedPattern.length]; - if (i < strokePattern.length) { - // The margin is the space segment. - marginRight = normalizedPattern[(i + 1) % normalizedPattern.length]; - } else { - // The repeated first segment has no right margin. - marginRight = 0; - } - dash += "
"; - } - } - return dash; -}; - -exports["default"] = Legend; -module.exports = exports["default"]; - -},{"../dygraph-utils":17}],25:[function(require,module,exports){ -/** - * @license - * Copyright 2011 Paul Felix (paul.eric.felix@gmail.com) - * MIT-licensed (http://opensource.org/licenses/MIT) - */ -/*global Dygraph:false,TouchEvent:false */ - -/** - * @fileoverview This file contains the RangeSelector plugin used to provide - * a timeline range selector widget for dygraphs. - */ - -/*global Dygraph:false */ -"use strict"; - -Object.defineProperty(exports, '__esModule', { - value: true -}); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } - -function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } } - -var _dygraphUtils = require('../dygraph-utils'); - -var utils = _interopRequireWildcard(_dygraphUtils); - -var _dygraphInteractionModel = require('../dygraph-interaction-model'); - -var _dygraphInteractionModel2 = _interopRequireDefault(_dygraphInteractionModel); - -var _iframeTarp = require('../iframe-tarp'); - -var _iframeTarp2 = _interopRequireDefault(_iframeTarp); - -var rangeSelector = function rangeSelector() { - this.hasTouchInterface_ = typeof TouchEvent != 'undefined'; - this.isMobileDevice_ = /mobile|android/gi.test(navigator.appVersion); - this.interfaceCreated_ = false; -}; - -rangeSelector.prototype.toString = function () { - return "RangeSelector Plugin"; -}; - -rangeSelector.prototype.activate = function (dygraph) { - this.dygraph_ = dygraph; - if (this.getOption_('showRangeSelector')) { - this.createInterface_(); - } - return { - layout: this.reserveSpace_, - predraw: this.renderStaticLayer_, - didDrawChart: this.renderInteractiveLayer_ - }; -}; - -rangeSelector.prototype.destroy = function () { - this.bgcanvas_ = null; - this.fgcanvas_ = null; - this.leftZoomHandle_ = null; - this.rightZoomHandle_ = null; -}; - -//------------------------------------------------------------------ -// Private methods -//------------------------------------------------------------------ - -rangeSelector.prototype.getOption_ = function (name, opt_series) { - return this.dygraph_.getOption(name, opt_series); -}; - -rangeSelector.prototype.setDefaultOption_ = function (name, value) { - this.dygraph_.attrs_[name] = value; -}; - -/** - * @private - * Creates the range selector elements and adds them to the graph. - */ -rangeSelector.prototype.createInterface_ = function () { - this.createCanvases_(); - this.createZoomHandles_(); - this.initInteraction_(); - - // Range selector and animatedZooms have a bad interaction. See issue 359. - if (this.getOption_('animatedZooms')) { - console.warn('Animated zooms and range selector are not compatible; disabling animatedZooms.'); - this.dygraph_.updateOptions({ animatedZooms: false }, true); - } - - this.interfaceCreated_ = true; - this.addToGraph_(); -}; - -/** - * @private - * Adds the range selector to the graph. - */ -rangeSelector.prototype.addToGraph_ = function () { - var graphDiv = this.graphDiv_ = this.dygraph_.graphDiv; - graphDiv.appendChild(this.bgcanvas_); - graphDiv.appendChild(this.fgcanvas_); - graphDiv.appendChild(this.leftZoomHandle_); - graphDiv.appendChild(this.rightZoomHandle_); -}; - -/** - * @private - * Removes the range selector from the graph. - */ -rangeSelector.prototype.removeFromGraph_ = function () { - var graphDiv = this.graphDiv_; - graphDiv.removeChild(this.bgcanvas_); - graphDiv.removeChild(this.fgcanvas_); - graphDiv.removeChild(this.leftZoomHandle_); - graphDiv.removeChild(this.rightZoomHandle_); - this.graphDiv_ = null; -}; - -/** - * @private - * Called by Layout to allow range selector to reserve its space. - */ -rangeSelector.prototype.reserveSpace_ = function (e) { - if (this.getOption_('showRangeSelector')) { - e.reserveSpaceBottom(this.getOption_('rangeSelectorHeight') + 4); - } -}; - -/** - * @private - * Renders the static portion of the range selector at the predraw stage. - */ -rangeSelector.prototype.renderStaticLayer_ = function () { - if (!this.updateVisibility_()) { - return; - } - this.resize_(); - this.drawStaticLayer_(); -}; - -/** - * @private - * Renders the interactive portion of the range selector after the chart has been drawn. - */ -rangeSelector.prototype.renderInteractiveLayer_ = function () { - if (!this.updateVisibility_() || this.isChangingRange_) { - return; - } - this.placeZoomHandles_(); - this.drawInteractiveLayer_(); -}; - -/** - * @private - * Check to see if the range selector is enabled/disabled and update visibility accordingly. - */ -rangeSelector.prototype.updateVisibility_ = function () { - var enabled = this.getOption_('showRangeSelector'); - if (enabled) { - if (!this.interfaceCreated_) { - this.createInterface_(); - } else if (!this.graphDiv_ || !this.graphDiv_.parentNode) { - this.addToGraph_(); - } - } else if (this.graphDiv_) { - this.removeFromGraph_(); - var dygraph = this.dygraph_; - setTimeout(function () { - dygraph.width_ = 0;dygraph.resize(); - }, 1); - } - return enabled; -}; - -/** - * @private - * Resizes the range selector. - */ -rangeSelector.prototype.resize_ = function () { - function setElementRect(canvas, context, rect, pixelRatioOption) { - var canvasScale = pixelRatioOption || utils.getContextPixelRatio(context); - - canvas.style.top = rect.y + 'px'; - canvas.style.left = rect.x + 'px'; - canvas.width = rect.w * canvasScale; - canvas.height = rect.h * canvasScale; - canvas.style.width = rect.w + 'px'; - canvas.style.height = rect.h + 'px'; - - if (canvasScale != 1) { - context.scale(canvasScale, canvasScale); - } - } - - var plotArea = this.dygraph_.layout_.getPlotArea(); - - var xAxisLabelHeight = 0; - if (this.dygraph_.getOptionForAxis('drawAxis', 'x')) { - xAxisLabelHeight = this.getOption_('xAxisHeight') || this.getOption_('axisLabelFontSize') + 2 * this.getOption_('axisTickSize'); - } - this.canvasRect_ = { - x: plotArea.x, - y: plotArea.y + plotArea.h + xAxisLabelHeight + 4, - w: plotArea.w, - h: this.getOption_('rangeSelectorHeight') - }; - - var pixelRatioOption = this.dygraph_.getNumericOption('pixelRatio'); - setElementRect(this.bgcanvas_, this.bgcanvas_ctx_, this.canvasRect_, pixelRatioOption); - setElementRect(this.fgcanvas_, this.fgcanvas_ctx_, this.canvasRect_, pixelRatioOption); -}; - -/** - * @private - * Creates the background and foreground canvases. - */ -rangeSelector.prototype.createCanvases_ = function () { - this.bgcanvas_ = utils.createCanvas(); - this.bgcanvas_.className = 'dygraph-rangesel-bgcanvas'; - this.bgcanvas_.style.position = 'absolute'; - this.bgcanvas_.style.zIndex = 9; - this.bgcanvas_ctx_ = utils.getContext(this.bgcanvas_); - - this.fgcanvas_ = utils.createCanvas(); - this.fgcanvas_.className = 'dygraph-rangesel-fgcanvas'; - this.fgcanvas_.style.position = 'absolute'; - this.fgcanvas_.style.zIndex = 9; - this.fgcanvas_.style.cursor = 'default'; - this.fgcanvas_ctx_ = utils.getContext(this.fgcanvas_); -}; - -/** - * @private - * Creates the zoom handle elements. - */ -rangeSelector.prototype.createZoomHandles_ = function () { - var img = new Image(); - img.className = 'dygraph-rangesel-zoomhandle'; - img.style.position = 'absolute'; - img.style.zIndex = 10; - img.style.visibility = 'hidden'; // Initially hidden so they don't show up in the wrong place. - img.style.cursor = 'col-resize'; - // TODO: change image to more options - img.width = 9; - img.height = 16; - img.src = 'data:image/png;base64,' + 'iVBORw0KGgoAAAANSUhEUgAAAAkAAAAQCAYAAADESFVDAAAAAXNSR0IArs4c6QAAAAZiS0dEANAA' + 'zwDP4Z7KegAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAAd0SU1FB9sHGw0cMqdt1UwAAAAZdEVYdENv' + 'bW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAaElEQVQoz+3SsRFAQBCF4Z9WJM8KCDVwownl' + '6YXsTmCUsyKGkZzcl7zkz3YLkypgAnreFmDEpHkIwVOMfpdi9CEEN2nGpFdwD03yEqDtOgCaun7s' + 'qSTDH32I1pQA2Pb9sZecAxc5r3IAb21d6878xsAAAAAASUVORK5CYII='; - - if (this.isMobileDevice_) { - img.width *= 2; - img.height *= 2; - } - - this.leftZoomHandle_ = img; - this.rightZoomHandle_ = img.cloneNode(false); -}; - -/** - * @private - * Sets up the interaction for the range selector. - */ -rangeSelector.prototype.initInteraction_ = function () { - var self = this; - var topElem = document; - var clientXLast = 0; - var handle = null; - var isZooming = false; - var isPanning = false; - var dynamic = !this.isMobileDevice_; - - // We cover iframes during mouse interactions. See comments in - // dygraph-utils.js for more info on why this is a good idea. - var tarp = new _iframeTarp2['default'](); - - // functions, defined below. Defining them this way (rather than with - // "function foo() {...}" makes JSHint happy. - var toXDataWindow, onZoomStart, onZoom, onZoomEnd, doZoom, isMouseInPanZone, onPanStart, onPan, onPanEnd, doPan, onCanvasHover; - - // Touch event functions - var onZoomHandleTouchEvent, onCanvasTouchEvent, addTouchEvents; - - toXDataWindow = function (zoomHandleStatus) { - var xDataLimits = self.dygraph_.xAxisExtremes(); - var fact = (xDataLimits[1] - xDataLimits[0]) / self.canvasRect_.w; - var xDataMin = xDataLimits[0] + (zoomHandleStatus.leftHandlePos - self.canvasRect_.x) * fact; - var xDataMax = xDataLimits[0] + (zoomHandleStatus.rightHandlePos - self.canvasRect_.x) * fact; - return [xDataMin, xDataMax]; - }; - - onZoomStart = function (e) { - utils.cancelEvent(e); - isZooming = true; - clientXLast = e.clientX; - handle = e.target ? e.target : e.srcElement; - if (e.type === 'mousedown' || e.type === 'dragstart') { - // These events are removed manually. - utils.addEvent(topElem, 'mousemove', onZoom); - utils.addEvent(topElem, 'mouseup', onZoomEnd); - } - self.fgcanvas_.style.cursor = 'col-resize'; - tarp.cover(); - return true; - }; - - onZoom = function (e) { - if (!isZooming) { - return false; - } - utils.cancelEvent(e); - - var delX = e.clientX - clientXLast; - if (Math.abs(delX) < 4) { - return true; - } - clientXLast = e.clientX; - - // Move handle. - var zoomHandleStatus = self.getZoomHandleStatus_(); - var newPos; - if (handle == self.leftZoomHandle_) { - newPos = zoomHandleStatus.leftHandlePos + delX; - newPos = Math.min(newPos, zoomHandleStatus.rightHandlePos - handle.width - 3); - newPos = Math.max(newPos, self.canvasRect_.x); - } else { - newPos = zoomHandleStatus.rightHandlePos + delX; - newPos = Math.min(newPos, self.canvasRect_.x + self.canvasRect_.w); - newPos = Math.max(newPos, zoomHandleStatus.leftHandlePos + handle.width + 3); - } - var halfHandleWidth = handle.width / 2; - handle.style.left = newPos - halfHandleWidth + 'px'; - self.drawInteractiveLayer_(); - - // Zoom on the fly. - if (dynamic) { - doZoom(); - } - return true; - }; - - onZoomEnd = function (e) { - if (!isZooming) { - return false; - } - isZooming = false; - tarp.uncover(); - utils.removeEvent(topElem, 'mousemove', onZoom); - utils.removeEvent(topElem, 'mouseup', onZoomEnd); - self.fgcanvas_.style.cursor = 'default'; - - // If on a slower device, zoom now. - if (!dynamic) { - doZoom(); - } - return true; - }; - - doZoom = function () { - try { - var zoomHandleStatus = self.getZoomHandleStatus_(); - self.isChangingRange_ = true; - if (!zoomHandleStatus.isZoomed) { - self.dygraph_.resetZoom(); - } else { - var xDataWindow = toXDataWindow(zoomHandleStatus); - self.dygraph_.doZoomXDates_(xDataWindow[0], xDataWindow[1]); - } - } finally { - self.isChangingRange_ = false; - } - }; - - isMouseInPanZone = function (e) { - var rect = self.leftZoomHandle_.getBoundingClientRect(); - var leftHandleClientX = rect.left + rect.width / 2; - rect = self.rightZoomHandle_.getBoundingClientRect(); - var rightHandleClientX = rect.left + rect.width / 2; - return e.clientX > leftHandleClientX && e.clientX < rightHandleClientX; - }; - - onPanStart = function (e) { - if (!isPanning && isMouseInPanZone(e) && self.getZoomHandleStatus_().isZoomed) { - utils.cancelEvent(e); - isPanning = true; - clientXLast = e.clientX; - if (e.type === 'mousedown') { - // These events are removed manually. - utils.addEvent(topElem, 'mousemove', onPan); - utils.addEvent(topElem, 'mouseup', onPanEnd); - } - return true; - } - return false; - }; - - onPan = function (e) { - if (!isPanning) { - return false; - } - utils.cancelEvent(e); - - var delX = e.clientX - clientXLast; - if (Math.abs(delX) < 4) { - return true; - } - clientXLast = e.clientX; - - // Move range view - var zoomHandleStatus = self.getZoomHandleStatus_(); - var leftHandlePos = zoomHandleStatus.leftHandlePos; - var rightHandlePos = zoomHandleStatus.rightHandlePos; - var rangeSize = rightHandlePos - leftHandlePos; - if (leftHandlePos + delX <= self.canvasRect_.x) { - leftHandlePos = self.canvasRect_.x; - rightHandlePos = leftHandlePos + rangeSize; - } else if (rightHandlePos + delX >= self.canvasRect_.x + self.canvasRect_.w) { - rightHandlePos = self.canvasRect_.x + self.canvasRect_.w; - leftHandlePos = rightHandlePos - rangeSize; - } else { - leftHandlePos += delX; - rightHandlePos += delX; - } - var halfHandleWidth = self.leftZoomHandle_.width / 2; - self.leftZoomHandle_.style.left = leftHandlePos - halfHandleWidth + 'px'; - self.rightZoomHandle_.style.left = rightHandlePos - halfHandleWidth + 'px'; - self.drawInteractiveLayer_(); - - // Do pan on the fly. - if (dynamic) { - doPan(); - } - return true; - }; - - onPanEnd = function (e) { - if (!isPanning) { - return false; - } - isPanning = false; - utils.removeEvent(topElem, 'mousemove', onPan); - utils.removeEvent(topElem, 'mouseup', onPanEnd); - // If on a slower device, do pan now. - if (!dynamic) { - doPan(); - } - return true; - }; - - doPan = function () { - try { - self.isChangingRange_ = true; - self.dygraph_.dateWindow_ = toXDataWindow(self.getZoomHandleStatus_()); - self.dygraph_.drawGraph_(false); - } finally { - self.isChangingRange_ = false; - } - }; - - onCanvasHover = function (e) { - if (isZooming || isPanning) { - return; - } - var cursor = isMouseInPanZone(e) ? 'move' : 'default'; - if (cursor != self.fgcanvas_.style.cursor) { - self.fgcanvas_.style.cursor = cursor; - } - }; - - onZoomHandleTouchEvent = function (e) { - if (e.type == 'touchstart' && e.targetTouches.length == 1) { - if (onZoomStart(e.targetTouches[0])) { - utils.cancelEvent(e); - } - } else if (e.type == 'touchmove' && e.targetTouches.length == 1) { - if (onZoom(e.targetTouches[0])) { - utils.cancelEvent(e); - } - } else { - onZoomEnd(e); - } - }; - - onCanvasTouchEvent = function (e) { - if (e.type == 'touchstart' && e.targetTouches.length == 1) { - if (onPanStart(e.targetTouches[0])) { - utils.cancelEvent(e); - } - } else if (e.type == 'touchmove' && e.targetTouches.length == 1) { - if (onPan(e.targetTouches[0])) { - utils.cancelEvent(e); - } - } else { - onPanEnd(e); - } - }; - - addTouchEvents = function (elem, fn) { - var types = ['touchstart', 'touchend', 'touchmove', 'touchcancel']; - for (var i = 0; i < types.length; i++) { - self.dygraph_.addAndTrackEvent(elem, types[i], fn); - } - }; - - this.setDefaultOption_('interactionModel', _dygraphInteractionModel2['default'].dragIsPanInteractionModel); - this.setDefaultOption_('panEdgeFraction', 0.0001); - - var dragStartEvent = window.opera ? 'mousedown' : 'dragstart'; - this.dygraph_.addAndTrackEvent(this.leftZoomHandle_, dragStartEvent, onZoomStart); - this.dygraph_.addAndTrackEvent(this.rightZoomHandle_, dragStartEvent, onZoomStart); - - this.dygraph_.addAndTrackEvent(this.fgcanvas_, 'mousedown', onPanStart); - this.dygraph_.addAndTrackEvent(this.fgcanvas_, 'mousemove', onCanvasHover); - - // Touch events - if (this.hasTouchInterface_) { - addTouchEvents(this.leftZoomHandle_, onZoomHandleTouchEvent); - addTouchEvents(this.rightZoomHandle_, onZoomHandleTouchEvent); - addTouchEvents(this.fgcanvas_, onCanvasTouchEvent); - } -}; - -/** - * @private - * Draws the static layer in the background canvas. - */ -rangeSelector.prototype.drawStaticLayer_ = function () { - var ctx = this.bgcanvas_ctx_; - ctx.clearRect(0, 0, this.canvasRect_.w, this.canvasRect_.h); - try { - this.drawMiniPlot_(); - } catch (ex) { - console.warn(ex); - } - - var margin = 0.5; - this.bgcanvas_ctx_.lineWidth = this.getOption_('rangeSelectorBackgroundLineWidth'); - ctx.strokeStyle = this.getOption_('rangeSelectorBackgroundStrokeColor'); - ctx.beginPath(); - ctx.moveTo(margin, margin); - ctx.lineTo(margin, this.canvasRect_.h - margin); - ctx.lineTo(this.canvasRect_.w - margin, this.canvasRect_.h - margin); - ctx.lineTo(this.canvasRect_.w - margin, margin); - ctx.stroke(); -}; - -/** - * @private - * Draws the mini plot in the background canvas. - */ -rangeSelector.prototype.drawMiniPlot_ = function () { - var fillStyle = this.getOption_('rangeSelectorPlotFillColor'); - var fillGradientStyle = this.getOption_('rangeSelectorPlotFillGradientColor'); - var strokeStyle = this.getOption_('rangeSelectorPlotStrokeColor'); - if (!fillStyle && !strokeStyle) { - return; - } - - var stepPlot = this.getOption_('stepPlot'); - - var combinedSeriesData = this.computeCombinedSeriesAndLimits_(); - var yRange = combinedSeriesData.yMax - combinedSeriesData.yMin; - - // Draw the mini plot. - var ctx = this.bgcanvas_ctx_; - var margin = 0.5; - - var xExtremes = this.dygraph_.xAxisExtremes(); - var xRange = Math.max(xExtremes[1] - xExtremes[0], 1.e-30); - var xFact = (this.canvasRect_.w - margin) / xRange; - var yFact = (this.canvasRect_.h - margin) / yRange; - var canvasWidth = this.canvasRect_.w - margin; - var canvasHeight = this.canvasRect_.h - margin; - - var prevX = null, - prevY = null; - - ctx.beginPath(); - ctx.moveTo(margin, canvasHeight); - for (var i = 0; i < combinedSeriesData.data.length; i++) { - var dataPoint = combinedSeriesData.data[i]; - var x = dataPoint[0] !== null ? (dataPoint[0] - xExtremes[0]) * xFact : NaN; - var y = dataPoint[1] !== null ? canvasHeight - (dataPoint[1] - combinedSeriesData.yMin) * yFact : NaN; - - // Skip points that don't change the x-value. Overly fine-grained points - // can cause major slowdowns with the ctx.fill() call below. - if (!stepPlot && prevX !== null && Math.round(x) == Math.round(prevX)) { - continue; - } - - if (isFinite(x) && isFinite(y)) { - if (prevX === null) { - ctx.lineTo(x, canvasHeight); - } else if (stepPlot) { - ctx.lineTo(x, prevY); - } - ctx.lineTo(x, y); - prevX = x; - prevY = y; - } else { - if (prevX !== null) { - if (stepPlot) { - ctx.lineTo(x, prevY); - ctx.lineTo(x, canvasHeight); - } else { - ctx.lineTo(prevX, canvasHeight); - } - } - prevX = prevY = null; - } - } - ctx.lineTo(canvasWidth, canvasHeight); - ctx.closePath(); - - if (fillStyle) { - var lingrad = this.bgcanvas_ctx_.createLinearGradient(0, 0, 0, canvasHeight); - if (fillGradientStyle) { - lingrad.addColorStop(0, fillGradientStyle); - } - lingrad.addColorStop(1, fillStyle); - this.bgcanvas_ctx_.fillStyle = lingrad; - ctx.fill(); - } - - if (strokeStyle) { - this.bgcanvas_ctx_.strokeStyle = strokeStyle; - this.bgcanvas_ctx_.lineWidth = this.getOption_('rangeSelectorPlotLineWidth'); - ctx.stroke(); - } -}; - -/** - * @private - * Computes and returns the combined series data along with min/max for the mini plot. - * The combined series consists of averaged values for all series. - * When series have error bars, the error bars are ignored. - * @return {Object} An object containing combined series array, ymin, ymax. - */ -rangeSelector.prototype.computeCombinedSeriesAndLimits_ = function () { - var g = this.dygraph_; - var logscale = this.getOption_('logscale'); - var i; - - // Select series to combine. By default, all series are combined. - var numColumns = g.numColumns(); - var labels = g.getLabels(); - var includeSeries = new Array(numColumns); - var anySet = false; - var visibility = g.visibility(); - var inclusion = []; - - for (i = 1; i < numColumns; i++) { - var include = this.getOption_('showInRangeSelector', labels[i]); - inclusion.push(include); - if (include !== null) anySet = true; // it's set explicitly for this series - } - - if (anySet) { - for (i = 1; i < numColumns; i++) { - includeSeries[i] = inclusion[i - 1]; - } - } else { - for (i = 1; i < numColumns; i++) { - includeSeries[i] = visibility[i - 1]; - } - } - - // Create a combined series (average of selected series values). - // TODO(danvk): short-circuit if there's only one series. - var rolledSeries = []; - var dataHandler = g.dataHandler_; - var options = g.attributes_; - for (i = 1; i < g.numColumns(); i++) { - if (!includeSeries[i]) continue; - var series = dataHandler.extractSeries(g.rawData_, i, options); - if (g.rollPeriod() > 1) { - series = dataHandler.rollingAverage(series, g.rollPeriod(), options); - } - - rolledSeries.push(series); - } - - var combinedSeries = []; - for (i = 0; i < rolledSeries[0].length; i++) { - var sum = 0; - var count = 0; - for (var j = 0; j < rolledSeries.length; j++) { - var y = rolledSeries[j][i][1]; - if (y === null || isNaN(y)) continue; - count++; - sum += y; - } - combinedSeries.push([rolledSeries[0][i][0], sum / count]); - } - - // Compute the y range. - var yMin = Number.MAX_VALUE; - var yMax = -Number.MAX_VALUE; - for (i = 0; i < combinedSeries.length; i++) { - var yVal = combinedSeries[i][1]; - if (yVal !== null && isFinite(yVal) && (!logscale || yVal > 0)) { - yMin = Math.min(yMin, yVal); - yMax = Math.max(yMax, yVal); - } - } - - // Convert Y data to log scale if needed. - // Also, expand the Y range to compress the mini plot a little. - var extraPercent = 0.25; - if (logscale) { - yMax = utils.log10(yMax); - yMax += yMax * extraPercent; - yMin = utils.log10(yMin); - for (i = 0; i < combinedSeries.length; i++) { - combinedSeries[i][1] = utils.log10(combinedSeries[i][1]); - } - } else { - var yExtra; - var yRange = yMax - yMin; - if (yRange <= Number.MIN_VALUE) { - yExtra = yMax * extraPercent; - } else { - yExtra = yRange * extraPercent; - } - yMax += yExtra; - yMin -= yExtra; - } - - return { data: combinedSeries, yMin: yMin, yMax: yMax }; -}; - -/** - * @private - * Places the zoom handles in the proper position based on the current X data window. - */ -rangeSelector.prototype.placeZoomHandles_ = function () { - var xExtremes = this.dygraph_.xAxisExtremes(); - var xWindowLimits = this.dygraph_.xAxisRange(); - var xRange = xExtremes[1] - xExtremes[0]; - var leftPercent = Math.max(0, (xWindowLimits[0] - xExtremes[0]) / xRange); - var rightPercent = Math.max(0, (xExtremes[1] - xWindowLimits[1]) / xRange); - var leftCoord = this.canvasRect_.x + this.canvasRect_.w * leftPercent; - var rightCoord = this.canvasRect_.x + this.canvasRect_.w * (1 - rightPercent); - var handleTop = Math.max(this.canvasRect_.y, this.canvasRect_.y + (this.canvasRect_.h - this.leftZoomHandle_.height) / 2); - var halfHandleWidth = this.leftZoomHandle_.width / 2; - this.leftZoomHandle_.style.left = leftCoord - halfHandleWidth + 'px'; - this.leftZoomHandle_.style.top = handleTop + 'px'; - this.rightZoomHandle_.style.left = rightCoord - halfHandleWidth + 'px'; - this.rightZoomHandle_.style.top = this.leftZoomHandle_.style.top; - - this.leftZoomHandle_.style.visibility = 'visible'; - this.rightZoomHandle_.style.visibility = 'visible'; -}; - -/** - * @private - * Draws the interactive layer in the foreground canvas. - */ -rangeSelector.prototype.drawInteractiveLayer_ = function () { - var ctx = this.fgcanvas_ctx_; - ctx.clearRect(0, 0, this.canvasRect_.w, this.canvasRect_.h); - var margin = 1; - var width = this.canvasRect_.w - margin; - var height = this.canvasRect_.h - margin; - var zoomHandleStatus = this.getZoomHandleStatus_(); - - ctx.strokeStyle = this.getOption_('rangeSelectorForegroundStrokeColor'); - ctx.lineWidth = this.getOption_('rangeSelectorForegroundLineWidth'); - if (!zoomHandleStatus.isZoomed) { - ctx.beginPath(); - ctx.moveTo(margin, margin); - ctx.lineTo(margin, height); - ctx.lineTo(width, height); - ctx.lineTo(width, margin); - ctx.stroke(); - } else { - var leftHandleCanvasPos = Math.max(margin, zoomHandleStatus.leftHandlePos - this.canvasRect_.x); - var rightHandleCanvasPos = Math.min(width, zoomHandleStatus.rightHandlePos - this.canvasRect_.x); - - ctx.fillStyle = 'rgba(240, 240, 240, ' + this.getOption_('rangeSelectorAlpha').toString() + ')'; - ctx.fillRect(0, 0, leftHandleCanvasPos, this.canvasRect_.h); - ctx.fillRect(rightHandleCanvasPos, 0, this.canvasRect_.w - rightHandleCanvasPos, this.canvasRect_.h); - - ctx.beginPath(); - ctx.moveTo(margin, margin); - ctx.lineTo(leftHandleCanvasPos, margin); - ctx.lineTo(leftHandleCanvasPos, height); - ctx.lineTo(rightHandleCanvasPos, height); - ctx.lineTo(rightHandleCanvasPos, margin); - ctx.lineTo(width, margin); - ctx.stroke(); - } -}; - -/** - * @private - * Returns the current zoom handle position information. - * @return {Object} The zoom handle status. - */ -rangeSelector.prototype.getZoomHandleStatus_ = function () { - var halfHandleWidth = this.leftZoomHandle_.width / 2; - var leftHandlePos = parseFloat(this.leftZoomHandle_.style.left) + halfHandleWidth; - var rightHandlePos = parseFloat(this.rightZoomHandle_.style.left) + halfHandleWidth; - return { - leftHandlePos: leftHandlePos, - rightHandlePos: rightHandlePos, - isZoomed: leftHandlePos - 1 > this.canvasRect_.x || rightHandlePos + 1 < this.canvasRect_.x + this.canvasRect_.w - }; -}; - -exports['default'] = rangeSelector; -module.exports = exports['default']; - -},{"../dygraph-interaction-model":12,"../dygraph-utils":17,"../iframe-tarp":19}]},{},[18])(18) -}); -//# sourceMappingURL=dygraph.js.map diff --git a/bindings/dygraph/dist/dygraph.min.js b/bindings/dygraph/dist/dygraph.min.js deleted file mode 100644 index e24af99e..00000000 --- a/bindings/dygraph/dist/dygraph.min.js +++ /dev/null @@ -1,6 +0,0 @@ -/*! @license Copyright 2017 Dan Vanderkam (danvdk@gmail.com) MIT-licensed (http://opensource.org/licenses/MIT) */ -!function(t){var e;e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,e.Dygraph=function(){return function t(e,a,i){function n(o,s){if(!a[o]){if(!e[o]){var l="function"==typeof require&&require;if(!s&&l)return l(o,!0);if(r)return r(o,!0);var h=new Error("Cannot find module '"+o+"'");throw h.code="MODULE_NOT_FOUND",h}var u=a[o]={exports:{}};e[o][0].call(u.exports,function(t){var a=e[o][1][t];return n(a||t)},u,u.exports,t,e,a,i)}return a[o].exports}for(var r="function"==typeof require&&require,o=0;o1)for(var a=1;a=0){var d=t[l-e];null===d[1]||isNaN(d[1])||(n-=d[2][0],o-=d[1],r-=d[2][1],s-=1)}u[l]=s?[t[l][0],1*o/s,[1*n/s,1*r/s]]:[t[l][0],null,[null,null]]}return u},a.default=r,e.exports=a.default},{"./bars":5}],3:[function(t,e,a){"use strict";Object.defineProperty(a,"__esModule",{value:!0});var i=t("./bars"),n=function(t){return t&&t.__esModule?t:{default:t}}(i),r=function(){};r.prototype=new n.default,r.prototype.extractSeries=function(t,e,a){for(var i,n,r,o,s=[],l=a.get("sigma"),h=a.get("logscale"),u=0;u=0&&(u-=t[r-e][2][2],d-=t[r-e][2][3]);var c=t[r][0],p=d?u/d:0;if(h)if(d){var g=p<0?0:p,f=d,v=l*Math.sqrt(g*(1-g)/f+l*l/(4*f*f)),_=1+l*l/d;i=(g+l*l/(2*d)-v)/_,n=(g+l*l/(2*d)+v)/_,s[r]=[c,100*g,[100*i,100*n]]}else s[r]=[c,0,[0,0]];else o=d?l*Math.sqrt(p*(1-p)/d):1,s[r]=[c,100*p,[100*(p-o),100*(p+o)]]}return s},a.default=r,e.exports=a.default},{"./bars":5}],5:[function(t,e,a){"use strict";function i(t){return t&&t.__esModule?t:{default:t}}Object.defineProperty(a,"__esModule",{value:!0});var n=t("./datahandler"),r=i(n),o=t("../dygraph-layout"),s=i(o),l=function(){r.default.call(this)};l.prototype=new r.default,l.prototype.extractSeries=function(t,e,a){},l.prototype.rollingAverage=function(t,e,a){},l.prototype.onPointsCreated_=function(t,e){for(var a=0;ai&&(l=i),hr)&&(r=h),(null===n||l=0&&(r-=t[i-e][2][0],o-=t[i-e][2][1]);var s=t[i][0],l=o?r/o:0;n[i]=[s,100*l]}return n},a.default=s,e.exports=a.default},{"./datahandler":6,"./default":8}],8:[function(t,e,a){"use strict";Object.defineProperty(a,"__esModule",{value:!0});var i=t("./datahandler"),n=function(t){return t&&t.__esModule?t:{default:t}}(i),r=function(){};r.prototype=new n.default,r.prototype.extractSeries=function(t,e,a){for(var i=[],n=a.get("logscale"),r=0;rr)&&(r=i),(null===n||i=2,v=t.drawingContext;v.save(),f&&v.setLineDash&&v.setLineDash(i);var _=s._drawSeries(t,g,a,l,r,d,u,e);s._drawPointsOnLine(t,_,o,e,l),f&&v.setLineDash&&v.setLineDash([]),v.restore()},s._drawSeries=function(t,e,a,i,n,r,o,s){var l,h,u=null,d=null,c=null,p=[],g=!0,f=t.drawingContext;f.beginPath(),f.strokeStyle=s,f.lineWidth=a;for(var v=e.array_,_=e.end_,y=e.predicate_,x=e.start_;x<_;x++){if(h=v[x],y){for(;x<_&&!y(v,x);)x++;if(x==_)break;h=v[x]}if(null===h.canvasy||h.canvasy!=h.canvasy)o&&null!==u&&(f.moveTo(u,d),f.lineTo(h.canvasx,d)),u=d=null;else{if(l=!1,r||null===u){e.nextIdx_=x,e.next(),c=e.hasNext?e.peek.canvasy:null;var m=null===c||c!=c;l=null===u&&m,r&&(!g&&null===u||e.hasNext&&m)&&(l=!0)}null!==u?a&&(o&&(f.moveTo(u,d),f.lineTo(h.canvasx,d)),f.lineTo(h.canvasx,h.canvasy)):f.moveTo(h.canvasx,h.canvasy),(n||l)&&p.push([h.canvasx,h.canvasy,h.idx]),u=h.canvasx,d=h.canvasy}g=!1}return f.stroke(),p},s._drawPointsOnLine=function(t,e,a,i,n){for(var r=t.drawingContext,o=0;o0;a--){var i=e[a];if(2==i[0]){var n=e[a-1];n[1]==i[1]&&n[2]==i[2]&&e.splice(a,1)}}for(var a=0;a2&&!t){var r=0;2==e[0][0]&&r++;for(var o=null,s=null,a=r;ae[s][2]&&(s=a)}}var h=e[o],u=e[s];e.splice(r,e.length-r),os?(e.push(u),e.push(h)):e.push(h)}}},o=function(a){r(a);for(var o=0,s=e.length;o1,h=s-a>1;o(l||h),a=s}e.push([t,n,r])};return{moveTo:function(t,e){s(2,t,e)},lineTo:function(t,e){s(1,t,e)},stroke:function(){o(!0),t.stroke()},fill:function(){o(!0),t.fill()},beginPath:function(){o(!0),t.beginPath()},closePath:function(){o(!0),t.closePath()},_count:function(){return n}}},s._fillPlotter=function(t){if(!t.singleSeriesName&&0===t.seriesIndex){for(var e=t.dygraph,a=e.getLabels().slice(1),i=a.length;i>=0;i--)e.visibility()[i]||a.splice(i,1);if(function(){for(var t=0;t=0;n--){var r=i[n];t.lineTo(r[0],r[1])}},v=d-1;v>=0;v--){var _=t.drawingContext,y=a[v];if(e.getBooleanOption("fillGraph",y)){var x=e.getNumericOption("fillAlpha",y),m=e.getBooleanOption("stepPlot",y),b=p[v],w=e.axisPropertiesForSeries(y),A=1+w.minyval*w.yscale;A<0?A=0:A>1&&(A=1),A=h.h*A+h.y;var O,D=u[v],E=n.createIterator(D,0,D.length,s._getIteratorPredicate(e.getBooleanOption("connectSeparatedPoints",y))),L=NaN,T=[-1,-1],S=n.toRGB_(b),P="rgba("+S.r+","+S.g+","+S.b+","+x+")";_.fillStyle=P,_.beginPath();var C,M=!0;(D.length>2*e.width_||o.default.FORCE_FAST_PROXY)&&(_=s._fastCanvasProxy(_));for(var N,F=[];E.hasNext;)if(N=E.next(),n.isOK(N.y)||m){if(c){if(!M&&C==N.xval)continue;M=!1,C=N.xval,r=g[N.canvasx];var k;k=void 0===r?A:l?r[0]:r,O=[N.canvasy,k],m?-1===T[0]?g[N.canvasx]=[N.canvasy,A]:g[N.canvasx]=[N.canvasy,T[0]]:g[N.canvasx]=N.canvasy}else O=isNaN(N.canvasy)&&m?[h.y+h.h,A]:[N.canvasy,A];isNaN(L)?(_.moveTo(N.canvasx,O[1]),_.lineTo(N.canvasx,O[0])):(m?(_.lineTo(N.canvasx,T[0]),_.lineTo(N.canvasx,O[0])):_.lineTo(N.canvasx,O[0]),c&&(F.push([L,T[1]]),l&&r?F.push([N.canvasx,r[1]]):F.push([N.canvasx,O[1]]))),T=O,L=N.canvasx}else f(_,L,T[1],F),F=[],L=NaN,null===N.y_stacked||isNaN(N.y_stacked)||(g[N.canvasx]=h.h*N.y_stacked+h.y);l=m,O&&N&&(f(_,N.canvasx,O[1],F),F=[]),_.fill()}}}},a.default=s,e.exports=a.default},{"./dygraph":18,"./dygraph-utils":17}],10:[function(t,e,a){"use strict";function i(t){return t&&t.__esModule?t:{default:t}}function n(t){if(t&&t.__esModule)return t;var e={};if(null!=t)for(var a in t)Object.prototype.hasOwnProperty.call(t,a)&&(e[a]=t[a]);return e.default=t,e}Object.defineProperty(a,"__esModule",{value:!0});var r=t("./dygraph-tickers"),o=n(r),s=t("./dygraph-interaction-model"),l=i(s),h=t("./dygraph-canvas"),u=i(h),d=t("./dygraph-utils"),c=n(d),p={highlightCircleSize:3,highlightSeriesOpts:null,highlightSeriesBackgroundAlpha:.5,highlightSeriesBackgroundColor:"rgb(255, 255, 255)",labelsSeparateLines:!1,labelsShowZeroValues:!0,labelsKMB:!1,labelsKMG2:!1,showLabelsOnHighlight:!0,digitsAfterDecimal:2,maxNumberWidth:6,sigFigs:null,strokeWidth:1,strokeBorderWidth:0,strokeBorderColor:"white",axisTickSize:3,axisLabelFontSize:14,rightGap:5,showRoller:!1,xValueParser:void 0,delimiter:",",sigma:2,errorBars:!1,fractions:!1,wilsonInterval:!0,customBars:!1,fillGraph:!1,fillAlpha:.15,connectSeparatedPoints:!1,stackedGraph:!1,stackedGraphNaNFill:"all",hideOverlayOnMouseOut:!0,legend:"onmouseover",stepPlot:!1,xRangePad:0,yRangePad:null,drawAxesAtZero:!1,titleHeight:28,xLabelHeight:18,yLabelWidth:18,axisLineColor:"black",axisLineWidth:.3,gridLineWidth:.3,axisLabelWidth:50,gridLineColor:"rgb(128,128,128)",interactionModel:l.default.defaultModel,animatedZooms:!1,showRangeSelector:!1,rangeSelectorHeight:40,rangeSelectorPlotStrokeColor:"#808FAB",rangeSelectorPlotFillGradientColor:"white",rangeSelectorPlotFillColor:"#A7B1C4",rangeSelectorBackgroundStrokeColor:"gray",rangeSelectorBackgroundLineWidth:1,rangeSelectorPlotLineWidth:1.5,rangeSelectorForegroundStrokeColor:"black",rangeSelectorForegroundLineWidth:1,rangeSelectorAlpha:.6,showInRangeSelector:null,plotter:[u.default._fillPlotter,u.default._errorPlotter,u.default._linePlotter],plugins:[],axes:{x:{pixelsPerLabel:70,axisLabelWidth:60,axisLabelFormatter:c.dateAxisLabelFormatter,valueFormatter:c.dateValueFormatter,drawGrid:!0,drawAxis:!0,independentTicks:!0,ticker:o.dateTicker},y:{axisLabelWidth:50,pixelsPerLabel:30,valueFormatter:c.numberValueFormatter,axisLabelFormatter:c.numberAxisLabelFormatter,drawGrid:!0,drawAxis:!0,independentTicks:!0,ticker:o.numericTicks},y2:{axisLabelWidth:50,pixelsPerLabel:30,valueFormatter:c.numberValueFormatter,axisLabelFormatter:c.numberAxisLabelFormatter,drawAxis:!0,drawGrid:!1,independentTicks:!1,ticker:o.numericTicks}}};a.default=p,e.exports=a.default},{"./dygraph-canvas":9,"./dygraph-interaction-model":12,"./dygraph-tickers":16,"./dygraph-utils":17}],11:[function(t,e,a){"use strict";Object.defineProperty(a,"__esModule",{value:!0});var i=t("./dygraph"),n=function(t){return t&&t.__esModule?t:{default:t}}(i),r=function(t){this.container=t};r.prototype.draw=function(t,e){this.container.innerHTML="",void 0!==this.date_graph&&this.date_graph.destroy(),this.date_graph=new n.default(this.container,t,e)},r.prototype.setSelection=function(t){var e=!1;t.length&&(e=t[0].row),this.date_graph.setSelection(e)},r.prototype.getSelection=function(){var t=[],e=this.date_graph.getSelection();if(e<0)return t;for(var a=this.date_graph.layout_.points,i=0;ia.boundedDates[1]&&(i-=r-a.boundedDates[1],r=i+a.dateRange),e.getOptionForAxis("logscale","x")?e.dateWindow_=[Math.pow(n.LOG_SCALE,i),Math.pow(n.LOG_SCALE,r)]:e.dateWindow_=[i,r],a.is2DPan)for(var o=a.dragEndY-a.dragStartY,s=0;s=10&&a.dragDirection==n.HORIZONTAL){var o=Math.min(a.dragStartX,a.dragEndX),s=Math.max(a.dragStartX,a.dragEndX);o=Math.max(o,i.x),s=Math.min(s,i.x+i.w),o=10&&a.dragDirection==n.VERTICAL){var l=Math.min(a.dragStartY,a.dragEndY),h=Math.max(a.dragStartY,a.dragEndY);l=Math.max(l,i.y),h=Math.min(h,i.y+i.h),l1&&(a.startTimeForDoubleTapMs=null);for(var i=[],n=0;n=2){a.initialPinchCenter={pageX:.5*(i[0].pageX+i[1].pageX),pageY:.5*(i[0].pageY+i[1].pageY),dataX:.5*(i[0].dataX+i[1].dataX),dataY:.5*(i[0].dataY+i[1].dataY)};var o=180/Math.PI*Math.atan2(a.initialPinchCenter.pageY-i[0].pageY,i[0].pageX-a.initialPinchCenter.pageX);o=Math.abs(o),o>90&&(o=90-o),a.touchDirections={x:o<67.5,y:o>22.5}}a.initialRange={x:e.xAxisRange(),y:e.yAxisRange()}},r.moveTouch=function(t,e,a){a.startTimeForDoubleTapMs=null;var i,n=[];for(i=0;i=2){var g=s[1].pageX-l.pageX;c=(n[1].pageX-o.pageX)/g;var f=s[1].pageY-l.pageY;p=(n[1].pageY-o.pageY)/f}c=Math.min(8,Math.max(.125,c)),p=Math.min(8,Math.max(.125,p));var v=!1;if(a.touchDirections.x&&(e.dateWindow_=[l.dataX-h.dataX+(a.initialRange.x[0]-l.dataX)/c,l.dataX-h.dataX+(a.initialRange.x[1]-l.dataX)/c],v=!0),a.touchDirections.y)for(i=0;i<1;i++){var _=e.axes_[i],y=e.attributes_.getForAxis("logscale",i);y||(_.valueRange=[l.dataY-h.dataY+(a.initialRange.y[0]-l.dataY)/p,l.dataY-h.dataY+(a.initialRange.y[1]-l.dataY)/p],v=!0)}if(e.drawGraph_(!1),v&&n.length>1&&e.getFunctionOption("zoomCallback")){var x=e.xAxisRange();e.getFunctionOption("zoomCallback").call(e,x[0],x[1],e.yAxisRanges())}},r.endTouch=function(t,e,a){if(0!==t.touches.length)r.startTouch(t,e,a);else if(1==t.changedTouches.length){var i=(new Date).getTime(),n=t.changedTouches[0];a.startTimeForDoubleTapMs&&i-a.startTimeForDoubleTapMs<500&&a.doubleTapX&&Math.abs(a.doubleTapX-n.screenX)<50&&a.doubleTapY&&Math.abs(a.doubleTapY-n.screenY)<50?e.resetZoom():(a.startTimeForDoubleTapMs=i,a.doubleTapX=n.screenX,a.doubleTapY=n.screenY)}};var o=function(t,e,a){return ta?t-a:0},s=function(t,e){var a=n.findPos(e.canvas_),i={left:a.x,right:a.x+e.canvas_.offsetWidth,top:a.y,bottom:a.y+e.canvas_.offsetHeight},r={x:n.pageX(t),y:n.pageY(t)},s=o(r.x,i.left,i.right),l=o(r.y,i.top,i.bottom);return Math.max(s,l)};r.defaultModel={mousedown:function(t,e,a){if(!t.button||2!=t.button){a.initializeMouseDown(t,e,a),t.altKey||t.shiftKey?r.startPan(t,e,a):r.startZoom(t,e,a);var i=function(t){if(a.isZooming){s(t,e)<100?r.moveZoom(t,e,a):null!==a.dragEndX&&(a.dragEndX=null,a.dragEndY=null,e.clearZoomRect_())}else a.isPanning&&r.movePan(t,e,a)},o=function t(o){a.isZooming?null!==a.dragEndX?r.endZoom(o,e,a):r.maybeTreatMouseOpAsClick(o,e,a):a.isPanning&&r.endPan(o,e,a),n.removeEvent(document,"mousemove",i),n.removeEvent(document,"mouseup",t),a.destroy()};e.addAndTrackEvent(document,"mousemove",i),e.addAndTrackEvent(document,"mouseup",o)}},willDestroyContextMyself:!0,touchstart:function(t,e,a){r.startTouch(t,e,a)},touchmove:function(t,e,a){r.moveTouch(t,e,a)},touchend:function(t,e,a){r.endTouch(t,e,a)},dblclick:function(t,e,a){if(a.cancelNextDblclick)return void(a.cancelNextDblclick=!1);var i={canvasx:a.dragEndX,canvasy:a.dragEndY,cancelable:!0};e.cascadeEvents_("dblclick",i)||t.altKey||t.shiftKey||e.resetZoom()}},r.nonInteractiveModel_={mousedown:function(t,e,a){a.initializeMouseDown(t,e,a)},mouseup:r.maybeTreatMouseOpAsClick},r.dragIsPanInteractionModel={mousedown:function(t,e,a){a.initializeMouseDown(t,e,a),r.startPan(t,e,a)},mousemove:function(t,e,a){a.isPanning&&r.movePan(t,e,a)},mouseup:function(t,e,a){a.isPanning&&r.endPan(t,e,a)}},a.default=r,e.exports=a.default},{"./dygraph-utils":17}],13:[function(t,e,a){"use strict";Object.defineProperty(a,"__esModule",{value:!0});var i=t("./dygraph-utils"),n=function(t){if(t&&t.__esModule)return t;var e={};if(null!=t)for(var a in t)Object.prototype.hasOwnProperty.call(t,a)&&(e[a]=t[a]);return e.default=t,e}(i),r=function(t){this.dygraph_=t,this.points=[],this.setNames=[],this.annotations=[],this.yAxes_=null,this.xTicks_=null,this.yTicks_=null};r.prototype.addDataset=function(t,e){this.points.push(e),this.setNames.push(t)},r.prototype.getPlotArea=function(){return this.area_},r.prototype.computePlotArea=function(){var t={x:0,y:0};t.w=this.dygraph_.width_-t.x-this.dygraph_.getOption("rightGap"),t.h=this.dygraph_.height_;var e={chart_div:this.dygraph_.graphDiv,reserveSpaceLeft:function(e){var a={x:t.x,y:t.y,w:e,h:t.h};return t.x+=e,t.w-=e,a},reserveSpaceRight:function(e){var a={x:t.x+t.w-e,y:t.y,w:e,h:t.h};return t.w-=e,a},reserveSpaceTop:function(e){var a={x:t.x,y:t.y,w:t.w,h:e};return t.y+=e,t.h-=e,a},reserveSpaceBottom:function(e){var a={x:t.x,y:t.y+t.h-e,w:t.w,h:e};return t.h-=e,a},chartRect:function(){return{x:t.x,y:t.y,w:t.w,h:t.h}}};this.dygraph_.cascadeEvents_("layout",e),this.area_=t},r.prototype.setAnnotations=function(t){this.annotations=[];for(var e=this.dygraph_.getOption("xValueParser")||function(t){return t},a=0;a=0&&i<1&&this.xticks.push({pos:i,label:a,has_tick:r});for(this.yticks=[],t=0;t0&&i<=1&&this.yticks.push({axis:t,pos:i,label:a,has_tick:r})},r.prototype._evaluateAnnotations=function(){var t,e={};for(t=0;t1&&o.update(this.yAxes_[1].options,s.y2||{}),o.update(this.xAxis_.options,s.x||{})}},u.prototype.get=function(t){var e=this.getGlobalUser_(t);return null!==e?e:this.getGlobalDefault_(t)},u.prototype.getGlobalUser_=function(t){return this.user_.hasOwnProperty(t)?this.user_[t]:null},u.prototype.getGlobalDefault_=function(t){return this.global_.hasOwnProperty(t)?this.global_[t]:l.default.hasOwnProperty(t)?l.default[t]:null},u.prototype.getForAxis=function(t,e){var a,i;if("number"==typeof e)a=e,i=0===a?"y":"y2";else{if("y1"==e&&(e="y"),"y"==e)a=0;else if("y2"==e)a=1;else{if("x"!=e)throw"Unknown axis "+e;a=-1}i=e}var n=-1==a?this.xAxis_:this.yAxes_[a];if(n){var r=n.options;if(r.hasOwnProperty(t))return r[t]}if("x"!==e||"logscale"!==t){var o=this.getGlobalUser_(t);if(null!==o)return o}var s=l.default.axes[i];return s.hasOwnProperty(t)?s[t]:this.getGlobalDefault_(t)},u.prototype.getForSeries=function(t,e){if(e===this.dygraph_.getHighlightSeries()&&this.highlightSeries_.hasOwnProperty(t))return this.highlightSeries_[t];if(!this.series_.hasOwnProperty(e))throw"Unknown series: "+e;var a=this.series_[e],i=a.options;return i.hasOwnProperty(t)?i[t]:this.getForAxis(t,a.yAxis)},u.prototype.numAxes=function(){return this.yAxes_.length},u.prototype.axisForSeries=function(t){return this.series_[t].yAxis},u.prototype.axisOptions=function(t){return this.yAxes_[t].options},u.prototype.seriesForAxis=function(t){return this.yAxes_[t].series},u.prototype.seriesNames=function(){return this.labels_},void 0!==i);a.default=u,e.exports=a.default}).call(this,t("_process"))},{"./dygraph-default-attrs":10,"./dygraph-options-reference":14,"./dygraph-utils":17,_process:1}],16:[function(t,e,a){"use strict";Object.defineProperty(a,"__esModule",{value:!0});var i=t("./dygraph-utils"),n=function(t){if(t&&t.__esModule)return t;var e={};if(null!=t)for(var a in t)Object.prototype.hasOwnProperty.call(t,a)&&(e[a]=t[a]);return e.default=t,e}(i),r=function(t,e,a,i,n,r){return o(t,e,a,function(t){return"logscale"!==t&&i(t)},n,r)};a.numericLinearTicks=r;var o=function(t,e,a,i,r,o){var s,l,h,u,c=i("pixelsPerLabel"),p=[];if(o)for(s=0;s=u/4){for(var _=f;_>=g;_--){var y=d[_],x=Math.log(y/t)/Math.log(e/t)*a,m={v:y};null===v?v={tickValue:y,pixel_coord:x}:Math.abs(x-v.pixel_coord)>=c?v={tickValue:y,pixel_coord:x}:m.label="",p.push(m)}p.reverse()}}if(0===p.length){var b,w,A=i("labelsKMG2");A?(b=[1,2,4,8,16,32,64,128,256],w=16):(b=[1,2,5,10,20,50,100],w=10);var O,D,E,L=Math.ceil(a/c),T=Math.abs(e-t)/L,S=Math.floor(Math.log(T)/Math.log(w)),P=Math.pow(w,S);for(l=0;lc));l++);for(D>E&&(O*=-1),s=0;s<=u;s++)h=D+s*O,p.push({v:h})}}var C=i("axisLabelFormatter");for(s=0;s=0?g(t,e,o,i,n):[]};a.dateTicker=s;var l={MILLISECONDLY:0,TWO_MILLISECONDLY:1,FIVE_MILLISECONDLY:2,TEN_MILLISECONDLY:3,FIFTY_MILLISECONDLY:4,HUNDRED_MILLISECONDLY:5,FIVE_HUNDRED_MILLISECONDLY:6,SECONDLY:7,TWO_SECONDLY:8,FIVE_SECONDLY:9,TEN_SECONDLY:10,THIRTY_SECONDLY:11,MINUTELY:12,TWO_MINUTELY:13,FIVE_MINUTELY:14,TEN_MINUTELY:15,THIRTY_MINUTELY:16,HOURLY:17,TWO_HOURLY:18,SIX_HOURLY:19,DAILY:20,TWO_DAILY:21,WEEKLY:22,MONTHLY:23,QUARTERLY:24,BIANNUAL:25,ANNUAL:26,DECADAL:27,CENTENNIAL:28,NUM_GRANULARITIES:29};a.Granularity=l;var h={DATEFIELD_Y:0,DATEFIELD_M:1,DATEFIELD_D:2,DATEFIELD_HH:3,DATEFIELD_MM:4,DATEFIELD_SS:5,DATEFIELD_MS:6,NUM_DATEFIELDS:7},u=[];u[l.MILLISECONDLY]={datefield:h.DATEFIELD_MS,step:1,spacing:1},u[l.TWO_MILLISECONDLY]={datefield:h.DATEFIELD_MS,step:2,spacing:2},u[l.FIVE_MILLISECONDLY]={datefield:h.DATEFIELD_MS,step:5,spacing:5},u[l.TEN_MILLISECONDLY]={datefield:h.DATEFIELD_MS,step:10,spacing:10},u[l.FIFTY_MILLISECONDLY]={datefield:h.DATEFIELD_MS,step:50,spacing:50},u[l.HUNDRED_MILLISECONDLY]={datefield:h.DATEFIELD_MS,step:100,spacing:100},u[l.FIVE_HUNDRED_MILLISECONDLY]={datefield:h.DATEFIELD_MS,step:500,spacing:500},u[l.SECONDLY]={datefield:h.DATEFIELD_SS,step:1,spacing:1e3},u[l.TWO_SECONDLY]={datefield:h.DATEFIELD_SS,step:2,spacing:2e3},u[l.FIVE_SECONDLY]={datefield:h.DATEFIELD_SS,step:5,spacing:5e3},u[l.TEN_SECONDLY]={datefield:h.DATEFIELD_SS,step:10,spacing:1e4},u[l.THIRTY_SECONDLY]={datefield:h.DATEFIELD_SS,step:30,spacing:3e4},u[l.MINUTELY]={datefield:h.DATEFIELD_MM,step:1,spacing:6e4},u[l.TWO_MINUTELY]={datefield:h.DATEFIELD_MM,step:2,spacing:12e4},u[l.FIVE_MINUTELY]={datefield:h.DATEFIELD_MM,step:5,spacing:3e5},u[l.TEN_MINUTELY]={datefield:h.DATEFIELD_MM,step:10,spacing:6e5},u[l.THIRTY_MINUTELY]={datefield:h.DATEFIELD_MM,step:30,spacing:18e5},u[l.HOURLY]={datefield:h.DATEFIELD_HH,step:1,spacing:36e5},u[l.TWO_HOURLY]={datefield:h.DATEFIELD_HH,step:2,spacing:72e5},u[l.SIX_HOURLY]={datefield:h.DATEFIELD_HH,step:6,spacing:216e5},u[l.DAILY]={datefield:h.DATEFIELD_D,step:1,spacing:864e5},u[l.TWO_DAILY]={datefield:h.DATEFIELD_D,step:2,spacing:1728e5},u[l.WEEKLY]={datefield:h.DATEFIELD_D,step:7,spacing:6048e5},u[l.MONTHLY]={datefield:h.DATEFIELD_M,step:1,spacing:2629817280},u[l.QUARTERLY]={datefield:h.DATEFIELD_M,step:3,spacing:216e5*365.2524},u[l.BIANNUAL]={datefield:h.DATEFIELD_M,step:6,spacing:432e5*365.2524},u[l.ANNUAL]={datefield:h.DATEFIELD_Y,step:1,spacing:864e5*365.2524},u[l.DECADAL]={datefield:h.DATEFIELD_Y,step:10,spacing:315578073600},u[l.CENTENNIAL]={datefield:h.DATEFIELD_Y,step:100,spacing:3155780736e3};var d=function(){for(var t=[],e=-39;e<=39;e++)for(var a=Math.pow(10,e),i=1;i<=9;i++){var n=a*i;t.push(n)}return t}(),c=function(t,e,a,i){for(var n=i("pixelsPerLabel"),r=0;r=n)return r}return-1},p=function(t,e,a){var i=u[a].spacing;return Math.round(1*(e-t)/i)},g=function(t,e,a,i,r){var o=i("axisLabelFormatter"),s=i("labelsUTC"),d=s?n.DateAccessorsUTC:n.DateAccessorsLocal,c=u[a].datefield,p=u[a].step,g=u[a].spacing,f=new Date(t),v=[];v[h.DATEFIELD_Y]=d.getFullYear(f),v[h.DATEFIELD_M]=d.getMonth(f),v[h.DATEFIELD_D]=d.getDate(f),v[h.DATEFIELD_HH]=d.getHours(f),v[h.DATEFIELD_MM]=d.getMinutes(f),v[h.DATEFIELD_SS]=d.getSeconds(f),v[h.DATEFIELD_MS]=d.getMilliseconds(f);var _=v[c]%p;a==l.WEEKLY&&(_=d.getDay(f)),v[c]-=_;for(var y=c+1;y=l.DAILY||d.getHours(m)%p==0)&&x.push({v:b,label:o.call(r,m,a,i,r)}),v[c]+=p,m=d.makeDate.apply(null,v),b=m.getTime();return x};a.getDateAxis=g},{"./dygraph-utils":17}],17:[function(t,e,a){"use strict";function i(t,e,a){t.removeEventListener(e,a,!1)}function n(t){return t=t||window.event,t.stopPropagation&&t.stopPropagation(),t.preventDefault&&t.preventDefault(),t.cancelBubble=!0,t.cancel=!0,t.returnValue=!1,!1}function r(t,e,a){var i,n,r;if(0===e)i=a,n=a,r=a;else{var o=Math.floor(6*t),s=6*t-o,l=a*(1-e),h=a*(1-e*s),u=a*(1-e*(1-s));switch(o){case 1:i=h,n=a,r=l;break;case 2:i=l,n=a,r=u;break;case 3:i=l,n=h,r=a;break;case 4:i=u,n=l,r=a;break;case 5:i=a,n=l,r=h;break;case 6:case 0:i=a,n=u,r=l}}return i=Math.floor(255*i+.5),n=Math.floor(255*n+.5),r=Math.floor(255*r+.5),"rgb("+i+","+n+","+r+")"}function o(t){var e=t.getBoundingClientRect(),a=window,i=document.documentElement;return{x:e.left+(a.pageXOffset||i.scrollLeft),y:e.top+(a.pageYOffset||i.scrollTop)}}function s(t){return!t.pageX||t.pageX<0?0:t.pageX}function l(t){return!t.pageY||t.pageY<0?0:t.pageY}function h(t,e){return s(t)-e.px}function u(t,e){return l(t)-e.py}function d(t){return!!t&&!isNaN(t)}function c(t,e){return!!t&&(null!==t.yval&&(null!==t.x&&void 0!==t.x&&(null!==t.y&&void 0!==t.y&&!(isNaN(t.x)||!e&&isNaN(t.y)))))}function p(t,e){var a=Math.min(Math.max(1,e||2),21);return Math.abs(t)<.001&&0!==t?t.toExponential(a-1):t.toPrecision(a)}function g(t){return t<10?"0"+t:""+t}function f(t,e,a,i){var n=g(t)+":"+g(e);if(a&&(n+=":"+g(a),i)){var r=""+i;n+="."+("000"+r).substring(r.length)}return n}function v(t,e){var a=e?tt:$,i=new Date(t),n=a.getFullYear(i),r=a.getMonth(i),o=a.getDate(i),s=a.getHours(i),l=a.getMinutes(i),h=a.getSeconds(i),u=a.getMilliseconds(i),d=""+n,c=g(r+1),p=g(o),v=3600*s+60*l+h+.001*u,_=d+"/"+c+"/"+p;return v&&(_+=" "+f(s,l,h,u)),_}function _(t,e){var a=Math.pow(10,e);return Math.round(t*a)/a}function y(t,e,a,i,n){for(var r=!0;r;){var o=t,s=e,l=a,h=i,u=n;if(r=!1,null!==h&&void 0!==h&&null!==u&&void 0!==u||(h=0,u=s.length-1),h>u)return-1;null!==l&&void 0!==l||(l=0);var d,c=function(t){return t>=0&&to){if(l>0&&(d=p-1,c(d)&&s[d]o))return p;t=o,e=s,a=l,i=p+1,n=u,r=!0,c=p=g=d=void 0}}}function x(t){var e,a;if((-1==t.search("-")||-1!=t.search("T")||-1!=t.search("Z"))&&(a=m(t))&&!isNaN(a))return a;if(-1!=t.search("-")){for(e=t.replace("-","/","g");-1!=e.search("-");)e=e.replace("-","/");a=m(e)}else 8==t.length?(e=t.substr(0,4)+"/"+t.substr(4,2)+"/"+t.substr(6,2),a=m(e)):a=m(t);return a&&!isNaN(a)||console.error("Couldn't parse "+t+" as a date"),a}function m(t){return new Date(t).getTime()}function b(t,e){if(void 0!==e&&null!==e)for(var a in e)e.hasOwnProperty(a)&&(t[a]=e[a]);return t}function w(t,e){if(void 0!==e&&null!==e)for(var a in e)e.hasOwnProperty(a)&&(null===e[a]?t[a]=null:A(e[a])?t[a]=e[a].slice():!function(t){return"object"==typeof Node?t instanceof Node:"object"==typeof t&&"number"==typeof t.nodeType&&"string"==typeof t.nodeName}(e[a])&&"object"==typeof e[a]?("object"==typeof t[a]&&null!==t[a]||(t[a]={}),w(t[a],e[a])):t[a]=e[a]);return t}function A(t){var e=typeof t;return("object"==e||"function"==e&&"function"==typeof t.item)&&null!==t&&"number"==typeof t.length&&3!==t.nodeType}function O(t){return"object"==typeof t&&null!==t&&"function"==typeof t.getTime}function D(t){for(var e=[],a=0;a=e||et.call(window,function(){var e=(new Date).getTime(),h=e-o;n=r,r=Math.floor(h/a);var u=r-n;r+u>s||r>=s?(t(s),i()):(0!==u&&t(r),l())})}()}function C(t,e){var a={};if(t)for(var i=1;i=Math.pow(10,r)||Math.abs(t)=0;g--,c/=l)if(d>=c){i=_(t/c,n)+h[g];break}if(s){var f=String(t.toExponential()).split("e-");2===f.length&&f[1]>=3&&f[1]<=24&&(i=f[1]%3>0?_(f[0]/F(10,f[1]%3),n):Number(f[0]).toFixed(2),i+=u[Math.floor(f[1]/3)-1])}}return i}function X(t,e,a){return Y.call(this,t,a)}function V(t,e,a){var i=a("labelsUTC"),n=i?tt:$,r=n.getFullYear(t),o=n.getMonth(t),s=n.getDate(t),l=n.getHours(t),h=n.getMinutes(t),u=n.getSeconds(t),d=n.getMilliseconds(t);if(e>=G.Granularity.DECADAL)return""+r;if(e>=G.Granularity.MONTHLY)return lt[o]+" "+r;if(0===3600*l+60*h+u+.001*d||e>=G.Granularity.DAILY)return g(s)+" "+lt[o];if(eG.Granularity.MINUTELY?f(l,h,u,0):f(l,h,u,d)}function Z(t,e){return v(t,e("labelsUTC"))}Object.defineProperty(a,"__esModule",{value:!0}),a.removeEvent=i,a.cancelEvent=n,a.hsvToRGB=r,a.findPos=o,a.pageX=s,a.pageY=l,a.dragGetX_=h,a.dragGetY_=u,a.isOK=d,a.isValidPoint=c,a.floatFormat=p,a.zeropad=g,a.hmsString_=f,a.dateString_=v,a.round_=_,a.binarySearch=y,a.dateParser=x,a.dateStrToMillis=m,a.update=b,a.updateDeep=w,a.isArrayLike=A,a.isDateLike=O,a.clone=D,a.createCanvas=E,a.getContextPixelRatio=L,a.Iterator=T,a.createIterator=S,a.repeatAndCleanup=P,a.isPixelChangingOptionList=C,a.detectLineDelimiter=M,a.isNodeContainedBy=N,a.pow=F,a.toRGB_=R,a.isCanvasSupported=I,a.parseFloat_=H,a.numberValueFormatter=Y,a.numberAxisLabelFormatter=X,a.dateAxisLabelFormatter=V,a.dateValueFormatter=Z;var B=t("./dygraph-tickers"),G=function(t){if(t&&t.__esModule)return t;var e={};if(null!=t)for(var a in t)Object.prototype.hasOwnProperty.call(t,a)&&(e[a]=t[a]);return e.default=t,e}(B);a.LOG_SCALE=10;var W=Math.log(10);a.LN_TEN=W;var U=function(t){return Math.log(t)/W};a.log10=U;var z=function(t,e,a){var i=U(t),n=U(e),r=i+a*(n-i);return Math.pow(10,r)};a.logRangeFraction=z;var j=[2,2];a.DOTTED_LINE=j;var K=[7,3];a.DASHED_LINE=K;var q=[7,2,2,2];a.DOT_DASH_LINE=q;a.HORIZONTAL=1;a.VERTICAL=2;var Q=function(t){return t.getContext("2d")};a.getContext=Q;var J=function(t,e,a){t.addEventListener(e,a,!1)};a.addEvent=J;var $={getFullYear:function(t){return t.getFullYear()},getMonth:function(t){return t.getMonth()},getDate:function(t){return t.getDate()},getHours:function(t){return t.getHours()},getMinutes:function(t){return t.getMinutes()},getSeconds:function(t){return t.getSeconds()},getMilliseconds:function(t){return t.getMilliseconds()},getDay:function(t){return t.getDay()},makeDate:function(t,e,a,i,n,r,o){return new Date(t,e,a,i,n,r,o)}};a.DateAccessorsLocal=$;var tt={getFullYear:function(t){return t.getUTCFullYear()},getMonth:function(t){return t.getUTCMonth()},getDate:function(t){return t.getUTCDate()},getHours:function(t){return t.getUTCHours()},getMinutes:function(t){return t.getUTCMinutes()},getSeconds:function(t){return t.getUTCSeconds()},getMilliseconds:function(t){return t.getUTCMilliseconds()},getDay:function(t){return t.getUTCDay()},makeDate:function(t,e,a,i,n,r,o){return new Date(Date.UTC(t,e,a,i,n,r,o))}};a.DateAccessorsUTC=tt,T.prototype.next=function(){if(!this.hasNext)return null;for(var t=this.peek,e=this.nextIdx_+1,a=!1;e=0;n--){var r=i[n][0],o=i[n][1];if(o.call(r,a),a.propagationStopped)break}return a.defaultPrevented},Q.prototype.getPluginInstance_=function(t){for(var e=0;e=0;if(null===t||void 0===t)return e||a;if("y"===t)return a;throw new Error("axis parameter is ["+t+"] must be null, 'x' or 'y'.")},Q.prototype.toString=function(){var t=this.maindiv_;return"[Dygraph "+(t&&t.id?t.id:t)+"]"},Q.prototype.attr_=function(t,e){return e?this.attributes_.getForSeries(t,e):this.attributes_.get(t)},Q.prototype.getOption=function(t,e){return this.attr_(t,e)},Q.prototype.getNumericOption=function(t,e){return this.getOption(t,e)},Q.prototype.getStringOption=function(t,e){return this.getOption(t,e)},Q.prototype.getBooleanOption=function(t,e){return this.getOption(t,e)},Q.prototype.getFunctionOption=function(t,e){return this.getOption(t,e)},Q.prototype.getOptionForAxis=function(t,e){return this.attributes_.getForAxis(t,e)},Q.prototype.optionsViewForAxis_=function(t){var e=this;return function(a){var i=e.user_attrs_.axes;return i&&i[t]&&i[t].hasOwnProperty(a)?i[t][a]:("x"!==t||"logscale"!==a)&&(void 0!==e.user_attrs_[a]?e.user_attrs_[a]:(i=e.attrs_.axes,i&&i[t]&&i[t].hasOwnProperty(a)?i[t][a]:"y"==t&&e.axes_[0].hasOwnProperty(a)?e.axes_[0][a]:"y2"==t&&e.axes_[1].hasOwnProperty(a)?e.axes_[1][a]:e.attr_(a)))}},Q.prototype.rollPeriod=function(){return this.rollPeriod_},Q.prototype.xAxisRange=function(){return this.dateWindow_?this.dateWindow_:this.xAxisExtremes()},Q.prototype.xAxisExtremes=function(){var t=this.getNumericOption("xRangePad")/this.plotter_.area.w;if(0===this.numRows())return[0-t,1+t];var e=this.rawData_[0][0],a=this.rawData_[this.rawData_.length-1][0];if(t){var i=a-e;e-=i*t,a+=i*t}return[e,a]},Q.prototype.yAxisExtremes=function(){var t=this.gatherDatasets_(this.rolledSeries_,null),e=t.extremes,a=this.axes_;this.computeYAxisRanges_(e);var i=this.axes_;return this.axes_=a,i.map(function(t){return t.extremeRange})},Q.prototype.yAxisRange=function(t){if(void 0===t&&(t=0),t<0||t>=this.axes_.length)return null;var e=this.axes_[t];return[e.computedValueRange[0],e.computedValueRange[1]]},Q.prototype.yAxisRanges=function(){for(var t=[],e=0;ethis.rawData_.length?null:e<0||e>this.rawData_[t].length?null:this.rawData_[t][e]},Q.prototype.createInterface_=function(){var t=this.maindiv_;this.graphDiv=document.createElement("div"),this.graphDiv.style.textAlign="left",this.graphDiv.style.position="relative",t.appendChild(this.graphDiv),this.canvas_=x.createCanvas(),this.canvas_.style.position="absolute",this.hidden_=this.createPlotKitCanvas_(this.canvas_),this.canvas_ctx_=x.getContext(this.canvas_),this.hidden_ctx_=x.getContext(this.hidden_),this.resizeElements_(),this.graphDiv.appendChild(this.hidden_),this.graphDiv.appendChild(this.canvas_),this.mouseEventElement_=this.createMouseEventElement_(),this.layout_=new h.default(this);var e=this;this.mouseMoveHandler_=function(t){e.mouseMove_(t)},this.mouseOutHandler_=function(t){var a=t.target||t.fromElement,i=t.relatedTarget||t.toElement;x.isNodeContainedBy(a,e.graphDiv)&&!x.isNodeContainedBy(i,e.graphDiv)&&e.mouseOut_(t)},this.addAndTrackEvent(window,"mouseout",this.mouseOutHandler_),this.addAndTrackEvent(this.mouseEventElement_,"mousemove",this.mouseMoveHandler_),this.resizeHandler_||(this.resizeHandler_=function(t){e.resize()},this.addAndTrackEvent(window,"resize",this.resizeHandler_))},Q.prototype.resizeElements_=function(){this.graphDiv.style.width=this.width_+"px",this.graphDiv.style.height=this.height_+"px";var t=this.getNumericOption("pixelRatio"),e=t||x.getContextPixelRatio(this.canvas_ctx_);this.canvas_.width=this.width_*e,this.canvas_.height=this.height_*e,this.canvas_.style.width=this.width_+"px",this.canvas_.style.height=this.height_+"px",1!==e&&this.canvas_ctx_.scale(e,e);var a=t||x.getContextPixelRatio(this.hidden_ctx_);this.hidden_.width=this.width_*a,this.hidden_.height=this.height_*a,this.hidden_.style.width=this.width_+"px",this.hidden_.style.height=this.height_+"px",1!==a&&this.hidden_ctx_.scale(a,a)},Q.prototype.destroy=function(){this.canvas_ctx_.restore(),this.hidden_ctx_.restore();for(var t=this.plugins_.length-1;t>=0;t--){var e=this.plugins_.pop();e.plugin.destroy&&e.plugin.destroy()}this.removeTrackedEvents_(),x.removeEvent(window,"mouseout",this.mouseOutHandler_), -x.removeEvent(this.mouseEventElement_,"mousemove",this.mouseMoveHandler_),x.removeEvent(window,"resize",this.resizeHandler_),this.resizeHandler_=null,function t(e){for(;e.hasChildNodes();)t(e.firstChild),e.removeChild(e.firstChild)}(this.maindiv_);var a=function(t){for(var e in t)"object"==typeof t[e]&&(t[e]=null)};a(this.layout_),a(this.plotter_),a(this)},Q.prototype.createPlotKitCanvas_=function(t){var e=x.createCanvas();return e.style.position="absolute",e.style.top=t.style.top,e.style.left=t.style.left,e.width=this.width_,e.height=this.height_,e.style.width=this.width_+"px",e.style.height=this.height_+"px",e},Q.prototype.createMouseEventElement_=function(){return this.canvas_},Q.prototype.setColors_=function(){var t=this.getLabels(),e=t.length-1;this.colors_=[],this.colorsMap_={};for(var a=this.getNumericOption("colorSaturation")||1,i=this.getNumericOption("colorValue")||.5,n=Math.ceil(e/2),r=this.getOption("colors"),o=this.visibility(),s=0;s=0;--u)for(var d=this.layout_.points[u],c=0;c=l.length)){var h=l[s];if(x.isValidPoint(h)){var u=h.canvasy;if(t>h.canvasx&&s+10){var p=(t-h.canvasx)/c;u+=p*(d.canvasy-h.canvasy)}}}else if(t0){var g=l[s-1];if(x.isValidPoint(g)){var c=h.canvasx-g.canvasx;if(c>0){var p=(h.canvasx-t)/c;u+=p*(g.canvasy-h.canvasy)}}}(0===r||u=0){var r=0,o=this.attr_("labels");for(e=1;er&&(r=s)}var l=this.previousVerticalX_;a.clearRect(l-r-1,0,2*r+2,this.height_)}if(this.selPoints_.length>0){var h=this.selPoints_[0].canvasx;for(a.save(),e=0;e=0){t!=this.lastRow_&&(i=!0),this.lastRow_=t;for(var n=0;n=0&&o=0&&(i=!0),this.lastRow_=-1;return this.selPoints_.length?this.lastx_=this.selPoints_[0].xval:this.lastx_=-1,void 0!==e&&(this.highlightSet_!==e&&(i=!0),this.highlightSet_=e),void 0!==a&&(this.lockedSet_=a),i&&this.updateSelection_(void 0),i},Q.prototype.mouseOut_=function(t){this.getFunctionOption("unhighlightCallback")&&this.getFunctionOption("unhighlightCallback").call(this,t),this.getBooleanOption("hideOverlayOnMouseOut")&&!this.lockedSet_&&this.clearSelection()},Q.prototype.clearSelection=function(){if(this.cascadeEvents_("deselect",{}),this.lockedSet_=!1,this.fadeLevel)return void this.animateSelection_(-1);this.canvas_ctx_.clearRect(0,0,this.width_,this.height_),this.fadeLevel=0,this.selPoints_=[],this.lastx_=-1,this.lastRow_=-1,this.highlightSet_=null},Q.prototype.getSelection=function(){if(!this.selPoints_||this.selPoints_.length<1)return-1;for(var t=0;t1&&(a=this.dataHandler_.rollingAverage(a,this.rollPeriod_,this.attributes_)),this.rolledSeries_.push(a)}this.drawGraph_();var i=new Date;this.drawingTimeMs_=i-t},Q.PointType=void 0,Q.stackPoints_=function(t,e,a,i){for(var n=null,r=null,o=null,s=-1,l=0;l=e))for(var a=e;aa[1]&&(a[1]=c),c=1;a--)if(this.visibility()[a-1]){if(e){s=t[a];var p=e[0],g=e[1];for(n=null,r=null,i=0;i=p&&null===n&&(n=i),s[i][0]<=g&&(r=i);null===n&&(n=0);for(var f=n,v=!0;v&&f>0;)f--,v=null===s[f][1];null===r&&(r=s.length-1);var _=r;for(v=!0;v&&_0;){var n=this.readyFns_.pop();n(this)}},Q.prototype.computeYAxes_=function(){var t,e,a;for(this.axes_=[],t=0;t0&&(v=0),_<0&&(_=0)),v==1/0&&(v=0),_==-1/0&&(_=1),a=_-v,0===a&&(0!==_?a=Math.abs(_):(_=1,a=1));var m=_,b=v;e&&(u?(m=_+n*a,b=v):(m=_+n*a,b=v-n*a,b<0&&v>=0&&(b=0),m>0&&_<=0&&(m=0))),h.extremeRange=[b,m]}if(h.valueRange){var w=o(h.valueRange[0])?h.extremeRange[0]:h.valueRange[0],A=o(h.valueRange[1])?h.extremeRange[1]:h.valueRange[1];h.computedValueRange=[w,A]}else h.computedValueRange=h.extremeRange;if(!e)if(u){w=h.computedValueRange[0],A=h.computedValueRange[1];var O=n/(2*n-1),D=(n-1)/(2*n-1);h.computedValueRange[0]=x.logRangeFraction(w,A,O),h.computedValueRange[1]=x.logRangeFraction(w,A,D)}else w=h.computedValueRange[0],A=h.computedValueRange[1],a=A-w,h.computedValueRange[0]=w-a*n,h.computedValueRange[1]=A+a*n;if(c){h.independentTicks=c;var E=this.optionsViewForAxis_("y"+(l?"2":"")),L=E("ticker");h.ticks=L(h.computedValueRange[0],h.computedValueRange[1],this.plotter_.area.h,E,this),r||(r=h)}}if(void 0===r)throw'Configuration Error: At least one axis has to have the "independentTicks" option activated.';for(var l=0;l0&&"e"!=t[a-1]&&"E"!=t[a-1]||t.indexOf("/")>=0||isNaN(parseFloat(t))?e=!0:8==t.length&&t>"19700101"&&t<"20371231"&&(e=!0),this.setXAxisOptions_(e)},Q.prototype.setXAxisOptions_=function(t){t?(this.attrs_.xValueParser=x.dateParser,this.attrs_.axes.x.valueFormatter=x.dateValueFormatter,this.attrs_.axes.x.ticker=_.dateTicker,this.attrs_.axes.x.axisLabelFormatter=x.dateAxisLabelFormatter):(this.attrs_.xValueParser=function(t){return parseFloat(t)},this.attrs_.axes.x.valueFormatter=function(t){return t},this.attrs_.axes.x.ticker=_.numericTicks,this.attrs_.axes.x.axisLabelFormatter=this.attrs_.axes.x.valueFormatter)},Q.prototype.parseCSV_=function(t){var e,a,i=[],n=x.detectLineDelimiter(t),r=t.split(n||"\n"),o=this.getStringOption("delimiter");-1==r[0].indexOf(o)&&r[0].indexOf("\t")>=0&&(o="\t");var s=0;"labels"in this.user_attrs_||(s=1,this.attrs_.labels=r[0].split(o),this.attributes_.reparseSeries());for(var l,h=!1,u=this.attr_("labels").length,d=!1,c=s;c0&&f[0]0;)e=String.fromCharCode(65+(t-1)%26)+e.toLowerCase(),t=Math.floor((t-1)/26);return e}(g.length),y.text="";for(var m=0;m0&&f[0]0&&this.setAnnotations(g,!0),this.attributes_.reparseSeries()},Q.prototype.cascadeDataDidUpdateEvent_=function(){this.cascadeEvents_("dataDidUpdate",{})},Q.prototype.start_=function(){var t=this.file_;if("function"==typeof t&&(t=t()),x.isArrayLike(t))this.rawData_=this.parseArray_(t),this.cascadeDataDidUpdateEvent_(),this.predraw_();else if("object"==typeof t&&"function"==typeof t.getColumnRange)this.parseDataTable_(t),this.cascadeDataDidUpdateEvent_(),this.predraw_();else if("string"==typeof t){var e=x.detectLineDelimiter(t);if(e)this.loadedEvent_(t);else{var a;a=window.XMLHttpRequest?new XMLHttpRequest:new ActiveXObject("Microsoft.XMLHTTP");var i=this;a.onreadystatechange=function(){4==a.readyState&&(200!==a.status&&0!==a.status||i.loadedEvent_(a.responseText))},a.open("GET",t,!0),a.send(null)}}else console.error("Unknown data format: "+typeof t)},Q.prototype.updateOptions=function(t,e){void 0===e&&(e=!1);var a=t.file,i=Q.copyUserAttrs_(t);"rollPeriod"in i&&(this.rollPeriod_=i.rollPeriod),"dateWindow"in i&&(this.dateWindow_=i.dateWindow);var n=x.isPixelChangingOptionList(this.attr_("labels"),i);x.updateDeep(this.user_attrs_,i),this.attributes_.reparseSeries(),a?(this.cascadeEvents_("dataWillUpdate",{}),this.file_=a,e||this.start_()):e||(n?this.predraw_():this.renderGraph_(!1))},Q.copyUserAttrs_=function(t){var e={};for(var a in t)t.hasOwnProperty(a)&&"file"!=a&&t.hasOwnProperty(a)&&(e[a]=t[a]);return e},Q.prototype.resize=function(t,e){if(!this.resize_lock){this.resize_lock=!0,null===t!=(null===e)&&(console.warn("Dygraph.resize() should be called with zero parameters or two non-NULL parameters. Pretending it was zero."),t=e=null);var a=this.width_,i=this.height_;t?(this.maindiv_.style.width=t+"px",this.maindiv_.style.height=e+"px",this.width_=t,this.height_=e):(this.width_=this.maindiv_.clientWidth,this.height_=this.maindiv_.clientHeight),a==this.width_&&i==this.height_||(this.resizeElements_(),this.predraw_()),this.resize_lock=!1}},Q.prototype.adjustRoll=function(t){this.rollPeriod_=t,this.predraw_()},Q.prototype.visibility=function(){for(this.getOption("visibility")||(this.attrs_.visibility=[]);this.getOption("visibility").length=a.length?console.warn("Invalid series number in setVisibility: "+n):a[n]=t[n]);else for(var n=0;n=a.length?console.warn("Invalid series number in setVisibility: "+n):a[n]=t[n]:t[n]<0||t[n]>=a.length?console.warn("Invalid series number in setVisibility: "+t[n]):a[t[n]]=e;this.predraw_()},Q.prototype.size=function(){return{width:this.width_,height:this.height_}},Q.prototype.setAnnotations=function(t,e){if(this.annotations_=t,!this.layout_)return void console.warn("Tried to setAnnotations before dygraph was ready. Try setting them in a ready() block. See dygraphs.com/tests/annotation.html");this.layout_.setAnnotations(this.annotations_),e||this.predraw_()},Q.prototype.annotations=function(){return this.annotations_},Q.prototype.getLabels=function(){var t=this.attr_("labels");return t?t.slice():null},Q.prototype.indexFromSetName=function(t){return this.setIndexByName_[t]},Q.prototype.getRowForX=function(t){for(var e=0,a=this.numRows()-1;e<=a;){var i=a+e>>1,n=this.getValue(i,0);if(nt)a=i-1;else{if(e==i)return i;a=i}}return null},Q.prototype.ready=function(t){this.is_initial_draw_?this.readyFns_.push(t):t.call(this,this)},Q.prototype.addAndTrackEvent=function(t,e,a){x.addEvent(t,e,a),this.registeredEvents_.push({elem:t,type:e,fn:a})},Q.prototype.removeTrackedEvents_=function(){if(this.registeredEvents_)for(var t=0;tr.x+r.w||l.canvasyr.y+r.h)){var h=l.annotation,u=6;h.hasOwnProperty("tickHeight")&&(u=h.tickHeight);var d=document.createElement("div");d.style.fontSize=e.getOption("axisLabelFontSize")+"px";var c="dygraph-annotation";h.hasOwnProperty("icon")||(c+=" dygraphDefaultAnnotation dygraph-default-annotation"),h.hasOwnProperty("cssClass")&&(c+=" "+h.cssClass),d.className=c;var p=h.hasOwnProperty("width")?h.width:16,g=h.hasOwnProperty("height")?h.height:16;if(h.hasOwnProperty("icon")){var f=document.createElement("img");f.src=h.icon,f.width=p,f.height=g,d.appendChild(f)}else l.annotation.hasOwnProperty("shortText")&&d.appendChild(document.createTextNode(l.annotation.shortText));var v=l.canvasx-p/2;d.style.left=v+"px";var _=0;if(h.attachAtBottom){var y=r.y+r.h-g-u;o[v]?y-=o[v]:o[v]=0,o[v]+=u+g,_=y}else _=l.canvasy-g-u;d.style.top=_+"px",d.style.width=p+"px",d.style.height=g+"px",d.title=l.annotation.text,d.style.color=e.colorsMap_[l.name],d.style.borderColor=e.colorsMap_[l.name],h.div=d,e.addAndTrackEvent(d,"click",n("clickHandler","annotationClickHandler",l)),e.addAndTrackEvent(d,"mouseover",n("mouseOverHandler","annotationMouseOverHandler",l)),e.addAndTrackEvent(d,"mouseout",n("mouseOutHandler","annotationMouseOutHandler",l)),e.addAndTrackEvent(d,"dblclick",n("dblClickHandler","annotationDblClickHandler",l)),i.appendChild(d),this.annotations_.push(d);var x=t.drawingContext;if(x.save(),x.strokeStyle=h.hasOwnProperty("tickColor")?h.tickColor:e.colorsMap_[l.name],x.lineWidth=h.hasOwnProperty("tickWidth")?h.tickWidth:e.getOption("strokeWidth"),x.beginPath(),h.attachAtBottom){var y=_+g;x.moveTo(l.canvasx,y),x.lineTo(l.canvasx,y+u)}else x.moveTo(l.canvasx,l.canvasy),x.lineTo(l.canvasx,l.canvasy-2-u);x.closePath(),x.stroke(),x.restore()}}},i.prototype.destroy=function(){this.detachLabels()},a.default=i,e.exports=a.default},{}],21:[function(t,e,a){"use strict";Object.defineProperty(a,"__esModule",{value:!0});var i=t("../dygraph-utils"),n=function(t){if(t&&t.__esModule)return t;var e={};if(null!=t)for(var a in t)Object.prototype.hasOwnProperty.call(t,a)&&(e[a]=t[a]);return e.default=t,e}(i),r=function(){this.xlabels_=[],this.ylabels_=[]};r.prototype.toString=function(){return"Axes Plugin"},r.prototype.activate=function(t){return{layout:this.layout,clearChart:this.clearChart,willDrawChart:this.willDrawChart}},r.prototype.layout=function(t){var e=t.dygraph;if(e.getOptionForAxis("drawAxis","y")){var a=e.getOptionForAxis("axisLabelWidth","y")+2*e.getOptionForAxis("axisTickSize","y");t.reserveSpaceLeft(a)}if(e.getOptionForAxis("drawAxis","x")){var i;i=e.getOption("xAxisHeight")?e.getOption("xAxisHeight"):e.getOptionForAxis("axisLabelFontSize","x")+2*e.getOptionForAxis("axisTickSize","x"),t.reserveSpaceBottom(i)}if(2==e.numAxes()){if(e.getOptionForAxis("drawAxis","y2")){var a=e.getOptionForAxis("axisLabelWidth","y2")+2*e.getOptionForAxis("axisTickSize","y2");t.reserveSpaceRight(a)}}else e.numAxes()>2&&e.error("Only two y-axes are supported at this time. (Trying to use "+e.numAxes()+")")},r.prototype.detachLabels=function(){function t(t){for(var e=0;e0){var x=r.numAxes(),m=[y("y"),y("y2")];v.yticks.forEach(function(t){if(void 0!==t.label){s=_.x;var e="y1",a=m[0];1==t.axis&&(s=_.x+_.w,-1,e="y2",a=m[1]);var n=a("axisLabelFontSize");l=_.y+t.pos*_.h,o=f(t.label,"y",2==x?e:null);var r=l-n/2;r<0&&(r=0),r+n+3>c?o.style.bottom="0":o.style.top=r+"px",0===t.axis?(o.style.left=_.x-a("axisLabelWidth")-a("axisTickSize")+"px",o.style.textAlign="right"):1==t.axis&&(o.style.left=_.x+_.w+a("axisTickSize")+"px",o.style.textAlign="left"),o.style.width=a("axisLabelWidth")+"px",u.appendChild(o),i.ylabels_.push(o)}});var b=this.ylabels_[0],w=r.getOptionForAxis("axisLabelFontSize","y");parseInt(b.style.top,10)+w>c-w&&(b.style.top=parseInt(b.style.top,10)-w/2+"px")}var A;if(r.getOption("drawAxesAtZero")){var O=r.toPercentXCoord(0);(O>1||O<0||isNaN(O))&&(O=0),A=e(_.x+O*_.w)}else A=e(_.x);h.strokeStyle=r.getOptionForAxis("axisLineColor","y"),h.lineWidth=r.getOptionForAxis("axisLineWidth","y"),h.beginPath(),h.moveTo(A,a(_.y)),h.lineTo(A,a(_.y+_.h)),h.closePath(),h.stroke(),2==r.numAxes()&&(h.strokeStyle=r.getOptionForAxis("axisLineColor","y2"),h.lineWidth=r.getOptionForAxis("axisLineWidth","y2"),h.beginPath(),h.moveTo(a(_.x+_.w),a(_.y)),h.lineTo(a(_.x+_.w),a(_.y+_.h)),h.closePath(),h.stroke())}if(r.getOptionForAxis("drawAxis","x")){if(v.xticks){var D=y("x");v.xticks.forEach(function(t){if(void 0!==t.label){s=_.x+t.pos*_.w,l=_.y+_.h,o=f(t.label,"x"),o.style.textAlign="center",o.style.top=l+D("axisTickSize")+"px";var e=s-D("axisLabelWidth")/2;e+D("axisLabelWidth")>d&&(e=d-D("axisLabelWidth"),o.style.textAlign="right"),e<0&&(e=0,o.style.textAlign="left"),o.style.left=e+"px",o.style.width=D("axisLabelWidth")+"px",u.appendChild(o),i.xlabels_.push(o)}})}h.strokeStyle=r.getOptionForAxis("axisLineColor","x"),h.lineWidth=r.getOptionForAxis("axisLineWidth","x"),h.beginPath();var E;if(r.getOption("drawAxesAtZero")){var O=r.toPercentYCoord(0,0);(O>1||O<0)&&(O=1),E=a(_.y+O*_.h)}else E=a(_.y+_.h);h.moveTo(e(_.x),E),h.lineTo(e(_.x+_.w),E),h.closePath(),h.stroke()}h.restore()}},a.default=r,e.exports=a.default},{"../dygraph-utils":17}],22:[function(t,e,a){"use strict";Object.defineProperty(a,"__esModule",{value:!0});var i=function(){this.title_div_=null,this.xlabel_div_=null,this.ylabel_div_=null,this.y2label_div_=null};i.prototype.toString=function(){return"ChartLabels Plugin"},i.prototype.activate=function(t){return{layout:this.layout,didDrawChart:this.didDrawChart}};var n=function(t){var e=document.createElement("div");return e.style.position="absolute",e.style.left=t.x+"px",e.style.top=t.y+"px",e.style.width=t.w+"px",e.style.height=t.h+"px",e};i.prototype.detachLabels_=function(){for(var t=[this.title_div_,this.xlabel_div_,this.ylabel_div_,this.y2label_div_],e=0;e=2);o=h.yticks,l.save(),o.forEach(function(t){if(t.has_tick){var r=t.axis;g[r]&&(l.save(),f[r]&&l.setLineDash&&l.setLineDash(v[r]),l.strokeStyle=c[r],l.lineWidth=p[r],i=e(u.x),n=a(u.y+t.pos*u.h),l.beginPath(),l.moveTo(i,n),l.lineTo(i+u.w,n),l.stroke(),l.restore())}}),l.restore()}if(s.getOptionForAxis("drawGrid","x")){o=h.xticks,l.save();var v=s.getOptionForAxis("gridLinePattern","x"),f=v&&v.length>=2;f&&l.setLineDash&&l.setLineDash(v),l.strokeStyle=s.getOptionForAxis("gridLineColor","x"),l.lineWidth=s.getOptionForAxis("gridLineWidth","x"),o.forEach(function(t){t.has_tick&&(i=e(u.x+t.pos*u.w),n=a(u.y+u.h),l.beginPath(),l.moveTo(i,n),l.lineTo(i,u.y),l.closePath(),l.stroke())}),f&&l.setLineDash&&l.setLineDash([]),l.restore()}},i.prototype.destroy=function(){},a.default=i,e.exports=a.default},{}],24:[function(t,e,a){"use strict";function i(t,e,a){if(!t||t.length<=1)return'
';var i,n,r,o,s,l=0,h=0,u=[];for(i=0;i<=t.length;i++)l+=t[i%t.length];if((s=Math.floor(a/(l-t[0])))>1){for(i=0;i';return d}Object.defineProperty(a,"__esModule",{value:!0});var n=t("../dygraph-utils"),r=function(t){if(t&&t.__esModule)return t;var e={};if(null!=t)for(var a in t)Object.prototype.hasOwnProperty.call(t,a)&&(e[a]=t[a]);return e.default=t,e}(n),o=function(){this.legend_div_=null,this.is_generated_div_=!1};o.prototype.toString=function(){return"Legend Plugin"},o.prototype.activate=function(t){var e,a=t.getOption("labelsDiv");return a&&null!==a?e="string"==typeof a||a instanceof String?document.getElementById(a):a:(e=document.createElement("div"),e.className="dygraph-legend",t.graphDiv.appendChild(e),this.is_generated_div_=!0),this.legend_div_=e,this.one_em_width_=10,{select:this.select,deselect:this.deselect,predraw:this.predraw,didDrawChart:this.didDrawChart}};var s=function(t){var e=document.createElement("span");e.setAttribute("style","margin: 0; padding: 0 0 0 1em; border: 0;"),t.appendChild(e);var a=e.offsetWidth;return t.removeChild(e),a},l=function(t){return t.replace(/&/g,"&").replace(/"/g,""").replace(//g,">")};o.prototype.select=function(t){var e=t.selectedX,a=t.selectedPoints,i=t.selectedRow,n=t.dygraph.getOption("legend");if("never"===n)return void(this.legend_div_.style.display="none");if("follow"===n){var r=t.dygraph.plotter_.area,s=this.legend_div_.offsetWidth,l=t.dygraph.getOptionForAxis("axisLabelWidth","y"),h=a[0].x*r.w+50,u=a[0].y*r.h-50;h+s+1>r.w&&(h=h-100-s-(l-r.x)),t.dygraph.graphDiv.appendChild(this.legend_div_),this.legend_div_.style.left=l+h+"px",this.legend_div_.style.top=u+"px"}var d=o.generateLegendHTML(t.dygraph,e,a,this.one_em_width_,i);this.legend_div_.innerHTML=d,this.legend_div_.style.display=""},o.prototype.deselect=function(t){"always"!==t.dygraph.getOption("legend")&&(this.legend_div_.style.display="none");var e=s(this.legend_div_);this.one_em_width_=e;var a=o.generateLegendHTML(t.dygraph,void 0,void 0,e,null);this.legend_div_.innerHTML=a},o.prototype.didDrawChart=function(t){this.deselect(t)},o.prototype.predraw=function(t){if(this.is_generated_div_){t.dygraph.graphDiv.appendChild(this.legend_div_);var e=t.dygraph.getArea(),a=this.legend_div_.offsetWidth;this.legend_div_.style.left=e.x+e.w-a-1+"px",this.legend_div_.style.top=e.y+"px"}},o.prototype.destroy=function(){this.legend_div_=null},o.generateLegendHTML=function(t,e,a,n,s){var h={dygraph:t,x:e,series:[]},u={},d=t.getLabels();if(d)for(var c=1;c":" "),a+=""+r.dashHTML+" "+r.labelHTML+"")}return a}a=t.xHTML+":";for(var n=0;n");a+=" "+r.labelHTML+": "+r.yHTML+""}}return a},a.default=o,e.exports=a.default},{"../dygraph-utils":17}],25:[function(t,e,a){"use strict";function i(t){return t&&t.__esModule?t:{default:t}}Object.defineProperty(a,"__esModule",{value:!0});var n=t("../dygraph-utils"),r=function(t){if(t&&t.__esModule)return t;var e={};if(null!=t)for(var a in t)Object.prototype.hasOwnProperty.call(t,a)&&(e[a]=t[a]);return e.default=t,e}(n),o=t("../dygraph-interaction-model"),s=i(o),l=t("../iframe-tarp"),h=i(l),u=function(){this.hasTouchInterface_="undefined"!=typeof TouchEvent,this.isMobileDevice_=/mobile|android/gi.test(navigator.appVersion),this.interfaceCreated_=!1};u.prototype.toString=function(){return"RangeSelector Plugin"},u.prototype.activate=function(t){return this.dygraph_=t,this.getOption_("showRangeSelector")&&this.createInterface_(),{layout:this.reserveSpace_,predraw:this.renderStaticLayer_,didDrawChart:this.renderInteractiveLayer_}},u.prototype.destroy=function(){this.bgcanvas_=null,this.fgcanvas_=null,this.leftZoomHandle_=null,this.rightZoomHandle_=null},u.prototype.getOption_=function(t,e){return this.dygraph_.getOption(t,e)},u.prototype.setDefaultOption_=function(t,e){this.dygraph_.attrs_[t]=e},u.prototype.createInterface_=function(){this.createCanvases_(),this.createZoomHandles_(),this.initInteraction_(),this.getOption_("animatedZooms")&&(console.warn("Animated zooms and range selector are not compatible; disabling animatedZooms."),this.dygraph_.updateOptions({animatedZooms:!1},!0)),this.interfaceCreated_=!0,this.addToGraph_()},u.prototype.addToGraph_=function(){var t=this.graphDiv_=this.dygraph_.graphDiv;t.appendChild(this.bgcanvas_),t.appendChild(this.fgcanvas_),t.appendChild(this.leftZoomHandle_),t.appendChild(this.rightZoomHandle_)},u.prototype.removeFromGraph_=function(){var t=this.graphDiv_;t.removeChild(this.bgcanvas_),t.removeChild(this.fgcanvas_),t.removeChild(this.leftZoomHandle_),t.removeChild(this.rightZoomHandle_),this.graphDiv_=null},u.prototype.reserveSpace_=function(t){this.getOption_("showRangeSelector")&&t.reserveSpaceBottom(this.getOption_("rangeSelectorHeight")+4)},u.prototype.renderStaticLayer_=function(){this.updateVisibility_()&&(this.resize_(),this.drawStaticLayer_())},u.prototype.renderInteractiveLayer_=function(){this.updateVisibility_()&&!this.isChangingRange_&&(this.placeZoomHandles_(),this.drawInteractiveLayer_())},u.prototype.updateVisibility_=function(){var t=this.getOption_("showRangeSelector");if(t)this.interfaceCreated_?this.graphDiv_&&this.graphDiv_.parentNode||this.addToGraph_():this.createInterface_();else if(this.graphDiv_){this.removeFromGraph_();var e=this.dygraph_;setTimeout(function(){e.width_=0,e.resize()},1)}return t},u.prototype.resize_=function(){function t(t,e,a,i){var n=i||r.getContextPixelRatio(e);t.style.top=a.y+"px",t.style.left=a.x+"px",t.width=a.w*n,t.height=a.h*n,t.style.width=a.w+"px",t.style.height=a.h+"px",1!=n&&e.scale(n,n)}var e=this.dygraph_.layout_.getPlotArea(),a=0;this.dygraph_.getOptionForAxis("drawAxis","x")&&(a=this.getOption_("xAxisHeight")||this.getOption_("axisLabelFontSize")+2*this.getOption_("axisTickSize")),this.canvasRect_={x:e.x,y:e.y+e.h+a+4,w:e.w,h:this.getOption_("rangeSelectorHeight")};var i=this.dygraph_.getNumericOption("pixelRatio");t(this.bgcanvas_,this.bgcanvas_ctx_,this.canvasRect_,i),t(this.fgcanvas_,this.fgcanvas_ctx_,this.canvasRect_,i)},u.prototype.createCanvases_=function(){this.bgcanvas_=r.createCanvas(),this.bgcanvas_.className="dygraph-rangesel-bgcanvas",this.bgcanvas_.style.position="absolute",this.bgcanvas_.style.zIndex=9,this.bgcanvas_ctx_=r.getContext(this.bgcanvas_),this.fgcanvas_=r.createCanvas(),this.fgcanvas_.className="dygraph-rangesel-fgcanvas",this.fgcanvas_.style.position="absolute",this.fgcanvas_.style.zIndex=9,this.fgcanvas_.style.cursor="default",this.fgcanvas_ctx_=r.getContext(this.fgcanvas_)},u.prototype.createZoomHandles_=function(){var t=new Image;t.className="dygraph-rangesel-zoomhandle",t.style.position="absolute",t.style.zIndex=10,t.style.visibility="hidden",t.style.cursor="col-resize",t.width=9,t.height=16,t.src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAQCAYAAADESFVDAAAAAXNSR0IArs4c6QAAAAZiS0dEANAAzwDP4Z7KegAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAAd0SU1FB9sHGw0cMqdt1UwAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAaElEQVQoz+3SsRFAQBCF4Z9WJM8KCDVwownl6YXsTmCUsyKGkZzcl7zkz3YLkypgAnreFmDEpHkIwVOMfpdi9CEEN2nGpFdwD03yEqDtOgCaun7sqSTDH32I1pQA2Pb9sZecAxc5r3IAb21d6878xsAAAAAASUVORK5CYII=",this.isMobileDevice_&&(t.width*=2,t.height*=2),this.leftZoomHandle_=t,this.rightZoomHandle_=t.cloneNode(!1)},u.prototype.initInteraction_=function(){var t,e,a,i,n,o,l,u,d,c,p,g,f,v,_=this,y=document,x=0,m=null,b=!1,w=!1,A=!this.isMobileDevice_,O=new h.default;t=function(t){var e=_.dygraph_.xAxisExtremes(),a=(e[1]-e[0])/_.canvasRect_.w;return[e[0]+(t.leftHandlePos-_.canvasRect_.x)*a,e[0]+(t.rightHandlePos-_.canvasRect_.x)*a]},e=function(t){return r.cancelEvent(t),b=!0,x=t.clientX,m=t.target?t.target:t.srcElement,"mousedown"!==t.type&&"dragstart"!==t.type||(r.addEvent(y,"mousemove",a),r.addEvent(y,"mouseup",i)),_.fgcanvas_.style.cursor="col-resize",O.cover(),!0},a=function(t){if(!b)return!1;r.cancelEvent(t);var e=t.clientX-x;if(Math.abs(e)<4)return!0;x=t.clientX;var a,i=_.getZoomHandleStatus_();m==_.leftZoomHandle_?(a=i.leftHandlePos+e,a=Math.min(a,i.rightHandlePos-m.width-3),a=Math.max(a,_.canvasRect_.x)):(a=i.rightHandlePos+e,a=Math.min(a,_.canvasRect_.x+_.canvasRect_.w),a=Math.max(a,i.leftHandlePos+m.width+3));var o=m.width/2;return m.style.left=a-o+"px",_.drawInteractiveLayer_(),A&&n(),!0},i=function(t){return!!b&&(b=!1,O.uncover(),r.removeEvent(y,"mousemove",a),r.removeEvent(y,"mouseup",i),_.fgcanvas_.style.cursor="default",A||n(),!0)},n=function(){try{var e=_.getZoomHandleStatus_();if(_.isChangingRange_=!0,e.isZoomed){var a=t(e);_.dygraph_.doZoomXDates_(a[0],a[1])}else _.dygraph_.resetZoom()}finally{_.isChangingRange_=!1}},o=function(t){var e=_.leftZoomHandle_.getBoundingClientRect(),a=e.left+e.width/2;e=_.rightZoomHandle_.getBoundingClientRect();var i=e.left+e.width/2;return t.clientX>a&&t.clientX=_.canvasRect_.x+_.canvasRect_.w?(n=_.canvasRect_.x+_.canvasRect_.w,i=n-o):(i+=e,n+=e);var s=_.leftZoomHandle_.width/2;return _.leftZoomHandle_.style.left=i-s+"px",_.rightZoomHandle_.style.left=n-s+"px",_.drawInteractiveLayer_(),A&&c(),!0},d=function(t){return!!w&&(w=!1,r.removeEvent(y,"mousemove",u),r.removeEvent(y,"mouseup",d),A||c(),!0)},c=function(){try{_.isChangingRange_=!0,_.dygraph_.dateWindow_=t(_.getZoomHandleStatus_()),_.dygraph_.drawGraph_(!1)}finally{_.isChangingRange_=!1}},p=function(t){if(!b&&!w){var e=o(t)?"move":"default";e!=_.fgcanvas_.style.cursor&&(_.fgcanvas_.style.cursor=e)}},g=function(t){"touchstart"==t.type&&1==t.targetTouches.length?e(t.targetTouches[0])&&r.cancelEvent(t):"touchmove"==t.type&&1==t.targetTouches.length?a(t.targetTouches[0])&&r.cancelEvent(t):i(t)},f=function(t){"touchstart"==t.type&&1==t.targetTouches.length?l(t.targetTouches[0])&&r.cancelEvent(t):"touchmove"==t.type&&1==t.targetTouches.length?u(t.targetTouches[0])&&r.cancelEvent(t):d(t)},v=function(t,e){for(var a=["touchstart","touchend","touchmove","touchcancel"],i=0;i1&&(g=c.rollingAverage(g,e.rollPeriod(),p)),d.push(g)}var f=[];for(t=0;t0)&&(m=Math.min(m,w),b=Math.max(b,w))}if(a)for(b=r.log10(b),b+=.25*b,m=r.log10(m),t=0;tthis.canvasRect_.x||a+1=t}function x(t){var e=typeof t;return null!=t&&("object"==e||"function"==e); -}function F(t){return null!=t&&typeof t=="object"}function S(t){return!(!F(t)||"[object Object]"!=f(t))&&(t=tt(t),null===t||(t=H.call(t,"constructor")&&t.constructor,typeof t=="function"&&t instanceof t&&W.call(t)==Q))}function k(t){var e=P(t),n=void 0,r=!n;n||(n={});for(var o=-1,i=e.length;++ot)&&(t==e.length-1?e.pop():rt.call(e,t,1),--this.size,true)},o.prototype.get=function(t){var e=this.__data__;return t=a(e,t),0>t?L:e[t][1]},o.prototype.has=function(t){ -return-1r?(++this.size,n.push([t,e])):n[r][1]=e,this},i.prototype.clear=function(){this.size=0,this.__data__={hash:new r,map:new(at||o),string:new r}},i.prototype.delete=function(t){return t=y(this,t).delete(t),this.size-=t?1:0,t},i.prototype.get=function(t){return y(this,t).get(t)},i.prototype.has=function(t){return y(this,t).has(t)},i.prototype.set=function(t,e){var n=y(this,t),r=n.size;return n.set(t,e),this.size+=n.size==r?0:1, -this},c.prototype.clear=function(){this.__data__=new o,this.size=0},c.prototype.delete=function(t){var e=this.__data__;return t=e.delete(t),this.size=e.size,t},c.prototype.get=function(t){return this.__data__.get(t)},c.prototype.has=function(t){return this.__data__.has(t)},c.prototype.set=function(t,e){var n=this.__data__;if(n instanceof o){var r=n.__data__;if(!at||199>r.length)return r.push([t,e]),this.size=++n.size,this;n=this.__data__=new i(r)}return n.set(t,e),this.size=n.size,this};var lt=function(t){ -return function(e,n,r){var o=-1,i=Object(e);r=r(e);for(var c=r.length;c--;){var u=r[t?c:++o];if(false===n(i[u],u,i))break}return e}}(),_t=function(t){var e=0,n=0;return function(){var r=ut(),o=16-(r-n);if(n=r,0i?L:c,i=1),e=Object(e);++o t = - fun (x2 : Ojs.t) -> - { x = Ojs.int_of_js (Ojs.get_prop_ascii x2 "x") - ; y = Ojs.int_of_js (Ojs.get_prop_ascii x2 "y") - ; w = Ojs.int_of_js (Ojs.get_prop_ascii x2 "w") - ; h = Ojs.int_of_js (Ojs.get_prop_ascii x2 "h") - } - -and t_to_js : t -> Ojs.t = - fun (x1 : t) -> - Ojs.obj - [| "x", Ojs.int_to_js x1.x - ; "y", Ojs.int_to_js x1.y - ; "w", Ojs.int_to_js x1.w - ; "h", Ojs.int_to_js x1.h - |] -;; diff --git a/bindings/dygraph/src/area.mli b/bindings/dygraph/src/area.mli deleted file mode 100644 index ded1809f..00000000 --- a/bindings/dygraph/src/area.mli +++ /dev/null @@ -1,16 +0,0 @@ -open! Core -open! Import -open Gen_js_api - -(** An object with {x,y,w,h} properties describing the drawing area, for use in - [underlayCallback]. All units are in pixels (I think). *) - -type t = - { x : int - ; y : int - ; w : int - ; h : int - } - -val t_to_js : t -> Ojs.t -val t_of_js : Ojs.t -> t diff --git a/bindings/dygraph/src/canvas_rendering_context_2D.ml b/bindings/dygraph/src/canvas_rendering_context_2D.ml deleted file mode 100644 index ba59005c..00000000 --- a/bindings/dygraph/src/canvas_rendering_context_2D.ml +++ /dev/null @@ -1,6 +0,0 @@ -open! Core -open Import - -include Js_obj.Make (struct - type t = Dom_html.canvasRenderingContext2D -end) diff --git a/bindings/dygraph/src/canvas_rendering_context_2D.mli b/bindings/dygraph/src/canvas_rendering_context_2D.mli deleted file mode 100644 index 96797820..00000000 --- a/bindings/dygraph/src/canvas_rendering_context_2D.mli +++ /dev/null @@ -1,3 +0,0 @@ -open! Core -open Import -include Js_obj.S with type t = Dom_html.canvasRenderingContext2D Js.t diff --git a/bindings/dygraph/src/color.ml b/bindings/dygraph/src/color.ml deleted file mode 100644 index f03a7985..00000000 --- a/bindings/dygraph/src/color.ml +++ /dev/null @@ -1,7 +0,0 @@ -open! Core -open! Import -open Gen_js_api - -type t = Css_gen.Color.t [@@deriving sexp] - -let t_to_js t = Ojs.string_to_js (Css_gen.Color.to_string_css t) diff --git a/bindings/dygraph/src/color.mli b/bindings/dygraph/src/color.mli deleted file mode 100644 index 23b77deb..00000000 --- a/bindings/dygraph/src/color.mli +++ /dev/null @@ -1,7 +0,0 @@ -open! Core -open! Import -open Gen_js_api - -type t = Css_gen.Color.t [@@deriving sexp] - -val t_to_js : t -> Ojs.t diff --git a/bindings/dygraph/src/css.ml b/bindings/dygraph/src/css.ml deleted file mode 100644 index 92bb5543..00000000 --- a/bindings/dygraph/src/css.ml +++ /dev/null @@ -1,122 +0,0 @@ -let install_css () = - Inline_css.Private.append - {| -/** - * Default styles for the dygraphs charting library. - */ - -.dygraph-legend { - position: absolute; - font-size: 14px; - z-index: 10; - width: 250px; /* labelsDivWidth */ - /* - dygraphs determines these based on the presence of chart labels. - It might make more sense to create a wrapper div around the chart proper. - top: 0px; - right: 2px; - */ - background: white; - line-height: normal; - text-align: left; - overflow: hidden; -} - -/* styles for a solid line in the legend */ -.dygraph-legend-line { - display: inline-block; - position: relative; - bottom: .5ex; - padding-left: 1em; - height: 1px; - border-bottom-width: 2px; - border-bottom-style: solid; - /* border-bottom-color is set based on the series color */ -} - -/* styles for a dashed line in the legend, e.g. when strokePattern is set */ -.dygraph-legend-dash { - display: inline-block; - position: relative; - bottom: .5ex; - height: 1px; - border-bottom-width: 2px; - border-bottom-style: solid; - /* border-bottom-color is set based on the series color */ - /* margin-right is set based on the stroke pattern */ - /* padding-left is set based on the stroke pattern */ -} - -.dygraph-roller { - position: absolute; - z-index: 10; -} - -/* This class is shared by all annotations, including those with icons */ -.dygraph-annotation { - position: absolute; - z-index: 10; - overflow: hidden; -} - -/* This class only applies to annotations without icons */ -/* Old class name: .dygraphDefaultAnnotation */ -.dygraph-default-annotation { - border: 1px solid black; - background-color: white; - text-align: center; -} - -.dygraph-axis-label { - /* position: absolute; */ - /* font-size: 14px; */ - z-index: 10; - line-height: normal; - overflow: hidden; - color: black; /* replaces old axisLabelColor option */ -} - -.dygraph-axis-label-x { -} - -.dygraph-axis-label-y { -} - -.dygraph-axis-label-y2 { -} - -.dygraph-title { - font-weight: bold; - z-index: 10; - text-align: center; - /* font-size: based on titleHeight option */ -} - -.dygraph-xlabel { - text-align: center; - /* font-size: based on xLabelHeight option */ -} - -/* For y-axis label */ -.dygraph-label-rotate-left { - text-align: center; - /* See http://caniuse.com/#feat=transforms2d */ - transform: rotate(90deg); - -webkit-transform: rotate(90deg); - -moz-transform: rotate(90deg); - -o-transform: rotate(90deg); - -ms-transform: rotate(90deg); -} - -/* For y2-axis label */ -.dygraph-label-rotate-right { - text-align: center; - /* See http://caniuse.com/#feat=transforms2d */ - transform: rotate(-90deg); - -webkit-transform: rotate(-90deg); - -moz-transform: rotate(-90deg); - -o-transform: rotate(-90deg); - -ms-transform: rotate(-90deg); -} -|} -;; diff --git a/bindings/dygraph/src/css.mli b/bindings/dygraph/src/css.mli deleted file mode 100644 index a60d415a..00000000 --- a/bindings/dygraph/src/css.mli +++ /dev/null @@ -1 +0,0 @@ -val install_css : unit -> unit diff --git a/bindings/dygraph/src/data.ml b/bindings/dygraph/src/data.ml deleted file mode 100644 index 5c1b54d5..00000000 --- a/bindings/dygraph/src/data.ml +++ /dev/null @@ -1,123 +0,0 @@ -open Base -open Core -open Import -open Gen_js_api - -type t = Ojs.t - -let t_to_js x = x -let create data = Ojs.array_to_js (Ojs.array_to_js Ojs.float_to_js) data - -let create' data ~x_to_js ~y_to_js = - let row_to_js (x, data) = - let values = Ojs.array_make (Array.length data + 1) in - Ojs.array_set values 0 (x_to_js x); - for i = 0 to Array.length data - 1 do - Ojs.array_set values (i + 1) (y_to_js data.(i)) - done; - values - in - Ojs.array_to_js row_to_js data -;; - -let create_option data = - create' data ~x_to_js:Ojs.float_to_js ~y_to_js:(Ojs.option_to_js Ojs.float_to_js) -;; - -let time_ns_to_js time = - let date = - new%js Js.date_fromTimeValue (Time_ns.to_span_since_epoch time |> Time_ns.Span.to_ms) - in - (Stdlib.Obj.magic (date : Js.date Js.t) : Ojs.t) -;; - -let create_date data ~zone = - let date_to_js date = - Time_ns.of_date_ofday ~zone date Time_ns.Ofday.start_of_day |> time_ns_to_js - in - create' data ~x_to_js:date_to_js ~y_to_js:Ojs.float_to_js -;; - -let create_time_ns data = create' data ~x_to_js:time_ns_to_js ~y_to_js:Ojs.float_to_js - -let create_time_ns_option data = - create' data ~x_to_js:time_ns_to_js ~y_to_js:(Ojs.option_to_js Ojs.float_to_js) -;; - -let create_from_independent_series' ~min ~equal series = - if Array.length series = 0 - then [||] - else ( - let current_idxes = Array.map series ~f:(fun _ -> 0) in - let safe_get series ~idx = - if idx < Array.length series then Some series.(idx) else None - in - let next_x () = - Array.fold2_exn ~init:None current_idxes series ~f:(fun earliest_x idx series -> - match safe_get series ~idx with - | None -> earliest_x - | Some (x, _) -> - Some - (match earliest_x with - | None -> x - | Some earliest_x -> min x earliest_x)) - in - (* This loop will make an array (in chronological order) of: - {[ (x-value * (float option array)) ]} - - With points at the union of all series' xs. - *) - let rec loop ~data_acc = - match next_x () with - | None -> Array.of_list_rev data_acc - | Some next_x -> - let next_row = - Array.mapi series ~f:(fun i series -> - let idx = current_idxes.(i) in - match safe_get series ~idx with - | None -> None - | Some (x, value) -> - if equal x next_x - then ( - (* increment index *) - current_idxes.(i) <- idx + 1; - Some value) - else None) - in - loop ~data_acc:((next_x, next_row) :: data_acc) - in - loop ~data_acc:[]) -;; - -let create_from_independent_series series = - let data = create_from_independent_series' ~min:Float.min ~equal:Float.equal series in - create_option data -;; - -let create_from_independent_time_series' series = - create_from_independent_series' ~min:Time_ns.min ~equal:Time_ns.equal series -;; - -let create_from_independent_time_series series = - let data = create_from_independent_time_series' series in - create' data ~x_to_js:time_ns_to_js ~y_to_js:(Ojs.option_to_js Ojs.float_to_js) -;; - -let%expect_test "test [create_from_independent_time_series]" = - let t1 = Time_ns.epoch in - let t2 = Time_ns.add t1 Time_ns.Span.day in - let t3 = Time_ns.add t2 Time_ns.Span.day in - let t4 = Time_ns.add t3 Time_ns.Span.day in - let ts1 = [| t1, 1.; t3, 3. |] in - let ts2 = [| t2, 2.; t3, 3.; t4, 4. |] in - create_from_independent_time_series' [| ts1; ts2 |] - |> Array.iter ~f:(fun row -> - [%sexp_of: Time_ns.Alternate_sexp.t * float option array] row |> print_s); - [%expect - {| - ("1970-01-01 00:00:00Z" ((1) ())) - ("1970-01-02 00:00:00Z" (() (2))) - ("1970-01-03 00:00:00Z" ((3) (3))) - ("1970-01-04 00:00:00Z" (() (4))) - |}] -;; diff --git a/bindings/dygraph/src/data.mli b/bindings/dygraph/src/data.mli deleted file mode 100644 index 1107262d..00000000 --- a/bindings/dygraph/src/data.mli +++ /dev/null @@ -1,44 +0,0 @@ -open! Core -open! Gen_js_api - -(** http://dygraphs.com/data.html - - This API does not yet support errorBars, customBars, or fractions. Feel free to - add. *) -type t - -val t_to_js : t -> Ojs.t - -(** [create] is the right constructor when your x-values are floats. *) -val create : float array array -> t - -(** [create_option] is for when your x-values are floats, and your y-values are optional. *) -val create_option : (float * float option array) array -> t - -(** [create_date] is for when your x-values are dates. - - Remember to include the following script when using the Timezone libaray: - -*) -val create_date : (Date.t * float array) array -> zone:Timezone.t -> t - -(** [create_time_ns] is for when your x-values are times. *) -val create_time_ns : (Time_ns.t * float array) array -> t - -(** [create_time_ns_option] is for when your x-values are times and your y-values are options. *) -val create_time_ns_option : (Time_ns.t * float option array) array -> t - -(** [create_from_independent_series] and [create_from_independent_time_series] are helper - functions for when you are creating a single graph from multiple series which do not - necessarily have points at the same x-values. This function will produce points at the - union of all input series' x-values. - - Note: This function will *not* interpolate or fill-forward, but rather fill in missing - values with None (null). See http://dygraphs.com/tests/independent-series.html - - For a similar function that does fill forward, see - [Ts_server_protocol_kernel.Time_series_data.transpose]. -*) - -val create_from_independent_series : (float * float) array array -> t -val create_from_independent_time_series : (Time_ns.t * float) array array -> t diff --git a/bindings/dygraph/src/default_legend.ml b/bindings/dygraph/src/default_legend.ml deleted file mode 100644 index b4dbfef3..00000000 --- a/bindings/dygraph/src/default_legend.ml +++ /dev/null @@ -1,294 +0,0 @@ -open Core -open Import - -module Model = struct - module Series = struct - type t = - { label : string - ; override_label_for_visibility : string option - ; value : Raw_html.t option - ; dash : Raw_html.t option - ; color : string option - ; is_visible : bool - ; is_highlighted : bool - } - [@@deriving equal, fields ~getters, sexp] - - let toggle_visibility t = { t with is_visible = not t.is_visible } - - let label_for_visibility t = - Option.value t.override_label_for_visibility ~default:t.label - ;; - - let view - { label - ; override_label_for_visibility = _ - ; value - ; dash - ; color - ; is_visible - ; is_highlighted - } - ~on_toggle - = - let dash = - match dash with - | None -> Vdom.Node.none - | Some html -> Raw_html.view ~tag:"span" html - in - let value = - match value with - | None -> Vdom.Node.none - | Some html -> Raw_html.view ~tag:"span" html - in - let create_style l = List.filter_opt l |> Css_gen.concat |> Vdom.Attr.style in - let label_style = - let margin_left = Css_gen.margin_left (`Px 5) in - let color = - Option.map color ~f:(fun value -> Css_gen.create ~field:"color" ~value) - in - create_style [ color; Some margin_left ] - in - let style = - create_style - [ Option.some_if is_highlighted (Css_gen.font_weight `Bold) - ; Option.some_if - is_highlighted - (Css_gen.text_decoration () ~line:[ `Underline ]) - ] - in - Vdom.Node.label - ~attrs:[ style ] - [ Vdom.Node.input - ~attrs: - [ Vdom.Attr.many_without_merge - [ Vdom.Attr.type_ "checkbox" - ; Vdom.Attr.on_click (fun _ev -> on_toggle ()) - ; Vdom.Attr.bool_property "checked" is_visible - ] - ] - () - ; dash - ; Vdom.Node.span ~attrs:[ label_style ] [ Vdom.Node.textf "%s: " label ] - ; value - ] - ;; - end - - type t = - { x_label : string - ; x_value : Raw_html.t option - ; series : Series.t list - ; past_series_visibility : bool Map.M(String).t - } - [@@deriving equal, sexp] - - let view - { x_label; x_value; series; past_series_visibility = _ } - ~on_toggle - ~select_all - ~select_none - = - let x = - let value = - match x_value with - | None -> Vdom.Node.none - | Some html -> Raw_html.view ~tag:"span" html - in - Vdom.Node.label [ Vdom.Node.textf "%s: " x_label; value ] - in - (* mostly copied from bonsai_multi_select *) - let select_all_or_none = - let open Vdom in - let link ~text ~action ~class_ = - Node.a - ~attrs: - [ Vdom.Attr.many_without_merge - [ Attr.href "about:blank" - ; Attr.on_click (fun _ev -> - Effect.Many [ action (); Effect.Prevent_default ]) - ; Attr.class_ class_ - ] - ] - [ Node.text text ] - in - Node.div - ~attrs:[ Attr.class_ "multi-select-select-all-none" ] - [ Node.text "Select: " - ; link ~text:"all" ~action:select_all ~class_:"multi-select-select-all" - ; Node.text "; " - ; link ~text:"none" ~action:select_none ~class_:"multi-select-select-none" - ] - in - let list_elements = - select_all_or_none - :: x - :: List.map series ~f:(fun series -> - Series.view series ~on_toggle:(fun () -> - on_toggle (Series.label_for_visibility series))) - in - (* Mostly copied from vdom_input_widgets *) - Vdom.Node.div - ~attrs: - [ Vdom.Attr.many_without_merge - [ Vdom.Attr.classes [ "widget-checklist"; "checkbox-container" ] - ; Vdom.Attr.style - Css_gen.(create ~field:"list-style" ~value:"none" @> margin_left (`Px 0)) - ] - ] - (List.map list_elements ~f:(fun li -> Vdom.Node.div [ li ])) - ;; -end - -module Action = struct - type t = - | From_graph of Legend_data.t - | Toggle_visibility of { label_for_visibility : string } - | Select_none - | Select_all - [@@deriving equal, sexp] -end - -let apply_action - (_ : _ Bonsai.Apply_action_context.t) - (model : Model.t) - (action : Action.t) - = - let map_series ~f = { model with series = List.map model.series ~f } in - match action with - | From_graph legend_data -> - let series = - List.map model.series ~f:(fun series -> - match - List.find legend_data.series ~f:(fun s -> String.equal series.label s.label) - with - | None -> series - | Some legend_data -> - let { Legend_data.Series.dashHTML - ; isHighlighted - ; color - ; yHTML - ; label = _ - ; labelHTML = _ - ; isVisible = _ - ; y = _ - } - = - legend_data - in - let color = - (* keep last color if [color] is none *) - Option.first_some color series.color - in - { series with - dash = Some dashHTML - ; color - ; value = yHTML - ; is_highlighted = Option.value ~default:false isHighlighted - }) - in - let x_value = - Option.map legend_data.xHTML ~f:(function - | `number f -> Float.to_string f |> Raw_html.of_string - | `html raw_html -> raw_html) - in - { model with x_value; series } - | Select_none -> map_series ~f:(fun series -> { series with is_visible = false }) - | Select_all -> map_series ~f:(fun series -> { series with is_visible = true }) - | Toggle_visibility { label_for_visibility } -> - map_series ~f:(fun series -> - if String.(Model.Series.label_for_visibility series = label_for_visibility) - then Model.Series.toggle_visibility series - else series) -;; - -let series_from_info - { Per_series_info.label; override_label_for_visibility; visible_by_default } - = - { Model.Series.label - ; override_label_for_visibility - ; is_visible = visible_by_default - ; is_highlighted = false - ; value = None - ; dash = None - ; color = None - } -;; - -let create ~x_label ~per_series_info - : (Model.t * Vdom.Node.t * (Action.t -> unit Vdom.Effect.t)) Bonsai.Computation.t - = - let create_model = - let%map.Bonsai x_label = x_label - and per_series_info = per_series_info in - function - | None -> - { Model.x_label - ; x_value = None - ; series = List.map per_series_info ~f:series_from_info - ; past_series_visibility = String.Map.empty - } - | Some (model : Model.t) -> - let existing_y_labels = List.map model.series ~f:Model.Series.label in - let model_y_labels = List.map per_series_info ~f:Per_series_info.label in - if [%equal: string] model.x_label x_label - && [%equal: string list] model_y_labels existing_y_labels - then { model with x_label } - else ( - (* Every time the [model_y_labels] changes, we want to remember the visibility - status of all the series labels we know about so far. This will help in the - case where we toggle visibility on series A, flip to a graph which does not - have that series, and then flip back to the original graph. Without - remembering, the visibility status of series A revert back to the default - status. *) - let past_series_visibility = - List.fold - ~init:model.past_series_visibility - model.series - ~f:(fun past_series_visibility series -> - Map.set - past_series_visibility - ~key:(Model.Series.label_for_visibility series) - ~data:series.is_visible) - in - let series = - List.map per_series_info ~f:(fun per_series_info -> - let { Per_series_info.label - ; override_label_for_visibility - ; visible_by_default = _ - } - = - per_series_info - in - let series = series_from_info per_series_info in - let label_for_visibility = - Option.value override_label_for_visibility ~default:label - in - match Map.find past_series_visibility label_for_visibility with - | None -> series - | Some is_visible -> { series with is_visible }) - in - { model with x_label; series; past_series_visibility }) - in - let%sub state = - Bonsai_extra.state_machine0_dynamic_model - ~sexp_of_action:[%sexp_of: Action.t] - ~sexp_of_model:[%sexp_of: Model.t] - ~equal:[%equal: Model.t] - ~model:(`Computed create_model) - ~apply_action - () - in - return - @@ - let%map model, inject_action = state in - let view = - Model.view - model - ~on_toggle:(fun label_for_visibility -> - inject_action (Toggle_visibility { label_for_visibility })) - ~select_all:(fun () -> inject_action Select_all) - ~select_none:(fun () -> inject_action Select_none) - in - model, view, inject_action -;; diff --git a/bindings/dygraph/src/default_legend.mli b/bindings/dygraph/src/default_legend.mli deleted file mode 100644 index 6f1da8c6..00000000 --- a/bindings/dygraph/src/default_legend.mli +++ /dev/null @@ -1,57 +0,0 @@ -open! Core -open! Import - -(** This is the default legend that [With_bonsai.create] will use if you don't pass - [custom_legend]. - - This is nicer than the default dygraph legend in that it's in a fixed location (to the - right of the graph), allows toggling visibility, and underlines/bolds the highlighted - series. The look of this legend was largely copied from dwu's htmlplot dygraph - legend. *) - -module Model : sig - module Series : sig - type t = - { label : string - ; override_label_for_visibility : string option - ; value : Raw_html.t option - ; dash : Raw_html.t option - ; color : string option - ; is_visible : bool - ; is_highlighted : bool - } - [@@deriving equal, fields ~getters, sexp] - - val label_for_visibility : t -> string - end - - type t = - { x_label : string - ; x_value : Raw_html.t option - ; series : Series.t list - ; past_series_visibility : bool Map.M(String).t - (** [past_series_visibility] remembers all the series (by [label_for_visibility]) that - we've ever seen. This means that if someone makes a change to a particular series - (e.g. toggles visibility), moves to a graph without that series, and then moves - back to the original graph, the information will not be lost. - - This may sound like a memory leak, and it kind of is, but the hope is that the - total number of unique series labels that one sees over the lifetime of a graph is - very small. *) - } - [@@deriving equal, sexp] -end - -module Action : sig - type t = - | From_graph of Legend_data.t - | Toggle_visibility of { label_for_visibility : string } - | Select_none - | Select_all - [@@deriving equal, sexp] -end - -val create - : x_label:string Bonsai.Value.t - -> per_series_info:Per_series_info.t list Bonsai.Value.t - -> (Model.t * Vdom.Node.t * (Action.t -> unit Ui_effect.t)) Bonsai.Computation.t diff --git a/bindings/dygraph/src/dune b/bindings/dygraph/src/dune deleted file mode 100644 index dd0dc669..00000000 --- a/bindings/dygraph/src/dune +++ /dev/null @@ -1,8 +0,0 @@ -(library - (name dygraph) - (preprocess - (pps ppx_jane js_of_ocaml-ppx gen_js_api.ppx ppx_bonsai ppx_pattern_bind)) - (js_of_ocaml - (javascript_files ../dist/dygraph.min.js ../dist/lodash_merge.js)) - (libraries base bonsai_extra bonsai_web bonsai_web_ui_widget core - virtual_dom.css_gen gen_js_api js_of_ocaml timezone ppx_css.inline_css)) diff --git a/bindings/dygraph/src/dygraph.ml b/bindings/dygraph/src/dygraph.ml deleted file mode 100644 index f04c1a82..00000000 --- a/bindings/dygraph/src/dygraph.ml +++ /dev/null @@ -1,19 +0,0 @@ -(* With names like "Raw" and "Options", open Dygraph at your own peril. Fully qualified - names are likely the way to go *) -module Area = Area -module Data = Data -module Default_legend = Default_legend -module Granularity = Granularity -module Graph = Graph -module Html_or_number = Html_or_number -module Legend_data = Legend_data -module Options = Options -module Per_series_info = Per_series_info -module Plotter = Plotter -module Point = Point -module Range = Range -module Raw_html = Raw_html -module Update_options = Update_options -module X_axis_mapping = X_axis_mapping -module With_bonsai = With_bonsai -module Number_or_js_date = Number_or_js_date diff --git a/bindings/dygraph/src/gen_js_api.ml b/bindings/dygraph/src/gen_js_api.ml deleted file mode 100644 index b18a978a..00000000 --- a/bindings/dygraph/src/gen_js_api.ml +++ /dev/null @@ -1,2 +0,0 @@ -module Ojs = Ojs -module Ojs_exn = Ojs_exn diff --git a/bindings/dygraph/src/granularity.ml b/bindings/dygraph/src/granularity.ml deleted file mode 100644 index b2a45711..00000000 --- a/bindings/dygraph/src/granularity.ml +++ /dev/null @@ -1,110 +0,0 @@ -[@@@js.dummy "!! This code has been generated by gen_js_api !!"] -[@@@ocaml.warning "-7-32-39"] - -open! Core -open! Import -open! Gen_js_api - -type t = - | MILLISECONDLY - | TWO_MILLISECONDLY - | FIVE_MILLISECONDLY - | TEN_MILLISECONDLY - | FIFTY_MILLISECONDLY - | HUNDRED_MILLISECONDLY - | FIVE_HUNDRED_MILLISECONDLY - | SECONDLY - | TWO_SECONDLY - | FIVE_SECONDLY - | TEN_SECONDLY - | THIRTY_SECONDLY - | MINUTELY - | TWO_MINUTELY - | FIVE_MINUTELY - | TEN_MINUTELY - | THIRTY_MINUTELY - | HOURLY - | TWO_HOURLY - | SIX_HOURLY - | DAILY - | TWO_DAILY - | WEEKLY - | MONTHLY - | QUARTERLY - | BIANNUAL - | ANNUAL - | DECADAL - | CENTENNIAL - | NUM_GRANULARITIES -[@@deriving compare, sexp] - -let rec t_of_js : Ojs.t -> t = - fun (x2 : Ojs.t) -> - let x3 = x2 in - match Ojs.int_of_js x3 with - | 0 -> MILLISECONDLY - | 1 -> TWO_MILLISECONDLY - | 2 -> FIVE_MILLISECONDLY - | 3 -> TEN_MILLISECONDLY - | 4 -> FIFTY_MILLISECONDLY - | 5 -> HUNDRED_MILLISECONDLY - | 6 -> FIVE_HUNDRED_MILLISECONDLY - | 7 -> SECONDLY - | 8 -> TWO_SECONDLY - | 9 -> FIVE_SECONDLY - | 10 -> TEN_SECONDLY - | 11 -> THIRTY_SECONDLY - | 12 -> MINUTELY - | 13 -> TWO_MINUTELY - | 14 -> FIVE_MINUTELY - | 15 -> TEN_MINUTELY - | 16 -> THIRTY_MINUTELY - | 17 -> HOURLY - | 18 -> TWO_HOURLY - | 19 -> SIX_HOURLY - | 20 -> DAILY - | 21 -> TWO_DAILY - | 22 -> WEEKLY - | 23 -> MONTHLY - | 24 -> QUARTERLY - | 25 -> BIANNUAL - | 26 -> ANNUAL - | 27 -> DECADAL - | 28 -> CENTENNIAL - | 29 -> NUM_GRANULARITIES - | _ -> assert false - -and t_to_js : t -> Ojs.t = - fun (x1 : t) -> - match x1 with - | MILLISECONDLY -> Ojs.int_to_js 0 - | TWO_MILLISECONDLY -> Ojs.int_to_js 1 - | FIVE_MILLISECONDLY -> Ojs.int_to_js 2 - | TEN_MILLISECONDLY -> Ojs.int_to_js 3 - | FIFTY_MILLISECONDLY -> Ojs.int_to_js 4 - | HUNDRED_MILLISECONDLY -> Ojs.int_to_js 5 - | FIVE_HUNDRED_MILLISECONDLY -> Ojs.int_to_js 6 - | SECONDLY -> Ojs.int_to_js 7 - | TWO_SECONDLY -> Ojs.int_to_js 8 - | FIVE_SECONDLY -> Ojs.int_to_js 9 - | TEN_SECONDLY -> Ojs.int_to_js 10 - | THIRTY_SECONDLY -> Ojs.int_to_js 11 - | MINUTELY -> Ojs.int_to_js 12 - | TWO_MINUTELY -> Ojs.int_to_js 13 - | FIVE_MINUTELY -> Ojs.int_to_js 14 - | TEN_MINUTELY -> Ojs.int_to_js 15 - | THIRTY_MINUTELY -> Ojs.int_to_js 16 - | HOURLY -> Ojs.int_to_js 17 - | TWO_HOURLY -> Ojs.int_to_js 18 - | SIX_HOURLY -> Ojs.int_to_js 19 - | DAILY -> Ojs.int_to_js 20 - | TWO_DAILY -> Ojs.int_to_js 21 - | WEEKLY -> Ojs.int_to_js 22 - | MONTHLY -> Ojs.int_to_js 23 - | QUARTERLY -> Ojs.int_to_js 24 - | BIANNUAL -> Ojs.int_to_js 25 - | ANNUAL -> Ojs.int_to_js 26 - | DECADAL -> Ojs.int_to_js 27 - | CENTENNIAL -> Ojs.int_to_js 28 - | NUM_GRANULARITIES -> Ojs.int_to_js 29 -;; diff --git a/bindings/dygraph/src/granularity.mli b/bindings/dygraph/src/granularity.mli deleted file mode 100644 index e0ba3eb9..00000000 --- a/bindings/dygraph/src/granularity.mli +++ /dev/null @@ -1,43 +0,0 @@ -open! Core -open! Import -open! Gen_js_api - -(** An enum to represent the time granularity of the current graph. - - See https://github.com/danvk/dygraphs/blob/da2a028fc41e5573868358b3d9eda9826211d217/src/dygraph-tickers.js#L222 *) - -type t = - | MILLISECONDLY [@js 0] - | TWO_MILLISECONDLY [@js 1] - | FIVE_MILLISECONDLY [@js 2] - | TEN_MILLISECONDLY [@js 3] - | FIFTY_MILLISECONDLY [@js 4] - | HUNDRED_MILLISECONDLY [@js 5] - | FIVE_HUNDRED_MILLISECONDLY [@js 6] - | SECONDLY [@js 7] - | TWO_SECONDLY [@js 8] - | FIVE_SECONDLY [@js 9] - | TEN_SECONDLY [@js 10] - | THIRTY_SECONDLY [@js 11] - | MINUTELY [@js 12] - | TWO_MINUTELY [@js 13] - | FIVE_MINUTELY [@js 14] - | TEN_MINUTELY [@js 15] - | THIRTY_MINUTELY [@js 16] - | HOURLY [@js 17] - | TWO_HOURLY [@js 18] - | SIX_HOURLY [@js 19] - | DAILY [@js 20] - | TWO_DAILY [@js 21] - | WEEKLY [@js 22] - | MONTHLY [@js 23] - | QUARTERLY [@js 24] - | BIANNUAL [@js 25] - | ANNUAL [@js 26] - | DECADAL [@js 27] - | CENTENNIAL [@js 28] - | NUM_GRANULARITIES [@js 29] -[@@js.enum] [@@deriving compare, sexp] - -val t_of_js : Ojs.t -> t -val t_to_js : t -> Ojs.t diff --git a/bindings/dygraph/src/graph.ml b/bindings/dygraph/src/graph.ml deleted file mode 100644 index 1062bc2a..00000000 --- a/bindings/dygraph/src/graph.ml +++ /dev/null @@ -1,78 +0,0 @@ -[@@@js.dummy "!! This code has been generated by gen_js_api !!"] -[@@@ocaml.warning "-7-32-39"] - -open! Core -open! Import -open Gen_js_api - -type t = Ojs.t - -let rec t_of_js : Ojs.t -> t = fun (x2 : Ojs.t) -> x2 -and t_to_js : t -> Ojs.t = fun (x1 : Ojs.t) -> x1 - -let create : Native_node.t -> Data.t -> Options.t -> t = - fun (x3 : Native_node.t) (x4 : Data.t) (x5 : Options.t) -> - t_of_js - (Ojs.new_obj - (Ojs.get_prop_ascii Ojs.global "Dygraph") - [| Native_node.t_to_js x3; Data.t_to_js x4; Options.t_to_js x5 |]) -;; - -let destroy : t -> unit = fun (x6 : t) -> ignore (Ojs.call (t_to_js x6) "destroy" [||]) -let resize : t -> unit = fun (x7 : t) -> ignore (Ojs.call (t_to_js x7) "resize" [||]) - -let resize_explicit : t -> width:int -> height:int -> unit = - fun (x10 : t) ~width:(x8 : int) ~height:(x9 : int) -> - ignore (Ojs.call (t_to_js x10) "resize" [| Ojs.int_to_js x8; Ojs.int_to_js x9 |]) -;; - -let updateOptions : t -> Update_options.t -> unit = - fun (x12 : t) (x11 : Update_options.t) -> - ignore (Ojs.call (t_to_js x12) "updateOptions" [| Update_options.t_to_js x11 |]) -;; - -let getArea : t -> Area.t = - fun (x13 : t) -> Area.t_of_js (Ojs.call (t_to_js x13) "getArea" [||]) -;; - -let isZoomed : t -> [ `x | `y ] -> bool = - fun (x15 : t) (x14 : [ `x | `y ]) -> - Ojs.bool_of_js - (Ojs.call - (t_to_js x15) - "isZoomed" - [| (match x14 with - | `x -> Ojs.string_to_js "x" - | `y -> Ojs.string_to_js "y") - |]) -;; - -let resetZoom : t -> unit = - fun (x16 : t) -> ignore (Ojs.call (t_to_js x16) "resetZoom" [||]) -;; - -let primary_context : t -> Canvas_rendering_context_2D.t = - fun (x17 : t) -> - Canvas_rendering_context_2D.t_of_js (Ojs.get_prop_ascii (t_to_js x17) "hidden_ctx_") -;; - -let overlay_context : t -> Canvas_rendering_context_2D.t = - fun (x18 : t) -> - Canvas_rendering_context_2D.t_of_js (Ojs.get_prop_ascii (t_to_js x18) "canvas_ctx_") -;; - -let toDomCoords ?(axis = `y1) ~(x : float) ~(y : float) (t : t) = - let coords = - Ojs.call - (t_to_js t) - "toDomCoords" - [| Ojs.float_to_js x - ; Ojs.float_to_js y - ; Ojs.int_to_js - (match axis with - | `y1 -> 0 - | `y2 -> 1) - |] - in - Ojs.float_of_js (Ojs.array_get coords 0), Ojs.float_of_js (Ojs.array_get coords 1) -;; diff --git a/bindings/dygraph/src/graph.mli b/bindings/dygraph/src/graph.mli deleted file mode 100644 index 0f465983..00000000 --- a/bindings/dygraph/src/graph.mli +++ /dev/null @@ -1,56 +0,0 @@ -open! Core -open! Import -open Gen_js_api - -(** API: http://dygraphs.com/jsdoc/symbols/Dygraph.html *) -type t - -val create : Native_node.t -> Data.t -> Options.t -> t [@@js.new "Dygraph"] -val destroy : t -> unit [@@js.call] -val resize : t -> unit [@@js.call] -val resize_explicit : t -> width:int -> height:int -> unit [@@js.call "resize"] -val updateOptions : t -> Update_options.t -> unit [@@js.call] -val getArea : t -> Area.t [@@js.call] -val isZoomed : t -> ([ `x | `y ][@js.enum]) -> bool [@@js.call] -val resetZoom : t -> unit [@@js.call] - -(** [primary_context] is the rendering context to which Dygraphs draws data points and - axes and underlays -- everything except the interactive highlights drawn to - [overlay_context]. *) -val primary_context : t -> Canvas_rendering_context_2D.t - [@@js.get "hidden_ctx_"] - -(** [overlay_context] is the rendering context for temporary overlays that change - interactively, such as the highlighted point on mouse hover or the selected zoom - region during a drag event. The separate canvas allows Dygraphs to quickly clear and - redraw those overlays without having to re-render the entire graph. *) -val overlay_context : t -> Canvas_rendering_context_2D.t - [@@js.get "canvas_ctx_"] - -(** Despite the name of the function, this returns a point in the coordinate space of the - graph's canvas element. This function is suitable for finding drawing coordinates when - rendering a custom overlay or underlay. - - Unlike when using Dygraph annotations, the point that you pass in does not actually - have to exist in any data series on the graph. But if you are using a multi-series - graph with multiple Y axes, make sure that you pass the correct [axis]. *) -val toDomCoords : ?axis:Which_y_axis.t -> x:float -> y:float -> t -> float * float - [@@js.custom - let toDomCoords ?(axis = `y1) ~(x : float) ~(y : float) (t : t) = - let coords = - Ojs.call - (t_to_js t) - "toDomCoords" - [| Ojs.float_to_js x - ; Ojs.float_to_js y - ; Ojs.int_to_js - (match axis with - | `y1 -> 0 - | `y2 -> 1) - |] - in - Ojs.float_of_js (Ojs.array_get coords 0), Ojs.float_of_js (Ojs.array_get coords 1) - ;;] - -val t_to_js : t -> Ojs.t -val t_of_js : Ojs.t -> t diff --git a/bindings/dygraph/src/html_or_number.ml b/bindings/dygraph/src/html_or_number.ml deleted file mode 100644 index c8b11164..00000000 --- a/bindings/dygraph/src/html_or_number.ml +++ /dev/null @@ -1,20 +0,0 @@ -open! Core -open! Import -open Gen_js_api - -type t = - [ `html of Raw_html.t - | `number of float - ] -[@@deriving compare, equal, sexp] - -let t_to_js = function - | `html s -> Raw_html.t_to_js s - | `number f -> Ojs.float_to_js f -;; - -let t_of_js ojs = - if String.equal "number" (Ojs.type_of ojs) - then `number (Ojs.float_of_js ojs) - else `html (Raw_html.t_of_js ojs) -;; diff --git a/bindings/dygraph/src/html_or_number.mli b/bindings/dygraph/src/html_or_number.mli deleted file mode 100644 index a3b69d0d..00000000 --- a/bindings/dygraph/src/html_or_number.mli +++ /dev/null @@ -1,12 +0,0 @@ -open! Core -open! Import -open Gen_js_api - -type t = - [ `html of Raw_html.t - | `number of float - ] -[@@deriving compare, equal, sexp] - -val t_to_js : t -> Ojs.t -val t_of_js : Ojs.t -> t diff --git a/bindings/dygraph/src/import.ml b/bindings/dygraph/src/import.ml deleted file mode 100644 index a3aaa3fc..00000000 --- a/bindings/dygraph/src/import.ml +++ /dev/null @@ -1,3 +0,0 @@ -include Js_of_ocaml -include Bonsai_web -include Bonsai.Let_syntax diff --git a/bindings/dygraph/src/js_date.ml b/bindings/dygraph/src/js_date.ml deleted file mode 100644 index d5bb7407..00000000 --- a/bindings/dygraph/src/js_date.ml +++ /dev/null @@ -1,6 +0,0 @@ -open! Core -open Import - -include Js_obj.Make (struct - type t = Js.date -end) diff --git a/bindings/dygraph/src/js_date.mli b/bindings/dygraph/src/js_date.mli deleted file mode 100644 index 2b02e0a3..00000000 --- a/bindings/dygraph/src/js_date.mli +++ /dev/null @@ -1,3 +0,0 @@ -open! Core -open Import -include Js_obj.S with type t = Js.date Js.t diff --git a/bindings/dygraph/src/js_obj.ml b/bindings/dygraph/src/js_obj.ml deleted file mode 100644 index 9e5a533c..00000000 --- a/bindings/dygraph/src/js_obj.ml +++ /dev/null @@ -1,11 +0,0 @@ -open! Base -open Import -open Gen_js_api -include Js_obj_intf - -module Make (M : T) = struct - type t = M.t Js.t - - let t_of_js : Ojs.t -> t = Stdlib.Obj.magic - let t_to_js : t -> Ojs.t = Stdlib.Obj.magic -end diff --git a/bindings/dygraph/src/js_obj.mli b/bindings/dygraph/src/js_obj.mli deleted file mode 100644 index 3222657c..00000000 --- a/bindings/dygraph/src/js_obj.mli +++ /dev/null @@ -1 +0,0 @@ -include Js_obj_intf.Js_obj (** @inline *) diff --git a/bindings/dygraph/src/js_obj_intf.ml b/bindings/dygraph/src/js_obj_intf.ml deleted file mode 100644 index 3ede3fac..00000000 --- a/bindings/dygraph/src/js_obj_intf.ml +++ /dev/null @@ -1,18 +0,0 @@ -open! Base -open Import -open Gen_js_api - -module type S = sig - type t - - val t_of_js : Ojs.t -> t - val t_to_js : t -> Ojs.t -end - -module type Js_obj = sig - module type S = S - - (** This just provides an Obj.magic-based implementation of [t_to_js] and [t_of_js] - since Js.t objects are, in fact, objects in javascript (e.g. Ojs). *) - module Make (M : T) : S with type t = M.t Js.t -end diff --git a/bindings/dygraph/src/legend_data.ml b/bindings/dygraph/src/legend_data.ml deleted file mode 100644 index 8ccbf704..00000000 --- a/bindings/dygraph/src/legend_data.ml +++ /dev/null @@ -1,70 +0,0 @@ -[@@@js.dummy "!! This code has been generated by gen_js_api !!"] -[@@@ocaml.warning "-7-32-39"] - -open! Core -open! Import -open Gen_js_api - -module Series = struct - type t = - { dashHTML : Raw_html.t - ; label : string - ; labelHTML : Raw_html.t - ; isVisible : bool - ; isHighlighted : bool option - ; color : string option - ; y : float option - ; yHTML : Raw_html.t option - } - [@@deriving equal, sexp] - - let rec t_of_js : Ojs.t -> t = - fun (x6 : Ojs.t) -> - { dashHTML = Raw_html.t_of_js (Ojs.get_prop_ascii x6 "dashHTML") - ; label = Ojs.string_of_js (Ojs.get_prop_ascii x6 "label") - ; labelHTML = Raw_html.t_of_js (Ojs.get_prop_ascii x6 "labelHTML") - ; isVisible = Ojs.bool_of_js (Ojs.get_prop_ascii x6 "isVisible") - ; isHighlighted = - Ojs.option_of_js Ojs.bool_of_js (Ojs.get_prop_ascii x6 "isHighlighted") - ; color = Ojs.option_of_js Ojs.string_of_js (Ojs.get_prop_ascii x6 "color") - ; y = Ojs.option_of_js Ojs.float_of_js (Ojs.get_prop_ascii x6 "y") - ; yHTML = Ojs.option_of_js Raw_html.t_of_js (Ojs.get_prop_ascii x6 "yHTML") - } - - and t_to_js : t -> Ojs.t = - fun (x1 : t) -> - Ojs.obj - [| "dashHTML", Raw_html.t_to_js x1.dashHTML - ; "label", Ojs.string_to_js x1.label - ; "labelHTML", Raw_html.t_to_js x1.labelHTML - ; "isVisible", Ojs.bool_to_js x1.isVisible - ; "isHighlighted", Ojs.option_to_js Ojs.bool_to_js x1.isHighlighted - ; "color", Ojs.option_to_js Ojs.string_to_js x1.color - ; "y", Ojs.option_to_js Ojs.float_to_js x1.y - ; "yHTML", Ojs.option_to_js Raw_html.t_to_js x1.yHTML - |] - ;; -end - -type t = - { x : float option - ; xHTML : Html_or_number.t option - ; series : Series.t list - } -[@@deriving equal, sexp] - -let rec t_of_js : Ojs.t -> t = - fun (x15 : Ojs.t) -> - { x = Ojs.option_of_js Ojs.float_of_js (Ojs.get_prop_ascii x15 "x") - ; xHTML = Ojs.option_of_js Html_or_number.t_of_js (Ojs.get_prop_ascii x15 "xHTML") - ; series = Ojs.list_of_js Series.t_of_js (Ojs.get_prop_ascii x15 "series") - } - -and t_to_js : t -> Ojs.t = - fun (x11 : t) -> - Ojs.obj - [| "x", Ojs.option_to_js Ojs.float_to_js x11.x - ; "xHTML", Ojs.option_to_js Html_or_number.t_to_js x11.xHTML - ; "series", Ojs.list_to_js Series.t_to_js x11.series - |] -;; diff --git a/bindings/dygraph/src/legend_data.mli b/bindings/dygraph/src/legend_data.mli deleted file mode 100644 index 20bb2ae8..00000000 --- a/bindings/dygraph/src/legend_data.mli +++ /dev/null @@ -1,35 +0,0 @@ -open! Core -open! Import -open Gen_js_api - -(** Legend_data is what you receive on the legendFormatter callback, as described here: - https://github.com/danvk/dygraphs/pull/683 *) - -module Series : sig - type t = - { dashHTML : Raw_html.t - ; label : string - ; labelHTML : Raw_html.t - ; isVisible : bool - ; isHighlighted : bool option - ; color : string option - ; y : float option - ; yHTML : Raw_html.t option - } - [@@deriving equal, sexp] - - val t_of_js : Ojs.t -> t - val t_to_js : t -> Ojs.t -end - -type t = - { x : float option - ; xHTML : Html_or_number.t option - (** Empirically, it seems that xHTML comes back as html (a string) for date/time-based - x-axes and a number for number-based x-axes. *) - ; series : Series.t list - } -[@@deriving equal, sexp] - -val t_of_js : Ojs.t -> t -val t_to_js : t -> Ojs.t diff --git a/bindings/dygraph/src/native_node.ml b/bindings/dygraph/src/native_node.ml deleted file mode 100644 index 25ffc110..00000000 --- a/bindings/dygraph/src/native_node.ml +++ /dev/null @@ -1,6 +0,0 @@ -open! Core -open Import - -include Js_obj.Make (struct - type t = Dom_html.element -end) diff --git a/bindings/dygraph/src/native_node.mli b/bindings/dygraph/src/native_node.mli deleted file mode 100644 index 6f245a75..00000000 --- a/bindings/dygraph/src/native_node.mli +++ /dev/null @@ -1,3 +0,0 @@ -open! Core -open Import -include Js_obj.S with type t = Dom_html.element Js.t diff --git a/bindings/dygraph/src/number_or_js_date.ml b/bindings/dygraph/src/number_or_js_date.ml deleted file mode 100644 index 89d97a6e..00000000 --- a/bindings/dygraph/src/number_or_js_date.ml +++ /dev/null @@ -1,19 +0,0 @@ -open! Core -open! Import -open Gen_js_api - -type t = - [ `number of float - | `date of Js_date.t - ] - -let t_to_js = function - | `number f -> Ojs.float_to_js f - | `date d -> Js_date.t_to_js d -;; - -let t_of_js ojs = - if String.equal "number" (Ojs.type_of ojs) - then `number (Ojs.float_of_js ojs) - else `date (Js_date.t_of_js ojs) -;; diff --git a/bindings/dygraph/src/number_or_js_date.mli b/bindings/dygraph/src/number_or_js_date.mli deleted file mode 100644 index a1d47e65..00000000 --- a/bindings/dygraph/src/number_or_js_date.mli +++ /dev/null @@ -1,11 +0,0 @@ -open! Core -open! Import -open Gen_js_api - -type t = - [ `number of float - | `date of Js_date.t - ] - -val t_to_js : t -> Ojs.t -val t_of_js : Ojs.t -> t diff --git a/bindings/dygraph/src/options.ml b/bindings/dygraph/src/options.ml deleted file mode 100644 index 70c92d0e..00000000 --- a/bindings/dygraph/src/options.ml +++ /dev/null @@ -1,956 +0,0 @@ -[@@@js.dummy "!! This code has been generated by gen_js_api !!"] -[@@@ocaml.warning "-7-32-39"] - -open! Core -open! Import -open Gen_js_api - -module Line_pattern = struct - type t = int array - - let rec t_of_js : Ojs.t -> t = fun (x3 : Ojs.t) -> Ojs.array_of_js Ojs.int_of_js x3 - and t_to_js : t -> Ojs.t = fun (x1 : int array) -> Ojs.array_to_js Ojs.int_to_js x1 - - let dashed = [| 7; 2 |] -end - -module Legend = struct - type t = - [ `always - | `follow - | `never - | `onmouseover - ] - - let rec t_of_js : Ojs.t -> t = - fun (x6 : Ojs.t) -> - let x7 = x6 in - match Ojs.string_of_js x7 with - | "always" -> `always - | "follow" -> `follow - | "never" -> `never - | "onmouseover" -> `onmouseover - | _ -> assert false - - and t_to_js : t -> Ojs.t = - fun (x5 : [ `always | `follow | `never | `onmouseover ]) -> - match x5 with - | `always -> Ojs.string_to_js "always" - | `follow -> Ojs.string_to_js "follow" - | `never -> Ojs.string_to_js "never" - | `onmouseover -> Ojs.string_to_js "onmouseover" - ;; -end - -module Series_options = struct - type t = Ojs.t - - let rec t_of_js : Ojs.t -> t = fun (x9 : Ojs.t) -> x9 - and t_to_js : t -> Ojs.t = fun (x8 : Ojs.t) -> x8 - - let create - : ?axis:Which_y_axis.t -> ?color:Color.t -> ?drawPoints:bool - -> ?drawHighlightPointCallback: - (graph:Ojs.t - -> seriesName:string option - -> context:Canvas_rendering_context_2D.t - -> cx:float - -> cy:float - -> color:Ojs.t - -> pointSize:int - -> idx:int - -> unit) - -> ?drawPointCallback: - (graph:Ojs.t - -> seriesName:string option - -> context:Canvas_rendering_context_2D.t - -> cx:float - -> cy:float - -> color:Ojs.t - -> pointSize:int - -> idx:int - -> unit) - -> ?plotter:Plotter.t - -> ?plotterFinishedCallback:(context:Canvas_rendering_context_2D.t -> unit) - -> ?showInRangeSelector:bool -> ?stepPlot:bool -> ?strokePattern:Line_pattern.t - -> ?strokeWidth:float -> unit -> t - = - fun ?axis:(x10 : Which_y_axis.t option) - ?color:(x11 : Color.t option) - ?drawPoints:(x12 : bool option) - ?drawHighlightPointCallback: - (x13 : - (graph:Ojs.t - -> seriesName:string option - -> context:Canvas_rendering_context_2D.t - -> cx:float - -> cy:float - -> color:Ojs.t - -> pointSize:int - -> idx:int - -> unit) - option) - ?drawPointCallback: - (x14 : - (graph:Ojs.t - -> seriesName:string option - -> context:Canvas_rendering_context_2D.t - -> cx:float - -> cy:float - -> color:Ojs.t - -> pointSize:int - -> idx:int - -> unit) - option) - ?plotter:(x15 : Plotter.t option) - ?plotterFinishedCallback: - (x16 : (context:Canvas_rendering_context_2D.t -> unit) option) - ?showInRangeSelector:(x17 : bool option) - ?stepPlot:(x18 : bool option) - ?strokePattern:(x19 : Line_pattern.t option) - ?strokeWidth:(x20 : float option) - () -> - let x21 = Ojs.empty_obj () in - (match x10 with - | Some x51 -> Ojs.set_prop_ascii x21 "axis" (Which_y_axis.t_to_js x51) - | None -> ()); - (match x11 with - | Some x50 -> Ojs.set_prop_ascii x21 "color" (Color.t_to_js x50) - | None -> ()); - (match x12 with - | Some x49 -> Ojs.set_prop_ascii x21 "drawPoints" (Ojs.bool_to_js x49) - | None -> ()); - (match x13 with - | Some x39 -> - Ojs.set_prop_ascii - x21 - "drawHighlightPointCallback" - (Ojs.fun_to_js - 8 - (fun - (x40 : Ojs.t) - (x41 : Ojs.t) - (x43 : Ojs.t) - (x44 : Ojs.t) - (x45 : Ojs.t) - (x46 : Ojs.t) - (x47 : Ojs.t) - (x48 : Ojs.t) - -> - x39 - ~graph:x40 - ~seriesName:(Ojs.option_of_js Ojs.string_of_js x41) - ~context:(Canvas_rendering_context_2D.t_of_js x43) - ~cx:(Ojs.float_of_js x44) - ~cy:(Ojs.float_of_js x45) - ~color:x46 - ~pointSize:(Ojs.int_of_js x47) - ~idx:(Ojs.int_of_js x48))) - | None -> ()); - (match x14 with - | Some x29 -> - Ojs.set_prop_ascii - x21 - "drawPointCallback" - (Ojs.fun_to_js - 8 - (fun - (x30 : Ojs.t) - (x31 : Ojs.t) - (x33 : Ojs.t) - (x34 : Ojs.t) - (x35 : Ojs.t) - (x36 : Ojs.t) - (x37 : Ojs.t) - (x38 : Ojs.t) - -> - x29 - ~graph:x30 - ~seriesName:(Ojs.option_of_js Ojs.string_of_js x31) - ~context:(Canvas_rendering_context_2D.t_of_js x33) - ~cx:(Ojs.float_of_js x34) - ~cy:(Ojs.float_of_js x35) - ~color:x36 - ~pointSize:(Ojs.int_of_js x37) - ~idx:(Ojs.int_of_js x38))) - | None -> ()); - (match x15 with - | Some x28 -> Ojs.set_prop_ascii x21 "plotter" (Plotter.t_to_js x28) - | None -> ()); - (match x16 with - | Some x26 -> - Ojs.set_prop_ascii - x21 - "plotterFinishedCallback" - (Ojs.fun_to_js 1 (fun (x27 : Ojs.t) -> - x26 ~context:(Canvas_rendering_context_2D.t_of_js x27))) - | None -> ()); - (match x17 with - | Some x25 -> Ojs.set_prop_ascii x21 "showInRangeSelector" (Ojs.bool_to_js x25) - | None -> ()); - (match x18 with - | Some x24 -> Ojs.set_prop_ascii x21 "stepPlot" (Ojs.bool_to_js x24) - | None -> ()); - (match x19 with - | Some x23 -> Ojs.set_prop_ascii x21 "strokePattern" (Line_pattern.t_to_js x23) - | None -> ()); - (match x20 with - | Some x22 -> Ojs.set_prop_ascii x21 "strokeWidth" (Ojs.float_to_js x22) - | None -> ()); - t_of_js x21 - ;; -end - -module Series = struct - type t = Ojs.t - - let rec t_of_js : Ojs.t -> t = fun (x53 : Ojs.t) -> x53 - and t_to_js : t -> Ojs.t = fun (x52 : Ojs.t) -> x52 - - let create data = - data |> List.Assoc.map ~f:Series_options.t_to_js |> Array.of_list |> Ojs.obj - ;; -end - -module Opts = struct - type t = Ojs.t - - let rec t_of_js : Ojs.t -> t = fun (x55 : Ojs.t) -> x55 - and t_to_js : t -> Ojs.t = fun (x54 : Ojs.t) -> x54 -end - -module Axis_options = struct - type t = Ojs.t - - let rec t_of_js : Ojs.t -> t = fun (x57 : Ojs.t) -> x57 - and t_to_js : t -> Ojs.t = fun (x56 : Ojs.t) -> x56 - - let create - : ?axisLabelFormatter:(Number_or_js_date.t -> Granularity.t -> Opts.t -> string) - -> ?valueFormatter:(float -> Opts.t -> string) -> ?axisLabelWidth:int - -> ?axisLineColor:Color.t -> ?axisLineWidth:float -> ?axisTickSize:float - -> ?drawAxis:bool -> ?includeZero:bool -> ?independentTicks:bool -> ?logscale:bool - -> ?pixelsPerLabel:int -> ?valueRange:Range.Spec.t -> ?drawGrid:bool - -> ?gridLineColor:Color.t -> ?gridLinePattern:Line_pattern.t -> ?gridLineWidth:float - -> ?pixelsPerLabel:int -> unit -> t - = - fun ?axisLabelFormatter: - (x58 : (Number_or_js_date.t -> Granularity.t -> Opts.t -> string) option) - ?valueFormatter:(x59 : (float -> Opts.t -> string) option) - ?axisLabelWidth:(x60 : int option) - ?axisLineColor:(x61 : Color.t option) - ?axisLineWidth:(x62 : float option) - ?axisTickSize:(x63 : float option) - ?drawAxis:(x64 : bool option) - ?includeZero:(x65 : bool option) - ?independentTicks:(x66 : bool option) - ?logscale:(x67 : bool option) - ?pixelsPerLabel:(x68 : int option) - ?valueRange:(x69 : Range.Spec.t option) - ?drawGrid:(x70 : bool option) - ?gridLineColor:(x71 : Color.t option) - ?gridLinePattern:(x72 : Line_pattern.t option) - ?gridLineWidth:(x73 : float option) - ?pixelsPerLabel:(x74 : int option) - () -> - let x75 = Ojs.empty_obj () in - (match x58 with - | Some x94 -> - Ojs.set_prop_ascii - x75 - "axisLabelFormatter" - (Ojs.fun_to_js 3 (fun (x95 : Ojs.t) (x96 : Ojs.t) (x97 : Ojs.t) -> - Ojs.string_to_js - (x94 - (Number_or_js_date.t_of_js x95) - (Granularity.t_of_js x96) - (Opts.t_of_js x97)))) - | None -> ()); - (match x59 with - | Some x91 -> - Ojs.set_prop_ascii - x75 - "valueFormatter" - (Ojs.fun_to_js 2 (fun (x92 : Ojs.t) (x93 : Ojs.t) -> - Ojs.string_to_js (x91 (Ojs.float_of_js x92) (Opts.t_of_js x93)))) - | None -> ()); - (match x60 with - | Some x90 -> Ojs.set_prop_ascii x75 "axisLabelWidth" (Ojs.int_to_js x90) - | None -> ()); - (match x61 with - | Some x89 -> Ojs.set_prop_ascii x75 "axisLineColor" (Color.t_to_js x89) - | None -> ()); - (match x62 with - | Some x88 -> Ojs.set_prop_ascii x75 "axisLineWidth" (Ojs.float_to_js x88) - | None -> ()); - (match x63 with - | Some x87 -> Ojs.set_prop_ascii x75 "axisTickSize" (Ojs.float_to_js x87) - | None -> ()); - (match x64 with - | Some x86 -> Ojs.set_prop_ascii x75 "drawAxis" (Ojs.bool_to_js x86) - | None -> ()); - (match x65 with - | Some x85 -> Ojs.set_prop_ascii x75 "includeZero" (Ojs.bool_to_js x85) - | None -> ()); - (match x66 with - | Some x84 -> Ojs.set_prop_ascii x75 "independentTicks" (Ojs.bool_to_js x84) - | None -> ()); - (match x67 with - | Some x83 -> Ojs.set_prop_ascii x75 "logscale" (Ojs.bool_to_js x83) - | None -> ()); - (match x68 with - | Some x82 -> Ojs.set_prop_ascii x75 "pixelsPerLabel" (Ojs.int_to_js x82) - | None -> ()); - (match x69 with - | Some x81 -> Ojs.set_prop_ascii x75 "valueRange" (Range.Spec.t_to_js x81) - | None -> ()); - (match x70 with - | Some x80 -> Ojs.set_prop_ascii x75 "drawGrid" (Ojs.bool_to_js x80) - | None -> ()); - (match x71 with - | Some x79 -> Ojs.set_prop_ascii x75 "gridLineColor" (Color.t_to_js x79) - | None -> ()); - (match x72 with - | Some x78 -> Ojs.set_prop_ascii x75 "gridLinePattern" (Line_pattern.t_to_js x78) - | None -> ()); - (match x73 with - | Some x77 -> Ojs.set_prop_ascii x75 "gridLineWidth" (Ojs.float_to_js x77) - | None -> ()); - (match x74 with - | Some x76 -> Ojs.set_prop_ascii x75 "pixelsPerLabel" (Ojs.int_to_js x76) - | None -> ()); - t_of_js x75 - ;; -end - -module Axes = struct - type t = Ojs.t - - let rec t_of_js : Ojs.t -> t = fun (x99 : Ojs.t) -> x99 - and t_to_js : t -> Ojs.t = fun (x98 : Ojs.t) -> x98 - - let create : ?x:Axis_options.t -> ?y:Axis_options.t -> ?y2:Axis_options.t -> unit -> t = - fun ?x:(x100 : Axis_options.t option) - ?y:(x101 : Axis_options.t option) - ?y2:(x102 : Axis_options.t option) - () -> - let x103 = Ojs.empty_obj () in - (match x100 with - | Some x106 -> Ojs.set_prop_ascii x103 "x" (Axis_options.t_to_js x106) - | None -> ()); - (match x101 with - | Some x105 -> Ojs.set_prop_ascii x103 "y" (Axis_options.t_to_js x105) - | None -> ()); - (match x102 with - | Some x104 -> Ojs.set_prop_ascii x103 "y2" (Axis_options.t_to_js x104) - | None -> ()); - t_of_js x103 - ;; -end - -module Highlight_series_options = struct - type t = Ojs.t - - let rec t_of_js : Ojs.t -> t = fun (x108 : Ojs.t) -> x108 - and t_to_js : t -> Ojs.t = fun (x107 : Ojs.t) -> x107 - - let create - : ?highlightCircleSize:int -> ?strokeWidth:float -> ?strokeBorderWidth:float -> unit - -> t - = - fun ?highlightCircleSize:(x109 : int option) - ?strokeWidth:(x110 : float option) - ?strokeBorderWidth:(x111 : float option) - () -> - let x112 = Ojs.empty_obj () in - (match x109 with - | Some x115 -> Ojs.set_prop_ascii x112 "highlightCircleSize" (Ojs.int_to_js x115) - | None -> ()); - (match x110 with - | Some x114 -> Ojs.set_prop_ascii x112 "strokeWidth" (Ojs.float_to_js x114) - | None -> ()); - (match x111 with - | Some x113 -> Ojs.set_prop_ascii x112 "strokeBorderWidth" (Ojs.float_to_js x113) - | None -> ()); - t_of_js x112 - ;; -end - -type t = Ojs.t - -let rec t_of_js : Ojs.t -> t = fun (x117 : Ojs.t) -> x117 -and t_to_js : t -> Ojs.t = fun (x116 : Ojs.t) -> x116 - -let create - : ?axisLabelFontSize:int -> ?axisLabelWidth:int -> ?axisLineColor:Color.t - -> ?axisLineWidth:float -> ?axisTickSize:float -> ?dateWindow:Range.t - -> ?drawAxesAtZero:bool -> ?drawAxis:bool -> ?includeZero:bool -> ?logscale:bool - -> ?panEdgeFraction:float -> ?valueRange:Range.Spec.t -> ?xAxisHeight:int - -> ?xRangePad:float -> ?yRangePad:float -> ?customBars:bool -> ?errorBars:bool - -> ?fractions:bool -> ?title:string -> ?titleHeight:int -> ?xLabelHeight:int - -> ?xlabel:string -> ?y2label:string -> ?yLabelWidth:int -> ?ylabel:string - -> ?axes:Axes.t -> ?connectSeparatedPoints:bool -> ?drawGapEdgePoints:bool - -> ?drawPoints:bool -> ?fillGraph:bool -> ?pointSize:int -> ?stackedGraph:bool - -> ?stackedGraphNaNFill:string -> ?stepPlot:bool -> ?strokeBorderColor:Color.t - -> ?strokeBorderWidth:float -> ?strokePattern:Line_pattern.t -> ?strokeWidth:float - -> ?visibility:bool list -> ?colorSaturation:float -> ?colorValue:float - -> ?colors:Color.t array -> ?fillAlpha:float -> ?rollPeriod:int -> ?sigma:float - -> ?wilsonInterval:bool -> ?drawGrid:bool -> ?gridLineColor:Color.t - -> ?gridLinePattern:Line_pattern.t -> ?gridLineWidth:float -> ?animatedZooms:bool - -> ?hideOverlayOnMouseOut:bool -> ?highlightCircleSize:int - -> ?highlightSeriesBackgroundAlpha:float -> ?highlightSeriesBackgroundColor:Color.t - -> ?highlightSeriesOpts:Highlight_series_options.t -> ?showLabelsOnHighlight:bool - -> ?showRoller:bool -> ?hideOverlayOnMouseOut:bool -> ?labels:string list - -> ?labelsDiv_string:string -> ?labelsDiv_el:Native_node.t -> ?labelsSeparateLines:bool - -> ?labelsShowZeroValues:bool -> ?legend:Legend.t - -> ?legendFormatter:(Legend_data.t -> string) -> ?showLabelsOnHighlight:bool - -> ?height:int -> ?clickCallback:(evt:Ojs.t -> x:float -> points:Point.t array -> unit) - -> ?highlightCallback: - (evt:Ojs.t - -> x:float - -> points:Point.t array - -> row:int - -> seriesName:string option - -> unit) - -> ?unhighlightCallback:(evt:Ojs.t -> unit) - -> ?pointClickCallback:(evt:Ojs.t -> point:Point.t -> unit) - -> ?underlayCallback: - (context:Canvas_rendering_context_2D.t -> area:Area.t -> dygraph:Ojs.t -> unit) - -> ?drawCallback:(graph:Ojs.t -> isInitial:bool -> unit) - -> ?zoomCallback:(xmin:float -> xmax:float -> yRanges:Range.t array -> unit) - -> ?pixelRatio:float -> ?plotter:Plotter.t list -> ?rightGap:int -> ?width:int - -> ?rangeSelectorAlpha:float -> ?rangeSelectorBackgroundLineWidth:float - -> ?rangeSelectorBackgroundStrokeColor:Color.t - -> ?rangeSelectorForegroundLineWidth:float - -> ?rangeSelectorForegroundStrokeColor:Color.t -> ?rangeSelectorHeight:int - -> ?rangeSelectorPlotFillColor:Color.t -> ?rangeSelectorPlotFillGradientColor:Color.t - -> ?rangeSelectorPlotLineWidth:float -> ?rangeSelectorPlotStrokeColor:Color.t - -> ?showRangeSelector:bool -> ?series:Series.t -> ?digitsAfterDecimal:int - -> ?labelsKMB:bool -> ?labelsKMG2:bool -> ?labelsUTC:bool -> ?maxNumberWidth:int - -> ?sigFigs:int -> unit -> t - = - fun ?axisLabelFontSize:(x118 : int option) - ?axisLabelWidth:(x119 : int option) - ?axisLineColor:(x120 : Color.t option) - ?axisLineWidth:(x121 : float option) - ?axisTickSize:(x122 : float option) - ?dateWindow:(x123 : Range.t option) - ?drawAxesAtZero:(x124 : bool option) - ?drawAxis:(x125 : bool option) - ?includeZero:(x126 : bool option) - ?logscale:(x127 : bool option) - ?panEdgeFraction:(x128 : float option) - ?valueRange:(x129 : Range.Spec.t option) - ?xAxisHeight:(x130 : int option) - ?xRangePad:(x131 : float option) - ?yRangePad:(x132 : float option) - ?customBars:(x133 : bool option) - ?errorBars:(x134 : bool option) - ?fractions:(x135 : bool option) - ?title:(x136 : string option) - ?titleHeight:(x137 : int option) - ?xLabelHeight:(x138 : int option) - ?xlabel:(x139 : string option) - ?y2label:(x140 : string option) - ?yLabelWidth:(x141 : int option) - ?ylabel:(x142 : string option) - ?axes:(x143 : Axes.t option) - ?connectSeparatedPoints:(x144 : bool option) - ?drawGapEdgePoints:(x145 : bool option) - ?drawPoints:(x146 : bool option) - ?fillGraph:(x147 : bool option) - ?pointSize:(x148 : int option) - ?stackedGraph:(x149 : bool option) - ?stackedGraphNaNFill:(x150 : string option) - ?stepPlot:(x151 : bool option) - ?strokeBorderColor:(x152 : Color.t option) - ?strokeBorderWidth:(x153 : float option) - ?strokePattern:(x154 : Line_pattern.t option) - ?strokeWidth:(x155 : float option) - ?visibility:(x156 : bool list option) - ?colorSaturation:(x157 : float option) - ?colorValue:(x158 : float option) - ?colors:(x159 : Color.t array option) - ?fillAlpha:(x160 : float option) - ?rollPeriod:(x161 : int option) - ?sigma:(x162 : float option) - ?wilsonInterval:(x163 : bool option) - ?drawGrid:(x164 : bool option) - ?gridLineColor:(x165 : Color.t option) - ?gridLinePattern:(x166 : Line_pattern.t option) - ?gridLineWidth:(x167 : float option) - ?animatedZooms:(x168 : bool option) - ?hideOverlayOnMouseOut:(x169 : bool option) - ?highlightCircleSize:(x170 : int option) - ?highlightSeriesBackgroundAlpha:(x171 : float option) - ?highlightSeriesBackgroundColor:(x172 : Color.t option) - ?highlightSeriesOpts:(x173 : Highlight_series_options.t option) - ?showLabelsOnHighlight:(x174 : bool option) - ?showRoller:(x175 : bool option) - ?hideOverlayOnMouseOut:(x176 : bool option) - ?labels:(x177 : string list option) - ?labelsDiv_string:(x178 : string option) - ?labelsDiv_el:(x179 : Native_node.t option) - ?labelsSeparateLines:(x180 : bool option) - ?labelsShowZeroValues:(x181 : bool option) - ?legend:(x182 : Legend.t option) - ?legendFormatter:(x183 : (Legend_data.t -> string) option) - ?showLabelsOnHighlight:(x184 : bool option) - ?height:(x185 : int option) - ?clickCallback: - (x186 : (evt:Ojs.t -> x:float -> points:Point.t array -> unit) option) - ?highlightCallback: - (x187 : - (evt:Ojs.t - -> x:float - -> points:Point.t array - -> row:int - -> seriesName:string option - -> unit) - option) - ?unhighlightCallback:(x188 : (evt:Ojs.t -> unit) option) - ?pointClickCallback:(x189 : (evt:Ojs.t -> point:Point.t -> unit) option) - ?underlayCallback: - (x190 : - (context:Canvas_rendering_context_2D.t -> area:Area.t -> dygraph:Ojs.t -> unit) - option) - ?drawCallback:(x191 : (graph:Ojs.t -> isInitial:bool -> unit) option) - ?zoomCallback: - (x192 : (xmin:float -> xmax:float -> yRanges:Range.t array -> unit) option) - ?pixelRatio:(x193 : float option) - ?plotter:(x194 : Plotter.t list option) - ?rightGap:(x195 : int option) - ?width:(x196 : int option) - ?rangeSelectorAlpha:(x197 : float option) - ?rangeSelectorBackgroundLineWidth:(x198 : float option) - ?rangeSelectorBackgroundStrokeColor:(x199 : Color.t option) - ?rangeSelectorForegroundLineWidth:(x200 : float option) - ?rangeSelectorForegroundStrokeColor:(x201 : Color.t option) - ?rangeSelectorHeight:(x202 : int option) - ?rangeSelectorPlotFillColor:(x203 : Color.t option) - ?rangeSelectorPlotFillGradientColor:(x204 : Color.t option) - ?rangeSelectorPlotLineWidth:(x205 : float option) - ?rangeSelectorPlotStrokeColor:(x206 : Color.t option) - ?showRangeSelector:(x207 : bool option) - ?series:(x208 : Series.t option) - ?digitsAfterDecimal:(x209 : int option) - ?labelsKMB:(x210 : bool option) - ?labelsKMG2:(x211 : bool option) - ?labelsUTC:(x212 : bool option) - ?maxNumberWidth:(x213 : int option) - ?sigFigs:(x214 : int option) - () -> - let x215 = Ojs.empty_obj () in - (match x118 with - | Some x340 -> Ojs.set_prop_ascii x215 "axisLabelFontSize" (Ojs.int_to_js x340) - | None -> ()); - (match x119 with - | Some x339 -> Ojs.set_prop_ascii x215 "axisLabelWidth" (Ojs.int_to_js x339) - | None -> ()); - (match x120 with - | Some x338 -> Ojs.set_prop_ascii x215 "axisLineColor" (Color.t_to_js x338) - | None -> ()); - (match x121 with - | Some x337 -> Ojs.set_prop_ascii x215 "axisLineWidth" (Ojs.float_to_js x337) - | None -> ()); - (match x122 with - | Some x336 -> Ojs.set_prop_ascii x215 "axisTickSize" (Ojs.float_to_js x336) - | None -> ()); - (match x123 with - | Some x335 -> Ojs.set_prop_ascii x215 "dateWindow" (Range.t_to_js x335) - | None -> ()); - (match x124 with - | Some x334 -> Ojs.set_prop_ascii x215 "drawAxesAtZero" (Ojs.bool_to_js x334) - | None -> ()); - (match x125 with - | Some x333 -> Ojs.set_prop_ascii x215 "drawAxis" (Ojs.bool_to_js x333) - | None -> ()); - (match x126 with - | Some x332 -> Ojs.set_prop_ascii x215 "includeZero" (Ojs.bool_to_js x332) - | None -> ()); - (match x127 with - | Some x331 -> Ojs.set_prop_ascii x215 "logscale" (Ojs.bool_to_js x331) - | None -> ()); - (match x128 with - | Some x330 -> Ojs.set_prop_ascii x215 "panEdgeFraction" (Ojs.float_to_js x330) - | None -> ()); - (match x129 with - | Some x329 -> Ojs.set_prop_ascii x215 "valueRange" (Range.Spec.t_to_js x329) - | None -> ()); - (match x130 with - | Some x328 -> Ojs.set_prop_ascii x215 "xAxisHeight" (Ojs.int_to_js x328) - | None -> ()); - (match x131 with - | Some x327 -> Ojs.set_prop_ascii x215 "xRangePad" (Ojs.float_to_js x327) - | None -> ()); - (match x132 with - | Some x326 -> Ojs.set_prop_ascii x215 "yRangePad" (Ojs.float_to_js x326) - | None -> ()); - (match x133 with - | Some x325 -> Ojs.set_prop_ascii x215 "customBars" (Ojs.bool_to_js x325) - | None -> ()); - (match x134 with - | Some x324 -> Ojs.set_prop_ascii x215 "errorBars" (Ojs.bool_to_js x324) - | None -> ()); - (match x135 with - | Some x323 -> Ojs.set_prop_ascii x215 "fractions" (Ojs.bool_to_js x323) - | None -> ()); - (match x136 with - | Some x322 -> Ojs.set_prop_ascii x215 "title" (Ojs.string_to_js x322) - | None -> ()); - (match x137 with - | Some x321 -> Ojs.set_prop_ascii x215 "titleHeight" (Ojs.int_to_js x321) - | None -> ()); - (match x138 with - | Some x320 -> Ojs.set_prop_ascii x215 "xLabelHeight" (Ojs.int_to_js x320) - | None -> ()); - (match x139 with - | Some x319 -> Ojs.set_prop_ascii x215 "xlabel" (Ojs.string_to_js x319) - | None -> ()); - (match x140 with - | Some x318 -> Ojs.set_prop_ascii x215 "y2label" (Ojs.string_to_js x318) - | None -> ()); - (match x141 with - | Some x317 -> Ojs.set_prop_ascii x215 "yLabelWidth" (Ojs.int_to_js x317) - | None -> ()); - (match x142 with - | Some x316 -> Ojs.set_prop_ascii x215 "ylabel" (Ojs.string_to_js x316) - | None -> ()); - (match x143 with - | Some x315 -> Ojs.set_prop_ascii x215 "axes" (Axes.t_to_js x315) - | None -> ()); - (match x144 with - | Some x314 -> Ojs.set_prop_ascii x215 "connectSeparatedPoints" (Ojs.bool_to_js x314) - | None -> ()); - (match x145 with - | Some x313 -> Ojs.set_prop_ascii x215 "drawGapEdgePoints" (Ojs.bool_to_js x313) - | None -> ()); - (match x146 with - | Some x312 -> Ojs.set_prop_ascii x215 "drawPoints" (Ojs.bool_to_js x312) - | None -> ()); - (match x147 with - | Some x311 -> Ojs.set_prop_ascii x215 "fillGraph" (Ojs.bool_to_js x311) - | None -> ()); - (match x148 with - | Some x310 -> Ojs.set_prop_ascii x215 "pointSize" (Ojs.int_to_js x310) - | None -> ()); - (match x149 with - | Some x309 -> Ojs.set_prop_ascii x215 "stackedGraph" (Ojs.bool_to_js x309) - | None -> ()); - (match x150 with - | Some x308 -> Ojs.set_prop_ascii x215 "stackedGraphNaNFill" (Ojs.string_to_js x308) - | None -> ()); - (match x151 with - | Some x307 -> Ojs.set_prop_ascii x215 "stepPlot" (Ojs.bool_to_js x307) - | None -> ()); - (match x152 with - | Some x306 -> Ojs.set_prop_ascii x215 "strokeBorderColor" (Color.t_to_js x306) - | None -> ()); - (match x153 with - | Some x305 -> Ojs.set_prop_ascii x215 "strokeBorderWidth" (Ojs.float_to_js x305) - | None -> ()); - (match x154 with - | Some x304 -> Ojs.set_prop_ascii x215 "strokePattern" (Line_pattern.t_to_js x304) - | None -> ()); - (match x155 with - | Some x303 -> Ojs.set_prop_ascii x215 "strokeWidth" (Ojs.float_to_js x303) - | None -> ()); - (match x156 with - | Some x301 -> - Ojs.set_prop_ascii x215 "visibility" (Ojs.list_to_js Ojs.bool_to_js x301) - | None -> ()); - (match x157 with - | Some x300 -> Ojs.set_prop_ascii x215 "colorSaturation" (Ojs.float_to_js x300) - | None -> ()); - (match x158 with - | Some x299 -> Ojs.set_prop_ascii x215 "colorValue" (Ojs.float_to_js x299) - | None -> ()); - (match x159 with - | Some x297 -> Ojs.set_prop_ascii x215 "colors" (Ojs.array_to_js Color.t_to_js x297) - | None -> ()); - (match x160 with - | Some x296 -> Ojs.set_prop_ascii x215 "fillAlpha" (Ojs.float_to_js x296) - | None -> ()); - (match x161 with - | Some x295 -> Ojs.set_prop_ascii x215 "rollPeriod" (Ojs.int_to_js x295) - | None -> ()); - (match x162 with - | Some x294 -> Ojs.set_prop_ascii x215 "sigma" (Ojs.float_to_js x294) - | None -> ()); - (match x163 with - | Some x293 -> Ojs.set_prop_ascii x215 "wilsonInterval" (Ojs.bool_to_js x293) - | None -> ()); - (match x164 with - | Some x292 -> Ojs.set_prop_ascii x215 "drawGrid" (Ojs.bool_to_js x292) - | None -> ()); - (match x165 with - | Some x291 -> Ojs.set_prop_ascii x215 "gridLineColor" (Color.t_to_js x291) - | None -> ()); - (match x166 with - | Some x290 -> Ojs.set_prop_ascii x215 "gridLinePattern" (Line_pattern.t_to_js x290) - | None -> ()); - (match x167 with - | Some x289 -> Ojs.set_prop_ascii x215 "gridLineWidth" (Ojs.float_to_js x289) - | None -> ()); - (match x168 with - | Some x288 -> Ojs.set_prop_ascii x215 "animatedZooms" (Ojs.bool_to_js x288) - | None -> ()); - (match x169 with - | Some x287 -> Ojs.set_prop_ascii x215 "hideOverlayOnMouseOut" (Ojs.bool_to_js x287) - | None -> ()); - (match x170 with - | Some x286 -> Ojs.set_prop_ascii x215 "highlightCircleSize" (Ojs.int_to_js x286) - | None -> ()); - (match x171 with - | Some x285 -> - Ojs.set_prop_ascii x215 "highlightSeriesBackgroundAlpha" (Ojs.float_to_js x285) - | None -> ()); - (match x172 with - | Some x284 -> - Ojs.set_prop_ascii x215 "highlightSeriesBackgroundColor" (Color.t_to_js x284) - | None -> ()); - (match x173 with - | Some x283 -> - Ojs.set_prop_ascii x215 "highlightSeriesOpts" (Highlight_series_options.t_to_js x283) - | None -> ()); - (match x174 with - | Some x282 -> Ojs.set_prop_ascii x215 "showLabelsOnHighlight" (Ojs.bool_to_js x282) - | None -> ()); - (match x175 with - | Some x281 -> Ojs.set_prop_ascii x215 "showRoller" (Ojs.bool_to_js x281) - | None -> ()); - (match x176 with - | Some x280 -> Ojs.set_prop_ascii x215 "hideOverlayOnMouseOut" (Ojs.bool_to_js x280) - | None -> ()); - (match x177 with - | Some x278 -> Ojs.set_prop_ascii x215 "labels" (Ojs.list_to_js Ojs.string_to_js x278) - | None -> ()); - (match x178 with - | Some x277 -> Ojs.set_prop_ascii x215 "labelsDiv" (Ojs.string_to_js x277) - | None -> ()); - (match x179 with - | Some x276 -> Ojs.set_prop_ascii x215 "labelsDiv" (Native_node.t_to_js x276) - | None -> ()); - (match x180 with - | Some x275 -> Ojs.set_prop_ascii x215 "labelsSeparateLines" (Ojs.bool_to_js x275) - | None -> ()); - (match x181 with - | Some x274 -> Ojs.set_prop_ascii x215 "labelsShowZeroValues" (Ojs.bool_to_js x274) - | None -> ()); - (match x182 with - | Some x273 -> Ojs.set_prop_ascii x215 "legend" (Legend.t_to_js x273) - | None -> ()); - (match x183 with - | Some x271 -> - Ojs.set_prop_ascii - x215 - "legendFormatter" - (Ojs.fun_to_js 1 (fun (x272 : Ojs.t) -> - Ojs.string_to_js (x271 (Legend_data.t_of_js x272)))) - | None -> ()); - (match x184 with - | Some x270 -> Ojs.set_prop_ascii x215 "showLabelsOnHighlight" (Ojs.bool_to_js x270) - | None -> ()); - (match x185 with - | Some x269 -> Ojs.set_prop_ascii x215 "height" (Ojs.int_to_js x269) - | None -> ()); - (match x186 with - | Some x264 -> - Ojs.set_prop_ascii - x215 - "clickCallback" - (Ojs.fun_to_js 3 (fun (x265 : Ojs.t) (x266 : Ojs.t) (x267 : Ojs.t) -> - x264 - ~evt:x265 - ~x:(Ojs.float_of_js x266) - ~points:(Ojs.array_of_js Point.t_of_js x267))) - | None -> ()); - (match x187 with - | Some x256 -> - Ojs.set_prop_ascii - x215 - "highlightCallback" - (Ojs.fun_to_js - 5 - (fun - (x257 : Ojs.t) - (x258 : Ojs.t) - (x259 : Ojs.t) - (x261 : Ojs.t) - (x262 : Ojs.t) - -> - x256 - ~evt:x257 - ~x:(Ojs.float_of_js x258) - ~points:(Ojs.array_of_js Point.t_of_js x259) - ~row:(Ojs.int_of_js x261) - ~seriesName:(Ojs.option_of_js Ojs.string_of_js x262))) - | None -> ()); - (match x188 with - | Some x254 -> - Ojs.set_prop_ascii - x215 - "unhighlightCallback" - (Ojs.fun_to_js 1 (fun (x255 : Ojs.t) -> x254 ~evt:x255)) - | None -> ()); - (match x189 with - | Some x251 -> - Ojs.set_prop_ascii - x215 - "pointClickCallback" - (Ojs.fun_to_js 2 (fun (x252 : Ojs.t) (x253 : Ojs.t) -> - x251 ~evt:x252 ~point:(Point.t_of_js x253))) - | None -> ()); - (match x190 with - | Some x247 -> - Ojs.set_prop_ascii - x215 - "underlayCallback" - (Ojs.fun_to_js 3 (fun (x248 : Ojs.t) (x249 : Ojs.t) (x250 : Ojs.t) -> - x247 - ~context:(Canvas_rendering_context_2D.t_of_js x248) - ~area:(Area.t_of_js x249) - ~dygraph:x250)) - | None -> ()); - (match x191 with - | Some x244 -> - Ojs.set_prop_ascii - x215 - "drawCallback" - (Ojs.fun_to_js 2 (fun (x245 : Ojs.t) (x246 : Ojs.t) -> - x244 ~graph:x245 ~isInitial:(Ojs.bool_of_js x246))) - | None -> ()); - (match x192 with - | Some x239 -> - Ojs.set_prop_ascii - x215 - "zoomCallback" - (Ojs.fun_to_js 3 (fun (x240 : Ojs.t) (x241 : Ojs.t) (x242 : Ojs.t) -> - x239 - ~xmin:(Ojs.float_of_js x240) - ~xmax:(Ojs.float_of_js x241) - ~yRanges:(Ojs.array_of_js Range.t_of_js x242))) - | None -> ()); - (match x193 with - | Some x238 -> Ojs.set_prop_ascii x215 "pixelRatio" (Ojs.float_to_js x238) - | None -> ()); - (match x194 with - | Some x236 -> Ojs.set_prop_ascii x215 "plotter" (Ojs.list_to_js Plotter.t_to_js x236) - | None -> ()); - (match x195 with - | Some x235 -> Ojs.set_prop_ascii x215 "rightGap" (Ojs.int_to_js x235) - | None -> ()); - (match x196 with - | Some x234 -> Ojs.set_prop_ascii x215 "width" (Ojs.int_to_js x234) - | None -> ()); - (match x197 with - | Some x233 -> Ojs.set_prop_ascii x215 "rangeSelectorAlpha" (Ojs.float_to_js x233) - | None -> ()); - (match x198 with - | Some x232 -> - Ojs.set_prop_ascii x215 "rangeSelectorBackgroundLineWidth" (Ojs.float_to_js x232) - | None -> ()); - (match x199 with - | Some x231 -> - Ojs.set_prop_ascii x215 "rangeSelectorBackgroundStrokeColor" (Color.t_to_js x231) - | None -> ()); - (match x200 with - | Some x230 -> - Ojs.set_prop_ascii x215 "rangeSelectorForegroundLineWidth" (Ojs.float_to_js x230) - | None -> ()); - (match x201 with - | Some x229 -> - Ojs.set_prop_ascii x215 "rangeSelectorForegroundStrokeColor" (Color.t_to_js x229) - | None -> ()); - (match x202 with - | Some x228 -> Ojs.set_prop_ascii x215 "rangeSelectorHeight" (Ojs.int_to_js x228) - | None -> ()); - (match x203 with - | Some x227 -> - Ojs.set_prop_ascii x215 "rangeSelectorPlotFillColor" (Color.t_to_js x227) - | None -> ()); - (match x204 with - | Some x226 -> - Ojs.set_prop_ascii x215 "rangeSelectorPlotFillGradientColor" (Color.t_to_js x226) - | None -> ()); - (match x205 with - | Some x225 -> - Ojs.set_prop_ascii x215 "rangeSelectorPlotLineWidth" (Ojs.float_to_js x225) - | None -> ()); - (match x206 with - | Some x224 -> - Ojs.set_prop_ascii x215 "rangeSelectorPlotStrokeColor" (Color.t_to_js x224) - | None -> ()); - (match x207 with - | Some x223 -> Ojs.set_prop_ascii x215 "showRangeSelector" (Ojs.bool_to_js x223) - | None -> ()); - (match x208 with - | Some x222 -> Ojs.set_prop_ascii x215 "series" (Series.t_to_js x222) - | None -> ()); - (match x209 with - | Some x221 -> Ojs.set_prop_ascii x215 "digitsAfterDecimal" (Ojs.int_to_js x221) - | None -> ()); - (match x210 with - | Some x220 -> Ojs.set_prop_ascii x215 "labelsKMB" (Ojs.bool_to_js x220) - | None -> ()); - (match x211 with - | Some x219 -> Ojs.set_prop_ascii x215 "labelsKMG2" (Ojs.bool_to_js x219) - | None -> ()); - (match x212 with - | Some x218 -> Ojs.set_prop_ascii x215 "labelsUTC" (Ojs.bool_to_js x218) - | None -> ()); - (match x213 with - | Some x217 -> Ojs.set_prop_ascii x215 "maxNumberWidth" (Ojs.int_to_js x217) - | None -> ()); - (match x214 with - | Some x216 -> Ojs.set_prop_ascii x215 "sigFigs" (Ojs.int_to_js x216) - | None -> ()); - t_of_js x215 -;; - -let legendFormatter : t -> (Legend_data.t -> string) option = - fun (x341 : t) -> - Ojs.option_of_js - (fun (x342 : Ojs.t) (x343 : Legend_data.t) -> - Ojs.string_of_js (Ojs.apply x342 [| Legend_data.t_to_js x343 |])) - (Ojs.get_prop_ascii (t_to_js x341) "legendFormatter") -;; - -let zoomCallback : t -> (xmin:float -> xmax:float -> yRanges:Range.t array -> unit) option - = - fun (x344 : t) -> - Ojs.option_of_js - (fun (x345 : Ojs.t) - ~xmin:(x346 : float) - ~xmax:(x347 : float) - ~yRanges:(x348 : Range.t array) -> - ignore - (Ojs.apply - x345 - [| Ojs.float_to_js x346 - ; Ojs.float_to_js x347 - ; Ojs.array_to_js Range.t_to_js x348 - |])) - (Ojs.get_prop_ascii (t_to_js x344) "zoomCallback") -;; - -let height : t -> int option = - fun (x350 : t) -> - Ojs.option_of_js Ojs.int_of_js (Ojs.get_prop_ascii (t_to_js x350) "height") -;; - -let width : t -> int option = - fun (x352 : t) -> - Ojs.option_of_js Ojs.int_of_js (Ojs.get_prop_ascii (t_to_js x352) "width") -;; - -let merge_internal : t -> prefer:t -> t = - fun (x354 : t) ~prefer:(x355 : t) -> - t_of_js - (Ojs.call - (Ojs.get_prop_ascii Ojs.global "_") - "merge" - [| t_to_js x354; t_to_js x355 |]) -;; - -let merge t ~prefer = create () |> merge_internal ~prefer:t |> merge_internal ~prefer diff --git a/bindings/dygraph/src/options.mli b/bindings/dygraph/src/options.mli deleted file mode 100644 index d6a69c12..00000000 --- a/bindings/dygraph/src/options.mli +++ /dev/null @@ -1,1483 +0,0 @@ -open! Core -open! Import -open Gen_js_api - -(** Options Reference: http://dygraphs.com/options.html - - Note: A few callbacks are not bound. Feel free to add them! *) - -module Line_pattern : sig - (** A custom pattern array where the even index is a draw and odd is a space in - pixels. If null then it draws a solid line. The array should have a even length as - any odd lengthed array could be expressed as a smaller even length array. This is - used to create dashed gridlines. *) - type t = int array - - (** This is the same value as Dygraph.DASHED_LINE. For reasons that are not - fully understood (or even partially, let's be honest), the "Dygraph" value - isn't present in the global namespace when run in node-js, so [@@js.global - "Dygraph.DASHED_LINE"] will crash the program. *) - val dashed : t - [@@js.custom let dashed = [| 7; 2 |]] - - val t_of_js : Ojs.t -> t - val t_to_js : t -> Ojs.t -end - -module Legend : sig - type t = - ([ `always - | `follow - | `never - | `onmouseover - ] - [@js.enum]) - - val t_to_js : t -> Ojs.t -end - -module Series_options : sig - type t - - val t_of_js : Ojs.t -> t - val t_to_js : t -> Ojs.t - - val create - : ?axis:Which_y_axis.t - (** axis http://dygraphs.com/options.html#axis - - Set to either 'y1' or 'y2' to assign a series to a y-axis (primary or - secondary). Must be set per-series. - - Type: string - Default: (none) - *) - -> ?color:Color.t - (** color http://dygraphs.com/options.html#color - - A per-series color definition. Used in conjunction with, and overrides, the colors - option. - - Type: string - Default: (see description) - *) - -> ?drawPoints:bool - (** drawPoints http://dygraphs.com/options.html#drawPoints - - Draw a small dot at each point, in addition to a line going through the - point. This makes the individual data points easier to see, but can increase - visual clutter in the chart. The small dot can be replaced with a custom - rendering by supplying a drawPointCallback. - - Type: boolean - Default: false - *) - -> ?drawHighlightPointCallback: - (graph:Ojs.t - -> seriesName:string option - -> context:Canvas_rendering_context_2D.t - -> cx:float - -> cy:float - -> color:Ojs.t - -> pointSize:int - -> idx:int - -> unit) - (** drawHighlightPointCallback: https://dygraphs.com/options.html#drawHighlightPointCallback - - Draw a custom item when a point is highlighted. Default is a small dot matching the - series color. This method should constrain drawing to within pointSize pixels from - (cx, cy) Also see drawPointCallback. - - Type: function(g, seriesName, canvasContext, cx, cy, color, pointSize) - g: the reference graph - seriesName: the name of the series - canvasContext: the canvas to draw on - cx: center x coordinate - cy: center y coordinate - color: series color - pointSize: the radius of the image. - idx: the row-index of the point in the data. - Default: null - *) - -> ?drawPointCallback: - (graph:Ojs.t - -> seriesName:string option - -> context:Canvas_rendering_context_2D.t - -> cx:float - -> cy:float - -> color:Ojs.t - -> pointSize:int - -> idx:int - -> unit) - (** drawPointCallback: https://dygraphs.com/options.html#drawPointCallback - - Draw a custom item when drawPoints is enabled. Default is a small dot matching the - series color. This method should constrain drawing to within pointSize pixels from - (cx, cy). Also see drawHighlightPointCallback. - - Type: function(g, seriesName, canvasContext, cx, cy, color, pointSize) - g: the reference graph - seriesName: the name of the series - canvasContext: the canvas to draw on - cx: center x coordinate - cy: center y coordinate - color: series color - pointSize: the radius of the image. - idx: the row-index of the point in the data. - Default: null - *) - -> ?plotter:Plotter.t - (** plotter (undocumented) - - The Dygraph documentation merely says: - - TODO(danvk): more details! May be set per-series. - - Although the graph-wide default [plotter] can take an array of plotters, the - series-specific plotter must be a single value. - *) - -> ?plotterFinishedCallback:(context:Canvas_rendering_context_2D.t -> unit) - (** (Jane Street extension) - - [plotterFinishedCallback] is called every time a plotter finishes rendering this - series. - - If you specify a custom plotter for this series, this callback will be called - exactly once. - - If you don't specify a custom plotter for this series, it will be called once for - each of the graph's configured plotters. Note that some plotters may not actually - draw anything to the canvas depending on the configured attributes of this series - (e.g. the error bar plotter will only draw something if you request error bars), - but the callback will still fire after the plotter runs. - *) - -> ?showInRangeSelector:bool - (** showInRangeSelector http://dygraphs.com/options.html#showInRangeSelector - - Mark this series for inclusion in the range selector. The mini plot curve - will be an average of all such series. If this is not specified for any - series, the default behavior is to average all the visible series. Setting it - for one series will result in that series being charted alone in the range - selector. Once it's set for a single series, it needs to be set for all - series which should be included (regardless of visibility). - - Type: boolean - Default: null - *) - -> ?stepPlot:bool - (** stepPlot https://dygraphs.com/options.html#stepPlot - - When set, display the graph as a step plot instead of a line plot. This option may - either be set for the whole graph or for single series. - - Type: boolean - Default: false - *) - -> ?strokePattern:Line_pattern.t - (** strokePattern http://dygraphs.com/options.html#strokePattern - - A custom pattern array where the even index is a draw and odd is a space in - pixels. If null then it draws a solid line. The array should have a even - length as any odd lengthed array could be expressed as a smaller even length - array. This is used to create dashed lines. - - Type: array - Default: null - *) - -> ?strokeWidth:float - (** strokeWidth http://dygraphs.com/options.html#strokeWidth - - The width of the lines connecting data points. This can be - used to increase the contrast or some graphs. - - Type: float - Default: 1.0 - *) - -> unit - -> t - [@@js.builder] -end - -module Series : sig - type t - - val t_of_js : Ojs.t -> t - val t_to_js : t -> Ojs.t - - val create : (string * Series_options.t) list -> t - [@@js.custom - let create data = - data |> List.Assoc.map ~f:Series_options.t_to_js |> Array.of_list |> Ojs.obj - ;;] -end - -module Opts : sig - (** a function which provides access to various options on the dygraph, - e.g. opts('labelsKMB'). - - See [axisLabelFormatter] in [Axis_options.create] for an example of how you might - get your hands on one of these. - - For an example of how to use this, see [X_axis_mapping.t]. *) - type t -end - -module Axis_options : sig - type t - - val t_of_js : Ojs.t -> t - val t_to_js : t -> Ojs.t - - val create - : ?axisLabelFormatter:(Number_or_js_date.t -> Granularity.t -> Opts.t -> string) - (** axisLabelFormatter http://dygraphs.com/options.html#axisLabelFormatter - - Function to call to format the tick values that appear along an axis. This is - usually set on a per-axis basis. - - Type: function(number or Date, granularity, opts, dygraph) - - number or date: Either a number (for a numeric axis) or a Date object (for a date - axis) - - granularity: specifies how fine-grained the axis is. For date axes, this is a - reference to the time granularity enumeration, defined in dygraph-tickers.js, - e.g. Dygraph.WEEKLY. - - opts: a function which provides access to various options on the dygraph, - e.g. opts('labelsKMB'). - - dygraph: the referenced graph - - Default: Depends on the data type - Gallery Samples: NONE - Other Examples: value-axis-formatters x-axis-formatter - *) - -> ?valueFormatter:(float -> Opts.t -> string) - (** valueFormatter http://dygraphs.com/options.html#valueFormatter - - Function to provide a custom display format for the values displayed on - mouseover. This does not affect the values that appear on tick marks next to the - axes. To format those, see axisLabelFormatter. This is usually set on a per-axis - basis. . - - Type: function(num or millis, opts, seriesName, dygraph, row, col) - - num_or_millis: The value to be formatted. This is always a number. For date axes, - it's millis since epoch. You can call new Date(millis) to get a Date object. - - opts: This is a function you can call to access various options - (e.g. opts('labelsKMB')). It returns per-axis values for the option when - available. - - seriesName: The name of the series from which the point came, e.g. 'X', 'Y', 'A', - etc. - - dygraph: The dygraph object for which the formatting is being done - - row: The row of the data from which this point comes. g.getValue(row, 0) will - return the x-value for this point. - - col: The column of the data from which this point comes. g.getValue(row, col) will - return the original y-value for this point. This can be used to get the full - confidence interval for the point, or access un-rolled values for the point. - - Default: Depends on the type of your data. - Gallery Samples: NONE - Other Examples: hairlines labelsKMB multi-scale value-axis-formatters - *) - -> ?axisLabelWidth:int - (** axisLabelWidth http://dygraphs.com/options.html#axisLabelWidth - - Width (in pixels) of the containing divs for x- and y-axis labels. For the - y-axis, this also controls the width of the y-axis. Note that for the x-axis, - this is independent from pixelsPerLabel, which controls the spacing between - labels. - - Type: integer - Default: 50 (y-axis), 60 (x-axis) - *) - -> ?axisLineColor:Color.t - (** axisLineColor http://dygraphs.com/options.html#axisLineColor - - Color of the x- and y-axis lines. Accepts any value which the HTML canvas - strokeStyle attribute understands, e.g. 'black' or 'rgb(0, 100, 255)'. - - Type: string - Default: black - *) - -> ?axisLineWidth:float - (** axisLineWidth http://dygraphs.com/options.html#axisLineWidth - - Thickness (in pixels) of the x- and y-axis lines. - - Type: float - Default: 0.3 - *) - -> ?axisTickSize:float - (** axisTickSize http://dygraphs.com/options.html#axisTickSize - - The size of the line to display next to each tick mark on x- or y-axes. - - Type: number - Default: 3.0 - *) - -> ?drawAxis:bool - (** drawAxis http://dygraphs.com/options.html#drawAxis - - Whether to draw the specified axis. This may be set on a per-axis basis to - define the visibility of each axis separately. Setting this to false also - prevents axis ticks from being drawn and reclaims the space for the chart - grid/lines. - - Type: boolean - Default: true for x and y, false for y2 - *) - -> ?includeZero:bool - (** includeZero http://dygraphs.com/options.html#includeZero - - Usually, dygraphs will use the range of the data plus some padding to set the - range of the y-axis. If this option is set, the y-axis will always include zero, - typically as the lowest value. This can be used to avoid exaggerating the - variance in the data - - Type: boolean - Default: false - *) - -> ?independentTicks:bool - (** independentTicks http://dygraphs.com/options.html#independentTicks - - Only valid for y and y2, has no effect on x: This option defines whether the y - axes should align their ticks or if they should be independent. Possible - combinations: 1.) y=true, y2=false (default): y is the primary axis and the y2 - ticks are aligned to the the ones of y. (only 1 grid) 2.) y=false, y2=true: y2 - is the primary axis and the y ticks are aligned to the the ones of y2. (only 1 - grid) 3.) y=true, y2=true: Both axis are independent and have their own - ticks. (2 grids) 4.) y=false, y2=false: Invalid configuration causes an error. - - Type: boolean - Default: true for y, false for y2 - *) - -> ?logscale:bool - (** logscale http://dygraphs.com/options.html#logscale - - When set for the y-axis or x-axis, the graph shows that axis in log scale. Any - values less than or equal to zero are not displayed. Showing log scale with - ranges that go below zero will result in an unviewable graph. Not compatible - with showZero. connectSeparatedPoints is ignored. This is ignored for date-based - x-axes. - - Type: boolean - Default: false - *) - -> ?pixelsPerLabel:int - (** pixelsPerLabel http://dygraphs.com/options.html#pixelsPerLabel - - Number of pixels to require between each x- and y-label. Larger values will yield - a sparser axis with fewer ticks. This is set on a per-axis basis. - - Type: integer - Default: 70 (x-axis) or 30 (y-axes) *) - -> ?valueRange:Range.Spec.t - (** valueRange http://dygraphs.com/options.html#valueRange - - Explicitly set the vertical range of the graph to [low, high]. This may be set on - a per-axis basis to define each y-axis separately. If either limit is unspecified, - it will be calculated automatically (e.g. [null, 30] to automatically calculate - just the lower bound) - - Type: Array of two numbers - Default: Full range of the input is shown - *) - -> ?drawGrid:bool - (** drawGrid http://dygraphs.com/options.html#drawGrid - - Whether to display gridlines in the chart. This may be set on a per-axis - basis to define the visibility of each axis' grid separately. - - Type: boolean - Default: true for x and y, false for y2 - *) - -> ?gridLineColor:Color.t - (** gridLineColor http://dygraphs.com/options.html#gridLineColor - - The color of the gridlines. This may be set on a per-axis basis to define - each axis' grid separately. - - Type: red, blue - Default: rgb(128,128,128) - *) - -> ?gridLinePattern:Line_pattern.t - (** gridLinePattern http://dygraphs.com/options.html#gridLinePattern - - A custom pattern array where the even index is a draw and odd is a space in - pixels. If null then it draws a solid line. The array should have a even - length as any odd lengthed array could be expressed as a smaller even length - array. This is used to create dashed gridlines. - - Type: array - Default: null - *) - -> ?gridLineWidth:float - (** gridLineWidth http://dygraphs.com/options.html#gridLineWidth - - Thickness (in pixels) of the gridlines drawn under the chart. The - vertical/horizontal gridlines can be turned off entirely by using the - drawGrid option. This may be set on a per-axis basis to define each axis' - grid separately. - - Type: float - Default: 0.3 - *) - -> ?pixelsPerLabel:int - (** pixelsPerLabel http://dygraphs.com/options.html#pixelsPerLabel - - Number of pixels to require between each x- and y-label. Larger values will - yield a sparser axis with fewer ticks. This is set on a per-axis basis. - - Type: integer - Default: 70 (x-axis) or 30 (y-axes) - *) - -> unit - -> t - [@@js.builder] -end - -module Axes : sig - type t - - val t_of_js : Ojs.t -> t - val t_to_js : t -> Ojs.t - - val create : ?x:Axis_options.t -> ?y:Axis_options.t -> ?y2:Axis_options.t -> unit -> t - [@@js.builder] -end - -module Highlight_series_options : sig - type t - - val t_of_js : Ojs.t -> t - val t_to_js : t -> Ojs.t - - val create - : ?highlightCircleSize:int - (** highlightCircleSize http://dygraphs.com/options.html#highlightCircleSize - The size in pixels of the dot drawn over highlighted points. - - Type: integer - Default: 3 - *) - -> ?strokeWidth:float - (** strokeWidth http://dygraphs.com/options.html#strokeWidth - - The width of the lines connecting data points. This can be used to increase the - contrast or some graphs. - - Type: float - Default: 1.0 - *) - -> ?strokeBorderWidth:float - (** strokeBorderWidth http://dygraphs.com/options.html#strokeBorderWidth - - Draw a border around graph lines to make crossing lines more easily - distinguishable. Useful for graphs with many lines. - - Type: float - Default: null - *) - -> unit - -> t - [@@js.builder] -end - -type t - -val t_of_js : Ojs.t -> t -val t_to_js : t -> Ojs.t - -[@@@ocamlformat "disable"] - -val create - : ?axisLabelFontSize:int - (** Axis display: - - axisLabelFontSize http://dygraphs.com/options.html#LabelFontSize - Size of the font (in pixels) to use in the axis labels, both x- and y-axis. - - Type: integer - Default: 14 - *) - -> ?axisLabelWidth:int - (** axisLabelWidth http://dygraphs.com/options.html#axisLabelWidth - - Width (in pixels) of the containing divs for x- and y-axis labels. For the - y-axis, this also controls the width of the y-axis. Note that for the x-axis, - this is independent from pixelsPerLabel, which controls the spacing between - labels. - - Type: integer - Default: 50 (y-axis), 60 (x-axis) - *) - -> ?axisLineColor:Color.t - (** axisLineColor http://dygraphs.com/options.html#axisLineColor - - Color of the x- and y-axis lines. Accepts any value which the HTML canvas - strokeStyle attribute understands, e.g. 'black' or 'rgb(0, 100, 255)'. - - Type: string - Default: black - *) - -> ?axisLineWidth:float - (** axisLineWidth http://dygraphs.com/options.html#axisLineWidth - - Thickness (in pixels) of the x- and y-axis lines. - - Type: float - Default: 0.3 - *) - -> ?axisTickSize:float - (** axisTickSize http://dygraphs.com/options.html#axisTickSize - - The size of the line to display next to each tick mark on x- or y-axes. - - Type: number - Default: 3.0 - *) - -> ?dateWindow:Range.t - (** dateWindow http://dygraphs.com/options.html#dateWindow - - Initially zoom in on a section of the graph. Is of the form [earliest, latest], - where earliest/latest are milliseconds since epoch. If the data for the x-axis - is numeric, the values in dateWindow must also be numbers. - - Type: Array of two numbers - Default: Full range of the input is shown - *) - -> ?drawAxesAtZero:bool - (** drawAxesAtZero http://dygraphs.com/options.html#drawAxesAtZero - - When set, draw the X axis at the Y=0 position and the Y axis at the X=0 position - if those positions are inside the graph's visible area. Otherwise, draw the axes - at the bottom or left graph edge as usual. - - Type: boolean - Default: false - *) - -> ?drawAxis:bool - (** drawAxis http://dygraphs.com/options.html#drawAxis - - Whether to draw the specified axis. This may be set on a per-axis basis to - define the visibility of each axis separately. Setting this to false also - prevents axis ticks from being drawn and reclaims the space for the chart - grid/lines. - - Type: boolean - Default: true for x and y, false for y2 - *) - -> ?includeZero:bool - (** includeZero http://dygraphs.com/options.html#includeZero - - Usually, dygraphs will use the range of the data plus some padding to set the - range of the y-axis. If this option is set, the y-axis will always include zero, - typically as the lowest value. This can be used to avoid exaggerating the - variance in the data - - Type: boolean - Default: false - *) - -> ?logscale:bool - (** logscale http://dygraphs.com/options.html#logscale - - When set for the y-axis or x-axis, the graph shows that axis in log scale. Any - values less than or equal to zero are not displayed. Showing log scale with - ranges that go below zero will result in an unviewable graph. Not compatible - with showZero. connectSeparatedPoints is ignored. This is ignored for date-based - x-axes. - - Type: boolean - Default: false - *) - -> ?panEdgeFraction:float - (** panEdgeFraction http://dygraphs.com/options.html#panEdgeFraction - - A value representing the farthest a graph may be panned, in percent of the - display. For example, a value of 0.1 means that the graph can only be panned 10% - passed the edges of the displayed values. null means no bounds. - - Type: float - Default: null - *) - -> ?valueRange:Range.Spec.t - (** valueRange http://dygraphs.com/options.html#valueRange - - Explicitly set the vertical range of the graph to [low, high]. This may be set on - a per-axis basis to define each y-axis separately. If either limit is unspecified, - it will be calculated automatically (e.g. [null, 30] to automatically calculate - just the lower bound) - - Type: Array of two numbers - Default: Full range of the input is shown - *) - -> ?xAxisHeight:int - (** xAxisHeight http://dygraphs.com/options.html#xAxisHeight - - Height, in pixels, of the x-axis. If not set explicitly, this is computed based on - axisLabelFontSize and axisTickSize. - - Type: integer - Default: (null) - *) - -> ?xRangePad:float - (** xRangePad http://dygraphs.com/options.html#xRangePad - - Add the specified amount of extra space (in pixels) around the X-axis value range - to ensure points at the edges remain visible. - - Type: float - Default: 0 - *) - -> ?yRangePad:float - (** yRangePad http://dygraphs.com/options.html#yRangePad - - If set, add the specified amount of extra space (in pixels) around the Y-axis - value range to ensure points at the edges remain visible. If unset, use the - traditional Y padding algorithm. - - Type: float - Default: null - *) - -> ?customBars:bool - (** customBars http://dygraphs.com/options.html#customBars - - When set, parse each CSV cell as "low;middle;high". Error bars will be drawn for - each point between low and high, with the series itself going through middle. - - Type: boolean - Default: false - *) - -> ?errorBars:bool - (** errorBars http://dygraphs.com/options.html#errorBars - - Does the data contain standard deviations? Setting this to true alters the input - format (see above). - - Type: boolean - Default: false - *) - -> ?fractions:bool - (** fractions http://dygraphs.com/options.html#fractions - - When set, attempt to parse each cell in the CSV file as "a/b", where a and b are - integers. The ratio will be plotted. This allows computation of Wilson confidence - intervals (see below). - - Type: boolean - Default: false - *) - -> ?title:string - (** title http://dygraphs.com/options.html#title - - Text to display above the chart. You can supply any HTML for this value, not just - text. If you wish to style it using CSS, use the 'dygraph-label' or - 'dygraph-title' classes. - - Type: string - Default: null - *) - -> ?titleHeight:int - (** titleHeight http://dygraphs.com/options.html#titleHeight - - Height of the chart title, in pixels. This also controls the default font size of - the title. If you style the title on your own, this controls how much space is set - aside above the chart for the title's div. - - Type: integer - Default: 18 - *) - -> ?xLabelHeight:int - (** xLabelHeight http://dygraphs.com/options.html#xLabelHeight - - Height of the x-axis label, in pixels. This also controls the default font size of - the x-axis label. If you style the label on your own, this controls how much space - is set aside below the chart for the x-axis label's div. - - Type: integer - Default: 18 - *) - -> ?xlabel:string - (** xlabel http://dygraphs.com/options.html#xlabel - - Text to display below the chart's x-axis. You can supply any HTML for this value, - not just text. If you wish to style it using CSS, use the 'dygraph-label' or - 'dygraph-xlabel' classes. - - Type: string - Default: null - *) - -> ?y2label:string - (** y2label http://dygraphs.com/options.html#y2label - - Text to display to the right of the chart's secondary y-axis. This label is only - displayed if a secondary y-axis is present. See this test for an example of how to - do this. The comments for the 'ylabel' option generally apply here as well. This - label gets a 'dygraph-y2label' instead of a 'dygraph-ylabel' class. - - Type: string - Default: null - *) - -> ?yLabelWidth:int - (** yLabelWidth http://dygraphs.com/options.html#yLabelWidth - - Width of the div which contains the y-axis label. Since the y-axis label appears - rotated 90 degrees, this actually affects the height of its div. - - Type: integer - Default: 18 - *) - -> ?ylabel:string - (** ylabel http://dygraphs.com/options.html#ylabel - - Text to display to the left of the chart's y-axis. You can supply any HTML for - this value, not just text. If you wish to style it using CSS, use the - 'dygraph-label' or 'dygraph-ylabel' classes. The text will be rotated 90 degrees - by default, so CSS rules may behave in unintuitive ways. No additional space is - set aside for a y-axis label. If you need more space, increase the width of the - y-axis tick labels using the yAxisLabelWidth option. If you need a wider div for - the y-axis label, either style it that way with CSS (but remember that it's - rotated, so width is controlled by the 'height' property) or set the yLabelWidth - option. - - Type: string - Default: null - *) - -> ?axes:Axes.t - (** axes http://dygraphs.com/options.html#axes - - Defines per-axis options. Valid keys are 'x', 'y' and 'y2'. Only some options may - be set on a per-axis basis. If an option may be set in this way, it will be noted - on this page. See also documentation on per-series and per-axis options. - - Type: Object - Default: null - *) - -> ?connectSeparatedPoints:bool - (** connectSeparatedPoints http://dygraphs.com/options.html#connectSeparatedPoints - - Usually, when Dygraphs encounters a missing value in a data series, it interprets - this as a gap and draws it as such. If, instead, the missing values represents an - x-value for which only a different series has data, then you'll want to connect - the dots by setting this to true. To explicitly include a gap with this option - set, use a value of NaN. - - Type: boolean - Default: false - *) - -> ?drawGapEdgePoints:bool - (** drawGapEdgePoints http://dygraphs.com/options.html#drawGapEdgePoints - - Draw points at the edges of gaps in the data. This improves visibility of small - data segments or other data irregularities. - - Type: boolean - Default: false - *) - -> ?drawPoints:bool - (** drawPoints http://dygraphs.com/options.html#drawPoints - - Draw a small dot at each point, in addition to a line going through the - point. This makes the individual data points easier to see, but can increase - visual clutter in the chart. The small dot can be replaced with a custom - rendering by supplying a drawPointCallback. - - Type: boolean - Default: false - *) - -> ?fillGraph:bool - (** fillGraph http://dygraphs.com/options.html#fillGraph - - Should the area underneath the graph be filled? This option is not compatible - with error bars. This may be set on a per-series basis. - - Type: boolean - Default: false - *) - -> ?pointSize:int - (** pointSize http://dygraphs.com/options.html#pointSize - - The size of the dot to draw on each point in pixels (see drawPoints). A dot is always - drawn when a point is "isolated", i.e. there is a missing point on either side of - it. This also controls the size of those dots. - - Type: integer - Default: 1 - *) - -> ?stackedGraph:bool - (** stackedGraph http://dygraphs.com/options.html#stackedGraph - - If set, stack series on top of one another rather than drawing them - independently. The first series specified in the input data will wind up on - top of the chart and the last will be on bottom. NaN values are drawn as - white areas without a line on top, see stackedGraphNaNFill for details. - - Type: boolean - Default: false - *) - -> ?stackedGraphNaNFill:string - (** stackedGraphNaNFill http://dygraphs.com/options.html#stackedGraphNaNFill - - Controls handling of NaN values inside a stacked graph. NaN values are - interpolated/extended for stacking purposes, but the actual point value - remains NaN in the legend display. Valid option values are "all" (interpolate - internally, repeat leftmost and rightmost value as needed), "inside" - (interpolate internally only, use zero outside leftmost and rightmost value), - and "none" (treat NaN as zero everywhere). - - Type: string - Default: all - *) - -> ?stepPlot:bool - (** stepPlot http://dygraphs.com/options.html#stepPlot - - When set, display the graph as a step plot instead of a line plot. This - option may either be set for the whole graph or for single series. - - Type: boolean - Default: false - *) - -> ?strokeBorderColor:Color.t - (** strokeBorderColor http://dygraphs.com/options.html#strokeBorderColor - - Color for the line border used if strokeBorderWidth is set. - - Type: string - Default: white - *) - -> ?strokeBorderWidth:float - (** strokeBorderWidth http://dygraphs.com/options.html#strokeBorderWidth - - Draw a border around graph lines to make crossing lines more easily - distinguishable. Useful for graphs with many lines. - - Type: float - Default: null - *) - -> ?strokePattern:Line_pattern.t - (** strokePattern http://dygraphs.com/options.html#strokePattern - - A custom pattern array where the even index is a draw and odd is a space in - pixels. If null then it draws a solid line. The array should have a even - length as any odd lengthed array could be expressed as a smaller even length - array. This is used to create dashed lines. - - Type: array - Default: null - *) - -> ?strokeWidth:float - (** strokeWidth http://dygraphs.com/options.html#strokeWidth - - The width of the lines connecting data points. This can be - used to increase the contrast or some graphs. - - Type: float - Default: 1.0 - *) - -> ?visibility:bool list - (** visibility http://dygraphs.com/options.html#visibility - - Which series should initially be visible? Once the Dygraph has been - constructed, you can access and modify the visibility of each series using - the visibility and setVisibility methods. - - Type: Array of booleans - Default: [true, true, ...] - *) - -> ?colorSaturation:float - (** colorSaturation http://dygraphs.com/options.html#colorSaturation - - If colors is not specified, saturation of the - automatically-generated data series colors. - - Type: float (0.0 - 1.0) - Default: 1.0 - *) - -> ?colorValue:float - (** colorValue http://dygraphs.com/options.html#colorValue - - If colors is not specified, value of the data series colors, as in - hue/saturation/value. (0.0-1.0, default 0.5) - - Type: float (0.0 - 1.0) - Default: 1.0 - *) - -> ?colors:Color.t array - (** colors http://dygraphs.com/options.html#colors - - List of colors for the data series. These can be of the form "#AABBCC" or - "rgb(255,100,200)" or "yellow", etc. If not specified, equally-spaced points - around a color wheel are used. Overridden by the 'color' option. - - Type: array - Default: (see description) - *) - -> ?fillAlpha:float - (** fillAlpha http://dygraphs.com/options.html#fillAlpha - - Error bars (or custom bars) for each series are drawn in the same color as - the series, but with partial transparency. This sets the transparency. A - value of 0.0 means that the error bars will not be drawn, whereas a value of - 1.0 means that the error bars will be as dark as the line for the series - itself. This can be used to produce chart lines whose thickness varies at - each point. - - Type: float (0.0 - 1.0) - Default: 0.15 - *) - -> ?rollPeriod:int - (** rollPeriod http://dygraphs.com/options.html#rollPeriod - - Number of days over which to average data. Discussed extensively above. - - Type: integer >= 1 - Default: 1 - *) - -> ?sigma:float - (** sigma http://dygraphs.com/options.html#sigma - - When errorBars is set, shade this many standard deviations above/below each - point. - - Type: float - Default: 2.0 - *) - -> ?wilsonInterval:bool - (** wilsonInterval http://dygraphs.com/options.html#wilsonInterval - - Use in conjunction with the "fractions" option. Instead of plotting +/- N - standard deviations, dygraphs will compute a Wilson confidence interval and - plot that. This has more reasonable behavior for ratios close to 0 or 1. - - Type: boolean - Default: true - *) - -> ?drawGrid:bool - (** drawGrid http://dygraphs.com/options.html#drawGrid - - Whether to display gridlines in the chart. This may be set on a per-axis - basis to define the visibility of each axis' grid separately. - - Type: boolean - Default: true for x and y, false for y2 - *) - -> ?gridLineColor:Color.t - (** gridLineColor http://dygraphs.com/options.html#gridLineColor - - The color of the gridlines. This may be set on a per-axis basis to define - each axis' grid separately. - - Type: red, blue - Default: rgb(128,128,128) - *) - -> ?gridLinePattern:Line_pattern.t - (** gridLinePattern http://dygraphs.com/options.html#gridLinePattern - - A custom pattern array where the even index is a draw and odd is a space in - pixels. If null then it draws a solid line. The array should have a even - length as any odd lengthed array could be expressed as a smaller even length - array. This is used to create dashed gridlines. - - Type: array - Default: null - *) - -> ?gridLineWidth:float - (** gridLineWidth http://dygraphs.com/options.html#gridLineWidth - - Thickness (in pixels) of the gridlines drawn under the chart. The - vertical/horizontal gridlines can be turned off entirely by using the - drawGrid option. This may be set on a per-axis basis to define each axis' - grid separately. - - Type: float - Default: 0.3 - *) - -> ?animatedZooms:bool - (** animatedZooms http://dygraphs.com/options.html#animatedZooms - - Set this option to animate the transition between zoom windows. Applies to - programmatic and interactive zooms. Note that if you also set a drawCallback, - it will be called several times on each zoom. If you set a zoomCallback, it - will only be called after the animation is complete. - - Type: boolean - Default: false - *) - -> ?hideOverlayOnMouseOut:bool - (** hideOverlayOnMouseOut http://dygraphs.com/options.html#hideOverlayOnMouseOut - - Whether to hide the legend when the mouse leaves the chart area. - - Type: boolean - Default: true - *) - -> ?highlightCircleSize:int - (** highlightCircleSize http://dygraphs.com/options.html#highlightCircleSize - - The size in pixels of the dot drawn over highlighted points. - - Type: integer - Default: 3 - *) - -> ?highlightSeriesBackgroundAlpha:float - (** highlightSeriesBackgroundAlpha http://dygraphs.com/options.html#highlightSeriesBackgroundAlpha - - Fade the background while highlighting series. 1=fully visible background - (disable fading), 0=hiddden background (show highlighted series only). - - Type: float - Default: 0.5 - *) - -> ?highlightSeriesBackgroundColor:Color.t - (** highlightSeriesBackgroundColor http://dygraphs.com/options.html#highlightSeriesBackgroundColor - - Sets the background color used to fade out the series in conjunction with - 'highlightSeriesBackgroundAlpha'. - - Type: string - Default: rgb(255, 255, 255) - *) - -> ?highlightSeriesOpts:Highlight_series_options.t - (** highlightSeriesOpts http://dygraphs.com/options.html#highlightSeriesOpts - - When set, the options from this object are applied to the timeseries closest to - the mouse pointer for interactive highlighting. See also - 'highlightCallback'. Example: highlightSeriesOpts: { strokeWidth: 3 }. - - Type: Object - Default: null - *) - -> ?showLabelsOnHighlight:bool - (** showLabelsOnHighlight http://dygraphs.com/options.html#showLabelsOnHighlight - - Whether to show the legend upon mouseover. - - Type: boolean - Default: true - *) - -> ?showRoller:bool - (** showRoller http://dygraphs.com/options.html#showRoller - - If the rolling average period text box should be shown. - - Type: boolean - Default: false - *) - -> ?hideOverlayOnMouseOut:bool - (** hideOverlayOnMouseOut http://dygraphs.com/options.html#hideOverlayOnMouseOut - - Whether to hide the legend when the mouse leaves the chart area. - - Type: boolean - Default: true - *) - -> ?labels:string list - (** labels http://dygraphs.com/options.html#labels - - A name for each data series, including the independent (X) series. For CSV - files and DataTable objections, this is determined by context. For raw data, - this must be specified. If it is not, default values are supplied and a - warning is logged. - - Type: array - Default: ["X", "Y1", "Y2", ...]* - *) - -> ?labelsDiv_string:(string[@js "labelsDiv"]) - (** labelsDiv http://dygraphs.com/options.html#labelsDiv - - Show data labels in an external div, rather than on the graph. This value can - either be a div element or a div id. - - Type: DOM element or string - Default: null - *) - -> ?labelsDiv_el:(Native_node.t[@js "labelsDiv"]) - (** labelsDiv http://dygraphs.com/options.html#labelsDiv - - Show data labels in an external div, rather than on the graph. This value can - either be a div element or a div id. - - Type: DOM element or string - Default: null - *) - -> ?labelsSeparateLines:bool - (** labelsSeparateLines http://dygraphs.com/options.html#labelsSeparateLines - - Put
between lines in the label string. Often used in - conjunction with labelsDiv. - - Type: boolean - Default: false - *) - -> ?labelsShowZeroValues:bool - (** labelsShowZeroValues http://dygraphs.com/options.html#labelsShowZeroValues - - Show zero value labels in the labelsDiv. - - Type: boolean - Default: true - *) - -> ?legend:Legend.t - (** legend http://dygraphs.com/options.html#legend - - When to display the legend. By default, it only appears when a user mouses - over the chart. Set it to "always" to always display a legend of some - sort. When set to "follow", legend follows highlighted points. - - Type: string - Default: onmouseover - *) - -> ?legendFormatter:(Legend_data.t -> string) - (** legendFormatter http://dygraphs.com/options.html#legendFormatter - - Set this to supply a custom formatter for the legend. See this comment and the - legendFormatter demo for usage. - - Type: function(data): string - Default: null - *) - -> ?showLabelsOnHighlight:bool - (** showLabelsOnHighlight http://dygraphs.com/options.html#showLabelsOnHighlight - - Whether to show the legend upon mouseover. - - Type: boolean - Default: true - *) - -> ?height:int - (** height http://dygraphs.com/options.html#height - - Height, in pixels, of the chart. If the container div has been explicitly - sized, this will be ignored. - - Type: integer - Default: 320 - *) - -> ?clickCallback:(evt:Ojs.t -> x:float -> points:Point.t array -> unit) - (** clickCallback http://dygraphs.com/options.html#clickCallback - - A function to call when the canvas is clicked. - - Type: function(e, x, points) - e: The event object for the click - x: The x value that was clicked (for dates, this is milliseconds since epoch) - points: The closest points along that date. See Point properties for details. - Default: null - Gallery Samples: callbacks highlighted-series - Other Examples: callback - *) - -> ?highlightCallback:(evt:Ojs.t -> x:float -> points:Point.t array -> row:int -> seriesName:string option -> unit) - (** highlightCallback http://dygraphs.com/options.html#highlightCallback - - When set, this callback gets called every time a new point is highlighted. - - Type: function(event, x, points, row, seriesName) - event: the JavaScript mousemove event - x: the x-coordinate of the highlighted points - points: an array of highlighted points: [ {name: 'series', yval: y-value}, … ] - row: integer index of the highlighted row in the data table, starting from 0 - seriesName: name of the highlighted series, only present if highlightSeriesOpts is set. - Default: null - *) - -> ?unhighlightCallback:(evt:Ojs.t -> unit) - (** unhighlightCallback http://dygraphs.com/options.html#unhighlightCallback - - When set, this callback gets called every time the user stops highlighting any - point by mousing out of the graph. - - Type: function(event) - event: the mouse event - Default: null - *) - -> ?pointClickCallback:(evt:Ojs.t -> point:Point.t -> unit) - (** pointClickCallback http://dygraphs.com/options.html#pointClickCallback - - A function to call when a data point is clicked. and the point that was - clicked. - - Type: function(e, point) - e: the event object for the click - point: the point that was clicked See Point properties for details - Default: null - *) - -> ?underlayCallback:(context:Canvas_rendering_context_2D.t -> area:Area.t -> dygraph:Ojs.t -> unit) - (** underlayCallback http://dygraphs.com/options.html#underlayCallback - - When set, this callback gets called before the chart is drawn. It details on how - to use this. - - Type: function(context, area, dygraph) - context: the canvas drawing context on which to draw - area: An object with {x,y,w,h} properties describing the drawing area. - dygraph: the reference graph - Default: null - *) - -> ?drawCallback:(graph:Ojs.t -> isInitial:bool -> unit) - (** - When set, this callback gets called every time the dygraph is drawn. This includes - the initial draw, after zooming and repeatedly while panning. - - Type: function(dygraph, is_initial) - dygraph: The graph being drawn - is_initial: True if this is the initial draw, false for subsequent draws. - Default: null - *) - -> ?zoomCallback:(xmin:float -> xmax:float -> yRanges:Range.t array -> unit) - (** zoomCallback http://dygraphs.com/options.html#zoomCallback - - A function to call when the zoom window is changed (either by zooming in or - out). When animatedZooms is set, zoomCallback is called once at the end of the - transition (it will not be called for intermediate frames). - - Type: function(minDate, maxDate, yRanges) - minDate: milliseconds since epoch - maxDate: milliseconds since epoch. - yRanges: is an array of [bottom, top] pairs, one for each y-axis. - Default: null - *) - -> ?pixelRatio:float - (** pixelRatio http://dygraphs.com/options.html#pixelRatio - - Overrides the pixel ratio scaling factor for the canvas's 2d - context. Ordinarily, this is set to the devicePixelRatio / - (context.backingStoreRatio || 1), so on mobile devices, where the - devicePixelRatio can be somewhere around 3, performance can be improved by - overriding this value to something less precise, like 1, at the expense of - resolution. - - Type: float - Default: (devicePixelRatio / context.backingStoreRatio) - *) - -> ?plotter:(Plotter.t list) - (** plotter https://dygraphs.com/options.html#plotter - A function (or array of functions) which plot each data series on the chart. - - Type: array or function - Default: [DygraphCanvasRenderer.Plotters.fillPlotter, DygraphCanvasRenderer.Plotters.errorPlotter, DygraphCanvasRenderer.Plotters.linePlotter] - - Gallery Samples: NONE - Other Examples: plotters; smooth-plots - *) - -> ?rightGap:int - (** rightGap http://dygraphs.com/options.html#rightGap - - Number of pixels to leave blank at the right edge of the Dygraph. This makes - it easier to highlight the right-most data point. - - Type: integer - Default: 5 - *) - -> ?width:int - (** width http://dygraphs.com/options.html#width - - Width, in pixels, of the chart. If the container div has been explicitly - sized, this will be ignored. - - Type: integer - Default: 480 - *) - -> ?rangeSelectorAlpha:float - (** rangeSelectorAlpha http://dygraphs.com/options.html#rangeSelectorAlpha - - The transparency of the veil that is drawn over the unselected portions of - the range selector mini plot. A value of 0 represents full transparency and - the unselected portions of the mini plot will appear as normal. A value of 1 - represents full opacity and the unselected portions of the mini plot will be - hidden. - - Type: float (0.0 - 1.0) - Default: 0.6 - *) - -> ?rangeSelectorBackgroundLineWidth:float - (** rangeSelectorBackgroundLineWidth http://dygraphs.com/options.html#rangeSelectorBackgroundLineWidth - - The width of the lines below and on both sides of the range selector mini - plot. - - Type: float - Default: 1 - *) - -> ?rangeSelectorBackgroundStrokeColor:Color.t - (** rangeSelectorBackgroundStrokeColor http://dygraphs.com/options.html#rangeSelectorBackgroundStrokeColor - - The color of the lines below and on both sides of the range selector mini - plot. This can be of the form "#AABBCC" or "rgb(255,100,200)" or "yellow". - - Type: string - Default: gray - *) - -> ?rangeSelectorForegroundLineWidth:float - (** rangeSelectorForegroundLineWidth http://dygraphs.com/options.html#rangeSelectorForegroundLineWidth - - The width the lines in the interactive layer of the range selector. - - Type: float - Default: 1 - *) - -> ?rangeSelectorForegroundStrokeColor:Color.t - (** rangeSelectorForegroundStrokeColor http://dygraphs.com/options.html#rangeSelectorForegroundStrokeColor - - The color of the lines in the interactive layer of the range selector. This - can be of the form "#AABBCC" or "rgb(255,100,200)" or "yellow". - - Type: string - Default: black - *) - -> ?rangeSelectorHeight:int - (** rangeSelectorHeight http://dygraphs.com/options.html#rangeSelectorHeight - - Height, in pixels, of the range selector widget. This option can only be - specified at Dygraph creation time. - - Type: integer - Default: 40 - *) - -> ?rangeSelectorPlotFillColor:Color.t - (** rangeSelectorPlotFillColor http://dygraphs.com/options.html#rangeSelectorPlotFillColor - - The range selector mini plot fill color. This can be of the form "#AABBCC" or - "rgb(255,100,200)" or "yellow". You can also specify null or "" to turn off - fill. - - Type: string - Default: #A7B1C4 - *) - -> ?rangeSelectorPlotFillGradientColor:Color.t - (** rangeSelectorPlotFillGradientColor http://dygraphs.com/options.html#rangeSelectorPlotFillGradientColor - - The top color for the range selector mini plot fill color gradient. This can - be of the form "#AABBCC" or "rgb(255,100,200)" or "rgba(255,100,200,42)" or - "yellow". You can also specify null or "" to disable the gradient and fill - with one single color. - - Type: string - Default: white - *) - -> ?rangeSelectorPlotLineWidth:float - (** rangeSelectorPlotLineWidth http://dygraphs.com/options.html#rangeSelectorPlotLineWidth - - The width of the range selector mini plot line. - - Type: float - Default: 1.5 - *) - -> ?rangeSelectorPlotStrokeColor:Color.t - (** rangeSelectorPlotStrokeColor http://dygraphs.com/options.html#rangeSelectorPlotStrokeColor - - The range selector mini plot stroke color. This can be of the form "#AABBCC" - or "rgb(255,100,200)" or "yellow". You can also specify null or "" to turn - off stroke. - - Type: string - Default: #808FAB - *) - -> ?showRangeSelector:bool - (** showRangeSelector http://dygraphs.com/options.html#showRangeSelector - - Show or hide the range selector widget. - - Type: boolean - Default: false - *) - -> ?series:Series.t - (** series http://dygraphs.com/options.html#series - - Defines per-series options. Its keys match the y-axis label names, and the values - are dictionaries themselves that contain options specific to that series. - - Type: Object - Default: null - *) - -> ?digitsAfterDecimal:int - (** digitsAfterDecimal http://dygraphs.com/options.html#digitsAfterDecimal - - Unless it's run in scientific mode (see the sigFigs option), dygraphs - displays numbers with digitsAfterDecimal digits after the decimal - point. Trailing zeros are not displayed, so with a value of 2 you'll - get '0', '0.1', '0.12', '123.45' but not '123.456' (it will be - rounded to '123.46'). Numbers with absolute value less than - 0.1^digitsAfterDecimal (i.e. those which would show up as '0.00') - will be displayed in scientific notation. - - Type: integer - Default: 2 - *) - -> ?labelsKMB:bool - (** labelsKMB http://dygraphs.com/options.html#labelsKMB - - Show K/M/B for thousands/millions/billions on y-axis. - - Type: boolean - Default: false - *) - -> ?labelsKMG2:bool - (** labelsKMG2 http://dygraphs.com/options.html#labelsKMG2 - - Show k/M/G for kilo/Mega/Giga on y-axis. This is different than labelsKMB in - that it uses base 2, not 10. - - Type: boolean - Default: false - *) - -> ?labelsUTC:bool - (** labelsUTC http://dygraphs.com/options.html#labelsUTC - - Show date/time labels according to UTC (instead of local time). - - Type: boolean - Default: false - *) - -> ?maxNumberWidth:int - (** maxNumberWidth http://dygraphs.com/options.html#maxNumberWidth - - When displaying numbers in normal (not scientific) mode, large numbers will - be displayed with many trailing zeros (e.g. 100000000 instead of 1e9). This - can lead to unwieldy y-axis labels. If there are more than maxNumberWidth - digits to the left of the decimal in a number, dygraphs will switch to - scientific notation, even when not operating in scientific mode. If you'd - like to see all those digits, set this to something large, like 20 or 30. - - Type: integer - Default: 6 - *) - -> ?sigFigs:int - (** sigFigs http://dygraphs.com/options.html#sigFigs - - By default, dygraphs displays numbers with a fixed number of digits after the - decimal point. If you'd prefer to have a fixed number of significant figures, - set this option to that number of sig figs. A value of 2, for instance, would - cause 1 to be display as 1.0 and 1234 to be displayed as 1.23e+3. - - Type: integer - Default: null - *) - -> unit - -> t -[@@js.builder ] -[@@@ocamlformat "enable"] - -val legendFormatter : t -> (Legend_data.t -> string) option [@@js.get] - -val zoomCallback : t -> (xmin:float -> xmax:float -> yRanges:Range.t array -> unit) option - [@@js.get] - -val height : t -> int option [@@js.get] -val width : t -> int option [@@js.get] - -(** This is the lodash.js deep-merge implementation *) -val merge_internal : t -> prefer:t -> t - [@@js.global "_.merge"] - -(* [merge_internal] actually mutably changes the first [t] (and returns it) *) - -(** merge two [t]s, preferring options in [prefer] *) -val merge : t -> prefer:t -> t - [@@js.custom - let merge t ~prefer = create () |> merge_internal ~prefer:t |> merge_internal ~prefer] diff --git a/bindings/dygraph/src/per_series_info.ml b/bindings/dygraph/src/per_series_info.ml deleted file mode 100644 index 5e63a304..00000000 --- a/bindings/dygraph/src/per_series_info.ml +++ /dev/null @@ -1,18 +0,0 @@ -open Core -open! Import - -type t = - { label : string - ; override_label_for_visibility : string option - ; visible_by_default : bool - } -[@@deriving fields ~getters] - -let create ?override_label_for_visibility label ~visible_by_default = - { label; override_label_for_visibility; visible_by_default } -;; - -let create_all_visible labels = - List.map labels ~f:(fun label -> - { label; override_label_for_visibility = None; visible_by_default = true }) -;; diff --git a/bindings/dygraph/src/per_series_info.mli b/bindings/dygraph/src/per_series_info.mli deleted file mode 100644 index d6f35ed7..00000000 --- a/bindings/dygraph/src/per_series_info.mli +++ /dev/null @@ -1,27 +0,0 @@ -open! Core -open! Import - -type t = - { label : string - ; override_label_for_visibility : string option - (** It may be helpful to distinguish the series label from the "label for visibility". - - For example, in some graphs we encode information about the symbol we are looking at - in the series label. That information changes as you look at different symbols. - However, the second series always semantically means the same thing as you move from - symbol to symbol. If you uncheck the second series to disable visibility, you may - want to remember that change even if the series label changes. - - If you want to just use the [label] as the semantic identifier for persisting - visilibity, then just set [override_label_for_visibility] to None. *) - ; visible_by_default : bool - } -[@@deriving fields ~getters] - -val create - : ?override_label_for_visibility:string - -> string - -> visible_by_default:bool - -> t - -val create_all_visible : string list -> t list diff --git a/bindings/dygraph/src/plotter.ml b/bindings/dygraph/src/plotter.ml deleted file mode 100644 index 372ae642..00000000 --- a/bindings/dygraph/src/plotter.ml +++ /dev/null @@ -1,39 +0,0 @@ -[@@@js.dummy "!! This code has been generated by gen_js_api !!"] -[@@@ocaml.warning "-7-32-39"] - -open! Core -open! Import -open! Gen_js_api - -type t = Ojs.t - -let rec t_of_js : Ojs.t -> t = fun (x2 : Ojs.t) -> x2 -and t_to_js : t -> Ojs.t = fun (x1 : Ojs.t) -> x1 - -let line_plotter : t = - t_of_js - (Ojs.get_prop_ascii - (Ojs.get_prop_ascii (Ojs.get_prop_ascii Ojs.global "Dygraph") "Plotters") - "linePlotter") -;; - -let fill_plotter : t = - t_of_js - (Ojs.get_prop_ascii - (Ojs.get_prop_ascii (Ojs.get_prop_ascii Ojs.global "Dygraph") "Plotters") - "fillPlotter") -;; - -let error_bar_plotter : t = - t_of_js - (Ojs.get_prop_ascii - (Ojs.get_prop_ascii (Ojs.get_prop_ascii Ojs.global "Dygraph") "Plotters") - "errorPlotter") -;; - -let point_plotter : t = - t_of_js - (Ojs.get_prop_ascii - (Ojs.get_prop_ascii (Ojs.get_prop_ascii Ojs.global "Dygraph") "Plotters") - "pointPlotter") -;; diff --git a/bindings/dygraph/src/plotter.mli b/bindings/dygraph/src/plotter.mli deleted file mode 100644 index fd491d33..00000000 --- a/bindings/dygraph/src/plotter.mli +++ /dev/null @@ -1,12 +0,0 @@ -open! Core -open! Import -open! Gen_js_api - -type t - -val t_to_js : t -> Ojs.t -val t_of_js : Ojs.t -> t -val line_plotter : t [@@js.global "Dygraph.Plotters.linePlotter"] -val fill_plotter : t [@@js.global "Dygraph.Plotters.fillPlotter"] -val error_bar_plotter : t [@@js.global "Dygraph.Plotters.errorPlotter"] -val point_plotter : t [@@js.global "Dygraph.Plotters.pointPlotter"] diff --git a/bindings/dygraph/src/point.ml b/bindings/dygraph/src/point.ml deleted file mode 100644 index 94ab4b46..00000000 --- a/bindings/dygraph/src/point.ml +++ /dev/null @@ -1,37 +0,0 @@ -[@@@js.dummy "!! This code has been generated by gen_js_api !!"] -[@@@ocaml.warning "-7-32-39"] - -open! Core -open! Import -open Gen_js_api - -type t = - { xval : float - ; yval : float - ; canvasx : float - ; canvasy : float - ; name : string - ; idx : int - } - -let rec t_of_js : Ojs.t -> t = - fun (x2 : Ojs.t) -> - { xval = Ojs.float_of_js (Ojs.get_prop_ascii x2 "xval") - ; yval = Ojs.float_of_js (Ojs.get_prop_ascii x2 "yval") - ; canvasx = Ojs.float_of_js (Ojs.get_prop_ascii x2 "canvasx") - ; canvasy = Ojs.float_of_js (Ojs.get_prop_ascii x2 "canvasy") - ; name = Ojs.string_of_js (Ojs.get_prop_ascii x2 "name") - ; idx = Ojs.int_of_js (Ojs.get_prop_ascii x2 "idx") - } - -and t_to_js : t -> Ojs.t = - fun (x1 : t) -> - Ojs.obj - [| "xval", Ojs.float_to_js x1.xval - ; "yval", Ojs.float_to_js x1.yval - ; "canvasx", Ojs.float_to_js x1.canvasx - ; "canvasy", Ojs.float_to_js x1.canvasy - ; "name", Ojs.string_to_js x1.name - ; "idx", Ojs.int_to_js x1.idx - |] -;; diff --git a/bindings/dygraph/src/point.mli b/bindings/dygraph/src/point.mli deleted file mode 100644 index 033b8fc7..00000000 --- a/bindings/dygraph/src/point.mli +++ /dev/null @@ -1,17 +0,0 @@ -open! Core -open! Import -open Gen_js_api - -(** http://dygraphs.com/options.html#point_properties *) - -type t = - { xval : float - ; yval : float - ; canvasx : float - ; canvasy : float - ; name : string - ; idx : int - } - -val t_to_js : t -> Ojs.t -val t_of_js : Ojs.t -> t diff --git a/bindings/dygraph/src/range.ml b/bindings/dygraph/src/range.ml deleted file mode 100644 index 30fb8614..00000000 --- a/bindings/dygraph/src/range.ml +++ /dev/null @@ -1,29 +0,0 @@ -open Core -open! Import -open Gen_js_api - -type t = - { low : float - ; high : float - } -[@@deriving sexp, equal] - -(* ranges in dygraphs are represented as [| low; high |]. *) -let t_to_js { low; high } = Ojs.array_to_js Ojs.float_to_js [| low; high |] - -let t_of_js ojs = - let data = Ojs.array_of_js Ojs.float_of_js ojs in - { low = data.(0); high = data.(1) } -;; - -module Spec = struct - type nonrec t = - | Infer - | Specified of t - [@@deriving sexp, equal] - - let t_to_js = function - | Infer -> Ojs.null - | Specified t -> t_to_js t - ;; -end diff --git a/bindings/dygraph/src/range.mli b/bindings/dygraph/src/range.mli deleted file mode 100644 index e6b5178f..00000000 --- a/bindings/dygraph/src/range.mli +++ /dev/null @@ -1,24 +0,0 @@ -open! Core -open! Import -open Gen_js_api - -(** Ranges in dygraphs are represented as a number array with two elements. This makes - them a bit easier to work with. *) -type t = - { low : float - ; high : float - } -[@@deriving sexp, equal] - -val t_to_js : t -> Ojs.t -val t_of_js : Ojs.t -> t - -module Spec : sig - (** This is used when specifying a range as an input. *) - type nonrec t = - | Infer - | Specified of t - [@@deriving sexp, equal] - - val t_to_js : t -> Ojs.t -end diff --git a/bindings/dygraph/src/raw_html.ml b/bindings/dygraph/src/raw_html.ml deleted file mode 100644 index 217d4dac..00000000 --- a/bindings/dygraph/src/raw_html.ml +++ /dev/null @@ -1,17 +0,0 @@ -open Core -open Import -open Gen_js_api - -type t = string [@@deriving compare, equal, sexp] - -let t_to_js = Ojs.string_to_js -let t_of_js = Ojs.string_of_js -let of_string s = s - -let view ~tag t = - Vdom.Node.inner_html - ~tag - ~attrs:[ Vdom.Attr.empty ] - ~this_html_is_sanitized_and_is_totally_safe_trust_me:t - () -;; diff --git a/bindings/dygraph/src/raw_html.mli b/bindings/dygraph/src/raw_html.mli deleted file mode 100644 index 20677fec..00000000 --- a/bindings/dygraph/src/raw_html.mli +++ /dev/null @@ -1,13 +0,0 @@ -open! Core -open Import -open Gen_js_api - -(** Dygraphs returns "raw html" as strings in some callbacks (see [Legend_data] and - [legendFormatter]). We mint this type so that the types make it more clear which - fields are just normal strings and which are html strings. *) -type t [@@deriving compare, equal, sexp] - -val t_to_js : t -> Ojs.t -val t_of_js : Ojs.t -> t -val of_string : string -> t -val view : tag:string -> t -> Vdom.Node.t diff --git a/bindings/dygraph/src/update_options.ml b/bindings/dygraph/src/update_options.ml deleted file mode 100644 index bbc399e2..00000000 --- a/bindings/dygraph/src/update_options.ml +++ /dev/null @@ -1,18 +0,0 @@ -open! Core -open! Import -open Gen_js_api - -type t = Ojs.t - -let t_to_js x = x -let t_of_js x = x - -let create ?options ?data () = - (* This is intentionally different than an object with a property "options". *) - let options = - let default = Ojs.empty_obj () in - Option.value_map options ~default ~f:Options.t_to_js - in - Option.iter data ~f:(fun data -> Ojs.set_prop_ascii options "file" (Data.t_to_js data)); - options -;; diff --git a/bindings/dygraph/src/update_options.mli b/bindings/dygraph/src/update_options.mli deleted file mode 100644 index 0bf8ffcd..00000000 --- a/bindings/dygraph/src/update_options.mli +++ /dev/null @@ -1,9 +0,0 @@ -open! Core -open! Import -open Gen_js_api - -type t - -val t_to_js : t -> Ojs.t -val t_of_js : Ojs.t -> t -val create : ?options:Options.t -> ?data:Data.t -> unit -> t diff --git a/bindings/dygraph/src/which_y_axis.ml b/bindings/dygraph/src/which_y_axis.ml deleted file mode 100644 index 11b73330..00000000 --- a/bindings/dygraph/src/which_y_axis.ml +++ /dev/null @@ -1,26 +0,0 @@ -[@@@js.dummy "!! This code has been generated by gen_js_api !!"] -[@@@ocaml.warning "-7-32-39"] - -open! Core -open! Import -open Gen_js_api - -type t = - [ `y1 - | `y2 - ] - -let rec t_of_js : Ojs.t -> t = - fun (x2 : Ojs.t) -> - let x3 = x2 in - match Ojs.string_of_js x3 with - | "y1" -> `y1 - | "y2" -> `y2 - | _ -> assert false - -and t_to_js : t -> Ojs.t = - fun (x1 : [ `y1 | `y2 ]) -> - match x1 with - | `y1 -> Ojs.string_to_js "y1" - | `y2 -> Ojs.string_to_js "y2" -;; diff --git a/bindings/dygraph/src/which_y_axis.mli b/bindings/dygraph/src/which_y_axis.mli deleted file mode 100644 index 01cfcb29..00000000 --- a/bindings/dygraph/src/which_y_axis.mli +++ /dev/null @@ -1,11 +0,0 @@ -open! Core -open! Import -open Gen_js_api - -type t = - ([ `y1 - | `y2 - ] - [@js.enum]) - -val t_to_js : t -> Ojs.t diff --git a/bindings/dygraph/src/with_bonsai.ml b/bindings/dygraph/src/with_bonsai.ml deleted file mode 100644 index af78199a..00000000 --- a/bindings/dygraph/src/with_bonsai.ml +++ /dev/null @@ -1,259 +0,0 @@ -open Core -open Import -module Mutable_state_tracker = Bonsai_web_ui_widget.Low_level - -(* This top-level side-effect installs the CSS for dygraphs. - We need it in this file because if the side-effect lives - in an otherwise-empty file, or in a file that only contains - module aliases, then the dead-code-eliminator will remove - the file (including your side-effect). - - By putting it here, anyone that uses the With_bonsai module - will force the side-effect to be evaluated. *) -let () = Css.install_css () - -module Legend_model = struct - type t = { visibility : bool list } [@@deriving equal, fields ~getters] -end - -let id = Type_equal.Id.create ~name:"dygraph" [%sexp_of: opaque] - -(* Defaults come from Dygraph's documentation: - - https://dygraphs.com/options.html#height - https://dygraphs.com/options.html#width -*) -let default_width = 480 -let default_height = 320 - -let widget ?with_graph ?on_zoom data options ~graph_tracker = - (* This function tells the graph to resize itself to fit its contents. This is - required because at the point when the graph is created, the element [el] - (created down below in [init]) hasn't yet been attached to the Dom, so it - initially detects that it's size should be 0x0. When requestAnimationFrame - completes, according to the semantics of Bonsai_web, our graph has been - successfully inserted into the Dom, so we can trigger another resize and - it'll compute the correct size. *) - let resize_when_inserted_into_the_dom graph _time = Graph.resize graph in - let override_zoom_callback ~graph options = - match on_zoom with - | None -> options - | Some on_zoom -> - let zoomCallback = - let caller's_zoom_callback = Options.zoomCallback options in - fun ~xmin ~xmax ~yRanges -> - Option.iter caller's_zoom_callback ~f:(fun f -> f ~xmin ~xmax ~yRanges); - Vdom.Effect.Expert.handle_non_dom_event_exn (on_zoom graph ~xmin ~xmax ~yRanges) - in - let our_options = Options.create ~zoomCallback () in - Options.merge options ~prefer:our_options - in - let resize_if_width_or_height_changed graph ~old_options ~options = - (* Updating the width and height via [updateOptions] does not work. - We need to detect when the width/height change and call - [Graph.resize_explicit]. - https://dygraphs.com/jsdoc/symbols/Dygraph.html#resize *) - let old_width = Options.width old_options in - let old_height = Options.height old_options in - let width = Option.bind options ~f:Options.width in - let height = Option.bind options ~f:Options.height in - let pair_with_default w h = - match w, h with - | None, None -> None - | Some w, Some h -> Some (w, h) - | Some w, None -> Some (w, default_height) - | None, Some h -> Some (default_width, h) - in - let old_width_and_height = pair_with_default old_width old_height in - let new_width_and_height = pair_with_default width height in - match old_width_and_height, new_width_and_height with - | None, None -> () - | Some old_wh, Some new_wh when [%equal: int * int] old_wh new_wh -> () - | Some _, None -> Graph.resize graph - | Some _, Some (width, height) | None, Some (width, height) -> - Graph.resize_explicit graph ~width ~height - in - Vdom.Node.widget - () - ~id - ~destroy:(fun (_, _, _, graph, animation_id, graph_tracker_id) _el -> - graph_tracker.Mutable_state_tracker.unsafe_destroy graph_tracker_id; - (* Free resources allocated by the graph *) - Graph.destroy graph; - (* If for some reason the animation-frame never fired and we're already - being removed, go ahead and cancel the callback. *) - Dom_html.window##cancelAnimationFrame animation_id) - ~init:(fun () -> - let el = Dom_html.createDiv Dom_html.document in - let graph = Graph.create el data options in - let graph_tracker_id = graph_tracker.Mutable_state_tracker.unsafe_init graph in - let () = - let options = override_zoom_callback ~graph options in - let updateOptions = Update_options.create ~options ?data:None () in - Graph.updateOptions graph updateOptions - in - Option.iter with_graph ~f:(fun with_graph -> with_graph graph); - let animation_id = - Dom_html.window##requestAnimationFrame - (Js.wrap_callback (resize_when_inserted_into_the_dom graph)) - in - (data, options, on_zoom, graph, animation_id, graph_tracker_id), el) - ~update: - (fun - (old_data, old_options, old_on_zoom, graph, animation_id, graph_tracker_id) el -> - let () = - let data = Option.some_if (not (phys_equal old_data data)) data in - let options = - match phys_equal old_options options, phys_equal old_on_zoom on_zoom with - | true, true -> None - | _ -> Some (override_zoom_callback ~graph options) - in - (match data, options with - | None, None -> () - | _, options -> - let updateOptions = Update_options.create ?options ?data () in - Graph.updateOptions graph updateOptions); - resize_if_width_or_height_changed graph ~old_options ~options - in - (data, options, on_zoom, graph, animation_id, graph_tracker_id), el) -;; - -let create_graph ?with_graph ?on_zoom data options ~graph_tracker = - let on_zoom = - match on_zoom with - | None -> Bonsai.Value.return None - | Some on_zoom -> Bonsai.Value.map on_zoom ~f:Option.some - in - let%arr data = data - and options = options - and on_zoom = on_zoom - and graph_tracker = graph_tracker in - widget ?with_graph ?on_zoom data options ~graph_tracker -;; - -let create_options ~x_label ~y_labels ~visibility ~legendFormatter = - let labels = x_label :: y_labels in - (* We create an element but never actually put it anywhere. *) - let hidden_legend = Dom_html.createDiv Dom_html.document in - Options.create - () - ~xlabel:x_label - ~labels - ~visibility - ~legend:`always (* If [legend:`never], then [legendFormatter] doesn't fire. *) - ~labelsDiv_el:hidden_legend - ~legendFormatter -;; - -let create_default_legend ~x_label ~per_series_info = - let%sub model, view, inject = Default_legend.create ~x_label ~per_series_info in - (* project out visibility *) - let model = - let%map model = model in - { Legend_model.visibility = - List.map model.series ~f:Default_legend.Model.Series.is_visible - } - in - let inject = - let%map inject = inject in - fun data -> inject Default_legend.Action.(From_graph data) - in - return (Bonsai.Value.map3 model view inject ~f:Tuple3.create) -;; - -let format_legend inject_legend_data options data = - let caller's_legend_formatter = Option.bind options ~f:Options.legendFormatter in - (* we call the legendFormatter option set on [options] in case the caller is relying - on it for side effects. *) - Option.iter caller's_legend_formatter ~f:(fun f -> ignore (f data : string)); - Vdom.Effect.Expert.handle_non_dom_event_exn (inject_legend_data data); - (* we are pointing the legend managed by dygraph to a hidden div (see - [create_options]) so this should be invisible. *) - "this should not be visible" -;; - -let build_options options visibility legendFormatter x_label y_labels = - let our_options = create_options ~x_label ~y_labels ~visibility ~legendFormatter in - match options with - | None -> our_options - | Some options -> Options.merge options ~prefer:our_options -;; - -let visibility ~legend_model ~num_series = - let visibility = - let%map.Bonsai visibility_from_legend = legend_model >>| Legend_model.visibility - and num_series = num_series in - let visibility_len = List.length visibility_from_legend in - if visibility_len < num_series - then ( - (* Dygraphs has a bug where it will throw an error if the length of [visibility] is - ever less than the number of series in the data. To work around this, we pad - [visibility] with trues. *) - let padding = List.init (num_series - visibility_len) ~f:(Fn.const true) in - visibility_from_legend @ padding) - else visibility_from_legend - in - visibility |> Bonsai.Value.cutoff ~equal:[%equal: bool list] -;; - -type t = - { graph_view : Vdom.Node.t - ; modify_graph : (Graph.t -> unit) -> unit Effect.t - } - -let create - ~key - ~x_label - ~per_series_info - ?custom_legend - ?options - ?with_graph - ?on_zoom - ?(extra_attr = Value.return Vdom.Attr.empty) - ~data - () - = - let options = - Option.value_map - options - ~default:(Bonsai.Value.return None) - ~f:(Bonsai.Value.map ~f:Option.some) - in - let%sub legend = - match custom_legend with - | Some legend -> Bonsai.read legend - | None -> create_default_legend ~x_label ~per_series_info - in - let%pattern_bind legend_model, legend_view, inject_legend_data = legend in - let inject_legend_data = Bonsai.Value.cutoff inject_legend_data ~equal:phys_equal in - let y_labels = - Bonsai.Value.map per_series_info ~f:(List.map ~f:Per_series_info.label) - in - let visibility = - let num_series = Bonsai.Value.map per_series_info ~f:(List.length :> _ -> _) in - visibility ~legend_model ~num_series - in - let legendFormatter = Bonsai.Value.map2 inject_legend_data options ~f:format_legend in - let options = - Bonsai.Value.map5 options visibility legendFormatter x_label y_labels ~f:build_options - in - let%sub graph_tracker = Mutable_state_tracker.component () in - let%sub graph = create_graph ?with_graph ?on_zoom data options ~graph_tracker in - let%arr graph = graph - and legend_view = legend_view - and key = key - and graph_tracker = graph_tracker - and extra_attr = extra_attr in - let graph_view = - Vdom.Node.div - ~key - ~attrs: - [ Vdom.Attr.class_ "dygraph" - ; Vdom.Attr.style (Css_gen.flex_container ()) - ; extra_attr - ] - [ graph; legend_view ] - in - let modify_graph = graph_tracker.Mutable_state_tracker.modify in - { graph_view; modify_graph } -;; diff --git a/bindings/dygraph/src/with_bonsai.mli b/bindings/dygraph/src/with_bonsai.mli deleted file mode 100644 index 48e872e8..00000000 --- a/bindings/dygraph/src/with_bonsai.mli +++ /dev/null @@ -1,56 +0,0 @@ -open! Core -open Import - -(** The recommended way to create dygraphs (with bonsai). *) - -module Legend_model : sig - type t = { visibility : bool list } [@@deriving equal, fields ~getters] -end - -(** This is the legend that [create] will when nothing is passed to [custom_legend]. - - Even if you intend to pass this into [create], it may be useful to create this - yourself to gain access to the Legend_model.t. -*) -val create_default_legend - : x_label:string Bonsai.Value.t - -> per_series_info:Per_series_info.t list Bonsai.Value.t - -> (Legend_model.t * Vdom.Node.t * (Legend_data.t -> unit Ui_effect.t)) - Bonsai.Computation.t - -type t = - { graph_view : Vdom.Node.t - ; modify_graph : (Graph.t -> unit) -> unit Effect.t - } - -val create - : key:string Bonsai.Value.t - (** [key] is a virtualdom concept that allows it to identify which items in a list have - changed. For more information, see - https://reactjs.org/docs/lists-and-keys.html#keys. - - Every graph in your document should have a unique [key]. - - For a given graph, [key] should be constant. - *) - -> x_label:string Bonsai.Value.t - -> per_series_info:Per_series_info.t list Bonsai.Value.t - -> ?custom_legend: - (Legend_model.t * Vdom.Node.t * (Legend_data.t -> unit Ui_effect.t)) Bonsai.Value.t - (** [custom_legend] defaults to Default_legend. If you don't want that legend, you're - free to pass in your own bonsai computation. *) - -> ?options:Options.t Bonsai.Value.t - -> ?with_graph:(Graph.t -> unit) - (** This hook may be useful if you want to, for example, bind the graph to some global - variable on the window. That way you can poke at the graph in the console. *) - -> ?on_zoom: - (Graph.t - -> xmin:float - -> xmax:float - -> yRanges:Range.t array - -> unit Vdom.Effect.t) - Bonsai.Value.t - -> ?extra_attr:Vdom.Attr.t Bonsai.Value.t - -> data:Data.t Bonsai.Value.t - -> unit - -> t Bonsai.Computation.t diff --git a/bindings/dygraph/src/x_axis_mapping.ml b/bindings/dygraph/src/x_axis_mapping.ml deleted file mode 100644 index c668f3c9..00000000 --- a/bindings/dygraph/src/x_axis_mapping.ml +++ /dev/null @@ -1,91 +0,0 @@ -open Core -open Import - -(** How this works: - - The general idea here is that dygraphs does not have native support for putting - "breaks" or "gaps" in the x-axis. It assumes the x-axis will be continuous in time - (for time series). - - We don't want that. We want to hide overnights and weekends. - - In order to achieve that, we generate a (piecewise-linear) mapping between "real time" - and "graph time" where we squash the overnight and weekend portions of "real time" - into a tiny section of "graph time". - - This mapping is invertible, meaning that from any "real time" we can produce the - corresponding "graph time" and vice versa. For a more precise description of how we - produce this mapping, look below in [Time_mapping]. - - So, given this mapping, we hook into the dygraphs library at the following points: - - - Before handing over our time series data, we map "real time" to "graph time" - - - Before dygraphs displays a time value in the legend (via [valueFormatter]), we map - "graph time" back to "real time" - - - Before dygraphs display x-axis tick labels (via axisLabelFormatter), we map "graph - time" back to "real time". - - This is based on this stack overflow question - (https://stackoverflow.com/questions/17888989/how-to-skip-weekends-on-dygraps-x-axis) - in which the author of dygraphs (danvk) agrees this is how you need to do it. *) - -let dygraphs_date_axis_label_formatter - : unit -> Js.date Js.t -> Granularity.t -> Options.Opts.t -> Js.js_string Js.t - = - fun () -> Js.Unsafe.pure_js_expr {| Dygraph.dateAxisLabelFormatter |} -;; - -let dygraphs_number_axis_label_formatter - : unit -> Js.number Js.t -> Granularity.t -> Options.Opts.t -> Js.js_string Js.t - = - fun () -> Js.Unsafe.pure_js_expr {| Dygraph.numberAxisLabelFormatter |} -;; - -let default_axis_label_formatter x gran opts = - match x with - | `number x -> - let number = Js.number_of_float x in - dygraphs_number_axis_label_formatter () number gran opts |> Js.to_string - | `date d -> dygraphs_date_axis_label_formatter () d gran opts |> Js.to_string -;; - -(* due to the floatness of the piecewise_linear math, timestamps come can out weird. I - can't imagine anyone needs more precision than ms (and if they do, they can't get it - anyways b/c of dates in javascript), so this rounding feels relatively - uncontroversial and makes the output look a lot better. *) -let round_time_nearest_ms time ~zone = - let date, ofday = Time_ns.to_date_ofday time ~zone in - let span = Time_ns.Ofday.to_span_since_start_of_day ofday in - let ms = Time_ns.Span.to_ms span |> Float.iround_nearest_exn in - let ofday = Time_ns.Span.of_int_ms ms |> Time_ns.Ofday.of_span_since_start_of_day_exn in - Time_ns.of_date_ofday ~zone date ofday -;; - -let default_value_formatter ~zone ms_since_epoch = - let time = Time_ns.of_span_since_epoch (Time_ns.Span.of_ms ms_since_epoch) in - Time_ns.to_string_trimmed (round_time_nearest_ms time ~zone) ~zone -;; - -type t = - { time_to_x_value : Time_ns.t -> Time_ns.t - ; x_value_to_time : Time_ns.t -> Time_ns.t - ; value_formatter : float -> Options.Opts.t -> string - ; axis_label_formatter : - Number_or_js_date.t -> Granularity.t -> Options.Opts.t -> string - } - -let default ~zone = - { time_to_x_value = Fn.id - ; x_value_to_time = Fn.id - ; value_formatter = (fun x _opts -> default_value_formatter x ~zone) - ; axis_label_formatter = default_axis_label_formatter - } -;; - -module For_dygraph_libraries = struct - let round_time_nearest_ms = round_time_nearest_ms - let dygraphs_date_axis_label_formatter = dygraphs_date_axis_label_formatter - let dygraphs_number_axis_label_formatter = dygraphs_number_axis_label_formatter -end diff --git a/bindings/dygraph/src/x_axis_mapping.mli b/bindings/dygraph/src/x_axis_mapping.mli deleted file mode 100644 index 4eccf717..00000000 --- a/bindings/dygraph/src/x_axis_mapping.mli +++ /dev/null @@ -1,60 +0,0 @@ -open Core -open! Import - -(** This module is a helper function designed to make it easy (easier) to make the spacing - of the x-axis of a dygraphs graph differ from the x-values themselves. Currently, - this module deals only with time x-values, but may be extended to deal with numeric - x-values in the future. - - Dygraphs does not have native support for putting "breaks" or "gaps" in the x-axis. - It assumes the x-axis will be continuous in time (for time series) and that the space - on the x-axis between any two points in time should be proportional to the difference - of those times. - - Although that's a very reasonable default, we don't always want that - for example, we - may want to effectively hide overnights and weekends (i.e. give those very little - space on the x-axis). - - In order to do this, you can generate a [t] which provides a mapping between "real - time" and "x-axis time". To use a [t] correctly, you need to hook into dygraphs in - the following three places: - - - Before handing over our time series data, use [time_to_x_value] to map your data's - "real time" to the times we want to use for x-values. - - - Pass [valueFormatter] to [Dygraph.Options.Axis_options.create] to map x-values - back to "real time" before displaying values in the legend. - - - Pass [axisLabelFormatter] to [Dygraph.Options.Axis_options.create] to map x-values - back to "real time" before displaying x-axis tick labels. - - For an example usage, see [../examples/ocaml/hide_overnights.ml} -*) - -type t = - { time_to_x_value : Time_ns.t -> Time_ns.t - ; x_value_to_time : Time_ns.t -> Time_ns.t - ; value_formatter : float -> Options.Opts.t -> string - ; axis_label_formatter : - Number_or_js_date.t -> Granularity.t -> Options.Opts.t -> string - } - -val default : zone:Time_float.Zone.t -> t - -module For_dygraph_libraries : sig - val round_time_nearest_ms : Time_ns.t -> zone:Core_private.Time_zone.t -> Time_ns.t - - val dygraphs_date_axis_label_formatter - : unit - -> Js.date Js.t - -> Granularity.t - -> Options.Opts.t - -> Js.js_string Js.t - - val dygraphs_number_axis_label_formatter - : unit - -> Js.number Js.t - -> Granularity.t - -> Options.Opts.t - -> Js.js_string Js.t -end diff --git a/bindings/feather_icon/README.mdx b/bindings/feather_icon/README.mdx deleted file mode 100644 index 9d0b858b..00000000 --- a/bindings/feather_icon/README.mdx +++ /dev/null @@ -1,30 +0,0 @@ -# Feather icons - -This is a port of [feathericons](https://feathericons.com/) for use in jsoo. Check -out the bonsai demo [here](https://bonsai:8547/). - -To make one, it's as easy as: - -```ocaml skip -Feather_icon.svg Alert_circle -``` - -`Alert_circle` is just one of 286 options. See the rest [here](https://bonsai:8547/). - -You can optionally specify the `size`, `stroke`, `stroke_width`, and -`fill`. See the full `svg` function here: - - -```ocaml -val svg - : ?size:[< Css_gen.Length.t ] - -> ?stroke:[< Css_gen.Color.t ] - -> ?fill:[< Css_gen.Color.t ] - -> ?stroke_width:[< Css_gen.Length.t ] - -> ?extra_attrs:Vdom.Attr.t list - -> t - -> Vdom.Node.t -``` - - - diff --git a/bindings/feather_icon/dist/LICENSE-feather b/bindings/feather_icon/dist/LICENSE-feather deleted file mode 100644 index c2f512f4..00000000 --- a/bindings/feather_icon/dist/LICENSE-feather +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2013-2017 Cole Bemis - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/bindings/feather_icon/dune b/bindings/feather_icon/dune deleted file mode 100644 index e69de29b..00000000 diff --git a/bindings/feather_icon/src/dune b/bindings/feather_icon/src/dune deleted file mode 100644 index e096c890..00000000 --- a/bindings/feather_icon/src/dune +++ /dev/null @@ -1,96 +0,0 @@ -(library - (name feather_icon) - (public_name bonsai.feather_icon) - (libraries core bonsai_web) - (preprocess - (pps ppx_jane))) - -(rule - (targets paths.ml paths.mli) - (deps %{bin:ocaml-embed-file} paths/activity.svg paths/corner-down-left.svg - paths/link.svg paths/shopping-bag.svg paths/airplay.svg - paths/corner-down-right.svg paths/list.svg paths/shopping-cart.svg - paths/alert-circle.svg paths/corner-left-down.svg paths/loader.svg - paths/shuffle.svg paths/alert-octagon.svg paths/corner-left-up.svg - paths/lock.svg paths/sidebar.svg paths/alert-triangle.svg - paths/corner-right-down.svg paths/log-in.svg paths/skip-back.svg - paths/align-center.svg paths/corner-right-up.svg paths/log-out.svg - paths/skip-forward.svg paths/align-justify.svg paths/corner-up-left.svg - paths/mail.svg paths/slack.svg paths/align-left.svg - paths/corner-up-right.svg paths/map-pin.svg paths/slash.svg - paths/align-right.svg paths/cpu.svg paths/map.svg paths/sliders.svg - paths/anchor.svg paths/credit-card.svg paths/maximize-2.svg - paths/smartphone.svg paths/aperture.svg paths/crop.svg paths/maximize.svg - paths/smile.svg paths/archive.svg paths/crosshair.svg paths/meh.svg - paths/speaker.svg paths/arrow-down-circle.svg paths/database.svg - paths/menu.svg paths/square.svg paths/arrow-down-left.svg paths/delete.svg - paths/message-circle.svg paths/star.svg paths/arrow-down-right.svg - paths/disc.svg paths/message-square.svg paths/stop-circle.svg - paths/arrow-down.svg paths/divide-circle.svg paths/mic-off.svg - paths/sunrise.svg paths/arrow-left-circle.svg paths/divide-square.svg - paths/mic.svg paths/sunset.svg paths/arrow-left.svg paths/divide.svg - paths/minimize-2.svg paths/sun.svg paths/arrow-right-circle.svg - paths/dollar-sign.svg paths/minimize.svg paths/tablet.svg - paths/arrow-right.svg paths/download-cloud.svg paths/minus-circle.svg - paths/tag.svg paths/arrow-up-circle.svg paths/download.svg - paths/minus-square.svg paths/target.svg paths/arrow-up-left.svg - paths/dribbble.svg paths/minus.svg paths/terminal.svg - paths/arrow-up-right.svg paths/droplet.svg paths/monitor.svg - paths/thermometer.svg paths/arrow-up.svg paths/edit-2.svg paths/moon.svg - paths/thumbs-down.svg paths/at-sign.svg paths/edit-3.svg - paths/more-horizontal.svg paths/thumbs-up.svg paths/award.svg - paths/edit.svg paths/more-vertical.svg paths/toggle-left.svg - paths/bar-chart-2.svg paths/external-link.svg paths/mouse-pointer.svg - paths/toggle-right.svg paths/bar-chart.svg paths/eye-off.svg - paths/move.svg paths/tool.svg paths/battery-charging.svg paths/eye.svg - paths/music.svg paths/trash-2.svg paths/battery.svg paths/facebook.svg - paths/navigation-2.svg paths/trash.svg paths/bell-off.svg - paths/fast-forward.svg paths/navigation.svg paths/trello.svg - paths/bell.svg paths/feather.svg paths/octagon.svg paths/trending-down.svg - paths/bluetooth.svg paths/figma.svg paths/package.svg - paths/trending-up.svg paths/bold.svg paths/file-minus.svg - paths/paperclip.svg paths/triangle.svg paths/bookmark.svg - paths/file-plus.svg paths/pause-circle.svg paths/truck.svg - paths/book-open.svg paths/file.svg paths/pause.svg paths/tv.svg - paths/book.svg paths/file-text.svg paths/pen-tool.svg paths/twitch.svg - paths/box.svg paths/film.svg paths/percent.svg paths/twitter.svg - paths/briefcase.svg paths/filter.svg paths/phone-call.svg paths/type.svg - paths/calendar.svg paths/flag.svg paths/phone-forwarded.svg - paths/umbrella.svg paths/camera-off.svg paths/folder-minus.svg - paths/phone-incoming.svg paths/underline.svg paths/camera.svg - paths/folder-plus.svg paths/phone-missed.svg paths/unlock.svg - paths/cast.svg paths/folder.svg paths/phone-off.svg paths/upload-cloud.svg - paths/check-circle.svg paths/framer.svg paths/phone-outgoing.svg - paths/upload.svg paths/check-square.svg paths/frown.svg paths/phone.svg - paths/user-check.svg paths/check.svg paths/gift.svg paths/pie-chart.svg - paths/user-minus.svg paths/chevron-down.svg paths/git-branch.svg - paths/play-circle.svg paths/user-plus.svg paths/chevron-left.svg - paths/git-commit.svg paths/play.svg paths/users.svg - paths/chevron-right.svg paths/github.svg paths/plus-circle.svg - paths/user.svg paths/chevrons-down.svg paths/gitlab.svg - paths/plus-square.svg paths/user-x.svg paths/chevrons-left.svg - paths/git-merge.svg paths/plus.svg paths/video-off.svg - paths/chevrons-right.svg paths/git-pull-request.svg paths/pocket.svg - paths/video.svg paths/chevrons-up.svg paths/globe.svg paths/power.svg - paths/voicemail.svg paths/chevron-up.svg paths/grid.svg paths/printer.svg - paths/volume-1.svg paths/chrome.svg paths/hard-drive.svg paths/radio.svg - paths/volume-2.svg paths/circle.svg paths/hash.svg paths/refresh-ccw.svg - paths/volume.svg paths/clipboard.svg paths/headphones.svg - paths/refresh-cw.svg paths/volume-x.svg paths/clock.svg paths/heart.svg - paths/repeat.svg paths/watch.svg paths/cloud-drizzle.svg - paths/help-circle.svg paths/rewind.svg paths/wifi-off.svg - paths/cloud-lightning.svg paths/hexagon.svg paths/rotate-ccw.svg - paths/wifi.svg paths/cloud-off.svg paths/home.svg paths/rotate-cw.svg - paths/wind.svg paths/cloud-rain.svg paths/image.svg paths/rss.svg - paths/x-circle.svg paths/cloud-snow.svg paths/inbox.svg paths/save.svg - paths/x-octagon.svg paths/cloud.svg paths/info.svg paths/scissors.svg - paths/x-square.svg paths/codepen.svg paths/instagram.svg paths/search.svg - paths/x.svg paths/codesandbox.svg paths/italic.svg paths/send.svg - paths/youtube.svg paths/code.svg paths/key.svg paths/server.svg - paths/zap-off.svg paths/coffee.svg paths/layers.svg paths/settings.svg - paths/zap.svg paths/columns.svg paths/layout.svg paths/share-2.svg - paths/zoom-in.svg paths/command.svg paths/life-buoy.svg paths/share.svg - paths/zoom-out.svg paths/compass.svg paths/link-2.svg paths/shield-off.svg - paths/copy.svg paths/linkedin.svg paths/shield.svg) - (action - (bash "%{deps} -output paths"))) diff --git a/bindings/feather_icon/src/feather_icon.ml b/bindings/feather_icon/src/feather_icon.ml deleted file mode 100644 index 073db00f..00000000 --- a/bindings/feather_icon/src/feather_icon.ml +++ /dev/null @@ -1,652 +0,0 @@ -open! Core -open! Import - -type t = - | Activity - | Corner_down_left - | Link - | Shopping_bag - | Airplay - | Corner_down_right - | List - | Shopping_cart - | Alert_circle - | Corner_left_down - | Loader - | Shuffle - | Alert_octagon - | Corner_left_up - | Lock - | Sidebar - | Alert_triangle - | Corner_right_down - | Log_in - | Skip_back - | Align_center - | Corner_right_up - | Log_out - | Skip_forward - | Align_justify - | Corner_up_left - | Mail - | Slack - | Align_left - | Corner_up_right - | Map_pin - | Slash - | Align_right - | Cpu - | Map - | Sliders - | Anchor - | Credit_card - | Maximize_2 - | Smartphone - | Aperture - | Crop - | Maximize - | Smile - | Archive - | Crosshair - | Meh - | Speaker - | Arrow_down_circle - | Database - | Menu - | Square - | Arrow_down_left - | Delete - | Message_circle - | Star - | Arrow_down_right - | Disc - | Message_square - | Stop_circle - | Arrow_down - | Divide_circle - | Mic_off - | Sunrise - | Arrow_left_circle - | Divide_square - | Mic - | Sunset - | Arrow_left - | Divide - | Minimize_2 - | Sun - | Arrow_right_circle - | Dollar_sign - | Minimize - | Tablet - | Arrow_right - | Download_cloud - | Minus_circle - | Tag - | Arrow_up_circle - | Download - | Minus_square - | Target - | Arrow_up_left - | Dribbble - | Minus - | Terminal - | Arrow_up_right - | Droplet - | Monitor - | Thermometer - | Arrow_up - | Edit_2 - | Moon - | Thumbs_down - | At_sign - | Edit_3 - | More_horizontal - | Thumbs_up - | Award - | Edit - | More_vertical - | Toggle_left - | Bar_chart_2 - | External_link - | Mouse_pointer - | Toggle_right - | Bar_chart - | Eye_off - | Move - | Tool - | Battery_charging - | Eye - | Music - | Trash_2 - | Battery - | Facebook - | Navigation_2 - | Trash - | Bell_off - | Fast_forward - | Navigation - | Trello - | Bell - | Feather - | Octagon - | Trending_down - | Bluetooth - | Figma - | Package - | Trending_up - | Bold - | File_minus - | Paperclip - | Triangle - | Bookmark - | File_plus - | Pause_circle - | Truck - | Book_open - | File - | Pause - | Tv - | Book - | File_text - | Pen_tool - | Twitch - | Box - | Film - | Percent - | Twitter - | Briefcase - | Filter - | Phone_call - | Type - | Calendar - | Flag - | Phone_forwarded - | Umbrella - | Camera_off - | Folder_minus - | Phone_incoming - | Underline - | Camera - | Folder_plus - | Phone_missed - | Unlock - | Cast - | Folder - | Phone_off - | Upload_cloud - | Check_circle - | Framer - | Phone_outgoing - | Upload - | Check_square - | Frown - | Phone - | User_check - | Check - | Gift - | Pie_chart - | User_minus - | Chevron_down - | Git_branch - | Play_circle - | User_plus - | Chevron_left - | Git_commit - | Play - | Users - | Chevron_right - | Github - | Plus_circle - | User - | Chevrons_down - | Gitlab - | Plus_square - | User_x - | Chevrons_left - | Git_merge - | Plus - | Video_off - | Chevrons_right - | Git_pull_request - | Pocket - | Video - | Chevrons_up - | Globe - | Power - | Voicemail - | Chevron_up - | Grid - | Printer - | Volume_1 - | Chrome - | Hard_drive - | Radio - | Volume_2 - | Circle - | Hash - | Refresh_ccw - | Volume - | Clipboard - | Headphones - | Refresh_cw - | Volume_x - | Clock - | Heart - | Repeat - | Watch - | Cloud_drizzle - | Help_circle - | Rewind - | Wifi_off - | Cloud_lightning - | Hexagon - | Rotate_ccw - | Wifi - | Cloud_off - | Home - | Rotate_cw - | Wind - | Cloud_rain - | Image - | Rss - | X_circle - | Cloud_snow - | Inbox - | Save - | X_octagon - | Cloud - | Info - | Scissors - | X_square - | Codepen - | Instagram - | Search - | X - | Codesandbox - | Italic - | Send - | Youtube - | Code - | Key - | Server - | Zap_off - | Coffee - | Layers - | Settings - | Zap - | Columns - | Layout - | Share_2 - | Zoom_in - | Command - | Life_buoy - | Share - | Zoom_out - | Compass - | Link_2 - | Shield_off - | Copy - | Linkedin - | Shield -[@@deriving compare, enumerate, equal, sexp, sexp_grammar] - -let path = function - | Activity -> Paths.activity_dot_svg - | Corner_down_left -> Paths.corner_down_left_dot_svg - | Link -> Paths.link_dot_svg - | Shopping_bag -> Paths.shopping_bag_dot_svg - | Airplay -> Paths.airplay_dot_svg - | Corner_down_right -> Paths.corner_down_right_dot_svg - | List -> Paths.list_dot_svg - | Shopping_cart -> Paths.shopping_cart_dot_svg - | Alert_circle -> Paths.alert_circle_dot_svg - | Corner_left_down -> Paths.corner_left_down_dot_svg - | Loader -> Paths.loader_dot_svg - | Shuffle -> Paths.shuffle_dot_svg - | Alert_octagon -> Paths.alert_octagon_dot_svg - | Corner_left_up -> Paths.corner_left_up_dot_svg - | Lock -> Paths.lock_dot_svg - | Sidebar -> Paths.sidebar_dot_svg - | Alert_triangle -> Paths.alert_triangle_dot_svg - | Corner_right_down -> Paths.corner_right_down_dot_svg - | Log_in -> Paths.log_in_dot_svg - | Skip_back -> Paths.skip_back_dot_svg - | Align_center -> Paths.align_center_dot_svg - | Corner_right_up -> Paths.corner_right_up_dot_svg - | Log_out -> Paths.log_out_dot_svg - | Skip_forward -> Paths.skip_forward_dot_svg - | Align_justify -> Paths.align_justify_dot_svg - | Corner_up_left -> Paths.corner_up_left_dot_svg - | Mail -> Paths.mail_dot_svg - | Slack -> Paths.slack_dot_svg - | Align_left -> Paths.align_left_dot_svg - | Corner_up_right -> Paths.corner_up_right_dot_svg - | Map_pin -> Paths.map_pin_dot_svg - | Slash -> Paths.slash_dot_svg - | Align_right -> Paths.align_right_dot_svg - | Cpu -> Paths.cpu_dot_svg - | Map -> Paths.map_dot_svg - | Sliders -> Paths.sliders_dot_svg - | Anchor -> Paths.anchor_dot_svg - | Credit_card -> Paths.credit_card_dot_svg - | Maximize_2 -> Paths.maximize_2_dot_svg - | Smartphone -> Paths.smartphone_dot_svg - | Aperture -> Paths.aperture_dot_svg - | Crop -> Paths.crop_dot_svg - | Maximize -> Paths.maximize_dot_svg - | Smile -> Paths.smile_dot_svg - | Archive -> Paths.archive_dot_svg - | Crosshair -> Paths.crosshair_dot_svg - | Meh -> Paths.meh_dot_svg - | Speaker -> Paths.speaker_dot_svg - | Arrow_down_circle -> Paths.arrow_down_circle_dot_svg - | Database -> Paths.database_dot_svg - | Menu -> Paths.menu_dot_svg - | Square -> Paths.square_dot_svg - | Arrow_down_left -> Paths.arrow_down_left_dot_svg - | Delete -> Paths.delete_dot_svg - | Message_circle -> Paths.message_circle_dot_svg - | Star -> Paths.star_dot_svg - | Arrow_down_right -> Paths.arrow_down_right_dot_svg - | Disc -> Paths.disc_dot_svg - | Message_square -> Paths.message_square_dot_svg - | Stop_circle -> Paths.stop_circle_dot_svg - | Arrow_down -> Paths.arrow_down_dot_svg - | Divide_circle -> Paths.divide_circle_dot_svg - | Mic_off -> Paths.mic_off_dot_svg - | Sunrise -> Paths.sunrise_dot_svg - | Arrow_left_circle -> Paths.arrow_left_circle_dot_svg - | Divide_square -> Paths.divide_square_dot_svg - | Mic -> Paths.mic_dot_svg - | Sunset -> Paths.sunset_dot_svg - | Arrow_left -> Paths.arrow_left_dot_svg - | Divide -> Paths.divide_dot_svg - | Minimize_2 -> Paths.minimize_2_dot_svg - | Sun -> Paths.sun_dot_svg - | Arrow_right_circle -> Paths.arrow_right_circle_dot_svg - | Dollar_sign -> Paths.dollar_sign_dot_svg - | Minimize -> Paths.minimize_dot_svg - | Tablet -> Paths.tablet_dot_svg - | Arrow_right -> Paths.arrow_right_dot_svg - | Download_cloud -> Paths.download_cloud_dot_svg - | Minus_circle -> Paths.minus_circle_dot_svg - | Tag -> Paths.tag_dot_svg - | Arrow_up_circle -> Paths.arrow_up_circle_dot_svg - | Download -> Paths.download_dot_svg - | Minus_square -> Paths.minus_square_dot_svg - | Target -> Paths.target_dot_svg - | Arrow_up_left -> Paths.arrow_up_left_dot_svg - | Dribbble -> Paths.dribbble_dot_svg - | Minus -> Paths.minus_dot_svg - | Terminal -> Paths.terminal_dot_svg - | Arrow_up_right -> Paths.arrow_up_right_dot_svg - | Droplet -> Paths.droplet_dot_svg - | Monitor -> Paths.monitor_dot_svg - | Thermometer -> Paths.thermometer_dot_svg - | Arrow_up -> Paths.arrow_up_dot_svg - | Edit_2 -> Paths.edit_2_dot_svg - | Moon -> Paths.moon_dot_svg - | Thumbs_down -> Paths.thumbs_down_dot_svg - | At_sign -> Paths.at_sign_dot_svg - | Edit_3 -> Paths.edit_3_dot_svg - | More_horizontal -> Paths.more_horizontal_dot_svg - | Thumbs_up -> Paths.thumbs_up_dot_svg - | Award -> Paths.award_dot_svg - | Edit -> Paths.edit_dot_svg - | More_vertical -> Paths.more_vertical_dot_svg - | Toggle_left -> Paths.toggle_left_dot_svg - | Bar_chart_2 -> Paths.bar_chart_2_dot_svg - | External_link -> Paths.external_link_dot_svg - | Mouse_pointer -> Paths.mouse_pointer_dot_svg - | Toggle_right -> Paths.toggle_right_dot_svg - | Bar_chart -> Paths.bar_chart_dot_svg - | Eye_off -> Paths.eye_off_dot_svg - | Move -> Paths.move_dot_svg - | Tool -> Paths.tool_dot_svg - | Battery_charging -> Paths.battery_charging_dot_svg - | Eye -> Paths.eye_dot_svg - | Music -> Paths.music_dot_svg - | Trash_2 -> Paths.trash_2_dot_svg - | Battery -> Paths.battery_dot_svg - | Facebook -> Paths.facebook_dot_svg - | Navigation_2 -> Paths.navigation_2_dot_svg - | Trash -> Paths.trash_dot_svg - | Bell_off -> Paths.bell_off_dot_svg - | Fast_forward -> Paths.fast_forward_dot_svg - | Navigation -> Paths.navigation_dot_svg - | Trello -> Paths.trello_dot_svg - | Bell -> Paths.bell_dot_svg - | Feather -> Paths.feather_dot_svg - | Octagon -> Paths.octagon_dot_svg - | Trending_down -> Paths.trending_down_dot_svg - | Bluetooth -> Paths.bluetooth_dot_svg - | Figma -> Paths.figma_dot_svg - | Package -> Paths.package_dot_svg - | Trending_up -> Paths.trending_up_dot_svg - | Bold -> Paths.bold_dot_svg - | File_minus -> Paths.file_minus_dot_svg - | Paperclip -> Paths.paperclip_dot_svg - | Triangle -> Paths.triangle_dot_svg - | Bookmark -> Paths.bookmark_dot_svg - | File_plus -> Paths.file_plus_dot_svg - | Pause_circle -> Paths.pause_circle_dot_svg - | Truck -> Paths.truck_dot_svg - | Book_open -> Paths.book_open_dot_svg - | File -> Paths.file_dot_svg - | Pause -> Paths.pause_dot_svg - | Tv -> Paths.tv_dot_svg - | Book -> Paths.book_dot_svg - | File_text -> Paths.file_text_dot_svg - | Pen_tool -> Paths.pen_tool_dot_svg - | Twitch -> Paths.twitch_dot_svg - | Box -> Paths.box_dot_svg - | Film -> Paths.film_dot_svg - | Percent -> Paths.percent_dot_svg - | Twitter -> Paths.twitter_dot_svg - | Briefcase -> Paths.briefcase_dot_svg - | Filter -> Paths.filter_dot_svg - | Phone_call -> Paths.phone_call_dot_svg - | Type -> Paths.type_dot_svg - | Calendar -> Paths.calendar_dot_svg - | Flag -> Paths.flag_dot_svg - | Phone_forwarded -> Paths.phone_forwarded_dot_svg - | Umbrella -> Paths.umbrella_dot_svg - | Camera_off -> Paths.camera_off_dot_svg - | Folder_minus -> Paths.folder_minus_dot_svg - | Phone_incoming -> Paths.phone_incoming_dot_svg - | Underline -> Paths.underline_dot_svg - | Camera -> Paths.camera_dot_svg - | Folder_plus -> Paths.folder_plus_dot_svg - | Phone_missed -> Paths.phone_missed_dot_svg - | Unlock -> Paths.unlock_dot_svg - | Cast -> Paths.cast_dot_svg - | Folder -> Paths.folder_dot_svg - | Phone_off -> Paths.phone_off_dot_svg - | Upload_cloud -> Paths.upload_cloud_dot_svg - | Check_circle -> Paths.check_circle_dot_svg - | Framer -> Paths.framer_dot_svg - | Phone_outgoing -> Paths.phone_outgoing_dot_svg - | Upload -> Paths.upload_dot_svg - | Check_square -> Paths.check_square_dot_svg - | Frown -> Paths.frown_dot_svg - | Phone -> Paths.phone_dot_svg - | User_check -> Paths.user_check_dot_svg - | Check -> Paths.check_dot_svg - | Gift -> Paths.gift_dot_svg - | Pie_chart -> Paths.pie_chart_dot_svg - | User_minus -> Paths.user_minus_dot_svg - | Chevron_down -> Paths.chevron_down_dot_svg - | Git_branch -> Paths.git_branch_dot_svg - | Play_circle -> Paths.play_circle_dot_svg - | User_plus -> Paths.user_plus_dot_svg - | Chevron_left -> Paths.chevron_left_dot_svg - | Git_commit -> Paths.git_commit_dot_svg - | Play -> Paths.play_dot_svg - | Users -> Paths.users_dot_svg - | Chevron_right -> Paths.chevron_right_dot_svg - | Github -> Paths.github_dot_svg - | Plus_circle -> Paths.plus_circle_dot_svg - | User -> Paths.user_dot_svg - | Chevrons_down -> Paths.chevrons_down_dot_svg - | Gitlab -> Paths.gitlab_dot_svg - | Plus_square -> Paths.plus_square_dot_svg - | User_x -> Paths.user_x_dot_svg - | Chevrons_left -> Paths.chevrons_left_dot_svg - | Git_merge -> Paths.git_merge_dot_svg - | Plus -> Paths.plus_dot_svg - | Video_off -> Paths.video_off_dot_svg - | Chevrons_right -> Paths.chevrons_right_dot_svg - | Git_pull_request -> Paths.git_pull_request_dot_svg - | Pocket -> Paths.pocket_dot_svg - | Video -> Paths.video_dot_svg - | Chevrons_up -> Paths.chevrons_up_dot_svg - | Globe -> Paths.globe_dot_svg - | Power -> Paths.power_dot_svg - | Voicemail -> Paths.voicemail_dot_svg - | Chevron_up -> Paths.chevron_up_dot_svg - | Grid -> Paths.grid_dot_svg - | Printer -> Paths.printer_dot_svg - | Volume_1 -> Paths.volume_1_dot_svg - | Chrome -> Paths.chrome_dot_svg - | Hard_drive -> Paths.hard_drive_dot_svg - | Radio -> Paths.radio_dot_svg - | Volume_2 -> Paths.volume_2_dot_svg - | Circle -> Paths.circle_dot_svg - | Hash -> Paths.hash_dot_svg - | Refresh_ccw -> Paths.refresh_ccw_dot_svg - | Volume -> Paths.volume_dot_svg - | Clipboard -> Paths.clipboard_dot_svg - | Headphones -> Paths.headphones_dot_svg - | Refresh_cw -> Paths.refresh_cw_dot_svg - | Volume_x -> Paths.volume_x_dot_svg - | Clock -> Paths.clock_dot_svg - | Heart -> Paths.heart_dot_svg - | Repeat -> Paths.repeat_dot_svg - | Watch -> Paths.watch_dot_svg - | Cloud_drizzle -> Paths.cloud_drizzle_dot_svg - | Help_circle -> Paths.help_circle_dot_svg - | Rewind -> Paths.rewind_dot_svg - | Wifi_off -> Paths.wifi_off_dot_svg - | Cloud_lightning -> Paths.cloud_lightning_dot_svg - | Hexagon -> Paths.hexagon_dot_svg - | Rotate_ccw -> Paths.rotate_ccw_dot_svg - | Wifi -> Paths.wifi_dot_svg - | Cloud_off -> Paths.cloud_off_dot_svg - | Home -> Paths.home_dot_svg - | Rotate_cw -> Paths.rotate_cw_dot_svg - | Wind -> Paths.wind_dot_svg - | Cloud_rain -> Paths.cloud_rain_dot_svg - | Image -> Paths.image_dot_svg - | Rss -> Paths.rss_dot_svg - | X_circle -> Paths.x_circle_dot_svg - | Cloud_snow -> Paths.cloud_snow_dot_svg - | Inbox -> Paths.inbox_dot_svg - | Save -> Paths.save_dot_svg - | X_octagon -> Paths.x_octagon_dot_svg - | Cloud -> Paths.cloud_dot_svg - | Info -> Paths.info_dot_svg - | Scissors -> Paths.scissors_dot_svg - | X_square -> Paths.x_square_dot_svg - | Codepen -> Paths.codepen_dot_svg - | Instagram -> Paths.instagram_dot_svg - | Search -> Paths.search_dot_svg - | X -> Paths.x_dot_svg - | Codesandbox -> Paths.codesandbox_dot_svg - | Italic -> Paths.italic_dot_svg - | Send -> Paths.send_dot_svg - | Youtube -> Paths.youtube_dot_svg - | Code -> Paths.code_dot_svg - | Key -> Paths.key_dot_svg - | Server -> Paths.server_dot_svg - | Zap_off -> Paths.zap_off_dot_svg - | Coffee -> Paths.coffee_dot_svg - | Layers -> Paths.layers_dot_svg - | Settings -> Paths.settings_dot_svg - | Zap -> Paths.zap_dot_svg - | Columns -> Paths.columns_dot_svg - | Layout -> Paths.layout_dot_svg - | Share_2 -> Paths.share_2_dot_svg - | Zoom_in -> Paths.zoom_in_dot_svg - | Command -> Paths.command_dot_svg - | Life_buoy -> Paths.life_buoy_dot_svg - | Share -> Paths.share_dot_svg - | Zoom_out -> Paths.zoom_out_dot_svg - | Compass -> Paths.compass_dot_svg - | Link_2 -> Paths.link_2_dot_svg - | Shield_off -> Paths.shield_off_dot_svg - | Copy -> Paths.copy_dot_svg - | Linkedin -> Paths.linkedin_dot_svg - | Shield -> Paths.shield_dot_svg -;; - -let to_string t = - sexp_of_t t - |> Sexp.to_string - |> String.lowercase - |> String.tr ~target:'_' ~replacement:'-' -;; - -let or_default_size size = - Css_gen.Length.to_string_css - (match size with - | Some size -> (size :> Css_gen.Length.t) - | None -> (`Px 24 :> Css_gen.Length.t)) -;; - -let or_default_stroke_width stroke_width = - Css_gen.Length.to_string_css - (match stroke_width with - | Some stroke_width -> (stroke_width :> Css_gen.Length.t) - | None -> (`Px 2 :> Css_gen.Length.t)) -;; - -let or_default_fill fill = - match fill with - | None -> "none" - | Some color -> Css_gen.Color.to_string_css (color :> Css_gen.Color.t) -;; - -let or_default_stroke stroke = - Css_gen.Color.to_string_css - (match stroke with - | None -> (`Name "currentColor" :> Css_gen.Color.t) - | Some stroke -> (stroke :> Css_gen.Color.t)) -;; - -let svg_string ?size ?stroke ?fill ?stroke_width (t : t) = - let size = or_default_size size in - let stroke_width = or_default_stroke_width stroke_width in - let fill = or_default_fill fill in - let stroke = or_default_stroke stroke in - [%string - {| %{path t} |}] -;; - -let svg ?size ?stroke ?fill ?stroke_width ?(extra_attrs = []) (t : t) = - let size = or_default_size size in - let stroke_width = or_default_stroke_width stroke_width in - let fill = or_default_fill fill in - let stroke = or_default_stroke stroke in - let module A = Vdom.Attr in - let specific_class = "feather-" ^ to_string t in - match Bonsai_web.am_running_how with - | `Browser | `Browser_benchmark | `Node | `Node_benchmark -> - Vdom.Node.inner_html_svg - ~tag:"svg" - ~attrs: - ([ A.string_property "width" size - ; A.string_property "height" size - ; A.string_property "viewBox" "0 0 24 24" - ; A.string_property "fill" fill - ; A.string_property "stroke" stroke - ; A.string_property "stroke-width" stroke_width - ; A.string_property "stroke-linecap" "round" - ; A.string_property "stroke-linejoin" "round" - ; A.classes [ "feather"; specific_class ] - ] - @ extra_attrs) - ~this_html_is_sanitized_and_is_totally_safe_trust_me:(path t) - () - | `Node_test -> - Vdom.Node.create ~attrs:extra_attrs [%string "feather_icons.%{to_string t}"] [] -;; diff --git a/bindings/feather_icon/src/feather_icon.mli b/bindings/feather_icon/src/feather_icon.mli deleted file mode 100644 index 91dd49a4..00000000 --- a/bindings/feather_icon/src/feather_icon.mli +++ /dev/null @@ -1,323 +0,0 @@ -open! Core -open! Import - -(** A simple way to construct feather icon svgs for use in jsoo. - - A few helpful resources: - - The external feather icons website: https://feathericons.com/ - - Our bonsai demo equivalent: https://bonsai:8548/ - - The code for the bonsai demo: ../../bonsai/examples/feather_icons -*) - -type t = - | Activity - | Corner_down_left - | Link - | Shopping_bag - | Airplay - | Corner_down_right - | List - | Shopping_cart - | Alert_circle - | Corner_left_down - | Loader - | Shuffle - | Alert_octagon - | Corner_left_up - | Lock - | Sidebar - | Alert_triangle - | Corner_right_down - | Log_in - | Skip_back - | Align_center - | Corner_right_up - | Log_out - | Skip_forward - | Align_justify - | Corner_up_left - | Mail - | Slack - | Align_left - | Corner_up_right - | Map_pin - | Slash - | Align_right - | Cpu - | Map - | Sliders - | Anchor - | Credit_card - | Maximize_2 - | Smartphone - | Aperture - | Crop - | Maximize - | Smile - | Archive - | Crosshair - | Meh - | Speaker - | Arrow_down_circle - | Database - | Menu - | Square - | Arrow_down_left - | Delete - | Message_circle - | Star - | Arrow_down_right - | Disc - | Message_square - | Stop_circle - | Arrow_down - | Divide_circle - | Mic_off - | Sunrise - | Arrow_left_circle - | Divide_square - | Mic - | Sunset - | Arrow_left - | Divide - | Minimize_2 - | Sun - | Arrow_right_circle - | Dollar_sign - | Minimize - | Tablet - | Arrow_right - | Download_cloud - | Minus_circle - | Tag - | Arrow_up_circle - | Download - | Minus_square - | Target - | Arrow_up_left - | Dribbble - | Minus - | Terminal - | Arrow_up_right - | Droplet - | Monitor - | Thermometer - | Arrow_up - | Edit_2 - | Moon - | Thumbs_down - | At_sign - | Edit_3 - | More_horizontal - | Thumbs_up - | Award - | Edit - | More_vertical - | Toggle_left - | Bar_chart_2 - | External_link - | Mouse_pointer - | Toggle_right - | Bar_chart - | Eye_off - | Move - | Tool - | Battery_charging - | Eye - | Music - | Trash_2 - | Battery - | Facebook - | Navigation_2 - | Trash - | Bell_off - | Fast_forward - | Navigation - | Trello - | Bell - | Feather - | Octagon - | Trending_down - | Bluetooth - | Figma - | Package - | Trending_up - | Bold - | File_minus - | Paperclip - | Triangle - | Bookmark - | File_plus - | Pause_circle - | Truck - | Book_open - | File - | Pause - | Tv - | Book - | File_text - | Pen_tool - | Twitch - | Box - | Film - | Percent - | Twitter - | Briefcase - | Filter - | Phone_call - | Type - | Calendar - | Flag - | Phone_forwarded - | Umbrella - | Camera_off - | Folder_minus - | Phone_incoming - | Underline - | Camera - | Folder_plus - | Phone_missed - | Unlock - | Cast - | Folder - | Phone_off - | Upload_cloud - | Check_circle - | Framer - | Phone_outgoing - | Upload - | Check_square - | Frown - | Phone - | User_check - | Check - | Gift - | Pie_chart - | User_minus - | Chevron_down - | Git_branch - | Play_circle - | User_plus - | Chevron_left - | Git_commit - | Play - | Users - | Chevron_right - | Github - | Plus_circle - | User - | Chevrons_down - | Gitlab - | Plus_square - | User_x - | Chevrons_left - | Git_merge - | Plus - | Video_off - | Chevrons_right - | Git_pull_request - | Pocket - | Video - | Chevrons_up - | Globe - | Power - | Voicemail - | Chevron_up - | Grid - | Printer - | Volume_1 - | Chrome - | Hard_drive - | Radio - | Volume_2 - | Circle - | Hash - | Refresh_ccw - | Volume - | Clipboard - | Headphones - | Refresh_cw - | Volume_x - | Clock - | Heart - | Repeat - | Watch - | Cloud_drizzle - | Help_circle - | Rewind - | Wifi_off - | Cloud_lightning - | Hexagon - | Rotate_ccw - | Wifi - | Cloud_off - | Home - | Rotate_cw - | Wind - | Cloud_rain - | Image - | Rss - | X_circle - | Cloud_snow - | Inbox - | Save - | X_octagon - | Cloud - | Info - | Scissors - | X_square - | Codepen - | Instagram - | Search - | X - | Codesandbox - | Italic - | Send - | Youtube - | Code - | Key - | Server - | Zap_off - | Coffee - | Layers - | Settings - | Zap - | Columns - | Layout - | Share_2 - | Zoom_in - | Command - | Life_buoy - | Share - | Zoom_out - | Compass - | Link_2 - | Shield_off - | Copy - | Linkedin - | Shield -[@@deriving compare, enumerate, equal, sexp, sexp_grammar] - -(** Useful for favicons and css backgrounds *) -val svg_string - : ?size:[< Css_gen.Length.t ] - -> ?stroke:[< Css_gen.Color.t ] - -> ?fill:[< Css_gen.Color.t ] - -> ?stroke_width:[< Css_gen.Length.t ] - -> t - -> string - -(* $MDX part-begin=svg *) - -val svg - : ?size:[< Css_gen.Length.t ] - -> ?stroke:[< Css_gen.Color.t ] - -> ?fill:[< Css_gen.Color.t ] - -> ?stroke_width:[< Css_gen.Length.t ] - -> ?extra_attrs:Vdom.Attr.t list - -> t - -> Vdom.Node.t - -(* $MDX part-end *) - -val to_string : t -> string diff --git a/bindings/feather_icon/src/import.ml b/bindings/feather_icon/src/import.ml deleted file mode 100644 index 083509d9..00000000 --- a/bindings/feather_icon/src/import.ml +++ /dev/null @@ -1,6 +0,0 @@ -open! Core - -include struct - open Virtual_dom - module Vdom = Vdom -end diff --git a/bindings/feather_icon/src/paths/activity.svg b/bindings/feather_icon/src/paths/activity.svg deleted file mode 100644 index 1faf593d..00000000 --- a/bindings/feather_icon/src/paths/activity.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/airplay.svg b/bindings/feather_icon/src/paths/airplay.svg deleted file mode 100644 index b97b4a06..00000000 --- a/bindings/feather_icon/src/paths/airplay.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/alert-circle.svg b/bindings/feather_icon/src/paths/alert-circle.svg deleted file mode 100644 index a9fb954b..00000000 --- a/bindings/feather_icon/src/paths/alert-circle.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/alert-octagon.svg b/bindings/feather_icon/src/paths/alert-octagon.svg deleted file mode 100644 index 1355ed77..00000000 --- a/bindings/feather_icon/src/paths/alert-octagon.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/alert-triangle.svg b/bindings/feather_icon/src/paths/alert-triangle.svg deleted file mode 100644 index f7d7c1ca..00000000 --- a/bindings/feather_icon/src/paths/alert-triangle.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/align-center.svg b/bindings/feather_icon/src/paths/align-center.svg deleted file mode 100644 index 1c76b9bd..00000000 --- a/bindings/feather_icon/src/paths/align-center.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/align-justify.svg b/bindings/feather_icon/src/paths/align-justify.svg deleted file mode 100644 index 75ef4115..00000000 --- a/bindings/feather_icon/src/paths/align-justify.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/align-left.svg b/bindings/feather_icon/src/paths/align-left.svg deleted file mode 100644 index 97d972c4..00000000 --- a/bindings/feather_icon/src/paths/align-left.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/align-right.svg b/bindings/feather_icon/src/paths/align-right.svg deleted file mode 100644 index da626379..00000000 --- a/bindings/feather_icon/src/paths/align-right.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/anchor.svg b/bindings/feather_icon/src/paths/anchor.svg deleted file mode 100644 index 7ecb4f96..00000000 --- a/bindings/feather_icon/src/paths/anchor.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/aperture.svg b/bindings/feather_icon/src/paths/aperture.svg deleted file mode 100644 index ba81e50c..00000000 --- a/bindings/feather_icon/src/paths/aperture.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/archive.svg b/bindings/feather_icon/src/paths/archive.svg deleted file mode 100644 index dabc1a78..00000000 --- a/bindings/feather_icon/src/paths/archive.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/arrow-down-circle.svg b/bindings/feather_icon/src/paths/arrow-down-circle.svg deleted file mode 100644 index 293fbbe1..00000000 --- a/bindings/feather_icon/src/paths/arrow-down-circle.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/arrow-down-left.svg b/bindings/feather_icon/src/paths/arrow-down-left.svg deleted file mode 100644 index cee32e82..00000000 --- a/bindings/feather_icon/src/paths/arrow-down-left.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/arrow-down-right.svg b/bindings/feather_icon/src/paths/arrow-down-right.svg deleted file mode 100644 index c5462484..00000000 --- a/bindings/feather_icon/src/paths/arrow-down-right.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/arrow-down.svg b/bindings/feather_icon/src/paths/arrow-down.svg deleted file mode 100644 index 81e6ee99..00000000 --- a/bindings/feather_icon/src/paths/arrow-down.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/arrow-left-circle.svg b/bindings/feather_icon/src/paths/arrow-left-circle.svg deleted file mode 100644 index 77d82d28..00000000 --- a/bindings/feather_icon/src/paths/arrow-left-circle.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/arrow-left.svg b/bindings/feather_icon/src/paths/arrow-left.svg deleted file mode 100644 index dcf53a03..00000000 --- a/bindings/feather_icon/src/paths/arrow-left.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/arrow-right-circle.svg b/bindings/feather_icon/src/paths/arrow-right-circle.svg deleted file mode 100644 index af2a8e9a..00000000 --- a/bindings/feather_icon/src/paths/arrow-right-circle.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/arrow-right.svg b/bindings/feather_icon/src/paths/arrow-right.svg deleted file mode 100644 index e7779608..00000000 --- a/bindings/feather_icon/src/paths/arrow-right.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/arrow-up-circle.svg b/bindings/feather_icon/src/paths/arrow-up-circle.svg deleted file mode 100644 index d5a4b4b3..00000000 --- a/bindings/feather_icon/src/paths/arrow-up-circle.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/arrow-up-left.svg b/bindings/feather_icon/src/paths/arrow-up-left.svg deleted file mode 100644 index 037f5f24..00000000 --- a/bindings/feather_icon/src/paths/arrow-up-left.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/arrow-up-right.svg b/bindings/feather_icon/src/paths/arrow-up-right.svg deleted file mode 100644 index 893bef0b..00000000 --- a/bindings/feather_icon/src/paths/arrow-up-right.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/arrow-up.svg b/bindings/feather_icon/src/paths/arrow-up.svg deleted file mode 100644 index e50b7216..00000000 --- a/bindings/feather_icon/src/paths/arrow-up.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/at-sign.svg b/bindings/feather_icon/src/paths/at-sign.svg deleted file mode 100644 index 936f898c..00000000 --- a/bindings/feather_icon/src/paths/at-sign.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/award.svg b/bindings/feather_icon/src/paths/award.svg deleted file mode 100644 index 0de6c285..00000000 --- a/bindings/feather_icon/src/paths/award.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/bar-chart-2.svg b/bindings/feather_icon/src/paths/bar-chart-2.svg deleted file mode 100644 index 08f30cca..00000000 --- a/bindings/feather_icon/src/paths/bar-chart-2.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/bar-chart.svg b/bindings/feather_icon/src/paths/bar-chart.svg deleted file mode 100644 index aaa21a71..00000000 --- a/bindings/feather_icon/src/paths/bar-chart.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/battery-charging.svg b/bindings/feather_icon/src/paths/battery-charging.svg deleted file mode 100644 index 480230d1..00000000 --- a/bindings/feather_icon/src/paths/battery-charging.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/battery.svg b/bindings/feather_icon/src/paths/battery.svg deleted file mode 100644 index 76ba03dd..00000000 --- a/bindings/feather_icon/src/paths/battery.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/bell-off.svg b/bindings/feather_icon/src/paths/bell-off.svg deleted file mode 100644 index 43bbcaa7..00000000 --- a/bindings/feather_icon/src/paths/bell-off.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/bell.svg b/bindings/feather_icon/src/paths/bell.svg deleted file mode 100644 index eddedf0b..00000000 --- a/bindings/feather_icon/src/paths/bell.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/bluetooth.svg b/bindings/feather_icon/src/paths/bluetooth.svg deleted file mode 100644 index 43845960..00000000 --- a/bindings/feather_icon/src/paths/bluetooth.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/bold.svg b/bindings/feather_icon/src/paths/bold.svg deleted file mode 100644 index 62d36e6e..00000000 --- a/bindings/feather_icon/src/paths/bold.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/book-open.svg b/bindings/feather_icon/src/paths/book-open.svg deleted file mode 100644 index fb168599..00000000 --- a/bindings/feather_icon/src/paths/book-open.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/book.svg b/bindings/feather_icon/src/paths/book.svg deleted file mode 100644 index b5e2906e..00000000 --- a/bindings/feather_icon/src/paths/book.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/bookmark.svg b/bindings/feather_icon/src/paths/bookmark.svg deleted file mode 100644 index 5aa182ef..00000000 --- a/bindings/feather_icon/src/paths/bookmark.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/box.svg b/bindings/feather_icon/src/paths/box.svg deleted file mode 100644 index e49c5ea6..00000000 --- a/bindings/feather_icon/src/paths/box.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/briefcase.svg b/bindings/feather_icon/src/paths/briefcase.svg deleted file mode 100644 index a81da6ac..00000000 --- a/bindings/feather_icon/src/paths/briefcase.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/calendar.svg b/bindings/feather_icon/src/paths/calendar.svg deleted file mode 100644 index 9496fd39..00000000 --- a/bindings/feather_icon/src/paths/calendar.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/camera-off.svg b/bindings/feather_icon/src/paths/camera-off.svg deleted file mode 100644 index fc2019bd..00000000 --- a/bindings/feather_icon/src/paths/camera-off.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/camera.svg b/bindings/feather_icon/src/paths/camera.svg deleted file mode 100644 index 1fb22c31..00000000 --- a/bindings/feather_icon/src/paths/camera.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/cast.svg b/bindings/feather_icon/src/paths/cast.svg deleted file mode 100644 index ae7114f0..00000000 --- a/bindings/feather_icon/src/paths/cast.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/check-circle.svg b/bindings/feather_icon/src/paths/check-circle.svg deleted file mode 100644 index 429f5660..00000000 --- a/bindings/feather_icon/src/paths/check-circle.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/check-square.svg b/bindings/feather_icon/src/paths/check-square.svg deleted file mode 100644 index 457f35dd..00000000 --- a/bindings/feather_icon/src/paths/check-square.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/check.svg b/bindings/feather_icon/src/paths/check.svg deleted file mode 100644 index 167a4cba..00000000 --- a/bindings/feather_icon/src/paths/check.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/chevron-down.svg b/bindings/feather_icon/src/paths/chevron-down.svg deleted file mode 100644 index 4311e91a..00000000 --- a/bindings/feather_icon/src/paths/chevron-down.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/chevron-left.svg b/bindings/feather_icon/src/paths/chevron-left.svg deleted file mode 100644 index 12a5f4bd..00000000 --- a/bindings/feather_icon/src/paths/chevron-left.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/chevron-right.svg b/bindings/feather_icon/src/paths/chevron-right.svg deleted file mode 100644 index 0d92cc0f..00000000 --- a/bindings/feather_icon/src/paths/chevron-right.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/chevron-up.svg b/bindings/feather_icon/src/paths/chevron-up.svg deleted file mode 100644 index 38f1c900..00000000 --- a/bindings/feather_icon/src/paths/chevron-up.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/chevrons-down.svg b/bindings/feather_icon/src/paths/chevrons-down.svg deleted file mode 100644 index f28ef766..00000000 --- a/bindings/feather_icon/src/paths/chevrons-down.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/chevrons-left.svg b/bindings/feather_icon/src/paths/chevrons-left.svg deleted file mode 100644 index 06cee0ef..00000000 --- a/bindings/feather_icon/src/paths/chevrons-left.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/chevrons-right.svg b/bindings/feather_icon/src/paths/chevrons-right.svg deleted file mode 100644 index f774b5d1..00000000 --- a/bindings/feather_icon/src/paths/chevrons-right.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/chevrons-up.svg b/bindings/feather_icon/src/paths/chevrons-up.svg deleted file mode 100644 index 930b78cd..00000000 --- a/bindings/feather_icon/src/paths/chevrons-up.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/chrome.svg b/bindings/feather_icon/src/paths/chrome.svg deleted file mode 100644 index 8d309b1c..00000000 --- a/bindings/feather_icon/src/paths/chrome.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/circle.svg b/bindings/feather_icon/src/paths/circle.svg deleted file mode 100644 index b1ecdcec..00000000 --- a/bindings/feather_icon/src/paths/circle.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/clipboard.svg b/bindings/feather_icon/src/paths/clipboard.svg deleted file mode 100644 index d79cb571..00000000 --- a/bindings/feather_icon/src/paths/clipboard.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/clock.svg b/bindings/feather_icon/src/paths/clock.svg deleted file mode 100644 index 2160d50e..00000000 --- a/bindings/feather_icon/src/paths/clock.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/cloud-drizzle.svg b/bindings/feather_icon/src/paths/cloud-drizzle.svg deleted file mode 100644 index 20d8ce68..00000000 --- a/bindings/feather_icon/src/paths/cloud-drizzle.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/cloud-lightning.svg b/bindings/feather_icon/src/paths/cloud-lightning.svg deleted file mode 100644 index 80eaa9d1..00000000 --- a/bindings/feather_icon/src/paths/cloud-lightning.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/cloud-off.svg b/bindings/feather_icon/src/paths/cloud-off.svg deleted file mode 100644 index 6bf3a243..00000000 --- a/bindings/feather_icon/src/paths/cloud-off.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/cloud-rain.svg b/bindings/feather_icon/src/paths/cloud-rain.svg deleted file mode 100644 index bb814724..00000000 --- a/bindings/feather_icon/src/paths/cloud-rain.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/cloud-snow.svg b/bindings/feather_icon/src/paths/cloud-snow.svg deleted file mode 100644 index 4daa43a0..00000000 --- a/bindings/feather_icon/src/paths/cloud-snow.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/cloud.svg b/bindings/feather_icon/src/paths/cloud.svg deleted file mode 100644 index ffe5aadf..00000000 --- a/bindings/feather_icon/src/paths/cloud.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/code.svg b/bindings/feather_icon/src/paths/code.svg deleted file mode 100644 index 07332333..00000000 --- a/bindings/feather_icon/src/paths/code.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/codepen.svg b/bindings/feather_icon/src/paths/codepen.svg deleted file mode 100644 index 781722e4..00000000 --- a/bindings/feather_icon/src/paths/codepen.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/codesandbox.svg b/bindings/feather_icon/src/paths/codesandbox.svg deleted file mode 100644 index 03bf5ebb..00000000 --- a/bindings/feather_icon/src/paths/codesandbox.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/coffee.svg b/bindings/feather_icon/src/paths/coffee.svg deleted file mode 100644 index 0339fa94..00000000 --- a/bindings/feather_icon/src/paths/coffee.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/columns.svg b/bindings/feather_icon/src/paths/columns.svg deleted file mode 100644 index 2f20e213..00000000 --- a/bindings/feather_icon/src/paths/columns.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/command.svg b/bindings/feather_icon/src/paths/command.svg deleted file mode 100644 index e52b93af..00000000 --- a/bindings/feather_icon/src/paths/command.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/compass.svg b/bindings/feather_icon/src/paths/compass.svg deleted file mode 100644 index cacc8c07..00000000 --- a/bindings/feather_icon/src/paths/compass.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/copy.svg b/bindings/feather_icon/src/paths/copy.svg deleted file mode 100644 index 2773f280..00000000 --- a/bindings/feather_icon/src/paths/copy.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/corner-down-left.svg b/bindings/feather_icon/src/paths/corner-down-left.svg deleted file mode 100644 index 211fa0ec..00000000 --- a/bindings/feather_icon/src/paths/corner-down-left.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/corner-down-right.svg b/bindings/feather_icon/src/paths/corner-down-right.svg deleted file mode 100644 index 64c1fcf8..00000000 --- a/bindings/feather_icon/src/paths/corner-down-right.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/corner-left-down.svg b/bindings/feather_icon/src/paths/corner-left-down.svg deleted file mode 100644 index 1b99c2c3..00000000 --- a/bindings/feather_icon/src/paths/corner-left-down.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/corner-left-up.svg b/bindings/feather_icon/src/paths/corner-left-up.svg deleted file mode 100644 index a2637269..00000000 --- a/bindings/feather_icon/src/paths/corner-left-up.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/corner-right-down.svg b/bindings/feather_icon/src/paths/corner-right-down.svg deleted file mode 100644 index 8521c9b9..00000000 --- a/bindings/feather_icon/src/paths/corner-right-down.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/corner-right-up.svg b/bindings/feather_icon/src/paths/corner-right-up.svg deleted file mode 100644 index f06703fd..00000000 --- a/bindings/feather_icon/src/paths/corner-right-up.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/corner-up-left.svg b/bindings/feather_icon/src/paths/corner-up-left.svg deleted file mode 100644 index 53741e8e..00000000 --- a/bindings/feather_icon/src/paths/corner-up-left.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/corner-up-right.svg b/bindings/feather_icon/src/paths/corner-up-right.svg deleted file mode 100644 index a97ff719..00000000 --- a/bindings/feather_icon/src/paths/corner-up-right.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/cpu.svg b/bindings/feather_icon/src/paths/cpu.svg deleted file mode 100644 index 2dd33b7d..00000000 --- a/bindings/feather_icon/src/paths/cpu.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/credit-card.svg b/bindings/feather_icon/src/paths/credit-card.svg deleted file mode 100644 index eff20cda..00000000 --- a/bindings/feather_icon/src/paths/credit-card.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/crop.svg b/bindings/feather_icon/src/paths/crop.svg deleted file mode 100644 index e2588e58..00000000 --- a/bindings/feather_icon/src/paths/crop.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/crosshair.svg b/bindings/feather_icon/src/paths/crosshair.svg deleted file mode 100644 index 84b961ff..00000000 --- a/bindings/feather_icon/src/paths/crosshair.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/database.svg b/bindings/feather_icon/src/paths/database.svg deleted file mode 100644 index 18d271a0..00000000 --- a/bindings/feather_icon/src/paths/database.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/delete.svg b/bindings/feather_icon/src/paths/delete.svg deleted file mode 100644 index 5acf77ba..00000000 --- a/bindings/feather_icon/src/paths/delete.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/disc.svg b/bindings/feather_icon/src/paths/disc.svg deleted file mode 100644 index 09a6b6a7..00000000 --- a/bindings/feather_icon/src/paths/disc.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/divide-circle.svg b/bindings/feather_icon/src/paths/divide-circle.svg deleted file mode 100644 index 58841bdf..00000000 --- a/bindings/feather_icon/src/paths/divide-circle.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/divide-square.svg b/bindings/feather_icon/src/paths/divide-square.svg deleted file mode 100644 index 8acf220e..00000000 --- a/bindings/feather_icon/src/paths/divide-square.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/divide.svg b/bindings/feather_icon/src/paths/divide.svg deleted file mode 100644 index 3d03687a..00000000 --- a/bindings/feather_icon/src/paths/divide.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/dollar-sign.svg b/bindings/feather_icon/src/paths/dollar-sign.svg deleted file mode 100644 index 7a6b8b3b..00000000 --- a/bindings/feather_icon/src/paths/dollar-sign.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/download-cloud.svg b/bindings/feather_icon/src/paths/download-cloud.svg deleted file mode 100644 index be9c44aa..00000000 --- a/bindings/feather_icon/src/paths/download-cloud.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/download.svg b/bindings/feather_icon/src/paths/download.svg deleted file mode 100644 index c0797a62..00000000 --- a/bindings/feather_icon/src/paths/download.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/dribbble.svg b/bindings/feather_icon/src/paths/dribbble.svg deleted file mode 100644 index fef1e64a..00000000 --- a/bindings/feather_icon/src/paths/dribbble.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/droplet.svg b/bindings/feather_icon/src/paths/droplet.svg deleted file mode 100644 index cd1a65cd..00000000 --- a/bindings/feather_icon/src/paths/droplet.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/edit-2.svg b/bindings/feather_icon/src/paths/edit-2.svg deleted file mode 100644 index 6c664105..00000000 --- a/bindings/feather_icon/src/paths/edit-2.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/edit-3.svg b/bindings/feather_icon/src/paths/edit-3.svg deleted file mode 100644 index e685b7a4..00000000 --- a/bindings/feather_icon/src/paths/edit-3.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/edit.svg b/bindings/feather_icon/src/paths/edit.svg deleted file mode 100644 index 89fee2b6..00000000 --- a/bindings/feather_icon/src/paths/edit.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/external-link.svg b/bindings/feather_icon/src/paths/external-link.svg deleted file mode 100644 index 477b9541..00000000 --- a/bindings/feather_icon/src/paths/external-link.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/eye-off.svg b/bindings/feather_icon/src/paths/eye-off.svg deleted file mode 100644 index d1d3fdb7..00000000 --- a/bindings/feather_icon/src/paths/eye-off.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/eye.svg b/bindings/feather_icon/src/paths/eye.svg deleted file mode 100644 index 562d0003..00000000 --- a/bindings/feather_icon/src/paths/eye.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/facebook.svg b/bindings/feather_icon/src/paths/facebook.svg deleted file mode 100644 index 07009472..00000000 --- a/bindings/feather_icon/src/paths/facebook.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/fast-forward.svg b/bindings/feather_icon/src/paths/fast-forward.svg deleted file mode 100644 index a5fa738b..00000000 --- a/bindings/feather_icon/src/paths/fast-forward.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/feather.svg b/bindings/feather_icon/src/paths/feather.svg deleted file mode 100644 index 22b0c658..00000000 --- a/bindings/feather_icon/src/paths/feather.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/figma.svg b/bindings/feather_icon/src/paths/figma.svg deleted file mode 100644 index 7d310061..00000000 --- a/bindings/feather_icon/src/paths/figma.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/file-minus.svg b/bindings/feather_icon/src/paths/file-minus.svg deleted file mode 100644 index 1d754af7..00000000 --- a/bindings/feather_icon/src/paths/file-minus.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/file-plus.svg b/bindings/feather_icon/src/paths/file-plus.svg deleted file mode 100644 index 24e75569..00000000 --- a/bindings/feather_icon/src/paths/file-plus.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/file-text.svg b/bindings/feather_icon/src/paths/file-text.svg deleted file mode 100644 index 715eb524..00000000 --- a/bindings/feather_icon/src/paths/file-text.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/file.svg b/bindings/feather_icon/src/paths/file.svg deleted file mode 100644 index 5d0b7787..00000000 --- a/bindings/feather_icon/src/paths/file.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/film.svg b/bindings/feather_icon/src/paths/film.svg deleted file mode 100644 index 702d6aa4..00000000 --- a/bindings/feather_icon/src/paths/film.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/filter.svg b/bindings/feather_icon/src/paths/filter.svg deleted file mode 100644 index 66cc1c13..00000000 --- a/bindings/feather_icon/src/paths/filter.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/flag.svg b/bindings/feather_icon/src/paths/flag.svg deleted file mode 100644 index a72fb09f..00000000 --- a/bindings/feather_icon/src/paths/flag.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/folder-minus.svg b/bindings/feather_icon/src/paths/folder-minus.svg deleted file mode 100644 index 184a6a1e..00000000 --- a/bindings/feather_icon/src/paths/folder-minus.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/folder-plus.svg b/bindings/feather_icon/src/paths/folder-plus.svg deleted file mode 100644 index 121f8675..00000000 --- a/bindings/feather_icon/src/paths/folder-plus.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/folder.svg b/bindings/feather_icon/src/paths/folder.svg deleted file mode 100644 index f43b5ea7..00000000 --- a/bindings/feather_icon/src/paths/folder.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/framer.svg b/bindings/feather_icon/src/paths/framer.svg deleted file mode 100644 index 919a6276..00000000 --- a/bindings/feather_icon/src/paths/framer.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/frown.svg b/bindings/feather_icon/src/paths/frown.svg deleted file mode 100644 index 3c9ee767..00000000 --- a/bindings/feather_icon/src/paths/frown.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/gift.svg b/bindings/feather_icon/src/paths/gift.svg deleted file mode 100644 index e62a451b..00000000 --- a/bindings/feather_icon/src/paths/gift.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/git-branch.svg b/bindings/feather_icon/src/paths/git-branch.svg deleted file mode 100644 index be7e39a9..00000000 --- a/bindings/feather_icon/src/paths/git-branch.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/git-commit.svg b/bindings/feather_icon/src/paths/git-commit.svg deleted file mode 100644 index ea168d08..00000000 --- a/bindings/feather_icon/src/paths/git-commit.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/git-merge.svg b/bindings/feather_icon/src/paths/git-merge.svg deleted file mode 100644 index 0038925e..00000000 --- a/bindings/feather_icon/src/paths/git-merge.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/git-pull-request.svg b/bindings/feather_icon/src/paths/git-pull-request.svg deleted file mode 100644 index 62363c6a..00000000 --- a/bindings/feather_icon/src/paths/git-pull-request.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/github.svg b/bindings/feather_icon/src/paths/github.svg deleted file mode 100644 index c1e1d1ba..00000000 --- a/bindings/feather_icon/src/paths/github.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/gitlab.svg b/bindings/feather_icon/src/paths/gitlab.svg deleted file mode 100644 index 72c026c3..00000000 --- a/bindings/feather_icon/src/paths/gitlab.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/globe.svg b/bindings/feather_icon/src/paths/globe.svg deleted file mode 100644 index 4744eba5..00000000 --- a/bindings/feather_icon/src/paths/globe.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/grid.svg b/bindings/feather_icon/src/paths/grid.svg deleted file mode 100644 index e7fa49cc..00000000 --- a/bindings/feather_icon/src/paths/grid.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/hard-drive.svg b/bindings/feather_icon/src/paths/hard-drive.svg deleted file mode 100644 index f0d83cdd..00000000 --- a/bindings/feather_icon/src/paths/hard-drive.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/hash.svg b/bindings/feather_icon/src/paths/hash.svg deleted file mode 100644 index c304cda0..00000000 --- a/bindings/feather_icon/src/paths/hash.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/headphones.svg b/bindings/feather_icon/src/paths/headphones.svg deleted file mode 100644 index d06a5266..00000000 --- a/bindings/feather_icon/src/paths/headphones.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/heart.svg b/bindings/feather_icon/src/paths/heart.svg deleted file mode 100644 index 79231ab4..00000000 --- a/bindings/feather_icon/src/paths/heart.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/help-circle.svg b/bindings/feather_icon/src/paths/help-circle.svg deleted file mode 100644 index 261fee9e..00000000 --- a/bindings/feather_icon/src/paths/help-circle.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/hexagon.svg b/bindings/feather_icon/src/paths/hexagon.svg deleted file mode 100644 index d4814bd7..00000000 --- a/bindings/feather_icon/src/paths/hexagon.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/home.svg b/bindings/feather_icon/src/paths/home.svg deleted file mode 100644 index d76f2a41..00000000 --- a/bindings/feather_icon/src/paths/home.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/image.svg b/bindings/feather_icon/src/paths/image.svg deleted file mode 100644 index fe1e68b6..00000000 --- a/bindings/feather_icon/src/paths/image.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/inbox.svg b/bindings/feather_icon/src/paths/inbox.svg deleted file mode 100644 index 3d564e76..00000000 --- a/bindings/feather_icon/src/paths/inbox.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/info.svg b/bindings/feather_icon/src/paths/info.svg deleted file mode 100644 index 2a851238..00000000 --- a/bindings/feather_icon/src/paths/info.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/instagram.svg b/bindings/feather_icon/src/paths/instagram.svg deleted file mode 100644 index 15ccd111..00000000 --- a/bindings/feather_icon/src/paths/instagram.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/italic.svg b/bindings/feather_icon/src/paths/italic.svg deleted file mode 100644 index cffb7c81..00000000 --- a/bindings/feather_icon/src/paths/italic.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/key.svg b/bindings/feather_icon/src/paths/key.svg deleted file mode 100644 index 953567ea..00000000 --- a/bindings/feather_icon/src/paths/key.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/layers.svg b/bindings/feather_icon/src/paths/layers.svg deleted file mode 100644 index 4e1fef30..00000000 --- a/bindings/feather_icon/src/paths/layers.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/layout.svg b/bindings/feather_icon/src/paths/layout.svg deleted file mode 100644 index 1e999177..00000000 --- a/bindings/feather_icon/src/paths/layout.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/life-buoy.svg b/bindings/feather_icon/src/paths/life-buoy.svg deleted file mode 100644 index 020b4716..00000000 --- a/bindings/feather_icon/src/paths/life-buoy.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/link-2.svg b/bindings/feather_icon/src/paths/link-2.svg deleted file mode 100644 index cfc86f74..00000000 --- a/bindings/feather_icon/src/paths/link-2.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/link.svg b/bindings/feather_icon/src/paths/link.svg deleted file mode 100644 index 1275f53e..00000000 --- a/bindings/feather_icon/src/paths/link.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/linkedin.svg b/bindings/feather_icon/src/paths/linkedin.svg deleted file mode 100644 index fc3284aa..00000000 --- a/bindings/feather_icon/src/paths/linkedin.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/list.svg b/bindings/feather_icon/src/paths/list.svg deleted file mode 100644 index 65832ef7..00000000 --- a/bindings/feather_icon/src/paths/list.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/loader.svg b/bindings/feather_icon/src/paths/loader.svg deleted file mode 100644 index c08ded49..00000000 --- a/bindings/feather_icon/src/paths/loader.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/lock.svg b/bindings/feather_icon/src/paths/lock.svg deleted file mode 100644 index bab5c18f..00000000 --- a/bindings/feather_icon/src/paths/lock.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/log-in.svg b/bindings/feather_icon/src/paths/log-in.svg deleted file mode 100644 index b357c1d1..00000000 --- a/bindings/feather_icon/src/paths/log-in.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/log-out.svg b/bindings/feather_icon/src/paths/log-out.svg deleted file mode 100644 index 2f038c40..00000000 --- a/bindings/feather_icon/src/paths/log-out.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/mail.svg b/bindings/feather_icon/src/paths/mail.svg deleted file mode 100644 index 4f54fc1f..00000000 --- a/bindings/feather_icon/src/paths/mail.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/map-pin.svg b/bindings/feather_icon/src/paths/map-pin.svg deleted file mode 100644 index ce5fa1ea..00000000 --- a/bindings/feather_icon/src/paths/map-pin.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/map.svg b/bindings/feather_icon/src/paths/map.svg deleted file mode 100644 index b8babaab..00000000 --- a/bindings/feather_icon/src/paths/map.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/maximize-2.svg b/bindings/feather_icon/src/paths/maximize-2.svg deleted file mode 100644 index f46065d5..00000000 --- a/bindings/feather_icon/src/paths/maximize-2.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/maximize.svg b/bindings/feather_icon/src/paths/maximize.svg deleted file mode 100644 index 40397ed8..00000000 --- a/bindings/feather_icon/src/paths/maximize.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/meh.svg b/bindings/feather_icon/src/paths/meh.svg deleted file mode 100644 index 3bdafc02..00000000 --- a/bindings/feather_icon/src/paths/meh.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/menu.svg b/bindings/feather_icon/src/paths/menu.svg deleted file mode 100644 index acae331e..00000000 --- a/bindings/feather_icon/src/paths/menu.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/message-circle.svg b/bindings/feather_icon/src/paths/message-circle.svg deleted file mode 100644 index dd1f406a..00000000 --- a/bindings/feather_icon/src/paths/message-circle.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/message-square.svg b/bindings/feather_icon/src/paths/message-square.svg deleted file mode 100644 index ee1aa80a..00000000 --- a/bindings/feather_icon/src/paths/message-square.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/mic-off.svg b/bindings/feather_icon/src/paths/mic-off.svg deleted file mode 100644 index 41377ed3..00000000 --- a/bindings/feather_icon/src/paths/mic-off.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/mic.svg b/bindings/feather_icon/src/paths/mic.svg deleted file mode 100644 index 0d330ec8..00000000 --- a/bindings/feather_icon/src/paths/mic.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/minimize-2.svg b/bindings/feather_icon/src/paths/minimize-2.svg deleted file mode 100644 index 9daa549a..00000000 --- a/bindings/feather_icon/src/paths/minimize-2.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/minimize.svg b/bindings/feather_icon/src/paths/minimize.svg deleted file mode 100644 index 1da090e3..00000000 --- a/bindings/feather_icon/src/paths/minimize.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/minus-circle.svg b/bindings/feather_icon/src/paths/minus-circle.svg deleted file mode 100644 index 2ddbc136..00000000 --- a/bindings/feather_icon/src/paths/minus-circle.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/minus-square.svg b/bindings/feather_icon/src/paths/minus-square.svg deleted file mode 100644 index 438e6b45..00000000 --- a/bindings/feather_icon/src/paths/minus-square.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/minus.svg b/bindings/feather_icon/src/paths/minus.svg deleted file mode 100644 index acbc9d13..00000000 --- a/bindings/feather_icon/src/paths/minus.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/monitor.svg b/bindings/feather_icon/src/paths/monitor.svg deleted file mode 100644 index f25049f7..00000000 --- a/bindings/feather_icon/src/paths/monitor.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/moon.svg b/bindings/feather_icon/src/paths/moon.svg deleted file mode 100644 index b6c832a1..00000000 --- a/bindings/feather_icon/src/paths/moon.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/more-horizontal.svg b/bindings/feather_icon/src/paths/more-horizontal.svg deleted file mode 100644 index 1a6bb2ea..00000000 --- a/bindings/feather_icon/src/paths/more-horizontal.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/more-vertical.svg b/bindings/feather_icon/src/paths/more-vertical.svg deleted file mode 100644 index 92ace8a1..00000000 --- a/bindings/feather_icon/src/paths/more-vertical.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/mouse-pointer.svg b/bindings/feather_icon/src/paths/mouse-pointer.svg deleted file mode 100644 index 33eeae91..00000000 --- a/bindings/feather_icon/src/paths/mouse-pointer.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/move.svg b/bindings/feather_icon/src/paths/move.svg deleted file mode 100644 index 0d5ea4c3..00000000 --- a/bindings/feather_icon/src/paths/move.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/music.svg b/bindings/feather_icon/src/paths/music.svg deleted file mode 100644 index d623d1d8..00000000 --- a/bindings/feather_icon/src/paths/music.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/navigation-2.svg b/bindings/feather_icon/src/paths/navigation-2.svg deleted file mode 100644 index b496b9b9..00000000 --- a/bindings/feather_icon/src/paths/navigation-2.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/navigation.svg b/bindings/feather_icon/src/paths/navigation.svg deleted file mode 100644 index c7f4d1d7..00000000 --- a/bindings/feather_icon/src/paths/navigation.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/octagon.svg b/bindings/feather_icon/src/paths/octagon.svg deleted file mode 100644 index 3e360422..00000000 --- a/bindings/feather_icon/src/paths/octagon.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/package.svg b/bindings/feather_icon/src/paths/package.svg deleted file mode 100644 index 9f890896..00000000 --- a/bindings/feather_icon/src/paths/package.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/paperclip.svg b/bindings/feather_icon/src/paths/paperclip.svg deleted file mode 100644 index f4a66932..00000000 --- a/bindings/feather_icon/src/paths/paperclip.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/pause-circle.svg b/bindings/feather_icon/src/paths/pause-circle.svg deleted file mode 100644 index 16977fa0..00000000 --- a/bindings/feather_icon/src/paths/pause-circle.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/pause.svg b/bindings/feather_icon/src/paths/pause.svg deleted file mode 100644 index f56972ef..00000000 --- a/bindings/feather_icon/src/paths/pause.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/pen-tool.svg b/bindings/feather_icon/src/paths/pen-tool.svg deleted file mode 100644 index 18a337fb..00000000 --- a/bindings/feather_icon/src/paths/pen-tool.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/percent.svg b/bindings/feather_icon/src/paths/percent.svg deleted file mode 100644 index 55735ced..00000000 --- a/bindings/feather_icon/src/paths/percent.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/phone-call.svg b/bindings/feather_icon/src/paths/phone-call.svg deleted file mode 100644 index 1099f1e5..00000000 --- a/bindings/feather_icon/src/paths/phone-call.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/phone-forwarded.svg b/bindings/feather_icon/src/paths/phone-forwarded.svg deleted file mode 100644 index 1e8c19c3..00000000 --- a/bindings/feather_icon/src/paths/phone-forwarded.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/phone-incoming.svg b/bindings/feather_icon/src/paths/phone-incoming.svg deleted file mode 100644 index 3d972ab8..00000000 --- a/bindings/feather_icon/src/paths/phone-incoming.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/phone-missed.svg b/bindings/feather_icon/src/paths/phone-missed.svg deleted file mode 100644 index 7f9c1959..00000000 --- a/bindings/feather_icon/src/paths/phone-missed.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/phone-off.svg b/bindings/feather_icon/src/paths/phone-off.svg deleted file mode 100644 index 61f7eed7..00000000 --- a/bindings/feather_icon/src/paths/phone-off.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/phone-outgoing.svg b/bindings/feather_icon/src/paths/phone-outgoing.svg deleted file mode 100644 index 9cf2c797..00000000 --- a/bindings/feather_icon/src/paths/phone-outgoing.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/phone.svg b/bindings/feather_icon/src/paths/phone.svg deleted file mode 100644 index ee734c79..00000000 --- a/bindings/feather_icon/src/paths/phone.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/pie-chart.svg b/bindings/feather_icon/src/paths/pie-chart.svg deleted file mode 100644 index fcabbda0..00000000 --- a/bindings/feather_icon/src/paths/pie-chart.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/play-circle.svg b/bindings/feather_icon/src/paths/play-circle.svg deleted file mode 100644 index a597c2de..00000000 --- a/bindings/feather_icon/src/paths/play-circle.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/play.svg b/bindings/feather_icon/src/paths/play.svg deleted file mode 100644 index 53c4d57f..00000000 --- a/bindings/feather_icon/src/paths/play.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/plus-circle.svg b/bindings/feather_icon/src/paths/plus-circle.svg deleted file mode 100644 index 5744e26c..00000000 --- a/bindings/feather_icon/src/paths/plus-circle.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/plus-square.svg b/bindings/feather_icon/src/paths/plus-square.svg deleted file mode 100644 index 24a19395..00000000 --- a/bindings/feather_icon/src/paths/plus-square.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/plus.svg b/bindings/feather_icon/src/paths/plus.svg deleted file mode 100644 index fe393a0f..00000000 --- a/bindings/feather_icon/src/paths/plus.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/pocket.svg b/bindings/feather_icon/src/paths/pocket.svg deleted file mode 100644 index 76088c4e..00000000 --- a/bindings/feather_icon/src/paths/pocket.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/power.svg b/bindings/feather_icon/src/paths/power.svg deleted file mode 100644 index 79ac0270..00000000 --- a/bindings/feather_icon/src/paths/power.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/printer.svg b/bindings/feather_icon/src/paths/printer.svg deleted file mode 100644 index 850c5b19..00000000 --- a/bindings/feather_icon/src/paths/printer.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/radio.svg b/bindings/feather_icon/src/paths/radio.svg deleted file mode 100644 index 8bb786c3..00000000 --- a/bindings/feather_icon/src/paths/radio.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/refresh-ccw.svg b/bindings/feather_icon/src/paths/refresh-ccw.svg deleted file mode 100644 index 0cd4395c..00000000 --- a/bindings/feather_icon/src/paths/refresh-ccw.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/refresh-cw.svg b/bindings/feather_icon/src/paths/refresh-cw.svg deleted file mode 100644 index 0abe9314..00000000 --- a/bindings/feather_icon/src/paths/refresh-cw.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/repeat.svg b/bindings/feather_icon/src/paths/repeat.svg deleted file mode 100644 index 2482b160..00000000 --- a/bindings/feather_icon/src/paths/repeat.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/rewind.svg b/bindings/feather_icon/src/paths/rewind.svg deleted file mode 100644 index d45373af..00000000 --- a/bindings/feather_icon/src/paths/rewind.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/rotate-ccw.svg b/bindings/feather_icon/src/paths/rotate-ccw.svg deleted file mode 100644 index 65f2f81b..00000000 --- a/bindings/feather_icon/src/paths/rotate-ccw.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/rotate-cw.svg b/bindings/feather_icon/src/paths/rotate-cw.svg deleted file mode 100644 index fde64148..00000000 --- a/bindings/feather_icon/src/paths/rotate-cw.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/rss.svg b/bindings/feather_icon/src/paths/rss.svg deleted file mode 100644 index feff0086..00000000 --- a/bindings/feather_icon/src/paths/rss.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/save.svg b/bindings/feather_icon/src/paths/save.svg deleted file mode 100644 index c8eacb87..00000000 --- a/bindings/feather_icon/src/paths/save.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/scissors.svg b/bindings/feather_icon/src/paths/scissors.svg deleted file mode 100644 index 7173d27c..00000000 --- a/bindings/feather_icon/src/paths/scissors.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/search.svg b/bindings/feather_icon/src/paths/search.svg deleted file mode 100644 index 85b5e642..00000000 --- a/bindings/feather_icon/src/paths/search.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/send.svg b/bindings/feather_icon/src/paths/send.svg deleted file mode 100644 index bce5aca8..00000000 --- a/bindings/feather_icon/src/paths/send.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/server.svg b/bindings/feather_icon/src/paths/server.svg deleted file mode 100644 index bff09e1d..00000000 --- a/bindings/feather_icon/src/paths/server.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/settings.svg b/bindings/feather_icon/src/paths/settings.svg deleted file mode 100644 index 8f58379f..00000000 --- a/bindings/feather_icon/src/paths/settings.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/share-2.svg b/bindings/feather_icon/src/paths/share-2.svg deleted file mode 100644 index b1f63f03..00000000 --- a/bindings/feather_icon/src/paths/share-2.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/share.svg b/bindings/feather_icon/src/paths/share.svg deleted file mode 100644 index f8ae3ba5..00000000 --- a/bindings/feather_icon/src/paths/share.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/shield-off.svg b/bindings/feather_icon/src/paths/shield-off.svg deleted file mode 100644 index 1b4e998b..00000000 --- a/bindings/feather_icon/src/paths/shield-off.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/shield.svg b/bindings/feather_icon/src/paths/shield.svg deleted file mode 100644 index a94fbd7b..00000000 --- a/bindings/feather_icon/src/paths/shield.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/shopping-bag.svg b/bindings/feather_icon/src/paths/shopping-bag.svg deleted file mode 100644 index b84ea4a7..00000000 --- a/bindings/feather_icon/src/paths/shopping-bag.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/shopping-cart.svg b/bindings/feather_icon/src/paths/shopping-cart.svg deleted file mode 100644 index d4a6a8ce..00000000 --- a/bindings/feather_icon/src/paths/shopping-cart.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/shuffle.svg b/bindings/feather_icon/src/paths/shuffle.svg deleted file mode 100644 index 24317bfa..00000000 --- a/bindings/feather_icon/src/paths/shuffle.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/sidebar.svg b/bindings/feather_icon/src/paths/sidebar.svg deleted file mode 100644 index 9278d498..00000000 --- a/bindings/feather_icon/src/paths/sidebar.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/skip-back.svg b/bindings/feather_icon/src/paths/skip-back.svg deleted file mode 100644 index 0589ad94..00000000 --- a/bindings/feather_icon/src/paths/skip-back.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/skip-forward.svg b/bindings/feather_icon/src/paths/skip-forward.svg deleted file mode 100644 index bbc2d64f..00000000 --- a/bindings/feather_icon/src/paths/skip-forward.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/slack.svg b/bindings/feather_icon/src/paths/slack.svg deleted file mode 100644 index 39661f9e..00000000 --- a/bindings/feather_icon/src/paths/slack.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/slash.svg b/bindings/feather_icon/src/paths/slash.svg deleted file mode 100644 index d86b2069..00000000 --- a/bindings/feather_icon/src/paths/slash.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/sliders.svg b/bindings/feather_icon/src/paths/sliders.svg deleted file mode 100644 index 4ce6d506..00000000 --- a/bindings/feather_icon/src/paths/sliders.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/smartphone.svg b/bindings/feather_icon/src/paths/smartphone.svg deleted file mode 100644 index aea42bf3..00000000 --- a/bindings/feather_icon/src/paths/smartphone.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/smile.svg b/bindings/feather_icon/src/paths/smile.svg deleted file mode 100644 index 87ea7a19..00000000 --- a/bindings/feather_icon/src/paths/smile.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/speaker.svg b/bindings/feather_icon/src/paths/speaker.svg deleted file mode 100644 index dc36630f..00000000 --- a/bindings/feather_icon/src/paths/speaker.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/square.svg b/bindings/feather_icon/src/paths/square.svg deleted file mode 100644 index abc832c8..00000000 --- a/bindings/feather_icon/src/paths/square.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/star.svg b/bindings/feather_icon/src/paths/star.svg deleted file mode 100644 index 50501877..00000000 --- a/bindings/feather_icon/src/paths/star.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/stop-circle.svg b/bindings/feather_icon/src/paths/stop-circle.svg deleted file mode 100644 index 894c4629..00000000 --- a/bindings/feather_icon/src/paths/stop-circle.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/sun.svg b/bindings/feather_icon/src/paths/sun.svg deleted file mode 100644 index 12944829..00000000 --- a/bindings/feather_icon/src/paths/sun.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/sunrise.svg b/bindings/feather_icon/src/paths/sunrise.svg deleted file mode 100644 index 15be2d59..00000000 --- a/bindings/feather_icon/src/paths/sunrise.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/sunset.svg b/bindings/feather_icon/src/paths/sunset.svg deleted file mode 100644 index b554493c..00000000 --- a/bindings/feather_icon/src/paths/sunset.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/tablet.svg b/bindings/feather_icon/src/paths/tablet.svg deleted file mode 100644 index 9a89e0a4..00000000 --- a/bindings/feather_icon/src/paths/tablet.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/tag.svg b/bindings/feather_icon/src/paths/tag.svg deleted file mode 100644 index b89580e1..00000000 --- a/bindings/feather_icon/src/paths/tag.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/target.svg b/bindings/feather_icon/src/paths/target.svg deleted file mode 100644 index 8e505efe..00000000 --- a/bindings/feather_icon/src/paths/target.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/terminal.svg b/bindings/feather_icon/src/paths/terminal.svg deleted file mode 100644 index 0ab17283..00000000 --- a/bindings/feather_icon/src/paths/terminal.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/thermometer.svg b/bindings/feather_icon/src/paths/thermometer.svg deleted file mode 100644 index e93552e3..00000000 --- a/bindings/feather_icon/src/paths/thermometer.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/thumbs-down.svg b/bindings/feather_icon/src/paths/thumbs-down.svg deleted file mode 100644 index 6f254dfe..00000000 --- a/bindings/feather_icon/src/paths/thumbs-down.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/thumbs-up.svg b/bindings/feather_icon/src/paths/thumbs-up.svg deleted file mode 100644 index 830198f7..00000000 --- a/bindings/feather_icon/src/paths/thumbs-up.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/toggle-left.svg b/bindings/feather_icon/src/paths/toggle-left.svg deleted file mode 100644 index 0d924da3..00000000 --- a/bindings/feather_icon/src/paths/toggle-left.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/toggle-right.svg b/bindings/feather_icon/src/paths/toggle-right.svg deleted file mode 100644 index 7c15499d..00000000 --- a/bindings/feather_icon/src/paths/toggle-right.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/tool.svg b/bindings/feather_icon/src/paths/tool.svg deleted file mode 100644 index 434692c8..00000000 --- a/bindings/feather_icon/src/paths/tool.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/trash-2.svg b/bindings/feather_icon/src/paths/trash-2.svg deleted file mode 100644 index d9a2a8ae..00000000 --- a/bindings/feather_icon/src/paths/trash-2.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/trash.svg b/bindings/feather_icon/src/paths/trash.svg deleted file mode 100644 index c7236e35..00000000 --- a/bindings/feather_icon/src/paths/trash.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/trello.svg b/bindings/feather_icon/src/paths/trello.svg deleted file mode 100644 index f5ee8a76..00000000 --- a/bindings/feather_icon/src/paths/trello.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/trending-down.svg b/bindings/feather_icon/src/paths/trending-down.svg deleted file mode 100644 index a588f97b..00000000 --- a/bindings/feather_icon/src/paths/trending-down.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/trending-up.svg b/bindings/feather_icon/src/paths/trending-up.svg deleted file mode 100644 index 0ee68410..00000000 --- a/bindings/feather_icon/src/paths/trending-up.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/triangle.svg b/bindings/feather_icon/src/paths/triangle.svg deleted file mode 100644 index a5385880..00000000 --- a/bindings/feather_icon/src/paths/triangle.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/truck.svg b/bindings/feather_icon/src/paths/truck.svg deleted file mode 100644 index 9899c287..00000000 --- a/bindings/feather_icon/src/paths/truck.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/tv.svg b/bindings/feather_icon/src/paths/tv.svg deleted file mode 100644 index f5fbc4e0..00000000 --- a/bindings/feather_icon/src/paths/tv.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/twitch.svg b/bindings/feather_icon/src/paths/twitch.svg deleted file mode 100644 index 02ca5ef1..00000000 --- a/bindings/feather_icon/src/paths/twitch.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/twitter.svg b/bindings/feather_icon/src/paths/twitter.svg deleted file mode 100644 index a762f1bb..00000000 --- a/bindings/feather_icon/src/paths/twitter.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/type.svg b/bindings/feather_icon/src/paths/type.svg deleted file mode 100644 index 4ef3c1ca..00000000 --- a/bindings/feather_icon/src/paths/type.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/umbrella.svg b/bindings/feather_icon/src/paths/umbrella.svg deleted file mode 100644 index f553287c..00000000 --- a/bindings/feather_icon/src/paths/umbrella.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/underline.svg b/bindings/feather_icon/src/paths/underline.svg deleted file mode 100644 index 1fda210c..00000000 --- a/bindings/feather_icon/src/paths/underline.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/unlock.svg b/bindings/feather_icon/src/paths/unlock.svg deleted file mode 100644 index 2c775148..00000000 --- a/bindings/feather_icon/src/paths/unlock.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/upload-cloud.svg b/bindings/feather_icon/src/paths/upload-cloud.svg deleted file mode 100644 index cce5e420..00000000 --- a/bindings/feather_icon/src/paths/upload-cloud.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/upload.svg b/bindings/feather_icon/src/paths/upload.svg deleted file mode 100644 index 9aa8dcf7..00000000 --- a/bindings/feather_icon/src/paths/upload.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/user-check.svg b/bindings/feather_icon/src/paths/user-check.svg deleted file mode 100644 index 10a4793b..00000000 --- a/bindings/feather_icon/src/paths/user-check.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/user-minus.svg b/bindings/feather_icon/src/paths/user-minus.svg deleted file mode 100644 index a299e300..00000000 --- a/bindings/feather_icon/src/paths/user-minus.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/user-plus.svg b/bindings/feather_icon/src/paths/user-plus.svg deleted file mode 100644 index 076fb50d..00000000 --- a/bindings/feather_icon/src/paths/user-plus.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/user-x.svg b/bindings/feather_icon/src/paths/user-x.svg deleted file mode 100644 index f5a5aacc..00000000 --- a/bindings/feather_icon/src/paths/user-x.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/user.svg b/bindings/feather_icon/src/paths/user.svg deleted file mode 100644 index 29bff14d..00000000 --- a/bindings/feather_icon/src/paths/user.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/users.svg b/bindings/feather_icon/src/paths/users.svg deleted file mode 100644 index 79b4eab4..00000000 --- a/bindings/feather_icon/src/paths/users.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/video-off.svg b/bindings/feather_icon/src/paths/video-off.svg deleted file mode 100644 index 62b70349..00000000 --- a/bindings/feather_icon/src/paths/video-off.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/video.svg b/bindings/feather_icon/src/paths/video.svg deleted file mode 100644 index 93de40ba..00000000 --- a/bindings/feather_icon/src/paths/video.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/voicemail.svg b/bindings/feather_icon/src/paths/voicemail.svg deleted file mode 100644 index f2b65c0c..00000000 --- a/bindings/feather_icon/src/paths/voicemail.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/volume-1.svg b/bindings/feather_icon/src/paths/volume-1.svg deleted file mode 100644 index cee2e7b0..00000000 --- a/bindings/feather_icon/src/paths/volume-1.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/volume-2.svg b/bindings/feather_icon/src/paths/volume-2.svg deleted file mode 100644 index c2febee1..00000000 --- a/bindings/feather_icon/src/paths/volume-2.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/volume-x.svg b/bindings/feather_icon/src/paths/volume-x.svg deleted file mode 100644 index 1d5ab0aa..00000000 --- a/bindings/feather_icon/src/paths/volume-x.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/volume.svg b/bindings/feather_icon/src/paths/volume.svg deleted file mode 100644 index 55c84891..00000000 --- a/bindings/feather_icon/src/paths/volume.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/watch.svg b/bindings/feather_icon/src/paths/watch.svg deleted file mode 100644 index c8c136fb..00000000 --- a/bindings/feather_icon/src/paths/watch.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/wifi-off.svg b/bindings/feather_icon/src/paths/wifi-off.svg deleted file mode 100644 index 4c65cbab..00000000 --- a/bindings/feather_icon/src/paths/wifi-off.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/wifi.svg b/bindings/feather_icon/src/paths/wifi.svg deleted file mode 100644 index e355ed13..00000000 --- a/bindings/feather_icon/src/paths/wifi.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/wind.svg b/bindings/feather_icon/src/paths/wind.svg deleted file mode 100644 index d49835cd..00000000 --- a/bindings/feather_icon/src/paths/wind.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/x-circle.svg b/bindings/feather_icon/src/paths/x-circle.svg deleted file mode 100644 index ddde8e52..00000000 --- a/bindings/feather_icon/src/paths/x-circle.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/x-octagon.svg b/bindings/feather_icon/src/paths/x-octagon.svg deleted file mode 100644 index 4d3d5818..00000000 --- a/bindings/feather_icon/src/paths/x-octagon.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/x-square.svg b/bindings/feather_icon/src/paths/x-square.svg deleted file mode 100644 index e826c304..00000000 --- a/bindings/feather_icon/src/paths/x-square.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/x.svg b/bindings/feather_icon/src/paths/x.svg deleted file mode 100644 index 22b3207b..00000000 --- a/bindings/feather_icon/src/paths/x.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/youtube.svg b/bindings/feather_icon/src/paths/youtube.svg deleted file mode 100644 index 002d5528..00000000 --- a/bindings/feather_icon/src/paths/youtube.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/zap-off.svg b/bindings/feather_icon/src/paths/zap-off.svg deleted file mode 100644 index 8137ab10..00000000 --- a/bindings/feather_icon/src/paths/zap-off.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/zap.svg b/bindings/feather_icon/src/paths/zap.svg deleted file mode 100644 index 7a2e7e13..00000000 --- a/bindings/feather_icon/src/paths/zap.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/zoom-in.svg b/bindings/feather_icon/src/paths/zoom-in.svg deleted file mode 100644 index 03a1fabe..00000000 --- a/bindings/feather_icon/src/paths/zoom-in.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bindings/feather_icon/src/paths/zoom-out.svg b/bindings/feather_icon/src/paths/zoom-out.svg deleted file mode 100644 index ea4bda15..00000000 --- a/bindings/feather_icon/src/paths/zoom-out.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/bonsai.opam b/bonsai.opam index edb94736..b4380f05 100644 --- a/bonsai.opam +++ b/bonsai.opam @@ -11,54 +11,22 @@ build: [ ] depends: [ "ocaml" {>= "5.1.0"} - "async" - "async_durable" - "async_extra" - "async_js" - "async_kernel" + "abstract_algebra" "async_rpc_kernel" - "async_rpc_websocket" - "babel" - "base" - "bin_prot" "core" - "core_bench" "core_kernel" - "core_unix" - "expect_test_helpers_core" - "fuzzy_match" "incr_dom" "incr_map" - "legacy_diffable" - "ordinal_abbreviation" - "patdiff" - "polling_state_rpc" - "ppx_css" - "ppx_diff" + "incremental" "ppx_here" "ppx_jane" "ppx_let" "ppx_pattern_bind" - "ppx_quick_test" - "ppx_typed_fields" - "profunctor" - "record_builder" - "sexp_grammar" - "sexplib0" - "streamable" - "textutils" - "versioned_polling_state_rpc" + "uopt" "virtual_dom" - "base64" {>= "3.4.0"} - "cohttp-async" {>= "2.5.7" & < "3.0.0" | >= "5.1.1" & < "6.0.0"} - "dune" {>= "3.11.0"} - "gen_js_api" {>= "1.0.8"} - "js_of_ocaml" {>= "5.1.1" & < "5.7.0"} - "js_of_ocaml-ppx" {>= "5.1.1" & < "5.7.0"} + "dune" {>= "3.11.0"} "ocaml-embed-file" - "ppxlib" {>= "0.28.0"} - "re" {>= "1.8.0"} - "uri" {>= "3.0.0"} + "ppxlib" {>= "0.28.0"} ] available: arch != "arm32" & arch != "x86_32" synopsis: "A library for building dynamic webapps, using Js_of_ocaml" diff --git a/docs/advanced/how_bonsai_works.md b/docs/advanced/how_bonsai_works.md deleted file mode 100644 index f68f01d8..00000000 --- a/docs/advanced/how_bonsai_works.md +++ /dev/null @@ -1,411 +0,0 @@ -How Bonsai Works - -This article talks about how Bonsai works. It is probably most useful to -Bonsai maintainers, although it can provide useful context for power -users as well. We'll cover: - -- How Bonsai's internals work -- How the `local_ graph` API works - -```{=html} - -``` -# `Computation.t` and `Value.t` - -One of Bonsai's invariants is that the computation graph is a static -DAG. This offers [a lot of benefits](./why_no_bind.mdx). - -Bonsai's internal representation of this graph is based around 2 types. - -`Value.t` is effectively a wrapper around Incremental's `Incr.t`, and -ultimately gets compiled down to `Incr.t`s. - -`Computation.t` represents the "structure" of the computation graph. -It's implemented as a variant of types such as: - -- `Leaf0` and `Leaf1`: structure around state nodes -- `Store` and `Fetch`: powers Bonsai's `Dynamic_scope` tools -- `Assoc`: allows creating a dynamic number of copies of some internal - `Computation.t`, each with their own state / lifecycle. -- `Switch`: allows selecting one of several `Computation.t`s to - evaluate. -- `Path`: returns the path to a new node in the computation graph, - which is useful as a unique ID. -- `Lifecycle`: allows running `on_activate` / `on_deactivate` / - `after_display` lifecycle events -- `Return`: a very important one: just return an incremental - `Value.t`. -- `Sub`: possibly the most important one: allows a computation to use - the output of **any other** computation in the graph. This is what - makes the computation graph a DAG, and not a tree. - -## How do `Computation.t` and `Value.t` interact? - -Many of the variants of `Computation.t` are records, that store some -`Value.t`. For example, `Switch` stores an `int Value.t` representing -which of the `Computation.t`s is currently active. And `Assoc` stores an -input `Map.t`. Of particular importance, `Return` stores some arbitrary -`Value.t`. - -## How was the graph constructed before? - -Up until late 2023, Bonsai's public-facing API directly constructed a -`Computation.t` graph, which provides structure for `Value.t` -incremental calculations. Let's go over the key parts. - -### sub - -`val sub: 'a Computation.t -> f:('a Value.t -> 'b Computation.t) -> 'b Computation.t`. -This was commonly used via the `let%sub` syntax. It: - -- Creates a new "named" `Value.t`, which essentially reads - `'a Computation.t`. -- Applies the provided `f` function to that value, generating a new - `'b Computation.t`. -- Creates a new `Sub` node for the graph, which contains these 2 - `Computation.t`s. - -Essentially, this allows us to compute a `'a Value.t`, use it in 2 -separate computations, and then `map` them together. - -If we only had a tree, we would have to clone that `'a Value.t` for each -of the 2 subcomputations. We would have to redo all calculations twice, -and we couldn't share state. - -But with `sub`, we essentially create a symlink / reference / pointer to -the shared `'a Value.t`, so we can use it in multiple places without -issue. This is why `Sub` is so important. - -### arr - -`val arr : 'a Value.t -> f:('a -> 'b) -> 'b Computation.t`. There are -also `arr2`, `arr3` versions, which combine several `Value.t`s into one -`Computation.t`. - -This was commonly used via the `let%arr` syntax. All `arr` does is call -`Value.map`, and wrap the result in a `Return` node. - -The entire point of `arr` is to force you to use `sub`, so that our new -`Value.t` is defined in terms of references/symlinks to any shared work, -and we don't accidentially clone that work / lose shared state. - -### And the rest? - -We've talked about the `Sub` node via `sub`, and how new `Value.t`s get -created (and then wrapped in `Return`) via `arr`. What about the other -`Computation.t`s? - -This is where all the `Bonsai.state_machine`, `Bonsai.Dynamic_scope.*`, -`Bonsai.assoc`, etc primitives come in; they create `Leaf0`, -`Fetch`/`Store`, `Assoc`, and other nodes. Then, using `sub` on those -nodes adds them into the graph. - -## Ok, but how do we get an `Incr.t` from all of this? - -Before Bonsai boots up your web UI, it performs a compilation pass. -First, it recursively "evaluates" your computation graph, resulting in a -single `Computation.info` record. - -```{=html} - -``` -### What is Computation.info - -For every `Computation.t`, we recursively create this -`Computation.info`, which includes: - -- `'model Meta.Model.t`, which contains the default value / type id - structure for the `Computation`. - - For many `Computation.t`s, such as `Path`, `Lifecycle`, and - `Return`, this is just `Meta.Model.unit`. - - For the "state" nodes `Leaf0` and `Leaf1`, this is the - user-provided model metadata. - - For nodes defined in terms of other `Computation.t`s, such as - `Sub`, `Assoc`, `Switch`, and `Store`, we recursively combine - the `Meta.Model.t` of subcomputations. -- `'input Meta.Input.t`, which contains a type id for inputs to the - `Computation.t`. For `Leaf1`, which is a state machine with input, - this is the user-provided input metadata. For recursive - `Computation.t`s, this combines inputs of subcomputations. - Otherwise, it's `Meta.Input.unit`. -- `'action Action.id`, which is a type id for a state machine's - `Action.t`, is assembled similarly. -- As is `reset`, which is a `'model -> 'model` function used to - implement `with_model_resetter`. -- And so is `apply_action`, which users provide when creating state - machines, and is the state machine transition, with the added - ability to dispatch events. - -### `run` and `Snapshot.t` - -The final, and most interesting part of `Computation.info` is `run`, -which takes: - -- An "environment", which contains all "named" `Value.t`s memoized via - `sub`, andthe available dynamically scoped variables. -- A `Time_source.t` -- The current computation path -- An `Incr.t` with the current state -- An `inject` function, which creates `Effect.t`s that apply - `'action`s - -and outputs a `Snapshot.t`. which represents the state of the -computation, and all its subcomputations. - -`Snapshot.t` packages an `Input.t`, which is all `Incr.t` (and empty -`unit`) inputs to the computation and its subcomputations, some -lifecycle state, and a result, which is an `Incr.t` with the dynamic -value produced by the computation. - -So essentially, `run` is the "logic" of the `Computation.t`. Let's -discuss a few examples: - -For `Return`, `run` creates a trivial snapshot with no input and no -lifecycle. It calls `Value.eval` on its internal `Value.t`, and uses -that for the `Snapshot.t`'s result. `Value.eval` is provided the -"environment", so that `Value.t`s built in terms of "named" -symlink/reference/pointer `Value.t`s can be evaluated. - -For `Leaf0`, `run` is similar, except that it returns the current state -`Incr.t` and the `inject` function for `result`. `Leaf1` is almost the -same, except that it uses the `input : 'input Value.t` field of its -`Computation.t` node for the `Snapshot.t`'s input. - -`Store`'s `run` is probably the simplest out of computations with -subcomputations; it adds whatever dynamically scoped value it is storing -into the environment, then calls the inner computation's `run` with this -new environment. - -For the rest, read the implementation in -[bonsai/src/eval](https://github.com/janestreet/bonsai/blob/master/src/eval.ml). - -### So where do models + actions live? - -The top-level `Computation.info` for our whole web UI now contains a -`'model Meta.Model.t`, which represents the structure + default values -of the state of everything. We can use that to create a -`'model Incr.Var.t`, which will be a single variable tracking the state -of our whole web UI! - -It's shaped roughly like the `Computation.t` graph itself: every `Sub` -is a tuple of the `'model` of its `from` and the `'model` of its `into`; -assocs are `Map.t`s of the inner's `'model`, and leaf `'model`s are what -you'd expect. - -There are similar tree-like structures storing the `apply_action` and -`reset` functions for everything. And the "actual" representation of an -action is a path in that tree. - -You can think of `gather` as a pass up the tree, which collects the -structure of everything and assembles these whole-app state/action/etc -trees, and a second pass down the tree, which provides `Incr.t` -accessors for recursive sub-parts of those trees. - -### And then what? - -The private `bonsai.driver` library uses `eval` to construct this -`Computation.info`, instantiates the environment, default model, -`Effect.t` queue, and some other things, calls `run` on the top-level -computation, extracts the `result Incr.t` and `input Incr.t` from the -resulting snapshot, and puts these all together in a `Bonsai_driver.t` -type that provides an API for running the Bonsai computation. - -This driver is mostly used for testing. - -### Testing??? I want a website! - -`Bonsai_web.Start` pretty much does the same thing, except that it does -some web specific stuff, and expects a `Vdom.Node.t Computation.t`. Once -it has a `Vdom.Node.t Incr.t`, it uses `virtual_dom` to generate DOM -from the vdom / attach it to some element, and then run `virtual_dom`'s -diff-and-patch algorithm when the `Incr.t` changes. - -`Bonsai_web.Start` actually mostly does this through -`Incr_dom.Start_app`. - -# The `local_ graph` API - -In late 2023, we released a new implementation of Bonsai's API, called -`Cont`. That's because it is implemented in the continuation-passing -style. - -As covered above, in the previous API, calls to `sub`, `arr`, and -`Bonsai.*` primitives directly created `Computation.t` nodes (and the -underlying `Value.t`s). - -The new API doesn't actually change the internals! As before, -`Computation.t` is the static structure of the computation graph, and -`Value.t` is a wrapper around `Incr.t`, adding support for -"reference/symlink" values, which enable `sub` and work-sharing, and a -bunch of other stuff. - -In the new API, we have one `Bonsai.t` type, which is actually just -`Value.t`. Instead of `Computation.t`s, we pass around an -abstractly-typed `Bonsai.graph` value. This `graph` value is used to -construct Bonsai's computation graph, as we'll discuss in a bit. - -## Why do we need `graph`, and what is `local_`? - -There are two "phases" for Bonsai apps: - -1. "`Computation.t` building", where we build up the static computation - graph. -2. "Runtime", which starts when `Bonsai_web.Start.start` is run. At - this point, the Computation.t templates are instantiated and flush - with data. Runtime code is all in functions provided to - `Bonsai.map`; `let%arr` hides this somewhat, but the pure OCaml - "contents" of `let%arr` calls are actually de-sugared to anonymous - functions. - -Once "runtime" starts, you can no longer do any `Computation.t` -building; there aren't any `Computation.t`s left to build! It's all just -one big `Incr.t`! And in fact, Bonsai's type signatures force separation -of "computation building" code and "runtime" code, even though all this -code lives together in the same files and functions. - -But with a new API, where there's just one `Bonsai.t` type, how do we -enforce this separation? What's stopping us from referring to `graph` in -runtime code, inside of some `let%arr`, and blowing up our application? - -The new [local\_ -mode](https://blog.janestreet.com/oxidizing-ocaml-locality/) gives us a -new way to have the type-checker guarantee the separation of phases! To -dramatically oversimplify `local_`: when a function annotates an -argument with `local_`, it's promising not to close over it, partially -apply it to any variables, put it in a ref, or otherwise stash it away. - -Because all our runtime code is put in closures, use of `local_` means -that we just can't use `graph`! And so, we get a "phase witness" -property in `graph` through `local_`. - -As a result, Bonsai can enforce the same invariants as before, but -provide a simpler API and provide even more power and flexibility to UI -authors! - -But what's going on under the surface? And what is `graph`? And why can -the new Bonsai API use `Bonsai.map`? - -## How `local_ graph` Works - -### Background on `Sub` - -Recall that the `sub` is the memoization-like pattern that powers -work-sharing for incrementality: we can store a reusable value with a -unique name (via Type_equal.id), which downstream incremental `Value.t`s -can reference (and reuse!) instead of repeating work. The "Sub" -`Computation.t` node has 3 fields: - -- `from` is the computation being memoized -- `via` is the `Type_equal.id` identifier -- `into` is the downstream computation that uses the value of `from`. - -Let's say we want to use the output of `comp1` and `comp2` (both -`Computation.t`s) in another computation called -`thing_we_want_to_compute`. This is what the corresponding computation -graph looks like: - -```{=html} - -``` - Sub { from=comp1; via=; into = - Sub { from=comp2; via=; into = - ... into = thing_we_want_to_compute ... - } - } - -### What is `graph`, and how is `Cont` implemented? - -The new `Cont` implementation has a very primitive API (not exposed to -end-users), which simulates algebraic effects: - -- `perform` takes a `local_ graph -> Computation.t`, evaluates it, and - adds it into the computation graph. It returns a `Value.t` memoized - alias to this new computation. -- `handle` places a `Value.t` in the context of a fresh computation - graph. It then wraps the result in a `Computation.t`. This is - generally used to construct a portion of the computation graph in - isolation. The subgraph is then added to the main graph via - `perform`. - -`graph` is the mechanism for composing / combining `Computation.t`s via -`Sub`. It is implemented as a ref holding a -`Computation.t -> Computation.t` function. `graph` starts as `Fn.id`. At -each invocation of `perform`, we overwrite it to still return its input, -but nested in a new layer of `Sub`. - -```{=html} - -``` -Let's look at how this works in practice. The top part is the code, and -the bottom is the value of `graph` after every line: - -```{=html} - -``` - 1.let computation graph = - 2. let model1, _inject = Bonsai.state 5 graph in - 3. let model2, _inject = Bonsai.state 6 graph in - 4. model2 - - --- - - 1. graph = { f = fun r -> r } - 2. graph = { f = fun r -> Sub { from=state 5; via=; into=r } } - 3. graph = { f = fun r -> Sub { from=state 5; via=; into=Sub { from=state 6; via=; into=r } } } - 4. graph.f () ==> Sub { from=state 5; via=; into=Sub { from=state 6; via=; into= } } - -Instead of `state 5/6`, we'd actually have something more like -`Leaf0 { ... }`, but that's not really relevant. We also do some -optimizations if `sub`ing isn't actually necessary. - -Handle is much simpler: we run our `Value.t` construction function (`f`) -on a fresh `graph`, which becomes constructed via calls to `perform` -presumably inside `f`. Then, we call `graph.f` on that result, so that -it is inside the nested `Sub`s, giving it access to those memoized -aliases. Finally, we wrap this in a `Computation.t`, which can be -`perform`ed into other computations. - -```{=html} - -``` -A consequence of `perform`'s implementation is that the shape of -`Computation.t` under the Cont API is very linear: with the exception of -`Switch`/`Assoc` nodes, it is a chain of `Sub`s, each with more-or-less -a single node on the `From` side. You can think of this as a list of all -the things that have been instantiated and memoized, in the order of -instantiation. - -### Why is `Bonsai.map` safe now? - -But why can we now use `Bonsai.map` instead of `let%sub` and `let%arr` -combos? After all, we're not passing `graph` to `let%map`... How could -it construct `Sub` nodes and add them into the graph? - -In short, We cheat and make `graph` global. We still require users to -pass it when possible, so that creating costly stateful nodes is an -explicit operation. diff --git a/docs/advanced/readme.md b/docs/advanced/readme.md deleted file mode 100644 index 52e0e9db..00000000 --- a/docs/advanced/readme.md +++ /dev/null @@ -1,3 +0,0 @@ -# Bonsai Docs: \[Advanced Topics\] - -Learn how to do advanced things in Bonsai diff --git a/docs/advanced/why_no_bind.md b/docs/advanced/why_no_bind.md deleted file mode 100644 index 5f0b3efe..00000000 --- a/docs/advanced/why_no_bind.md +++ /dev/null @@ -1,49 +0,0 @@ -Why No Bind? - -Many have asked the question "`Bonsai.t` is an Applicative, why isn't it -a Monad?" Certainly, for the users of a library, having a Monad gives -the user much more power. For example, take a look at this code using -monadic bind: - -```{=html} - -``` -``` ocaml -val x: bool t -val y: 'a t -val z: 'a t - -val bind: 'a t -> f:('a -> 'b t) -> 'b t - -bind x ~f:(fun x -> if x then y else z) -``` - -Look at that, we've built a `'a t` that dynamically chooses between -either `y` or `z` depending on the value of `x`. Try to do the same with -a type that is only an Applicative! (Do not try to do the same with an -Applicative, it is impossible.) - -The dynamism on display is quite useful, but sadly, it's dynamism that -would hurt Bonsai if `Value.t` were a Monad. It would prevent -"whole-program analysis" of the app, which would make some nice Bonsai -features impossible: - -1. Bonsai runs an optimization pass on the program, resulting in a - seriously condensed incremental graph. If an app were to dynamically - generate bonsai values, we would potentially have to run the - optimization many times a second, slowing things down considerably. -2. We can attach instrumentation for debugging to nodes in the - computation graph, or even add specialized "debugging" nodes. -3. With `bind`-based dynamism, it would become difficult to reason - about the "stateful" bits of Bonsai with models. If a `match%sub` - node were to toggle between arms, state of the inactive subtrees - would be lost. - -And from a performance perspective, `bind` would likely make Bonsai apps -considerably slower: dynamism is fairly slow in Incremental, the library -that Bonsai is built on top of. - -In practice, preventing Bonsai.t from implementing the Monad interface -hasn't blocked much: using the `match%sub` and `Bonsai.assoc` -primitives, pretty much anything you'd want to do with `bind` is -possible, and we get to keep all the benefits listed above. diff --git a/docs/blog/history.md b/docs/blog/history.md deleted file mode 100644 index d13ef4f0..00000000 --- a/docs/blog/history.md +++ /dev/null @@ -1,810 +0,0 @@ -This document chronicles the changes in the main `Bonsai.t` type through -its history. - -## Motivation - -Work on Bonsai started in late March 2019 after the conclusion of the -first Tools & Compilers teach-in. The 2nd half of the teach-in was -focused on `Incr_dom`, and it gave us an opportunity to look over the -shoulders (literally) of people writing applications using that -framework. One of the main frustrations from the assignments centered on -the composability and extensibility of `Incr_dom` programs, and the lack -of a conceptual "component." - -Most of the bugs that were encountered by students in the class were -either due to the application growing too big and becoming large and -messy, or due to mistakes being made while attempting to connect smaller -more cleanly-separated components. - -But what is a "component"? When most people use the word, they're -talking about a few behaviors. - -1. Components should be composable. Making larger components out of - many smaller ones is paramount. -2. A component should be isolated from the rest of the application's - code. It shouldn't have its fingers in other components' business, - nor should it expose too much of its own internal details. - -## Bonsai Pre-History - -The closest thing that `Incr_dom` has to a "component" as described -above would be the following type signature: - -``` ocaml -type ('model, 'action, 'state) component - = model: 'model Incr.t - -> old_model: 'model option Incr.t - -> inject: ('action -> Event.t) - -> ('action, 'model, 'state) Incr_dom.Component.t Incr.t -``` - -This type signature (or something like it) is found in many places -(though never actually given a type), from `Incr_dom`'s own -`App_intf.S`, to the Partial Render Table's `Table.S.create`, to the -internal organization of some well-factored applications written at Jane -Street. - -Realizing that this type signature was the "real" component type (as -opposed to `Incr_dom.Component.t`), the first iteration of the -`Bonsai.t` type looked roughly like this: - -``` ocaml -module Bonsai : sig - type ('model, 'action, 'state) t - = model: 'model Incr.t - -> old_model: 'model option Incr.t - -> inject: ('action -> Event.t) - -> ('action, 'model, 'state) Incr_dom.Component.t Incr.t -end -``` - -## Bonsai Alpha - -Pre-History Bonsai satisfies the "isolation" requirement of a -component's definition; hiding the types involved could be done manually -with OCaml's powerful abstraction primitives. However, it was difficult -to write code to automatically compose these components. - -A good proof-of-concept was a function that took two `Bonsai.t`s and -smooshed them together into a single `Bonsai.t`. The signature looked -something like this: - -``` ocaml -val both - : ('model, 'action_1, 'state) t - -> ('model, 'action_2, 'state) t - -> ('model, ('action_1, 'action_2) Either.t, 'state) t -``` - -While writing an implementation of this may look feasible, there are -some details of the `Incr_dom.Component.t` type that made the -composition impossible. - -### Infeasibility #1 - -The view that is contained inside of `Incr_dom.Component.t` has no -natural merge operation. Given two `Vdom.Node.t` values, how do you -produce a single `Vdom.Node.t`? Wrap it in a `
`? There is no -general, satisfying answer. - -### Infeasibility #2 - -An instance of `('action, 'model, 'state) Incr_dom.Component.t` contains -inside of it a callback called `update_visibility` that returns -`'model`, which is called every time that `Incr_dom` detects that the -visibility of an element on the page may have changed. `Incr_dom` -expects that a super-component will delegate these callbacks to its -subcomponents, and they, in turn, will delegate to their subcomponents. - -The way that `Incr_dom` was able to deal with this is by offloading the -"merge two different updates to the same model" complexity off onto the -developer. Since Bonsai wants to perform composition automatically, this -is not an acceptable tradeoff. - -### Solution: Drop ``{=html}Incr_dom.Component.t``{=html} - -`Component.t` isn't cutting it, so let's make our own type, loosely -modeled on it. It will have fewer callbacks, and a generic return type. - -``` ocaml -module Snapshot : sig - type ('model, 'action, 'result) t = - { apply_action : schedule_event:(Event.t -> unit) -> 'action -> 'model - ; result : 'result - } -end - -module Bonsai : sig - type ('model, 'action, 'result) t - = model: 'model Incr.t - -> old_model: 'model option Incr.t - -> inject: ('action -> Event.t) - -> ('model, 'action, 'result) Snapshot.t Incr.t - - val both - : ('model, 'action_1, 'result_1) t - -> ('model, 'action_2, 'result_2) t - -> ('model, ('action_1, 'action_2) Either.t, 'result_1 * 'result_2) t -end -``` - -The name "Snapshot" was chosen because it's how I described `Incr_dom`'s -`Component.t` type: - -> An instance of `Incr_dom.Component.t` is a snapshot of the component -> at a particular instant in time. Just look at the fields in the type, -> you've got `Vdom.Node.t`: the *current* view, an `apply_action` -> callback which only describes the *current* `apply_action`. Indeed, -> it's the `Incr.t` in `Incr_dom.Component.t Incr.t` that gives the -> component its ability to change its view or behavior over time. - -Gone are the `update_visibility` and `on_display` callbacks, and the -"result" of a component is generic instead of being restricted to -`Vdom.Node.t`. - -Now we can finally implement `both`! - -Alongside another trivially implementable function `map`, Bonsai -components now have a let-syntax for trivial, multi-component -composition. - -``` ocaml -module Bonsai : sig - type ('model, 'action, 'result) t - - val both - : ('model, 'action_1, 'result_1) t - -> ('model, 'action_2, 'result_2) t - -> ('model, ('action_1, 'action_2) Either.t, 'result_1 * 'result_2) t - - val return : 'result -> (_, Nothing.t, 'result) t - - val map - : ('model, 'action, 'r1) t - -> f:('r1 -> 'r2) - -> ('model, 'action, 'r2) t - - module Let_syntax : sig - ... - end -end -``` - -Note: At this point, the concrete type of `Bonsai.t` stops being the -`'model -> 'old_model -> etc...` function, and becomes a GADT which is -hidden from the user. Conceptually, though, it remains the same as the -function type seen above. - -## Bonsai Beta - -After using this form of Bonsai in personal projects for a while, a -certain problem with the `Bonsai.t` signature kept cropping up: the -explosion of `'action` type parameters. Every time that a component -composition occurred (either directly via `both`, or indirectly via the -let-syntax), a new layer of `Either.t` would show up on the resultant -`'action` type parameter. Infamously, a slide-show application built as -a Bonsai demo had a top-level component whose `'action` parameter was -the following: - -``` ocaml - (never_returns, - (never_returns, - (never_returns, - (never_returns, - (never_returns, - ((Interaction.Action.t, - (int Model_editor.Action.t, Code_slide.Action.t) Either.t) - Either.t, - ((Interaction.Action.t, - (int Model_editor.Action.t, Code_slide.Action.t) Either.t) - Either.t, - (((Interaction.Action.t, Interaction.Action.t) Either.t, - (Inner_model.t Model_editor.Action.t, Code_slide.Action.t) Either.t) - Either.t, - ((string * Interaction.Action.t, - (int Core_kernel.String.Map.t Model_editor.Action.t, - Code_slide.Action.t) - Either.t) - Either.t, - ((never_returns, (never_returns, Code_slide.Action.t) Either.t) - Either.t, - ((never_returns, (never_returns, Code_slide.Action.t) Either.t) - Either.t, - ((never_returns, (never_returns, Code_slide.Action.t) Either.t) - Either.t, - ((Interaction.Action.t, - (int Model_editor.Action.t, Code_slide.Action.t) Either.t) - Either.t, - (((Interaction.Action.t, Interaction.Action.t) Either.t, - (Inner_model.t Model_editor.Action.t, Code_slide.Action.t) - Either.t) - Either.t, - ((string * Interaction.Action.t, - (int Core_kernel.String.Map.t Model_editor.Action.t, - Code_slide.Action.t) - Either.t) - Either.t, - ((never_returns, - (never_returns, Code_slide.Action.t) Either.t) - Either.t, - (((string * Interaction.Action.t) - Bonsai_timetravel_example.Time_travel.Action.t, - (int Core_kernel.String.Map.t Model_editor.Action.t, - Code_slide.Action.t) - Either.t) - Either.t, - (never_returns, (never_returns, never_returns) Either.t) - Either.t) - Either.t) - Either.t) - Either.t) - Either.t) - Either.t) - Either.t) - Either.t) - Either.t) - Either.t) - Either.t) - Either.t) - Either.t) - Either.t) - Either.t) - Either.t) - Either.t) - Either.t -``` - -If you've ever wanted a binary tree of types, Bonsai Beta would gladly -provide. - -Not only were the types unwieldy and hard to hide, they were also -practically worthless. Raising another component's action -- while -somewhat common in `Incr_dom` apps -- is better accomplished by having -one component pass its `inject` function to another component. Passing -data from one component to another is considerably easier in Bonsai than -it was in `Incr_dom`, so component-to-component communication by -directly constructing the action type of a neighboring component is -prohibited. - -This prompted us to write a feature hiding the type parameter from the -public API via some GADT type hackery, bringing the sig for Bonsai down -to: - -``` ocaml -module Bonsai : sig - type ('model, 'result) t - - val both - : ('model, 'result_1) t - -> ('model, 'result_2) t - -> ('model, 'result_1 * 'result_2) t - - val return : 'result -> (_, 'result) t - - val map - : ('model, 'r1) t - -> f:('r1 -> 'r2) - -> ('model, 'r2) t - - module Let_syntax : sig - ... - end -end -``` - -The removal of this type parameter was sorely needed too, because we'd -been wanting to add another type parameter, but felt like 4 was too -much. - -### Bonsai Release - -The last type parameter is `'input`. `'input` is for data that a -component needs to read, but doesn't require write access to. Prior to -`'input`, users would either pass all their input into the component via -`'model` (accidentally making it writable in the process), or they'd -close over additional parameters like so: - -``` ocaml -let my_component something_to_close_over = - ... construct a component here ... -``` - -However, this proved to be a nightmare: - -1. Treating all of a component's input as mutable required storing - everything that a component might want to read in the application's - model. This makes all input data mutable, and requires severe - contortions in order to pass data from one component to another: the - first component would have to write data into the model at a - particular location that the second component is also looking at. - This is very fragile in practice. -2. Passing immutable output via closure made composition harder than we - would have liked. For any data to flow to a component, the function - calls constructing it need to be made during the initial - construction of the app rather than at runtime. - -For these reasons, a 3rd type was added to the `Bonsai` signature: -`'input`, for representing immutable input that the component would like -to read but doesn't need to write. - -``` ocaml -module Bonsai : sig - type ('input, 'model, 'result) t - - val both - : ('input, 'model, 'result_1) t - -> ('input, 'model, 'result_2) t - -> ('input, 'model, 'result_1 * 'result_2) t - - val return : 'result -> (_, _, 'result) t - - val map - : ('input, 'model, 'r1) t - -> f:('r1 -> 'r2) - -> ('input, 'model, 'r2) t - - module Let_syntax : sig - ... - end -end -``` - -In addition to simplifying the uses of components, `Bonsai.t` gains a -new power: the ability to chain components together, piping the output -of one component into the input of another! - -``` ocaml -val ( >>> ) - : ('input, 'model, 'result_1) t - -> ('result_1, 'model, 'result_2) t - -> ('input, 'model, 'result_2) t -``` - -with this operator, `Bonsai.t` is in the category of Arrows![^1] - -This infix operator (found inside `Bonsai.Infix`) is wildly powerful and -provides the 2nd form of composability: - -1. `Let_syntax` or `both`: Parallel composability -2. ``{=html}`>>>`: Sequential composability``{=html} - -And that brings us to the end of the `Bonsai.t` API journey! The type -signature remains `('input, 'model, 'result) t` and although its -implementation will likely change drastically throughout the coming -months and years, this core type will stay the same (I hope). - -### Model-less Bonsai - -As if to spite me, mere weeks after writing that the type parameter -would stay the same, some of the London T&C team dared me to remove the -model type parameter. - -At first I was skeptical, but after two days of prototyping, it was -clear that not only was the removal possible, but that it simultaneously -made normal usage of the library much easier while making most -unidiomatic patterns impossible. - -The feature jane/bonsai/modelectomy implements the removal of the type -parameter, changing the core type like so: - -``` diff -- ('input, 'model, 'result) Bonsai.t -+ ('input, 'result) Bonsai.t -``` - -To be clear, models are not going away. Much like how actions still -exist (but don't occupy a slot in the type), models become an -implementation detail of a component, instead of a core part of its API. - -Removing `'model` had many effects, some more impactful than others. The -biggest change is that components can no longer share models. Much like -how removing the action type parameter removed the ability for -components to send actions to one another, removing the model type -parameter prevents sharing of component-internal state. Fortunately, -during the tree-smash that came along with the change, I found that none -of the apps or libraries that actually relied on two components reading -and writing to the same model. There were a number of cases where one -component performed reads and writes and another component only -performed reads, but that pattern is expressible with the read+write -component returning the model value as a result, passing it into the -second component via read-only input via component composition. - -Another change is that a Bonsai.t value keeps track of its default model -internally. In practice, this means that `Bonsai.of_module` and -`Bonsai.state_machine` have new, required, `default_model` parameters. -However, the application developer no longer needs to recursively -construct `default_model` values for every sub-model in the application -as they climb up the component tree. Composition via `let%map` or `>>>` -automatically derives a default model based on the default model of its -constituent components. - -Likely the most visible change is the complete removal of all of the -model projection functions. No more `Bonsai.Model.field`, no more -`Bonsai.Model.f`, no more bugs where you copy and pasted a text box -component and forgot to change the model projection. - -Finally, the types that can become models appear to be more restricted. -A model must now be fully sexp-convertible and have a conservative -equality implementation. For most models, these requirements aren't too -onerous - models should almost always contain nothing but plain data. -However, for models that can't meet these criteria, it is acceptable for -the `sexp_of` function to be defined in terms of `sexp_of_opaque`, for -the `of_sexp` function to raise an exception, and for the equality -function to default to physical equality. Together with the default -model packed with each bonsai component, combinator authors have access -to a lot more power to introspect running Bonsai apps, paving the way -for powerful debugging tools. - -```{=html} -``` -### Leo's "Wormhole" proposal - -#### ACT 1 - -##### Leo's Combinator Proposal: Wormhole - -``` ocaml -val wormhole - : (read:(_, 'value) Bonsai.t -> ('input, 'r2) Bonsai.t) - -> ('input * 'value, 'result) Packed.t -``` - -##### Implementation - -During the construction of the incremental tree (what bonsai-devs call -"eval-time"), maintain an environment that maps the "read" Bonsai.t back -to the Incr.t that it is using. - -In concrete: a `Univ_map` from `'a Type_equal.Id.t` to `'a Incr.t`. - - ../../src/env.ml - -##### Benefits of wormhole: Ergonomics - -Instead of threading some extraneous state through the bonsai graph via -arrow combinators, you can use this "read" component only in the places -where you care about the `'value` value. - -``` ocaml -wormhole (fun ~read -> - let%map a = ... - and b = ... - and c = ... - and d = Bonsai.Map.assoc (module Int) (Bonsai.both (...) read) -) -``` - -##### Benefits of wormhole: Better incremental graph - -The graph produced with wormhole is more direct! - - o -- o -- o -- o - / - o -` - - o -- o -- o -- o - / - o -----------` - -##### Benefits of wormhole: Better semantics - -This is confusing to some people: - -``` ocaml -let a: _ Bonsai.t = ... in -let%map b = a >>| f -and c = a >>| g in -h b c - -(both (Bonsai.input) a) >>> (wormhole (fun ~read -> - let%map b = read >>| f - and c = read >>| g in - h b c -)) -``` - -But if 'a' is the `read` end of a wormhole, there's no duplication! - -##### Bind-like - -``` ocaml -val bind_like - : ('input, 'r1) Bonsai.t - -> f:((_, 'r1) Bonsai.t -> ('input, 'r2) Bonsai.t) - -> ('input, 'r2) Bonsai.t - - -(both (Bonsai.input) a) >>> (wormhole (fun ~read -> - let%map b = read >>| f - and c = read >>| g in - h b c -)) - -bind_like a (fun read -> - let%map b = read >>| f - and c = read >>| g in - h b c -) -``` - -##### Real world usage - - extend_second paging - >>> extend_second sorting - >>> extend_second cell_focus - >>> extend_second tbody - >>> extend_second merge_key_handlers - >>> extend_second table - >>| make_result - - old: ../../../../../../+share+/lib/bonsai_ui_components/src/table/table.ml - new : ../../../bonsai_ui_components/src/table/table.ml - -#### ACT 2 - -``` diff - module Let_syntax : sig - module Let_syntax : sig -+ val bind -+ : ('input, 'r1) Bonsai.t -+ -> f:((_, 'r1) Bonsai.t -> ('input, 'r2) Bonsai.t) -+ -> ('input, 'r2) Bonsai.t - end - end -``` - -##### Pros - -Huge ergonomics + performance win for basically all code that would -currently be implemented via the arrow combinators. - -New API's are easy to write, like the following for writing a tabbed-ui -interface: - -``` diff --val tabs -- : (module Enum with type t = 'a) -- -> specialize: -- ( 'a -- -> ('input * ('a -> Vdom.Event.t), Vdom.node.t) Bonsai.t) -- -> ('input, Vdom.Node.t) Bonsai.t - -+val tabs -+ : (module Enum with type t = 'a) -+ -> specialize: -+ ( set_tab:(_, 'a -> Event.t) Bonsai.t -+ -> 'a -+ -> ('input, Vdom.node.t) Bonsai.t) -+ -> ('input, Vdom.Node.t) Bonsai.t -``` - -Using that API - -``` ocaml -module My_tabs = struct - type t = A | B | C [@@deriving sexp, enumerate] -end - -tabs (module My_tabs) ~specialize:(fun set_tab -> function - | A -> a_component - | B -> b_component - | C -> build_c_component set_tab) -``` - -##### Cons - -It's not *really* bind? I wish we could use the new let-op syntax in -ocaml. - -`and` doesn't work. We'd need something like co-both for it to work? Leo -has a proposal here that I need to read more carefully. - -No storing these vars! - -#### ACT 3 - -##### Unit For All! - -``` diff --val bind_like -- : ('input, 'r1) Bonsai.t -- -> f:((_, 'r1) Bonsai.t -> ('input, 'r2) Bonsai.t) -- -> ('input, 'r2) Bonsai.t - -+val bind_like -+ : (unit, 'r1) Bonsai.t -+ -> f:((unit, 'r1) Bonsai.t -> (unit, 'r2) Bonsai.t) -+ -> (unit, 'r2) Bonsai.t -``` - -##### Proc - -``` ocaml -val proc - : ((unit, 'input) Bonsai.t -> (unit, 'result) Bonsai.t) - -> ('input, 'result) Bonsai.t - -proc (fun input -> - - let%bind a = input - let%bind b = apply some_component (let%map a = a and input = input in a, input) -) -``` - -##### Proc in use - -``` ocaml -let my_arrow = proc (fun i -> - let%bind x = some_arrow <<< (i >>| a_field) in - let%bind y = another_arrow <<< - let%map x = x - and i = i in - g x i - in - let%bind z = yet_another_arrow <<< - let%map x = x - and y = y - and i = i in - h x y i - in - z) -``` - -#### ACT 4 - -##### REMOVE BONSAI.T - -It's pretty clear that regular `('input, 'result) Bonsai.t` and -`(unit, 'result) Bonsai.t` are pretty different. Specifically the -semantics of `map` being different on the "read" end of a bind, makes me -think that these should be different types. - -``` ocaml -module Fun : sig - type ('input, 'result) t -end - -module Val : sig - type 'a t -end -``` - -##### Proc & other helpers - -``` ocaml -val proc : ('a Val.t -> 'b Val.t) -> ('a, 'b) Fun.t -val call : ('a, 'b) Fun.t -> 'a Val.t -> 'b Val.t -``` - -##### Syntax - -Only define a let-syntax for Val.t - - moduel Val : sig - module Let_syntax : sig - val sub: 'a Val.t -> f: ('a Val.t -> 'b Val.t) -> 'b Val.t - end - end - -##### DELETE ARROW - -``` diff --module Arrow : sig -- val first : ('input, 'result) t -> ('input * 'a, 'result * 'a) t -- val second : ('input, 'result) t -> ('a * 'input, 'a * 'result) t -- val split : ('i1, 'r1) t -> ('i2, 'r2) t -> ('i1 * 'i2, 'r1 * 'r2) t -- val extend_first : ('input, 'result) t -> ('input, 'result * 'input) t -- val extend_second : ('input, 'result) t -> ('input, 'input * 'result) t -- val ( *** ) : ('i1, 'r1) t -> ('i2, 'r2) t -> ('i1 * 'i2, 'r1 * 'r2) t -- val fanout : ('input, 'r1) t -> ('input, 'r2) t -> ('input, 'r1 * 'r2) t -- val ( &&& ) : ('input, 'r1) t -> ('input, 'r2) t -> ('input, 'r1 * 'r2) t -- val ( ^>> ) : ('i1 -> 'i2) -> ('i2, 'result) t -> ('i1, 'result) t -- val ( >>^ ) : ('input, 'r1) t -> ('r1 -> 'r2) -> ('input, 'r2) t -- val partial_compose_first -- : ('input, 'shared * 'output1) t -- -> ('input * 'shared, 'output2) t -- -> ('input, 'output1 * 'output2) t -- val pipe -- : ('input, 'r1) t -- -> into:('intermediate, 'r2) t -- -> via:('input -> 'r1 -> 'intermediate) -- -> finalize:('input -> 'r1 -> 'r2 -> 'r3) -- -> ('input, 'r3) t --end -``` - -##### Fin - -``` ocaml -module Fun : sig - type ('a, 'b) t - (* arrow*) -end - -module Val : sig - type 'a t (* = | Lookup 'a Type_equal.Id.t | Map of 'a t * 'a -> 'b | Both 'a t * 'b t *) - (* applicative *) - - (* make applicative syntax *) - - val map - : 'a t - -> f:('a -> 'b) - -> 'b t - - val return: 'a -> 'a t - val both : 'a t -> 'b t -> ('a * 'b) t -end - -module Computation : sig - type 'a t (* = (unit, 'a) Bonsai.t *) -end - -val apply - : ('a, 'b) Fun.t - -> 'a Val.t - -> 'b Computation.t - -val bind - : 'a Computation.t - -> f:('a Val.t -> 'b Computation.t) - -> 'b Computation.t - -val const: 'a -> 'a Computation.t - -val return: 'a Val.t -> 'a Computation.t - -val proc - : ('a Val.t -> 'b Computation.t) - -> ('a, 'b) Fun.t - - -let syntax : - sub - kinda like bind - defined on computation.t - map - - defined on Val.t - -val bind - : 'a Val.t - -> f:('a Val.t -> 'b Val.t) - -> 'b Val.t - -let call arrow value = - let%bind result = value >>> arrow in - result - - bind (value >>> arrow) ~f:(fun r -> r) - -let a = call arrow x -let - - -val bind - : ('a, 'b) Fun.t - -> f:('b Val.t -> ('b, 'c) Fun.t) - -> ('a, 'c) Fun.t - -val read : 'a Val.t -> (_, 'a) Fun.t - -type 'a computation -val call: ('a, 'b) Fun.t -> 'a Var.t -> 'a computation -val bind - : 'a computation - -> f:('a Val.t -> 'b computation) - -> 'b computation - -let a = call f x in -let%map a = a -and b = b -in (a, b) -``` - -Bonsai is not a UI framework, it's a programming language - -### Proc-like Bonsai and beyond - -In early 2020, once again London T&C suggested a change to the API for -the better similar to Haskell's Arrow's "proc" notation that changed -Bonsai's API. Please see [proc.md](./proc.md) for the current (as of -June 2022 at least) state of Bonsai's API. - -[^1]: [Arrow: Haskell Wiki](https://wiki.haskell.org/Arrow) diff --git a/docs/blog/proc.md b/docs/blog/proc.md deleted file mode 100644 index aa8edfbc..00000000 --- a/docs/blog/proc.md +++ /dev/null @@ -1,746 +0,0 @@ -Bonsai is somewhat unique among Jane Street libraries for embracing the -[Arrow](https://en.wikibooks.org/wiki/Haskell/Understanding_arrows) as -its primary abstraction. `('a, 'b) Bonsai.t` is conceptually very close -to a function `'a -> 'b`, but with two "superpowers" that regular OCaml -functions don't have: - -1. Maintaining and composing state-machines for dynamic, interactive - behavior -2. Building an incremental computation graph so that value - recomputation happens as infrequently as possible. - -Arrows occupy a comfy place [in between Applicatives and -Monads](http://homepages.inf.ed.ac.uk/slindley/papers/idioms-arrows-monads.pdf) -which is what gives Bonsai the ability to build a dependency graph -between UI components while still being static enough to permit -automated composition of the components state-machine-like models. - -Unfortunately, the Arrow APIs can be a real struggle to work with. With -functions like - -``` ocaml -val compose : ('a, 'b) t -> ('b, 'c) t -> ('a, 'c) t -``` - -and - -``` ocaml -val both : ('a, 'b) t -> ('a, 'c) t -> ('a, 'b * 'c) t` -``` - -`Bonsai.t` values are combined to form a new `Bonsai.t` with a different -shape. - -There are a number of issues with programming in this "higher-order" -form: - -- You can't give descriptive variable names to intermediate results -- "Tupling" many values together is required to pass more than one - value through to other parts of the program -- The programmer can accidentally introduce duplication of work - without intending to -- Many APIs are quite obtuse. For example, `Bonsai.if_` requires that - the condition, the `then_` branch, and the `else_` branch all have - the same input. This necessitates a product type containing all the - values that might be needed in all three parameters - -For the longest time, the Bonsai team thought that this was the best -that we could do, and we had plans to build a ppx ([like Haskell -did](https://downloads.haskell.org/~ghc/7.8.4/docs/html/users_guide/arrow-notation.html)) -to make Bonsai easier to work with. Fortunately, through a series of -small breakthroughs, we found a new way to implement Bonsai, and with -it, a new API that is *much* nicer to work with. This work, largely -inspired by the [Arrow -Calculus](http://homepages.inf.ed.ac.uk/slindley/papers/arrow-calculus.pdf) -is accompanied by an extension to `ppx_let` which we will cover later in -this document. - -# `Bonsai.Proc` - -The core Bonsai namespace remains unchanged (for now). The new API is -exposed in a new module: `Bonsai.Proc`. Here, we can find two types: - -``` ocaml -module Bonsai : sig - module Proc: sig - module Computation : sig - type 'a t - end - - module Value : sig - type 'a t - include Applicative.S with type 'a t := 'a t - end - end -end -``` - -In the place of `Bonsai.t` we have two types: `'a Computation.t` and -`'a Value.t`, which map onto the two Bonsai effects (state-machine -composition, and incremental value sharing respectively) described -above. - -## `let%sub` - -Alongside the new Bonsai features, an extension to `ppx_let` was also -developed to make using Bonsai easier. This extension - named -`let%sub` - is intended to be used with pairs of types that have a -function that converts between them with a function that looks very -similar to bind. - -Let's look at the three let-syntaxes and the type signatures for the -functions that they use. - -**Applicative `map`** - -``` ocaml -val map : 'a t -> f:('a -> 'b) -> 'b t -val v : 'a t - -let%map a = v in - (* [a] has type ['a], and the result-expression - is expected to be of type ['b] *) -``` - -**Monadic `bind`** - -``` ocaml -val bind : 'a t -> f:('a -> 'b t) -> 'b t -val v : 'a t - -let%bind a = Some "hello" in - (* [a] has type ['a] and the result-expression - is expected to be of type ['b t] *) -``` - -And finally, **Variable Substitution** - -``` ocaml -val sub : 'a t -> f:('a s -> 'b t) -> 'b t -val v : 'a t - -let%sub a = v in - (* [a] has type ['a s] and the result-expression - is expected to be of type ['b t] *) -``` - -On the surface, `let%sub` looks an awful lot like `let%bind`, but the -`~f` function takes a value of `'a s` instead of `'a`. - -In Bonsai, our `sub` function has this type signature: - -``` ocaml -val sub - : 'a Computation.t - -> f:('a Value.t -> 'b Computation.t) - -> 'b Computation.t -``` - -## `Bonsai.Proc.Computation` - -A value of type `'a Computation.t` represents the method for computing a -value of type `'a`. This value may change during the lifetime of a -program, and the computation has an internal state machine that it can -use to store state that can change while the application is in use. - -The same `'a Computation.t` can be used in multiple places in a program, -and these uses are independent, meaning that they *don't* share their -state, nor does any work performed by one instance get reused by -another. - -In this normal OCaml code, if we see the same function being called -multiple times: - -``` ocaml -let a = f () in -let b = f () in -a + b -``` - -Then if `f` has side-effects (maybe printing to the console), then those -side-effects happen twice because `f` was called twice. - -Similarly, if we wrote the code this way: - -``` ocaml -let a = f () in -let b = a in -a + b -``` - -you would (correctly) expect that the side-effect only happens once -(when computing `a`). In these examples, the *code* `f ()` is analogous -to `_ Computation.t`. If you want to have two separate values whose -computations maintain separate state, you would use two instances of -`let%sub` to bind them separately: - -``` ocaml -val some_computation : int Computation.t -val add : int Value.t -> int Value.t -> int Computation.t - -let open Proc.Let_syntax in -let%sub a = some_computation in -let%sub b = some_computation in -add a b -``` - -Here, `a` and `b` can take on different values depending on the states -of the computations that produce them. - -However, if you want to use just one value in multiple places, only use -`let%sub` once: - -``` ocaml -let open Proc.Let_syntax in -let%sub a = some_computation in -let b = a in (* This is just aliasing [b] to [a] to mirror the above example *) -add a b -``` - -Here, `a` and `b` always take on the same value. - -## `Bonsai.Proc.Value` - -A value of type `'a Value.t` represents a value that may change during -the lifetime of the program. For those familiar with the `Incremental` -library, this type is conceptually very similar to `Incr.t`. The main -method by which you acquire values of type `'a Value.t` is by evaluating -a `'a Computation.t` via the `let%sub` syntax extension. - -``` ocaml -val c : 'a Computation.t - -let%sub x = c in - (* [x] has type ['a Value.t] here *) -``` - -In the example above, we run a computation `c` and store the result of -that computation in `x` which has type `Value.t`. - -`Value.t` is an applicative, which means that you can combine multiple -`Value`s by using `Proc.Let_syntax`: - -``` ocaml -val a : int Value.t -val b : int Value.t - -let (_ : int Value.t) = - let open Proc.Let_syntax in - let%map a = a - and b = b in - a + b -;; -``` - -## Other `Bonsai.Proc` functions - -In addition to the new types, and the functions that convert between -them, all of the core Bonsai functions have been adapted. Take -`Bonsai.if_`; the pre-Proc type signature is this: - -``` ocaml -val if_ - : ('input -> bool) - -> then_:('input, 'result) t - -> else_:('input, 'result) t - -> ('input, 'result) t -``` - -This signature is quite confusing. The `then_` and `else_` branches are -conceptually functions that need to take the same `'input`. You need to -extract a `bool` out of the `'input` for the condition as well? A pro -Bonsai dev will know that you probably need to bundle up a whole bunch -of data into the `'input` value in order to make it useful for the -condition *and* both of the branches, which will involve judicious use -of `Bonsai.map_input` (AKA `@>>`), to get these types to line up. But -it's certainly not obvious, and is consistently a point of confusion for -new users, and one of frustration for experienced users. - -Compare that to the new `Bonsai.Proc.if_` - -``` ocaml -val if_ - : bool Value.t - -> then_:'a Computation.t - -> else_:'a Computation.t - -> 'a Computation.t -``` - -This signature is better for two reasons: - -1. It disassociates the condition, the `then_`-branch, and the - `else_`-branch, you can compute these any way that you like, and - compose them in this function without any bundling together of data - for all three usages or any `map_input` nonsense. -2. The types involved make it clear which parts of the computation are - evaluated eagerly (the condition, because `bool Value.t` requires - the value to have already been computed) and which are evaluated - on-demand (the `then_` and `else_` parameters, because - `'a Computation.t` represents a value that *can* be computed, but - won't be unless necessary). - -# `('a, 'b) Bonsai.t`? - -Seeing as both the "Proc" API, and the regular "Arrow-based" API can -exist in the same code base at the same time, there must be some -relationship between the following types: - -- `Bonsai.t` -- `Bonsai.Proc.Computation.t` -- `Bonsai.Proc.Value.t` - -Indeed, going forward, `Bonsai.t` is defined as follows: - -``` ocaml -type ('a, 'b) t = 'a Proc.Value.t -> 'b Proc.Computation.t - ^ - `-- (* Regular OCaml function! *) -``` - -This type equality is fundamental, and it is exposed in order to help -you transition your Bonsai apps to the new API without requiring a bunch -of conversion functions. - -# Benefits of Proc? - -We wouldn't have spent so much time writing the code (and this document) -if it wasn't for very good reasons. However, after receiving feedback -about some of the issues, and attempting to write complex components -ourselves, we believe that the benefits of the Proc API are enormous, -and that they will be the recommended way to build Bonsai programs going -forward. The next few sections detail the individual ways that the -development experience is improved. - -## Full Power of OCaml Functions - -As explained above, the old `('a, 'b) Bonsai.t` is now nothing more than -an alias for `'a Value.t -> 'b Computation.t`. - -The use of a regular OCaml function in that type signature implies -several brand new capabilities of Bonsai: - -- 2 (and higher!) arity components -- 0-arity components -- Optional and labeled arguments -- Partial application of components - -## Higher-arity components - -In the old Bonsai world, we needed to stick all of the input that a -component needs into a single value in order to pass it through the -`'input` type parameter. This frequently leads to a module that looks -like `module Input : type t = { ...` in every single module that defines -a Bonsai component. With Proc though, code that used to look like this: - -``` ocaml -(* Old Style *) - -module Input : sig - type t = - { foo : int - ; bar : string - } -end - -module Result : sig - type t = - { baz : float - ; view : Vdom.Node.t - } -end - -val component : (Input.t, Result.t) Bonsai.t -``` - -Could now be expressed like this: - -``` ocaml -(* New Style *) - -type t = - { baz : float - ; view : Vdom.Node.t - } - -val component - : foo:int Value.t - -> bar:string Value.t - -> t Computation.t -``` - -Using multiple arguments (when you want to) can make the code a lot -cleaner, and much of the code which merely packaged and unpackaged -records is gone. - -## Optional argument components - -Along with higher-arity components, optional arguments are now -expressible! The author of a reusable component may write a type -signature like this: - -``` ocaml -module Config : sig - type t = - { should_show_header : bool - ; rows_to_display : int option - ; allow_tabs : bool - } -end - -val component - : ?config:Config.t Value.t - -> int list Value.t - -> Vdom.Node.t Computation.t -``` - -Now, as the user of this component, you have three options available to -you: - -1. If the `Config.t` has defaults that you want to use, you can leave - the optional argument unspecified. -2. However, if you want a different config, but one that stays the same - for the entire lifetime of the program, you can pass in a constant - `Value.t` (via `Value.return`. -3. However, if the configuration is dynamic (maybe the user of the - application can interactively change parts of the config), then you - can pass in a `Config.t Value.t` which is computed by other parts of - the Bonsai program. - -## 0-arity component - -In pre-proc-bonsai, there was a common Bonsai type signature: -`(unit, something) Bonsai.t`. The `unit` in input-parameter-position -meant something special; the component didn't have any input. In -`Bonsai.Proc` land, this is completely avoided by having components that -have a type signature only including computation, like so: - -``` ocaml -type t = - { text : string - ; view : Vdom.Node.t - } - -val textbox : t Computation.t -``` - -No "input" or `Value.t` in sight. The `Computation` is all that matters, -so it's all that's present in the signature. - -## No weird duplication of work - -This code can be very confusing to most people that have approached -Bonsai. - -``` ocaml -val c : (input, result) Bonsai.t - -let r = - let%map a = c >>| foo - and b = c >>| bar in - ... -``` - -Many would expect that, because `c` is used in two locations, that the -computation (and the state machine) done by `c` is reused. However, this -is not the case! Both uses of `c` are different components in the Bonsai -graph, and their internal states are not the same at runtime. - -This behavior makes a lot of sense when you think about Bonsai -components as functions, like in the following OCaml code, where a -function `f` is called twice: - -``` ocaml -val f : input -> result - -let r input = - let a = foo (f input) - and b = bar (f input) in - ... -``` - -In the new `Proc` code, we no longer work with function-like arrows, so -code looks a lot more like typical OCaml where our preconceptions about -when code is executed will help us rather than hurt us. - -## Easier-to-use APIs - -As described above, `Bonsai.Proc.if_` has a much nicer API, but -improvements are found all throughout the `Bonsai.Proc` namespace. Take -the `Bonsai.Map.assoc_*` family of functions, in the past, they were -three very similar looking functions - -``` ocaml -val assoc_input - : ('key, 'cmp) comparator - -> ('data, 'result) t - -> (('key, 'data, 'cmp) Map.t, ('key, 'result, 'cmp) Map.t) t -``` - -`assoc_input` took a `Bonsai.t` that only looked at the "data" of an -element in a map and produced a component that had an input with the -type of the whole map. - -Meanwhile, its sibling `associ_input` accepted a component that took -both the "key" *and* the "data" in tupled-form: - -``` ocaml -val associ_input - : ('key, 'cmp) comparator - -> ('key * 'data, 'result) t - -> (('key, 'data, 'cmp) Map.t, ('key, 'result, 'cmp) Map.t) t -``` - -And finally, we have `associ_input_with_extra`, a beast of an API that -produces a component that expects not only a map, but also an extra -"input", which is unrelated to the data in the map, but is passed to -each internal component: - -``` ocaml -val associ_input_with_extra - : ('key, 'cmp) comparator - -> ('key * 'data * 'input, 'result) t - -> (('key, 'data, 'cmp) Map.t * 'input, ('key, 'result, 'cmp) Map.t) t -``` - -All three of these functions are replaced by `Bonsai.Proc.assoc`: - -``` ocaml -val assoc - : ('key, 'cmp) comparator - -> ('key, 'data, 'cmp) Map.t Value.t - -> f:('key Value.t -> 'data Value.t -> 'result Computation.t) - -> ('key, 'result, 'cmp) Map.t Computation.t -``` - -Here, the `_ Map.t Value.t` that is being mapped over is passed into the -function explicitly, and the `f` function parameter exposes both the -"key" and "data" values which you can choose to use or ignore! -Furthermore, if the value computed by `f` needs some extra data that -isn't inside the map, you can just close over them from the surrounding -environment. - -## No more arrow combinators - -Likely the worst feature of the existing Bonsai API is the -`Bonsai.Arrow` module. The functions found therein are the combinators -which are used to merge, combine, and compose `Bonsai.t` values. To -highlight a particularly egregious example, suppose you had the -following three bonsai components: - -``` ocaml -val year_picker: (unit, Vdom.Node.t * Year.t ) Bonsai.t -val month_picker: (Year.t, Vdom.Node.t * Month.t) Bonsai.t -val day_picker: (Year.t * Month.t, Vdom.Node.t * Day.t ) Bonsai.t -``` - -and you wanted to compose a `Bonsai.t` that had type - -``` ocaml -val date_picker: (unit, Vdom.Node.t * Year.t * Month.t * Day.t) Bonsai.t -``` - -In the old world, using the Arrow combinators, you'd need to write -something like this: - -``` ocaml -open Bonsai.Arrow - -let date_picker = - year_picker - >>| (fun (year_view, year) -> - (year_view, year), year) - |> second month_picker - >>| (fun ((year_view, year), (month_view, month) -> - (year_view, month_view, year, month), (year, month) - |> second day_picker - >>| (fun ((year_view, month_view, year, month), (day_view, day)) -> - let view = Vdom.Node.div [] [year_view; month_view; day_view] in - view, year, month, day) -``` - -The code above makes me sad in a way that I can't exactly put into a -markdown file. I needed to store values for later in the left-hand-side -of the tuple, while forwarding values to the next component in the -right-hand-side of that tuple. - -In `Bonsai.Proc` the above code would be written like this: - -``` ocaml -open Bonsai.Proc.Let_syntax - -let date_picker = - let%sub year_picker = year_picker (Value.return ()) in - let year = Value.map year_picker ~f:(fun (_, year) -> year) in - let%sub month_picker = month_picker year in - let month = Value.map month_picker ~f:(fun (_, month) -> month) in - let%sub day_picker = day_picker year month in - return ( - let%map year_view, year = year_picker - and month_view, month = month_picker - and day_view, day = day_picker in - let view = Vdom.Node.div [] [year_view; month_view; day_view] in - view, year, month, day) -``` - -You'll notice that the code isn't shorter, but it's considerably easier -to understand what's going on. Furthermore, adding or removing code from -this function won't cause chains of type errors to propagate throughout -the rest of the code. - -And it's going to get better too. With some upcoming improvements to the -`let%sub` ppx, soon you'll be able to write code with pattern -destructuring in the let, allowing this to be written instead: - -``` ocaml -open Bonsai.Proc.Let_syntax - -let date_picker = - let%sub year_view, year = year_picker in - let%sub month_view, month = month_picker year in - let%sub day_view, day = day_picker year month - return ( - let%map year_view = year_view - and month_view = month_view - and day_view = day_view - and year = year - and month = month - and day = day in - let view = Vdom.Node.div [] [year_view; month_view; day_view] in - view, year, month, day) -``` - -## Better incremental graph - -One huge downside of the arrow combinators is that it was frequently -impossible to build the best possible Incremental graph. A graph like -the one below - - o -> b => c => d - .---^ - a - -might have been required in order to pass `a` into `d` using the Arrow -API. But now, with `Proc`, the developer has much more flexibility in -how to compose the results of their computations, producing a more -accurate - and faster - graph: - - o -> b -> c -> d - .---^ - a - -# Roll out Plan - -`Bonsai.Proc` is available today! - -While we've solved many of the issues that current Bonsai users deal -with, we don't want to force everyone to move to the new APIs. However, -it's also unfortunate to have two APIs for Bonsai. To accommodate these -conflicting interests, our compromise is to phase out the "old" Bonsai -API gradually over the course of the next year. - -## Current Day (June 2020) - -The new "Proc" types and APIs are added under a "Proc" submodule. All -existing Bonsai code continues to build and function as it did before. - -``` ocaml -module Bonsai : sig - type ('a, 'b) t - - (* Arrow APIs *) - - module Proc : sig - module Computation : sig - type 'a t - end - - module Value : sig - type 'a t - end - - (* Proc APIs *) - end -end -``` - -## 2 Months Out (August 2020) - -The "Arrow"-based type and APIs are moved into a module called `Arrow`, -and the Proc types and APIs are promoted into the primary module. This -tree-smash will be automated, and no application or library author will -need to do any work to keep their code building and running as it did -before. - -``` ocaml -module Bonsai : sig - module Computation : sig - type 'a t - end - - module Value : sig - type 'a t - end - - (* Proc APIs *) - - module Arrow : sig - type ('a, 'b) t - - (* Arrow APIs *) - end -end -``` - -## 6 Months Out (Dec 2020) - -The "Arrow" submodule is renamed to `Arrow_deprecated`, but as before, -no application or library author will need to do anything. - -``` ocaml -module Bonsai : sig - module Computation : sig - type 'a t - end - - module Value : sig - type 'a t - end - - (* Proc APIs *) - - module Arrow_deprecated : sig - type ('a, 'b) t - - (* Arrow APIs *) - end -end -``` - -## 12 Months Out (June 2021) - -The `Arrow_deprecated` module is removed, and any apps or libraries -using it will need to be ported. This translation will likely be done -mechanically, and it likely won't require any intervention, but I make -no promises on the quality of the code that is translated, so please -don't make it come to this; I'm happy to help you port things over -manually if you approach the bonsai-dev team earlier. - -``` ocaml -module Bonsai : sig - module Computation : sig - type 'a t - end - - module Value : sig - type 'a t - end - - (* Proc APIs*) -end -``` - -# Conclusion - -`Bonsai.Proc` is out! Go use it! And let us know what you think! - -The Arrow is dead, long live the Arrow! diff --git a/docs/blog/readme.md b/docs/blog/readme.md deleted file mode 100644 index 1eb983b0..00000000 --- a/docs/blog/readme.md +++ /dev/null @@ -1,4 +0,0 @@ -# Bonsai Docs: \[Blog Posts\] - -A collection of blog posts about Bonsai, mostly focusing on the history -of its API. diff --git a/docs/guide/00-introduction.md b/docs/guide/00-introduction.md deleted file mode 100644 index ac2943bb..00000000 --- a/docs/guide/00-introduction.md +++ /dev/null @@ -1,251 +0,0 @@ -# Bonsai Docs: Guide (Introduction) - -This guide will teach you how to build web UIs in OCaml. We'll learn how -to: - -- Write [HTML with `virtual_dom`](./01-virtual_dom.mdx), with - interactivity powered by side-effects [encapsulated as - `Effect.t`s](./02-effects.mdx) -- Structure our web UI as a graph of composable, [incremental - computations with `Bonsai.t`](./03-incrementality.mdx) -- Instantiate and use [state](./04-state.mdx) -- Conditionally evaluate Bonsai code, or create a dynamic number of - `Bonsai.t`s, with [`match%sub` and `assoc`](./05-control_flow.mdx) - -These are the basic tools of writing OCaml web UIs. To learn how to -[style with ppx_css](../how_to/css.mdx), [send RPCs to a -server](../how_to/rpcs.mdx), [test your Bonsai -code](../how_to/testing.mdx), and more, see the [Bonsai -how-tos](../how_to/readme.md). - -```{=html} - -``` -This guide is not intended to replace -[bonsai.mli](https://github.com/janestreet/bonsai/blob/master/src/bonsai.mli), -which lists and documents everything that Bonsai provides. - -The rest of this intro previews building a simple web app in OCaml. -We'll discuss each step in depth in the guide chapters. - -## OCaml Web Apps at 10,000 Feet - -Functional web UIs are functions from *data* to a *view*. - -The *data* can be client-side state, data embedded in the URL, data from -the server, etc. - -The *view* is the part users see. In web UIs, the view is HTML (provided -by `virtual_dom`), styled by CSS. - -For example, a web UI that tells a user how many unread emails they have -might look like: - -```{=html} - -``` -``` ocaml -val message_vdom : name:string -> new_emails:int -> Vdom.Node.t -``` - -```{=html} - -``` -``` ocaml -open! Core -open Virtual_dom - -let message_vdom ~name ~new_emails = - Vdom.Node.div - ~attrs:[ [%css {|font-size: 16px;|}] ] - [ Vdom.Node.textf "hello %s! you have %d new emails" name new_emails ] -;; -``` - -```{=html} - -``` -User interactions, state updates, and RPC server calls are just *side -effects* of an otherwise pure function. We wrap these side effects in an -`Effect.t` type. - -For example, we could add a button that "reads" an email to our UI: - -```{=html} - -``` -``` ocaml -val read_email_button : on_click:unit Effect.t -> Vdom.Node.t -``` - -```{=html} - -``` -``` ocaml -let read_email_button ~on_click = - Vdom.Node.button - ~attrs:[ Vdom.Attr.on_click (fun _ -> on_click) ] - [ Vdom.Node.text "Read an email!" ] -;; -``` - -```{=html} - -``` -A desirable property is incrementality: when something changes, we only -recompute stuff that depends on it. We can do so by wrapping our inputs -and outputs in the incremental `Bonsai.t` type: - -```{=html} - -``` -``` ocaml -val emails_bonsai - : name:string Bonsai.t - -> new_emails:int Bonsai.t - -> read_email_effect:unit Effect.t Bonsai.t - -> Vdom.Node.t Bonsai.t -``` - -We can compose `Bonsai.t`s with the `let%arr` operator: - -```{=html} - -``` -``` ocaml -open! Bonsai_web.Cont -open Bonsai.Let_syntax - -let emails_bonsai ~name ~new_emails ~read_email_effect = - let message = - let%arr name = name - and new_emails = new_emails in - message_vdom ~name ~new_emails - in - let%arr message = message - and read_email_effect = read_email_effect in - Vdom.Node.div [ message; read_email_button ~on_click:read_email_effect ] -;; -``` - -```{=html} - -``` -In the code above, `message` will not be recomputed if only -`read_email_effect` changes. - -But incrementality doesn't matter if we only have constants. Interesting -apps are stateful. We can use `Bonsai.state` to create a simple -getter/setter state. To use `Bonsai.state` and other `Bonsai.*` -primitives, we need a `local_ Bonsai.graph` "graph-builder", which -Bonsai will pass into your top-level `app` function. - -In our email example, we can use `Bonsai.state` to keep track of how -many unread emails the user has and modify that count whenever they -"read" one: - -```{=html} - -``` -``` ocaml -val emails_stateful : name:string Bonsai.t -> local_ Bonsai.graph -> Vdom.Node.t Bonsai.t -``` - -```{=html} - -``` -``` ocaml -let emails_stateful ~name (local_ graph) = - let default_count = 999 in - let (count : int Bonsai.t), (set_count : (int -> unit Effect.t) Bonsai.t) = - Bonsai.state default_count graph - in - let read_email_effect = - let%arr count = count - and set_count = set_count in - set_count (count - 1) - in - emails_bonsai ~name ~new_emails:count ~read_email_effect -;; -``` - -```{=html} - -``` -Note that the state "setter" is an incrementally computed function that -produces a `unit Effect.t`. When this effect is scheduled via an event -handler, the state will update. - -And since our ultimate goal is to produce a single -incrementally-computed `Vdom.Node.t`, with state managed by Bonsai, a -complete app looks like: - -```{=html} - -``` -``` ocaml -val app : local_ Bonsai.graph -> Vdom.Node.t Bonsai.t -``` - -```{=html} - -``` -``` ocaml -let app (local_ graph) = emails_stateful ~name:(Bonsai.return "User") graph -``` - -```{=html} - -``` -We can run it with: - -```{=html} - -``` -``` ocaml -let () = Bonsai_web.Start.start app -``` - -## Bonsai is Generic - -Bonsai isn't actually web-specific: it's a library for building, -composing, and running pure, incremental, state-machines. It works -particularly well for web UIs, but it could also power other UI -backends, or even stateful, incremental computation on servers. - -That's why instead of `open! Bonsai`, you'll `open! Bonsai_web`: -`Bonsai_web` contains a bunch of web-specific utils and helpers, in -addition to the core functionality in `Bonsai`. - -## The Underlying Machinery - -Browsers can only really run -[JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript) -and [WebAssembly](https://developer.mozilla.org/en-US/docs/WebAssembly). -That's why we need -[js_of_ocaml](https://ocsigen.org/js_of_ocaml/latest/manual/overview), -which compiles OCaml bytecode to JavaScript, and provides bindings for -browser APIs. diff --git a/docs/guide/01-virtual_dom.md b/docs/guide/01-virtual_dom.md deleted file mode 100644 index 8f0d0cf8..00000000 --- a/docs/guide/01-virtual_dom.md +++ /dev/null @@ -1,253 +0,0 @@ -# 01 - Virtual-DOM - -Browser interfaces are described by a tree of HTML *elements*, each of -which can have some *attributes* attached. The `virtual_dom` library -provides an OCaml interface for constructing these trees. - -In this chapter, we'll learn how to write HTML in OCaml using -`virtual_dom` and `ppx_css`. - -## Vdom.Node.t - -This wouldn't be a programming tutorial without a hello world example, -which introduces the `Vdom.Node.text` node constructor. - -```{=html} - -``` -``` ocaml -let hello_world : Vdom.Node.t = Vdom.Node.text "hello world!" -``` - -```{=html} - -``` -```{=html} - -``` -The text node will frequently be the "leaf" of a view (there are no -"children" of a text node). Let's put some text inside a bulleted list -by using some more node constructors: - -```{=html} - -``` -``` ocaml -let bulleted_list : Vdom.Node.t = - let open Vdom.Node in - div - [ h3 [ text "Norwegian Pancakes" ] - ; ul - [ li [ text "3 eggs" ] - ; li [ text "2 cups of milk" ] - ; li [ text "1 cup of flour" ] - ] - ] -;; -``` - -```{=html} - -``` -For the bulleted list, the `ul` and `li` functions are required. These -correspond to the [ul -element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ul) -and the [li -element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/li), -which MDN helpfully tells us stands for -``{=html}U``{=html}nordered ``{=html}L``{=html}ist and -``{=html}L``{=html}ist ``{=html}I``{=html}tem. - -`h3` is short for "header level 3", and is responsible for the larger -font in the title text, and `div` is a ["content -division"](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/div) -and serves as a useful wrapper for the rest of the content. - -```{=html} - -``` -## Vdom.Attr.t - -An optional argument to the `Vdom.Node.*` constructor functions is a -`Vdom.Attr.t list`. These `Attr.t` correspond to [DOM -attributes](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes), -[DOM -properties](https://developer.mozilla.org/en-US/docs/Web/API/Element#properties), -and [DOM -event_handlers](https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Event_handlers). - -Attributes can be used to tweak the appearance and behavior of the nodes -that they are attached to, for instance, by adding placeholder text to a -textbox: - -```{=html} - -``` -``` ocaml -let input_placeholder : Vdom.Node.t = - Vdom.Node.input ~attrs:[ Vdom.Attr.placeholder "placeholder text here" ] () -;; -``` - -```{=html} - -``` -Or color text with inline css: - -```{=html} - -``` -``` ocaml -let css : Vdom.Node.t = - Vdom.Node.span ~attrs:[ [%css {|color: red;|}] ] [ Vdom.Node.text "this text is red" ] -;; -``` - -```{=html} - -``` -```{=html} - -``` -### Event Handlers - -An important group of `Vdom.Attr.t`s register "event handlers" for user -interaction (like clicking on buttons or typing into a text box). - -They usually receive a browser-level event value (which is almost always -ignored), alongside any useful data extracted from that event. For -example: - -```{=html} - -``` -``` ocaml -val Vdom.Attr.on_click : (Dom_html.mouseEvent Js.t -> unit Effect.t) -> Vdom.Attr.t -val Vdom.Attr.on_input : (Dom_html.event Js.t -> string -> unit Effect.t) -> Vdom.Attr.t -``` - -Here's how we can use `on_click`: - -```{=html} - -``` -``` ocaml -let clicky : Vdom.Node.t = - Vdom.Node.button - ~attrs: - [ Vdom.Attr.on_click (fun (_evt : mouse_event) -> - (* Alerts are generally bad UI; there's an `Effect.print_s` for logging *) - Effect.alert "hello there!") - ] - [ Vdom.Node.text "click me!" ] -;; -``` - -```{=html} - -``` -We'll learn about `Effect.t` --- our abstraction for side effects --- in -[chapter 2](./02-effects.mdx). - -```{=html} - -``` -## The Underlying Machinery - -A virtual-DOM is an immutable tree of immutable data structures that -represents the view of the application at a point in time. This is in -contrast to [the DOM (Document Object -Model)](https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model), -which is a mutable tree of mutable UI elements. - -```{=html} - -``` -When we first compute our `Vdom.Node.t`, `virtual_dom` creates a -matching DOM tree in the browser. On further recomputations, -`virtual_dom` diffs the new virtual-DOM tree against its previous -version, and updates the DOM elements that have changed. Bonsai -schedules this diffing for you, so all you need to worry about is -producing your desired `Vdom.Node.t`. - -```{=html} - -``` -Creating virtual-DOM is much, much cheaper than real DOM, so only -modifying the DOM we need to is a big performance win. But since -virtual-DOM is immutable, doesn't that mean we need to create an entire -new tree every time we recalculate view? That seems scary, but because -Bonsai computes view *incrementally*, and shares work between -subcomputations, we can build pretty big and complicated web apps with -great performance. - -### Diffing Lists - -Diffing vdom produced from dynamic lists can be tricky. Because elements -can move around, be added, or removed, we need to re-diff the entire -list whenever anything in it changes. If we have big lists, this can be -expensive. - -More concerningly, the virtual-DOM diffing algorithm won't associate -list elements with specific DOM nodes: if two elements in an input list -swap places, virtual-DOM will likely patch the two corresponding DOM -nodes to swap their content, instead of swapping the nodes themselves. -This can cause bugs if event listeners aren't properly moved around. - -The `vdom_node_with_map_children` allows you to provide a -`Vdom.Node.t Map.t` instead of a `Vdom.Node.t list`, and will perform -efficient diffing and stable association of input elements to DOM nodes. diff --git a/docs/guide/02-effects.md b/docs/guide/02-effects.md deleted file mode 100644 index 2de4910b..00000000 --- a/docs/guide/02-effects.md +++ /dev/null @@ -1,150 +0,0 @@ -# 02 - Effects - -In the previous chapter, we built a `clicky` button that used -`Effect.alert` and an `on_click` listener attr to show browser alerts -whenever a user clicks a button. - -This chapter explains the `Effect.t` type. - -```{=html} - -``` -## What Is `Effect.t`? - -A `'a Effect.t` encapsulates some side effect, which may execute -asynchronously and eventually produce a value of type `'a`. Common -effects that you'll likely use include: - -- Setting/updating [state](./04-state.mdx) -- Focusing [form elements](../how_to/forms.mdx) -- Invoking [RPCs](../how_to/rpcs.mdx) - -At first glance, `'a Effect.t` looks very similar to [Async's -`'a Deferred.t`](https://dev.realworldocaml.org/concurrent-programming.html). -Both represent potentially asynchronous side effects, and both are -monadic, which means that they have a `bind` operator which evaluates -the side-effects in sequence. - -The main difference between the two is that `Effect` is "pure", meaning -that: - -1. "making" an `Effect.t` doesn't trigger the side-effect. The actual - side-effect isn't performed until the effect is scheduled. -2. scheduling an `Effect.t` multiple times will trigger the side-effect - multiple times. Contrast this with `Deferred.t`, where you can - `bind` on the same deferred twice and it'll only run once. - -Here's a demonstration: - -```{=html} - -``` -``` ocaml -let clickies : Vdom.Node.t = - (* This won't run until scheduled... - But it will run every time it is scheduled! *) - let greet_effect = Effect.alert "hello there!" in - Vdom.Node.div - [ Vdom.Node.button - ~attrs:[ Vdom.Attr.on_click (fun (_evt : mouse_event) -> greet_effect) ] - [ Vdom.Node.text "click me!" ] - ; Vdom.Node.button - ~attrs:[ Vdom.Attr.on_click (fun (_evt : mouse_event) -> greet_effect) ] - [ Vdom.Node.text "or me!" ] - ] -;; -``` - -```{=html} - -``` -## How to Get `Effect.t`s - -Many Bonsai tools and libraries will return some `'a Effect.t`s. For -example: - -- Bonsai's [state primitives](./04-state.mdx) return `Effect.t`s to - modify the state. -- [Rpc_effect](../how_to/rpcs.mdx) return a `'response Effect.t` for - dispatching an RPC call. -- A modal library might return `Effect.t`s that open/close the modal. -- The Effect module contains some [commonly used effects for browser - APIs](../how_to/effects_for_browser_apis.mdx) - -You can also wrap arbitrary side-effectful OCaml functions in -`Effect.t`s: - -```{=html} - -``` -``` ocaml -val Effect.of_sync_fun : ('query -> 'result) -> 'query -> 'result Effect.t -val Effect.of_thunk : (unit -> 'result) -> 'result Effect.t - -val Effect.of_deferred_fun : ('query -> 'response Deferred.t) -> 'query -> 'response Effect.t -val Effect.of_deferred_thunk : (unit -> 'response Deferred.t) -> 'response Effect.t -``` - -These are most useful for authors of reusable components / libraries; -you generally shouldn't use them in app code. - -## How to Compose `Effect.t`s - -The `Effect.t` type is a -[monad](https://builtin.com/software-engineering-perspectives/monads), -which means we can sequence `Effect.t`s with `let%bind`: - -```{=html} - -``` -``` ocaml -let chain_some_effects - (a : int Effect.t) - (b : int -> bool Effect.t) - (c : unit Effect.t) - (d : unit Effect.t) - : unit Effect.t - = - let%bind.Effect a_return = a in - (* Sometimes we don't care about the effect's return value; - we just want to execute it. *) - let%bind.Effect (_ : bool) = b a_return in - let%bind.Effect () = c in - d -;; -``` - -If you don't care about passing anything between effects, and just want -to run them in sequence, there are some utils implemented via `bind`: - -```{=html} - -``` -``` ocaml -val Effect.all_unit : unit Ui_effect.t list -> unit Ui_effect.t -val Effect.all : 'a Ui_effect.t list -> 'a list Ui_effect.t -``` - -There's also an `Effect.Many`, which takes a list of `unit Effect.t`s, -dispatches them in parallel, and does not wait for any of them to -complete. - -## How to Schedule `Effect.t`s - -Most commonly, effects are scheduled in response to user interactions -with the web UI, using `Vdom.Attr.*` event handlers. - -But you can also schedule `Effect.t`s: - -- When [your code becomes active / inactive](../how_to/lifecycles.mdx) -- When an [incremental value - changes](../how_to/edge_triggered_effects.mdx) -- At a [particular time](../how_to/time.mdx) diff --git a/docs/guide/03-incrementality.md b/docs/guide/03-incrementality.md deleted file mode 100644 index 1411a5b3..00000000 --- a/docs/guide/03-incrementality.md +++ /dev/null @@ -1,94 +0,0 @@ -# 03 - Incrementality - -In the last 2 chapters, we learned how to build functional web UIs with -`virtual_dom`, and schedule side effects in response to user interaction -with `Effect.t`. - -For applications with a large amount of frequently-changing input data, -it's important that we only re-compute the parts of the application that -actually depend on the new data. In this chapter, we'll: - -- Learn how to build and compose incremental computations via the - `Bonsai.t` type and `let%arr` operator -- Note that the Directed Acyclical Graph (DAG) of `Bonsai.t`s is - actually static - -## `Bonsai.t` - -Bonsai is all about constructing graphs of incremental nodes. Some of -these are stateful, but most are derived as a function of the current -values of other nodes. A good analogy to help understand Bonsai is that -of the spreadsheet. From our blog post introducing the [Incremental -library](https://blog.janestreet.com/introducing-incremental/): - -> In a spreadsheet, each cell contains either simple data, or an -> equation that describes how the value in this cell should be derived -> from values in other cells. Collectively, this amounts to a -> graph-structured computation, and one of the critical optimizations in -> Excel is that when some of the cells change, Excel only recomputes the -> parts of the graph that depend on those changed cells. - -A `'a Bonsai.t` is a node in the incremental graph, kind of like a cell -in a spreadsheet. - -`val Bonsai.return : 'a -> 'a t` wraps a plain OCaml value in a -`Bonsai.t`. This is like an Excel cell that contains some constant -value. - -To create a new `Bonsai.t` as a function of other `Bonsai.t`s, we can -use the `let%arr` operator. It works just like [`ppx_let`'s -`let%map`](https://blog.janestreet.com/let-syntax-and-why-you-should-use-it/), -but with some extra performance optimizations for pattern matching on -incremental values. This is like a formula cell in Excel. - -```{=html} - -``` -``` ocaml -let int_view (a : int Bonsai.t) : Vdom.Node.t Bonsai.t = - let%arr a = a in - Vdom.Node.div [ Vdom.Node.text (Int.to_string a) ] -;; -``` - -```{=html} - -``` -`let%arr` is just pretty syntax for -`val Bonsai.map : 'a t -> f:('a -> 'b) -> 'b t`. It's ok to use -`Bonsai.map` directly, but `let%arr` is usually more ergonomic, -especially when mapping multiple `Bonsai.t`s together: - -```{=html} - -``` -``` ocaml -let sum_and_display (a : int Bonsai.t) (b : int Bonsai.t) : Vdom.Node.t Bonsai.t = - let%arr a = a - and b = b in - Vdom.Node.textf "%d + %d = %d" a b (a + b) -;; -``` - -```{=html} - -``` -For incrementality to be useful, inputs need to actually change. On to -[Chapter 4: state](./04-state.mdx)! - -## The Underlying Machinery - -`Bonsai.t` is actually a wrapper around [Incremental's -`Incr.t`](https://blog.janestreet.com/introducing-incremental/). The -biggest user-facing difference is that there is no `Bonsai.bind`, which -forces the computation graph to have a static shape. This enables some -[useful features and performance -optimizations](../advanced/why_no_bind.mdx). We'll learn how to write -control flow code without `bind` in a [later -chapter](./05-control_flow.mdx). diff --git a/docs/guide/04-state.md b/docs/guide/04-state.md deleted file mode 100644 index 65bfae61..00000000 --- a/docs/guide/04-state.md +++ /dev/null @@ -1,519 +0,0 @@ -```{=html} - -``` -# 04 - State - -In the previous chapters, we learned how to build and compose -incremental `Bonsai.t` computations via `let%arr`. But we don't yet have -any `Bonsai.t`s that actually change at runtime. We're missing a key -piece: state! - -In this chapter, we'll: - -- Remark on why UI elements should own their state -- Introduce `Bonsai.state`: a simple getter/setter primitive for state -- Emphasize that `let%arr`ing on `Bonsai.t`s does not instantiate - anything -- Explain `Bonsai.state_machine`, which can better model complex state - transition logic, and avoid race condition bugs. -- Emphasize that Bonsai computation graphs have a static shape - -## Why Should Functional Programmers Be Okay With State? - -Many of the tools that functional programmers use for dealing with state -move it out of their programs into a database, or some small "hazmat" -part of the codebase. These strategies can keep most of your code -relatively pure and easy to test, but don't really work well with UIs. - -Most programs produce some single output, updating some global state -through side effects during computation of that output. The view -computed by UIs is actually a structured set of many "leaf" UI elements, -many of which are interactive, and need their own state. - -Explicitly aggregating and distributing this state while composing -elements into a UI would be a nightmare: each one would need to manage -potentially dozens or hundreds of states for each transitive -sub-element. - -Additionally, if state lived outside of UI elements, any implementation -changes that added/removed/changed internal state would be breaking. - -Bonsai's state abstractions provide type-safe wrappers for reading and -changing state, allowing subparts of your UI to own and manage their own -state safely. - -## Simple Getter/Setter State - -The simplest state tool is `Bonsai.state`, which returns a -`'model Bonsai.t` tracking the current value, and a -`('a -> unit Effect.t) Bonsai.t` "setter [effect](./02-effects.mdx)" -producing function. It takes a default starting value and a -`local_ graph`. - -To explore `Bonsai.state`, we'll implement a counter with -increase/decrease buttons. The counter will return a -`Vdom.Node.t Bonsai.t` for the UI, and the current -`count : int Bonsai.t`, which we'll use later. - -```{=html} - -``` -``` ocaml -let counter (local_ graph) : Vdom.Node.t Bonsai.t * int Bonsai.t = - let count, set_count = Bonsai.state 0 graph in - let view = - let%arr count = count - and set_count = set_count in - (* view-construction logic *) - Vdom.Node.div - [ Vdom.Node.button - ~attrs:[ Vdom.Attr.on_click (fun _ -> set_count (count - 1)) ] - [ Vdom.Node.text "-1" ] - ; Vdom.Node.text [%string "Counter value: %{count#Int}"] - ; Vdom.Node.button - ~attrs:[ Vdom.Attr.on_click (fun _ -> set_count (count + 1)) ] - [ Vdom.Node.text "+1" ] - ] - in - view, count -;; -``` - -```{=html} - -``` -## Instantiating State - -To create several counters, we can simply call `counter` repeatedly: - -```{=html} - -``` -``` ocaml -let two_counters (local_ graph) = - let counter1, _count1 = counter graph in - let counter2, _count2 = counter graph in - let%arr counter1 = counter1 - and counter2 = counter2 in - Vdom.Node.div [ counter1; counter2 ] -;; -``` - -```{=html} - -``` -Critically, instances of state are created when the function is called -with `graph`, **not** when you `let%arr` on the resulting `Bonsai.t`s. -So this: - -```{=html} - -``` -``` ocaml -let two_counters_wrong_1 (local_ graph) = - let counter, _count = counter graph in - let%arr counter1 = counter - and counter2 = counter in - Vdom.Node.div [ counter1; counter2 ] -;; -``` - -```{=html} - -``` -is actually the same as: - -```{=html} - -``` -``` ocaml -let two_counters_wrong_2 (local_ graph) = - let counter, _count = counter graph in - let%arr counter = counter in - Vdom.Node.div [ counter; counter ] -;; -``` - -```{=html} - -``` -In both these cases, all 3 counters share the same state, which probably -isn't what you want. - -```{=html} - -``` -## State Machine - -While `Bonsai.state`'s getter/setter pattern is quite useful, sometimes -your web UI's model more closely resembles a state-machine with -well-defined transitions between states. - -There's a tricky bug hidden in our counter: if a user clicks the buttons -twice before Bonsai gets a chance to process the first click, the first -click will be "lost"! This is because the "count" `Bonsai.t` is closed -over by the event handler, so if the button is clicked again before the -new view is computed, the event handler will still have a stale value. - -```{=html} - -``` -There are some tools to deal with stale values at the [Effect.t -level](./02-effects.mdx), but this case is best solved by using -`Bonsai.state_machine0`: - -```{=html} - -``` -``` ocaml -val state_machine0 - : default_model:'model - -> apply_action:('action Bonsai.Apply_action_context.t -> 'model -> 'action -> 'model) - -> local_ Bonsai.graph - -> 'model Bonsai.t * ('action -> unit Effect.t) Bonsai.t -``` - -```{=html} - -``` -Compared to `Bonsai.state`, there are several similarities: - -1. The default model is required. -2. State is instantiated by passing in a `local_ graph`. -3. The return value is a tuple of `Bonsai.t`s that provides the current - state alongside a function which schedules changes to the state. - -The main difference is `apply_action`, which is the "state transition" -function of the state machine: "given the current model and an action, -produce a new model." The output also changes: instead of a "setter -effect" function, we get a function that takes an `Action.t` and -produces an `unit Effect.t` to "inject" it into our state machine. - -So how would we use `state_machine0` to fix the bug in the counter -application? - -```{=html} - -``` -``` ocaml -let counter_state_machine (local_ graph) : Vdom.Node.t Bonsai.t * int Bonsai.t = - let count, inject = - Bonsai.state_machine0 - ~default_model:0 - ~apply_action:(fun (_ : _ Bonsai.Apply_action_context.t) model action -> - match action with - | `Increment -> model + 1 - | `Decrement -> model - 1) - graph - in - let view = - let%arr count = count - and inject = inject in - Vdom.Node.div - [ Vdom.Node.button - ~attrs:[ Vdom.Attr.on_click (fun _ -> inject `Decrement) ] - [ Vdom.Node.text "-1" ] - ; Vdom.Node.text [%string "Counter value: %{count#Int}"] - ; Vdom.Node.button - ~attrs:[ Vdom.Attr.on_click (fun _ -> inject `Increment) ] - [ Vdom.Node.text "+1" ] - ] - in - view, count -;; -``` - -```{=html} - -``` -Now, when a button is clicked multiple times in quick succession, -instead of calling `set_state` multiple times with the same value, -Bonsai will call `inject` multiple times, and they'll be processed by -`apply_action` in order, producing the correct result. - -### State Machines with Inputs - -What if we wanted to increment / decrement our count by some dynamic -`step : int Bonsai.t`? Our first attempt might look like this: - -``` ocaml -# let counter_state_machine ~(step : int Bonsai.t) (local_ graph) = - let count, inject = - Bonsai.state_machine0 - ~default_model:0 - ~apply_action:(fun (_ : _ Bonsai.Apply_action_context.t) model action -> - let%arr step = step in - match action with - | `Increment -> model + step - | `Decrement -> model - step) - graph - in - let view = - let%arr count = count - and inject = inject in - Vdom.Node.div - [ Vdom.Node.button - ~attrs:[ Vdom.Attr.on_click (fun _ -> inject `Decrement) ] - [ Vdom.Node.text "-1" ] - ; Vdom.Node.text [%string "Counter value: %{count#Int}"] - ; Vdom.Node.button - ~attrs:[ Vdom.Attr.on_click (fun _ -> inject `Increment) ] - [ Vdom.Node.text "+1" ] - ] - in - view, count -Lines 6-9, characters 9-37: -Error: This expression has type int Bonsai.t - but an expression was expected of type int -``` - -Unfortunately, the compiler doesn't like that. Recall that -`apply_action` for `Bonsai.state_machine0` produces a `'model`, not a -`'model Bonsai.t`. Instead, we'll need some heavier machinery. - -`state_machine0` has a "0" at the end to indicate that it takes "0" -additional inputs. There's also a `state_machine1`, which allows -`apply_action` to depend on the current value of a `Bonsai.t`: - -``` diff --val state_machine0 -+val state_machine1 - : default_model:'model - -> apply_action: - ('action Apply_action_context.t -+ -> 'input Computation_status.t - -> 'model - -> 'action - -> 'model) -+ -> 'input Bonsai.t - -> local_ graph - -> 'model Bonsai.t * ('action -> unit Effect.t) Bonsai.t -``` - -```{=html} - -``` -Let's take `step` as an input and update our implementation to use -`state_machine1`: - -```{=html} - -``` -``` ocaml -let counter_state_machine1 ~(step : int Bonsai.t) (local_ graph) = - let count, inject = - Bonsai.state_machine1 - ~default_model:0 - ~apply_action:(fun (_ : _ Bonsai.Apply_action_context.t) input model action -> - match input with - | Bonsai.Computation_status.Inactive -> - (* This state machine is inactive, so it can't access the current value of [input]. - Just keep the original model *) - model - | Active step -> - (match action with - | `Increment -> model + step - | `Decrement -> model - step)) - step - graph - in - let view = - let%arr step = step - and count = count - and inject = inject in - Vdom.Node.div - [ Vdom.Node.button - ~attrs:[ Vdom.Attr.on_click (fun _ -> inject `Decrement) ] - [ Vdom.Node.text [%string "-%{step#Int}"] ] - ; Vdom.Node.text [%string "Counter value: %{count#Int}"] - ; Vdom.Node.button - ~attrs:[ Vdom.Attr.on_click (fun _ -> inject `Increment) ] - [ Vdom.Node.text [%string "+%{step#Int}"] ] - ] - in - view, count -;; -``` - -We can even chain our counters together! One counter's `count` can be -used as another counter's `step`, making what can only be described as a -frankencounter: - -```{=html} - -``` -``` ocaml -let counter_state_machine_chained (local_ graph) = - let counter1, count1 = counter_state_machine1 ~step:(Bonsai.return 1) graph in - let counter2, count2 = counter_state_machine1 ~step:count1 graph in - let counter3, _ = counter_state_machine1 ~step:count2 graph in - let%arr counter1 = counter1 - and counter2 = counter2 - and counter3 = counter3 in - Vdom.Node.div [ counter1; counter2; counter3 ] -;; -``` - -```{=html} - -``` -There is no `state_machine2` (or n), because multiple inputs could be -packaged together as a single `Bonsai.t`, and destructured inside -`apply_action`. - -### State Machines can Schedule Effects - -The `apply_action` function also receives an `Apply_action_context.t`, -which can - -1. schedule arbitrary `unit Effect.t`s via - `Apply_action_context.schedule_event` -2. dispatch other actions into itself with - `Apply_action_context.inject` - -This is necessary for any state machine that wants to send messages to -itself, e.g. when dealing with timeouts. - -It's also useful for stitching together components that talk to each -other. - -```{=html} -``` -```{=html} -``` -## Other State Primitives - -Bonsai has some other tools for state, such as `Bonsai.state_opt`, -`Bonsai.toggle`, and `Bonsai.actor`. You should read the [API -Docs](https://github.com/janestreet/bonsai/blob/master/src/bonsai.mli) -to learn more. - -All Bonsai state primitives also take an optional `reset` argument, -which allows you to control what happens when [state is -reset](../how_to/resetting_state.mdx). - -```{=html} -``` -Let's continue to [Bonsai Guide Part 5: Control -Flow](./05-control_flow.mdx). - -## The Underlying Machinery - -### `local_ graph` is a Graph Builder - -`local graph : Bonsai.graph` is used by Bonsai to build a -[static](../advanced/why_no_bind.mdx) computation graph. Most nodes in -the graph come from `let%arr` calls, but many "leaves" of the graph are -"state" nodes. At startup, Bonsai aggregates the entire state of your -app. - -The [`local_` -mode](https://blog.janestreet.com/oxidizing-ocaml-locality/) prevents -`graph` from being closed over / stashed away, so the compiler makes it -impossible to change the computation graph from any runtime code. - -Bonsai analyzes the entire computation at startup time and performs -optimizations to make apps faster! - -```{=html} - -``` -### Most Code Runs Once! - -When writing Bonsai code, you're actually doing 2 different things: - -- Defining the static computation graph; i.e. *what* is computed, and - *which* inputs it has. -- Dictating runtime behavior of the web app; i.e. *how* it is - computed. - -Only the contents of `let%arr` blocks (everything after the `in`), -`apply_action` state transition functions, and functions used to -construct [effects](./02-effects.mdx) are "runtime" code. Everything -else only runs *exactly once* at app startup to construct the -computation graph, before it gets compiled to a `Vdom.Node.t Incr.t`. diff --git a/docs/guide/05-control_flow.md b/docs/guide/05-control_flow.md deleted file mode 100644 index f5edfca5..00000000 --- a/docs/guide/05-control_flow.md +++ /dev/null @@ -1,352 +0,0 @@ -```{=html} - -``` -# 05 - Control Flow - -In [chapter 3](./03-incrementality.mdx), we learned how to build and -compose a static graph of incremental `Bonsai.t`s using the `let%arr` -operator. But often, web UIs need to express some dynamic patterns, and -`let%arr` just isn't enough. In this chapter, we'll: - -- Use `match%sub` to conditionally evaluate `Bonsai.t`s -- Evaluate a collection of `Bonsai.t`s separately for each of a - dynamically-sized number of inputs -- Learn what it means for a `Bonsai.t` to be "active" vs "inactive" -- Remark on higher-order functions in Bonsai - -## `match%sub` - -Let's say we want to show the counter we built in [the state -chapter](./04-state.mdx) only when `show: bool Bonsai.t` is true. With -the tools we have today, we might write: - -```{=html} - -``` -``` ocaml -let maybe_show_naive show (local_ graph) = - let counter = counter ~step:(return 1) graph in - let%arr counter = counter - and show = show in - match show with - | false -> Vdom.Node.none - | true -> counter -;; -``` - -```{=html} - -``` -But because we are `let%arr`-ing on `counter`, the incremental runtime -will continuously recompute it, even when we aren't actually using it. - -### Conditional Recomputation - -We can avoid this and get a performance boost using Bonsai's -`match%sub`: - -```{=html} - -``` -``` ocaml -let maybe_show show (local_ graph) = - let counter = counter ~step:(return 1) graph in - match%sub show with - | false -> Bonsai.return Vdom.Node.none - | true -> counter -;; -``` - -```{=html} - -``` -`match%sub` is like `match`, but for `Bonsai.t`s: - -1. The matched value should be a `'a Bonsai.t` or a literal tuple of - `Bonsai.t`s. -2. The values produced by each of the match-arms must be of type - `'b Bonsai.t`. -3. Any identifiers bound during matching are available as - `'c Bonsai.t`s inside the arms. (You can access them as plain `'c` - in guard clauses though.) -4. The overall type of the `match%sub` expression has type - `'b Bonsai.t`. - -### Conditional Instantiation - -`match%sub` has a superpower: you can use `graph` inside its arms. This -means we can instantiate some state that is local to one arm: - -```{=html} - -``` -``` ocaml -let maybe_show_2 show (local_ graph) = - match%sub show with - | `Count_by_1 -> counter ~step:(return 1) graph - | `Count_by_2 -> counter ~step:(return 2) graph - | `No -> Bonsai.return Vdom.Node.none -;; -``` - -```{=html} - -``` -Note that each branch has an independent counter with its own state. -You'll see this if you increment the first counter and then switch to -the second. - -Interestingly, state does not go away when a branch ceases to be active: -as we noted [last chapter](./04-state.mdx), this is because Bonsai -maintains a central copy of the entire application state. - -```{=html} - -``` -### Conditional Data Dependencies - -We can also use `match%sub` to pattern-match just like regular `match`, -allowing us to conditionally access data: - -```{=html} - -``` -``` ocaml -let maybe_show_var show (local_ graph) = - match%sub show with - | `Count_by step -> counter ~step graph - | `No -> Bonsai.return Vdom.Node.none -;; -``` - -```{=html} - -``` -Note that all cases of `Count_by`, share the same counter state. That's -because they all go to the same branch of the `match%sub`. If we wanted -to create separate versions of state for individual cases of `step`, we -could use guard clauses to create multiple branches that match the same -pattern, each with their own locally instantiated state: - -```{=html} - -``` -``` ocaml -let maybe_show_var_guard show (local_ graph) = - match%sub show with - | `Count_by step when Int.equal step 1 -> counter ~step graph - | `Count_by step when Int.equal step 4 -> counter ~step graph - | `Count_by step -> counter ~step graph - | `No -> Bonsai.return Vdom.Node.none -;; -``` - -```{=html} - -``` -This particular case is pretty silly: we're not going to write separate -`match%sub` branches for every potential value of `int`. Instead, we -could use [`scope_model`](../how_to/state_per_key.mdx), which maintains -separate copies of state for some value of a key: - -```{=html} - -``` -``` ocaml -let maybe_show_var_scope_model show (local_ graph) = - match%sub show with - | `Count_by step -> - Bonsai.scope_model - (module Int) - ~on:step - ~for_:(fun (local_ graph) -> counter ~step graph) - graph - | `No -> Bonsai.return Vdom.Node.none -;; -``` - -```{=html} - -``` -## Creating a Dynamic Number of `Bonsai.t`s - -In the [last chapter](./04-state.mdx), we created two separate counters -by calling `counter_ui graph` twice. But what if we want to create `n` -counters, where `n` is an `int Bonsai.t` that can change at runtime? - -Let's try to build this with the tools we have: - -``` ocaml -# let multiple_counters (n : int Bonsai.t) (local_ graph) = - let%arr n = n in - let (counters : Vdom.Node.t Bonsai.t list) = - List.init n ~f:(fun _ -> State_examples.counter_ui graph) - in - let%arr counters = Bonsai.all counters in - Vdom.Node.div counters -Line 4, characters 56-61: -Error: The value graph is local, so cannot be used inside a closure that might escape. -Hint: The closure might escape because it is an argument to a tail call -``` - -As you can see above, this won't even compile: the content of `let%arr` -blocks is runtime code, so the `local_` mode bans you from using `graph` -within them. Furthermore, if this code compiled, the output *would* have -type `Vdom.Node.t Bonsai.t Bonsai.t`, which is illegal: remember, the -Bonsai computation graph has to be static. - -Instead, we can use Bonsai's `assoc` primitive: - -```{=html} - -``` -``` ocaml -val assoc - : ('k, 'cmp) Bonsai.comparator - -> ('k, 'v, 'cmp) Map.t Bonsai.t - -> f:('k Bonsai.t -> 'v Bonsai.t -> local_ Bonsai.graph -> 'result Bonsai.t) - -> local_ Bonsai.graph - -> ('k, 'result, 'cmp) Map.t Bonsai.t -``` - -Bonsai evaluates the body of `f` exactly once when your app starts to -produce a `'v Incr.t -> 'result Incr.t` function. This new function is -then applied to each key/value pair in the input map when your app runs, -and to any new keys added to the input map. - -```{=html} - -``` -Each key/value pair in the output map has its own independent state and -dependencies. This means that if the input map is 100,000 elements -large, but only one of the keys has data that is changing frequently, -only that key's instance will be re-run to recompute the overall output. - -Here's an example, which will make multiple copies of the counter we -implemented [last chapter](./04-state.mdx): - -```{=html} - -``` -``` ocaml -let multiple_counters (input : unit Int.Map.t Bonsai.t) (local_ graph) = - let counters = - Bonsai.assoc - (module Int) - input - ~f:(fun key (_ : unit Bonsai.t) (local_ graph) -> - let%arr key = key - and counter = State_examples.counter_ui graph in - Vdom.Node.tr - [ Vdom.Node.td [ Vdom.Node.textf "counter #%d:" key ] - ; Vdom.Node.td [ counter ] - ]) - graph - in - let%arr counters = counters in - Vdom.Node.table (Map.data counters) -;; -``` - -Let's try it out! - -```{=html} - -``` -``` ocaml -let multiple_counters_dynamic graph = - let counter_view, n = State_examples.counter ~step:(Bonsai.return 1) graph in - let map_containing_n_entries = - let%arr n = n in - if n <= 0 - then Int.Map.empty - else List.init n ~f:(fun i -> i, ()) |> Int.Map.of_alist_exn - in - let%arr counter_view = counter_view - and table = multiple_counters map_containing_n_entries graph in - Vdom.Node.div [ counter_view; table ] -;; -``` - -```{=html} - -``` -Note that if you add, remove, and re-add a counter, it will retain its -state. - -```{=html} - -``` -## Further Reading - -- `match%sub` and `Bonsai.assoc` are [higher-order - functions](../how_to/higher_order_functions.mdx) -- The code inside `match%sub` branches or `assoc` can [become - inactive](../how_to/lifecycles.mdx). diff --git a/docs/how_to/css.md b/docs/how_to/css.md deleted file mode 100644 index 58c7ca72..00000000 --- a/docs/how_to/css.md +++ /dev/null @@ -1,338 +0,0 @@ -# Css: Styling Your UI - -"Styling" determines the layout and presentation of a web application. - -```{=html} - -``` -## Inline PPX CSS - -`ppx_css` allows you to write CSS in your `.ml` files, automatically -loading your styles into the page. - -If you had a `
..
`, it's easy to imagine -accidentially writing CSS rules for `.button` multiple times across the -codebase. To avoid this, `ppx_css` will generate unique selectors that -will not collide. - -Using `[%css {|CSS_PROPERTIES|}]` returns a `Vdom.Attr.t`, which will -add a newly created CSS class with the given styles to the `Vdom.Node.t` -it is attached to: - -```{=html} - -``` -``` ocaml -let view = - Vdom.Node.div - ~attrs: - [ [%css - {| - background-color: tomato; - min-width: 2rem; - min-height: 2rem; - |}] - ] - [ Vdom.Node.text "Very Red Background" ] -;; -``` - -```{=html} - -``` -It also supports string interpolation, [similar to -`ppx_string`](https://github.com/janestreet/ppx_string): - -```{=html} - -``` -``` ocaml -let box_with_border (color : Css_gen.Color.t) (width : Css_gen.Length.t) = - Vdom.Node.div - ~attrs: - [ [%css - {| - border: %{width#Css_gen.Length} solid %{color#Css_gen.Color}; - |}] - ] - [ Vdom.Node.text "Nice Borders!" ] -;; -``` - -```{=html} - -``` -While you can interpolate raw `string`s, it is highly recommended to use -the types in the `css_gen` library instead. - -You can also use pseudo-selectors, since ppx_css supports [nested -css](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_nesting/Using_CSS_nesting). - -```{=html} - -``` -``` ocaml -let hoverable_blocks = - let block = - Vdom.Node.div - ~attrs: - [ [%css - {| - background-color: green; - min-width: 2rem; - min-height: 2rem; - border: 1px solid black; - - &:hover { - background-color: tomato; - } - - &:not(:nth-child(odd)):hover { - background-color: purple; - } - |}] - ] - [ Vdom.Node.text "Hoverable" ] - in - Vdom.Node.div (List.init 6 ~f:(fun _ -> block)) -;; -``` - -```{=html} - -``` -You can split up your styling into a bunch of `[%css {||}]` calls: - -```{=html} - -``` -``` ocaml -let multiple_ppx_css = - Vdom.Node.div - ~attrs:[ [%css {|color: red|}] ] - [ Vdom.Node.text "Foo" - ; Vdom.Node.div ~attrs:[ [%css "color: blue"] ] [ Vdom.Node.text "Bar" ] - ] -;; -``` - -```{=html} - -``` -This helps avoid a lot of unintentional dependencies on the structure of -your HTML, though [inherited CSS -properties](https://web.dev/learn/css/inheritance) will still cause some -dependencies. - -### Downsides - -The inline `ppx_css` expression syntax is nice and concise, but but does -not support 1. -[Layers](https://developer.mozilla.org/en-US/docs/Web/CSS/@layer) 2. -[media blocks](https://developer.mozilla.org/en-US/docs/Web/CSS/@media) -3. other -[at-rules](https://developer.mozilla.org/en-US/docs/Web/CSS/At-rule) - -## PPX CSS Stylesheets - -For more complex rules, `[%css stylesheet {|MULTIPLE_CSS_RULES_HERE|}]` -accepts an entire style sheet and produces a module containing acessors -for the ids, classes, and variables defined within. - -```{=html} - -``` -``` ocaml -module Style = - [%css - stylesheet - {| -@media only screen and (max-width: 300px) { - .container { - display: none; - } -} - -@media only screen and (min-width: 300px) { - .container { - font-size: 10px; - } -} - -@media only screen and (min-width: 600px) { - .container { - font-size: 20px; - } -} - |}] - -let stylesheet_demo = Vdom.Node.div ~attrs:[ Style.container ] [ Vdom.Node.text "Hello" ] -``` - -```{=html} - -``` -It supports interpolation, just like the inline `[%css {||}]`: - -```{=html} - -``` -``` ocaml -let stylesheet_interpol small_bg large_bg = - let module Style = - [%css - stylesheet - {| -@media only screen and (max-width: 1200px) { - .container { - background-color: %{small_bg#Css_gen.Color}; - } -} - -@media only screen and (min-width: 1200px) { - .container { - background-color: %{large_bg#Css_gen.Color}; - } -} - |}] - in - Vdom.Node.div ~attrs:[ Style.container ] [ Vdom.Node.text "Hello" ] -;; -``` - -```{=html} - -``` -All classnames used in `[%css stylesheet {||}]` blocks will be "hashed" -to avoid naming collisions, so in the example above, `.container` will -actually be something like `.container_hash_099cf63fc3`. There exist -[config options](https://github.com/janestreet/ppx_css) to exclude some -identifiers from hashing. - -### CSS Variables - -If any `var(...)`s are used in a `[%css stylesheet {||}]` block, a -setter will be made available through a `Style.Variables` module: - -```{=html} - -``` -``` ocaml - module Style = - [%css - stylesheet - {| -@media only screen and (max-width: 1200px) { - .container { - background-color: var(--small-bg); - } -} - -@media only screen and (min-width: 1200px) { - .container { - background-color: var(--large-bg); - } -} -|}] - - let stylesheet_vars = - Vdom.Node.div - ~attrs: - [ Style.container; Style.Variables.set_all ~large_bg:"green" ~small_bg:"purple" ] - [ Vdom.Node.text "Hello" ] - ;; -``` - -```{=html} - -``` -Just like classnames, CSS variable names in `[%css stylesheet {||}]` -blocks will be hashed to avoid collisions. - -We strongly recommend interpolation over direct variables for several -reasons: - -- It's easy to forget to call `Variables.set_all`, or call it - somewhere you wouldn't expect to find it. -- Interpolation encourages use of the `Css_gen.*` types - -Interpolation is implemented via CSS variables, so there is no -performance downside. - -## Inline Styles - -There is an API for setting inline styles on vdom nodes directly with -`Vdom.Attr.style`: - -```{=html} - -``` - val Vdom.Attr.style : Css_gen.t -> Vdom.Attr.t - -We've already seen the `Css_gen` library, but until now have just been -using it to for its stringification functions to use with `ppx_css`'s -string interpolation syntax. But there are also constructors for making -`Css_gen.t`s, which can be combined via the `@>` operator: - -```{=html} - -``` -``` ocaml -let css_gen_inline = - let style : Css_gen.t = - let open Css_gen in - font_size (`Px 15) @> background_color (`Name "red") - in - Vdom.Node.div ~attrs:[ Vdom.Attr.style style ] [ Vdom.Node.text "Hello" ] -;; -``` - -```{=html} - -``` -This is much more verbose and hard to read than `ppx_css`, and is -missing many css attributes. It also doesn't support pseudo-selectors. - -# Conclusion - -When writing css, we recommend inline `[%css {||}]` attrs, falling back -to `[%css stylesheet {||}]` blocks or `.css` files if you need the -support for at-rules. - -Try to avoid use of `Css_gen` inline styles unless absolutely necessary. diff --git a/docs/how_to/edge_triggered_effects.md b/docs/how_to/edge_triggered_effects.md deleted file mode 100644 index 24e49131..00000000 --- a/docs/how_to/edge_triggered_effects.md +++ /dev/null @@ -1,44 +0,0 @@ -# Edge-Triggered Effects - -`Bonsai.Edge.on_change'` allows us to run some dynamically-computed -`Effect.t` whenever some `'a Bonsai.t` changes, and when it is first -calculated: - -```{=html} - -``` -``` ocaml -let on_change_demo (local_ graph) = - let view, value = State_examples.counter ~step:(Bonsai.return 1) graph in - Bonsai.Edge.on_change' - ~equal:Int.equal - ~callback: - (Bonsai.return (fun (prev_value : int option) (new_value : int) -> - match prev_value with - | None -> (* Do nothing on first render*) Effect.Ignore - | Some prev_value -> - Effect.alert - [%string "prev value: %{prev_value#Int}, new value: %{new_value#Int}"])) - value - graph; - view -;; -``` - -```{=html} - -``` -The `~equal` argument allows us to define what "change" means. - -Another category of edge-triggering is the `on_activate` and -`on_deactivate` [lifecycles](./lifecycles.mdx). - -### Downsides to Using `Edge` - -Declarative programs are easy to reason about and test. Extensive use of -the `Edge` module will make your program less and less declarative. As a -general rule, you should try to avoid using `Edge` if other, more -declarative solutions exist. diff --git a/docs/how_to/effects_and_stale_values.md b/docs/how_to/effects_and_stale_values.md deleted file mode 100644 index 06b75ae9..00000000 --- a/docs/how_to/effects_and_stale_values.md +++ /dev/null @@ -1,99 +0,0 @@ -# Effects and Stale Values - -Let's say we have some state, which is used to calculate some -`Bonsai.t`. This calculation might: - -1. Combine the input with other inputs -2. Send an RPC based on the input, and return the result -3. Do some expensive and complicated incremental transformations - -How do we build an effect that sets the state, and then does something -with the result of step 3 (the expensive transformation)? Let's try! - -```{=html} - -``` -``` ocaml -let set_and_run_effect_naive (other_input : string Bonsai.t) (local_ graph) = - let count, set_state = Bonsai.state 0 graph in - let computed = complicated_transformation other_input count in - let set_and_alert = - let%arr computed = computed - and set_state = set_state in - fun new_state -> - let%bind.Effect () = set_state new_state in - Effect.alert computed - in - let%arr count = count - and set_and_alert = set_and_alert in - Vdom.Node.div - [ Vdom.Node.text [%string "Counter value: %{count#Int}"] - ; Vdom.Node.button - ~attrs:[ Vdom.Attr.on_click (fun _ -> set_and_alert (count + 1)) ] - [ Vdom.Node.text "increment count" ] - ] -;; -``` - -```{=html} - -``` -Looks great! Except that it's wrong. The alert shows the *old* value of -`computed`. - -Why? When we click the button, we schedule the `set_and_alert` effect. -That effect closes over the value of `computed` from before the button -was clicked. So even though `computed` is recalculated when -`set_state new_state` runs, we are stuck with an outdated `computed`. - -What we'd really like to do is "peek" at the current value of `computed` -instead of having to close over it. Cue `Bonsai.peek`: - -```{=html} - -``` -``` ocaml -val peek : 'a Bonsai.t -> local_ Bonsai.graph -> 'a Computation_status.t Effect.t Bonsai.t -``` - -```{=html} - -``` -``` ocaml -let set_and_run_effect_peek (other_input : string Bonsai.t) (local_ graph) = - let count, set_state = Bonsai.state 0 graph in - let computed = complicated_transformation other_input count in - let peek_computed = Bonsai.peek computed graph in - let set_and_alert = - let%arr peek_computed = peek_computed - and set_state = set_state in - fun new_state -> - let%bind.Effect () = set_state new_state in - match%bind.Effect peek_computed with - | Active computed -> Effect.alert computed - | Inactive -> Effect.Ignore - in - let%arr count = count - and set_and_alert = set_and_alert in - Vdom.Node.div - [ Vdom.Node.text [%string "Counter value: %{count#Int}"] - ; Vdom.Node.button - ~attrs:[ Vdom.Attr.on_click (fun _ -> set_and_alert (count + 1)) ] - [ Vdom.Node.text "increment count" ] - ] -;; -``` - -```{=html} - -``` -If the `peek` backing an effect is in an [inactive code -branch](./lifecycles.mdx), it will be unable to retrieve a fresh value, -so it returns values wrapped in a `Computation_status.t`, just like -[`state_machine1`'s `input`](../guide/04-state.mdx). diff --git a/docs/how_to/effects_for_browser_apis.md b/docs/how_to/effects_for_browser_apis.md deleted file mode 100644 index 65b23fc6..00000000 --- a/docs/how_to/effects_for_browser_apis.md +++ /dev/null @@ -1,33 +0,0 @@ -# Effects for Browser APIs - -The `Bonsai_web.Effect` module contains some effects providing -abstractions over browser APIs. - -## Responding to DOM Events - -The following are useful in event handlers: - -- `val Effect.Prevent_default: unit Effect.t` -- `val Effect.Stop_propagation : unit Effect.t` -- `val Effect.Stop_immediate_propagation : unit Effect.t`; if using - this, you'll need to compose effects with - `Effect.sequence_as_sibling`, not `Effect.all`. - -If composing these with other effects, these **must** run first, because -if one of your other effects is asynchronous, these won't run in time to -modify the DOM event. - -You must compose them with other effects using `Effect.Many`; you cannot -sequence effects after them using `let%bind`. -``{=html} - -## Miscellaneous - -There are also helper functions for some common DOM operations you might -want to do in an `Effect.t`: - -- `val Effect.print_s : Sexp.t -> unit Effect.t` -- `val Effect.alert : string -> unit Effect.t` -- `val Effect.reload_page : unit Effect.t` -- `module Effect.Focus` contains some useful tools for focusing DOM - elements via effect. diff --git a/docs/how_to/forms.md b/docs/how_to/forms.md deleted file mode 100644 index bcc1d712..00000000 --- a/docs/how_to/forms.md +++ /dev/null @@ -1,393 +0,0 @@ -```{=html} -``` -# Forms - -Bonsai has an entire library dedicated to building and combining forms -called `bonsai_web_ui_form`. - -For the rest of this doc, this module alias will be in effect: - -```{=html} - -``` -``` ocaml -module Form = Bonsai_web_ui_form.With_automatic_view -``` - -# Form.t - -The primary type in the forms library is `'a Form.t`. A value of type -`'a Form.t` represents the state of a form at one particular instant in -time, where the form in question can edit values of type `'a`. - -Because of the inherently stateful nature of form UIs, form building -functions take `(local_ graph)`. For example, a textbox form element -that produces strings has type: - -```{=html} - -``` -``` ocaml -val Form.Elements.Textbox.string : unit -> local_ Bonsai.graph -> string Form.t Bonsai.t -``` - -And the type for a checkbox that produces bools has this type: - -```{=html} - -``` -``` ocaml -val Form.Elements.Checkbox.bool - : ?extra_attrs:Vdom.Attr.t list Value.t - -> default:bool - -> unit - -> local_ Bonsai.graph - -> bool Form.t Bonsai.t -``` - -There are three primary operations that can be performed on a -`'a Form.t` - -1. Extract the current value: - `val Form.value: 'a Form.t -> 'a Or_error.t` -2. Compute the view: `val Form.view_as_vdom: 'a Form.t -> Vdom.Node.t` -3. Set the value of the form: - `val Form.set: 'a Form.t -> 'a -> unit Vdom.Effect.t` - -```{=html} - -``` -Let's look at them all in action, using the textbox from above as an -example: - -## Form.value - -The "value" of a `'a Form.t` is the current output of the form as filled -in by the user. For a simple textbox, that value would be `string`, but -most forms are compositions of subforms, so the produced value can be a -record or variant. - -In the following example, the value of a textbox is extracted and -printed as a sexp: - -```{=html} - -``` -``` ocaml -let textbox_value (local_ graph) = - let textbox = - Form.Elements.Textbox.string ~allow_updates_when_focused:`Always () graph - in - let%arr textbox = textbox >>| Form.label "my textbox" in - let value = Form.value textbox in - Vdom.Node.div - [ Form.view_as_vdom textbox - ; Vdom.Node.sexp_for_debugging ([%sexp_of: string Or_error.t] value) - ] -;; -``` - -```{=html} - -``` -Forms returning their values inside of an `Or_error.t` may be surprising -at first, but in practice, more complex forms are fallible, either -because form validation for an element has failed, or because a large -form is only partially filled out. By making the assumption that *all* -forms are fallible, the rest of the API is simpler. - -## Form.view_as_vdom - -`view_as_vdom` renders the form into a `Vdom.Node.t`. However, it also -has an optional parameter that makes submitting the form easier. Its -full type signature is: - -```{=html} - -``` -``` ocaml -module Submit : sig - type 'a t - - val create - : ?handle_enter:bool - -> ?button:string option - -> f:('a -> unit Ui_effect.t) - -> unit - -> 'a t -end - -val view_as_vdom : ?on_submit:'a Submit.t -> 'a t -> Vdom.Node.t -``` - -Because forms are frequently paired with a "submit" button, the optional -submission options provide an easy way to submit the form, with the `f` -field being called with the value of the fully-validated form. The two -options for submitting the form are - -1. `handle_enter`, when `true` will cause the form to be submitted - whenever the `` key is pressed while focusing on a form - element inside this form. -2. `button`, if `Some`, a button with the provided text will be added - to the form. This button will be disabled whenever the form is in an - error state, but when the form is valid, the button will be enabled - and will trigger the submission function when pressed. - -```{=html} - -``` -``` ocaml -let textbox_on_submit (local_ graph) = - let textbox = - Form.Elements.Textbox.string ~allow_updates_when_focused:`Always () graph - in - let%arr textbox = textbox in - textbox - |> Form.label "text to alert" - |> Form.view_as_vdom ~on_submit:(Form.Submit.create () ~f:alert) -;; -``` - -```{=html} - -``` -## Form.set - -Setting the contents of a form is a rarer requirement. Most forms are -read-only (the user is the only one filling it out), but sometimes, a -form should be modified by the program, perhaps to initialize the form -in a specific state. - -```{=html} - -``` -``` ocaml -let form_set (local_ graph) = - let textbox = - Form.Elements.Textbox.string ~allow_updates_when_focused:`Always () graph - in - let%arr textbox = textbox >>| Form.label "my textbox" in - Vdom.Node.div - [ Form.view_as_vdom textbox - ; Vdom.Node.button - ~attrs:[ Vdom.Attr.on_click (fun _ -> Form.set textbox "hello world") ] - [ Vdom.Node.text "click me" ] - ] -;; -``` - -```{=html} - -``` -# Combinators - -Most forms contain many input elements, and Bonsai-Forms comes with a -set of combinators for combining many smaller subforms into a larger -form. - -For this example, we'll build a form for the following type: - -```{=html} - -``` -``` ocaml -type t = - { some_string : string - ; an_int : int - ; on_or_off : bool - } -[@@deriving typed_fields, sexp_of] -``` - -Building a form that produces values of this type requires the use of -[`ppx_typed_fields`](https://github.com/janestreet/ppx_typed_fields), -which you'll need to add to your jbuild. Deriving `typed_fields` will -make a module named `Typed_field` containing a type with a constructor -representing each field in the record it was derived on. - -```{=html} - -``` -``` ocaml -module Record = struct - type t = - { some_string : string - ; an_int : int - ; on_or_off : bool - } - [@@deriving typed_fields, sexp_of] - - let form : local_ Bonsai.graph -> t Form.t Bonsai.t = - Form.Typed.Record.make - (module struct - (* reimport the module that typed_fields just derived *) - module Typed_field = Typed_field - - let label_for_field = `Inferred - - (* provide a form computation for each field in the record *) - let form_for_field - : type a. a Typed_field.t -> local_ Bonsai.graph -> a Form.t Bonsai.t - = - fun typed_field (local_ graph) -> - match typed_field with - | Some_string -> - Form.Elements.Textbox.string ~allow_updates_when_focused:`Always () graph - | An_int -> - Form.Elements.Number.int - ~default:0 - ~step:1 - ~allow_updates_when_focused:`Always - () - graph - | On_or_off -> Form.Elements.Checkbox.bool ~default:false () graph - ;; - end) - ;; -end -``` - -We can also do the same for variants with `[@@deriving typed_variants]`: - -```{=html} - -``` -``` ocaml -module Variant = struct - type t = - | A - | B of int - | C of string - [@@deriving typed_variants, sexp_of] - - let form : local_ Bonsai.graph -> t Form.t Bonsai.t = - Form.Typed.Variant.make - (module struct - (* reimport the module that typed_fields just derived *) - module Typed_variant = Typed_variant - - let label_for_variant = `Inferred - let initial_choice = `First_constructor - - (* provide a form computation for constructor in the variant *) - let form_for_variant - : type a. a Typed_variant.t -> local_ Bonsai.graph -> a Form.t Bonsai.t - = - fun typed_field (local_ graph) -> - match typed_field with - | A -> Form.return () |> Bonsai.return - | B -> Form.Elements.Textbox.int ~allow_updates_when_focused:`Always () graph - | C -> Form.Elements.Textbox.string ~allow_updates_when_focused:`Always () graph - ;; - end) - ;; -end -``` - -Finally, using this new form and printing the results: - -```{=html} - -``` -``` ocaml -let view_for_form : local_ Bonsai.graph -> Vdom.Node.t Bonsai.t = - fun graph -> - let%arr record_form = Record.form graph - and variant_form = Variant.form graph in - let form = Form.both record_form variant_form in - let value = Form.value form in - Vdom.Node.div - [ Form.view_as_vdom form - ; Vdom.Node.sexp_for_debugging ([%sexp_of: (Record.t * Variant.t) Or_error.t] value) - ] -;; -``` - -```{=html} - -``` -# Projection - -Notably missing in the Forms API is a "map" function. In its place is -`Form.project`, which has this type signature: - -```{=html} - -``` -``` ocaml -val project - : 'a t - -> parse_exn:('a -> 'b) - -> unparse:('b -> 'a) - -> 'b t -``` - -`project` is a way to move from a form producing values of type `'a` to -a form producing values of type `'b`, but it requires two "mapping" -functions, `parse_exn`, which moves from `'a` to `'b` as you'd expect, -but the other, `unparse`, goes in the opposite direction! - -`unparse` is required because `Form.set` needs to be able to accept -values of type `'b` and route them through the setter for the input -form. - -In practice, `project` is used to build forms for types that can be -parsed from other types. For example, `Form.Elements.Textbox.int` is -implemented like so: - -```{=html} - -``` -``` ocaml -let int : local_ Bonsai.graph -> int Form.t Bonsai.t = - fun graph -> - let form = Form.Elements.Textbox.string ~allow_updates_when_focused:`Always () graph in - let%arr form = form in - Form.project form ~parse_exn:Int.of_string ~unparse:Int.to_string -;; -``` - -```{=html} - -``` -You'll notice that non-integers are reported as an error. `Form.project` -captures the exception thrown by `Int.of_string` and the `Form.value` -returned by the `project`ed form is an `Error`. - -## Validation is just projection! - -You may not realize, but `Form.project` can also be used for validation! -If we take `'b = 'a` in the type signature for `Form.project` above and -provide the identity function for `unparse`, the `parse_exn` function -can signal an invalid or valid state by raising or not raising, -respectively. - -Because this is a common pattern, Bonsai Forms provides the following -function, built on top of `Form.project` to help reduce boilerplate: - -```{=html} - -``` -``` ocaml -val validate : 'a t -> f:('a -> unit Or_error.t) -> 'a t -``` diff --git a/docs/how_to/higher_order_functions.md b/docs/how_to/higher_order_functions.md deleted file mode 100644 index 9badd8a3..00000000 --- a/docs/how_to/higher_order_functions.md +++ /dev/null @@ -1,122 +0,0 @@ -# Higher-Order Functions - -In regular OCaml, higher-order functions like `List.fold`, the `|>` -pipeline operator, and `match` statements enable control flow and -abstraction. - -The `match%sub` and `Bonsai.assoc` [control flow -primitives](../guide/05-control_flow.mdx) are higher-order functions -too! And we can use them to build higher-order abstractions like: - -- A generic query renderer, which dispatches an RPC to fetch some - data, and uses `match%sub` to display a loading indicator while the - fetch is in progress, and some - `local_ graph -> Vdom.Node.t Bonsai.t` when the data is present. -- A generic table/list, where every row/cell can have its own state, - implemented with `assoc`. -- A modal, where you'd use `match%sub` to only render some content if - the open state is `true`. - -These will have type signatures like: - -```{=html} - -``` -``` ocaml -val higher_order : ('a Bonsai.t -> local_ graph -> 'b Bonsai.t) -> ... -> local_ graph -> 'c Bonsai.t -``` - -For example, here's how we'd build a simple modal: - -```{=html} - -``` -``` ocaml -type t = - { view : Vdom.Node.t Bonsai.t - ; open_modal : unit Effect.t Bonsai.t - } - -let modal - ~(title : Vdom.Node.t Bonsai.t) - ~(content : local_ Bonsai.graph -> Vdom.Node.t Bonsai.t) - (local_ graph) - : t - = - let is_open, set_is_open = Bonsai.state false graph in - let open_modal = - let%arr set_is_open = set_is_open in - set_is_open true - in - let view = - match%sub is_open with - | false -> Bonsai.return Vdom.Node.none - | true -> - (* only instantiate [content] here in the [true] branch *) - let%arr content = content graph - and title = title - and set_is_open = set_is_open in - let close_button = - Vdom.Node.button - ~attrs:[ Vdom.Attr.on_click (fun _ -> set_is_open false) ] - [ Vdom.Node.text "X" ] - in - Vdom.Node.div - ~attrs: - [ [%css - {| - position: fixed; - top: 0; bottom: 0; left: 0; right: 0; - height: fit-content; width: fit-content; - margin: auto; - border: 1px solid black; - background-color: white;|}] - ] - [ Vdom.Node.h1 [ title; close_button ]; content ] - in - { view; open_modal } -;; -``` - -And here's how we could use the modal, with content that instantiates -state: - -```{=html} - -``` -``` ocaml -let modal_example (local_ graph) = - let title = Bonsai.return (Vdom.Node.text "Hi there!") in - let content (local_ graph) = - let count, set_count = Bonsai.state 0 graph in - let on_activate = - let%arr count = count - and set_count = set_count in - set_count (count + 1) - in - let () = Bonsai.Edge.lifecycle ~on_activate graph in - let%arr count = count in - Vdom.Node.div - [ Vdom.Node.text [%string "This modal has been opened %{count#Int} times..."] ] - in - let { view = modal_view; open_modal } = modal ~title ~content graph in - let%arr modal_view = modal_view - and open_modal = open_modal in - Vdom.Node.div - ~attrs:[ [%css {|height: 400px;|}] ] - [ modal_view - ; Vdom.Node.button - ~attrs:[ Vdom.Attr.on_click (fun _ -> open_modal) ] - [ Vdom.Node.text "open modal" ] - ] -;; -``` - -```{=html} - -``` -Note that state is retained, and [lifecycle effects](./lifecycles.mdx) -run on every open / close. diff --git a/docs/how_to/lifecycles.md b/docs/how_to/lifecycles.md deleted file mode 100644 index f39b0d75..00000000 --- a/docs/how_to/lifecycles.md +++ /dev/null @@ -1,97 +0,0 @@ -# Lifecycles - -The `match%sub` and `Bonsai.assoc` [control flow -operators](../guide/05-control_flow.mdx) are very similar to regular -OCaml `match` statements and `Map.map` calls. But because Bonsai is -incremental, their code is constantly being rerun whenever some -dependency changes. - -## Active / Inactive Code - -When a `match%sub` switches from one branch (A) to another (B), Bonsai -keeps around any state instantiated inside of A. But the output of (A) -isn't used, so its recomputation is paused: (A) is inactive, -until/unless the `match%sub` switches back. - -`assoc`s behave similarly: Bonsai maintains a separate copy of state for -every key in the input map. But if some key (K) is removed from the map, -its state is retained, and the code recomputing its output becomes -unactive until/unless the (K) is added back. - -```{=html} - -``` -## Lifecycle Events - -`Bonsai.Edge.lifecycle` allows you to schedule `Effect.t`s when the -containing code switches between active / inactive as `on_activate` and -`on_deactivate` arguments: - -```{=html} - -``` -``` ocaml -let lifecycle_demo (local_ graph) = - let log_val, log = - Bonsai.state_machine0 - ~default_model:"" - ~apply_action:(fun _ curr new_ -> curr ^ new_) - graph - in - let show, toggle_show = Bonsai.toggle ~default_model:false graph in - let main_view = - match%sub show with - | true -> - Bonsai.Edge.lifecycle - ~on_activate: - (let%arr log = log in - log "🚀") - ~on_deactivate: - (let%arr log = log in - log "🔥") - graph; - Vdom.Node.text [%string "Active!!!!"] |> Bonsai.return - | false -> Vdom.Node.text "Nothing to see here..." |> Bonsai.return - in - let%arr log_val = log_val - and toggle_show = toggle_show - and main_view = main_view in - Vdom.Node.( - div - [ div - [ button - ~attrs:[ Vdom.Attr.on_click (fun _ -> Effect.all_unit [ toggle_show ]) ] - [ text "toggle show" ] - ; text log_val - ] - ; main_view - ]) -;; -``` - -```{=html} - -``` -If `Bonsai.Edge.lifecycle` is used outside of any `match%sub`s or -`Bonsai.assoc`s, `on_activate` will run once when your app starts, and -`on_deactivate` will never run. - -```{=html} -``` -```{=html} - -``` diff --git a/docs/how_to/readme.md b/docs/how_to/readme.md deleted file mode 100644 index e57ec756..00000000 --- a/docs/how_to/readme.md +++ /dev/null @@ -1,5 +0,0 @@ -# Bonsai Docs: How To - -These are intended to be standalone guides, which you should read as -needed. Reading the [core Bonsai guide](../guide/00-introduction.mdx) is -a prerequisite. diff --git a/docs/how_to/resetting_state.md b/docs/how_to/resetting_state.md deleted file mode 100644 index b2f44f84..00000000 --- a/docs/how_to/resetting_state.md +++ /dev/null @@ -1,424 +0,0 @@ -# Resetting State - -We can reset all state created within some block of code by wrapping it -in `Bonsai.with_model_resetter`: - -```{=html} - -``` -``` ocaml -let two_counters (local_ graph) = - let%arr counter1 = State_examples.counter_ui graph - and counter2 = State_examples.counter_ui graph in - Vdom.Node.div - ~attrs:[ [%css {|border: 1px solid black; padding: 4px|}] ] - [ counter1; counter2 ] -;; - -let reset_ui (local_ graph) ~f = - let view, reset = Bonsai.with_model_resetter ~f graph in - let%arr view = view - and reset = reset in - Vdom.Node.div - [ view - ; Vdom.Node.button - ~attrs:[ Vdom.Attr.on_click (fun _ -> reset) ] - [ Vdom.Node.text "Reset" ] - ] -;; - -let resettable_counters = reset_ui ~f:two_counters -``` - -```{=html} - -``` -There's also a `Bonsai.with_model_resetter'`, which allows blocks of -code to reset their own state: - -```{=html} - -``` -``` ocaml -let resettable_counters_from_inside (local_ graph) = - Bonsai.with_model_resetter' - ~f:(fun ~reset (local_ graph) -> - let%arr counter1 = State_examples.counter_ui graph - and counter2 = State_examples.counter_ui graph - and reset = reset in - Vdom.Node.div - ~attrs:[ [%css {|border: 1px solid black; padding: 4px|}] ] - [ counter1 - ; Vdom.Node.button - ~attrs:[ Vdom.Attr.on_click (fun _ -> reset) ] - [ Vdom.Node.text "Reset" ] - ; counter2 - ]) - graph -;; -``` - -```{=html} - -``` -## Advanced: Customizing Reset Behavior - -By default, when the effect from a `with_model_resetter` is scheduled, -it resets all states instantiated inside it to their `default_model`s. -In most cases, this is the desired behaviour. However, there are a -couple of niche situations where you might want to customize the -behaviour by providing a custom `reset` function on state creation. - -### Ignoring Resets - -One use case of Bonsai state is tracking some non-Bonsai value. For -instance, let's say we want to use the following API for getting the -status of an RPC connection: - -```{=html} - -``` -``` ocaml -module Connection : sig - (* Some generic connection type that doesn't know of Bonsai *) - type t - - (* Connect to some resource *) - val create : uri:string -> t - - (* Notify subscribers whenever the connection status changes *) - val on_status_change - : t - -> ([ `Connected | `Connecting | `Disconnected ] -> unit Effect.t) - -> unit Effect.t -end -``` - -We could make its status available as a `Bonsai.t` by creating some -state, and updating it via the `on_status_change` subscription: - -```{=html} - -``` -``` ocaml -let connection_status (local_ graph) conn - : [ `Connected | `Connecting | `Disconnected ] Bonsai.t - = - let status, set_status = Bonsai.state `Disconnected graph in - let register_status_change = - let%arr set_status = set_status in - Connection.on_status_change conn set_status - in - let () = Bonsai.Edge.lifecycle ~on_activate:register_status_change graph in - status -;; -``` - -We could then use this `status Bonsai.t` as an input to a UI that lets -the user know if they are connected: - -```{=html} - -``` -``` ocaml -let conn = Connection.create ~uri:"https://google.com" - -let connection_status_ui (local_ graph) = - let connection_status = - match%sub connection_status graph conn with - | `Connected -> Bonsai.return (Vdom.Node.div [ Vdom.Node.text "Connected" ]) - | `Connecting -> Bonsai.return (Vdom.Node.div [ Vdom.Node.text "Connecting" ]) - | `Disconnected -> Bonsai.return (Vdom.Node.div [ Vdom.Node.text "Disconnected" ]) - in - let%arr connection_status = connection_status in - Vdom.Node.div [ connection_status ] -;; -``` - -```{=html} - -``` -This works fine, but things get a bit dicey if our connection status -function is included transitively in a call to `with_model_resetter`. -For example: - -```{=html} - -``` -``` ocaml -let connection_and_counters (local_ graph) = - let%arr connection_status_ui = connection_status_ui graph - and counters = two_counters graph in - Vdom.Node.div [ connection_status_ui; counters ] -;; - -let resettable_ui = reset_ui ~f:connection_and_counters -``` - -```{=html} - -``` -The `state` we use to track `connection_status` isn't really *state*: it -should persist across model resets. But if you click the reset, in -addition to resetting the counters, the connection status switches to -"Disconnected" until the next time the connection status *actually* -changes. - -We can exclude state from being reset by providing a custom `reset` -function: - -```{=html} - -``` -``` ocaml -let connection_status (local_ graph) conn - : [ `Connected | `Connecting | `Disconnected ] Bonsai.t - = - let status, set_status = - Bonsai.state ~reset:(fun model_when_reset -> model_when_reset) `Disconnected graph - in - let register_status_change = - let%arr set_status = set_status in - Connection.on_status_change conn set_status - in - let () = Bonsai.Edge.lifecycle ~on_activate:register_status_change graph in - status -;; -``` - -And now we observe the correct reset behaviour for both the counter and -connection status UIs: - -```{=html} - -``` -```{=html} - -``` -### Performing Cleanup - -In contrast, some Bonsai state machines may serve as the source of truth -for data, and work to make the *outside world* be consistent with that -data. For example, a trading UI might have a component that keeps track -of open/filled orders, and issues requests to an exchange to enact those -orders. - -An over-simplified interface for an exchange might look like: - -```{=html} - -``` -``` ocaml -module Exchange : sig - module Order_id = Int - - (* A connection to an exchange *) - type t - type event = Fill of Order_id.t - - val create : unit -> t - - (* Sends an order to the exchange *) - val send_order : t -> Order_id.t -> unit Effect.t - - (* Cancels an open order on the exchange *) - val cancel_order : t -> Order_id.t -> unit Effect.t - - (* Subscribe to notifications of which orders have been filled *) - val subscribe : t -> (event -> unit Effect.t) -> unit Effect.t -end -``` - -And we could write a state machine that lets users create and track -their open and filled orders: - -```{=html} - -``` -``` ocaml -module Model = struct - type t = - { open_orders : Order_id.Set.t - ; filled_orders : Order_id.t list - ; next_order_id : int - } - - let empty = { open_orders = Order_id.Set.empty; filled_orders = []; next_order_id = 0 } -end - -module Action = struct - type t = - | Create_ui_order - | Received_fill of Exchange.Order_id.t -end - -let order_manager (exchange : Exchange.t) (local_ graph) = - let model, inject_action = - Bonsai.state_machine0 - ~default_model:Model.empty - ~apply_action: - (fun - context { open_orders; next_order_id; filled_orders } (action : Action.t) -> - match action with - | Create_ui_order -> - let this_order_id = next_order_id in - let open_orders = Set.add open_orders this_order_id in - Apply_action_context.schedule_event - context - (Exchange.send_order exchange this_order_id); - { open_orders; next_order_id = this_order_id + 1; filled_orders } - | Received_fill order_id -> - let filled_orders = filled_orders @ [ order_id ] in - let open_orders = Set.remove open_orders order_id in - { open_orders; next_order_id; filled_orders }) - graph - in - let subscribe_to_exchange = - let%arr inject_action = inject_action in - Exchange.subscribe exchange (fun (Fill order_id) -> - inject_action (Received_fill order_id)) - in - let () = Bonsai.Edge.lifecycle ~on_activate:subscribe_to_exchange graph in - let inject_new_order = - let%arr inject_action = inject_action in - inject_action Create_ui_order - in - let open_orders = - let%arr { open_orders; _ } = model in - open_orders - in - let filled_orders = - let%arr { filled_orders; _ } = model in - filled_orders - in - open_orders, filled_orders, inject_new_order -;; -``` - -Our `order_manager` keeps track of open and filled orders that we get -from the exchange. It also provides an action we can inject to open an -order. When it receives that action, it updates its state and also sends -an order to the exchange, keeping the state of the outside world in sync -with itself. - -We can incorporate our `order_manager` into our trading UI: - -```{=html} - -``` -``` ocaml -let trading_ui exchange (local_ graph) = - let open_orders, filled_orders, inject_new_order = order_manager exchange graph in - let%arr open_orders = open_orders - and filled_orders = filled_orders - and inject_new_order = inject_new_order in - Vdom.Node.div - [ Vdom.Node.text [%string "Open orders:"] - ; Vdom.Node.sexp_for_debugging [%sexp (open_orders : Order_id.Set.t)] - ; Vdom.Node.text [%string "Filled orders:"] - ; Vdom.Node.sexp_for_debugging [%sexp (filled_orders : Order_id.t list)] - ; Vdom.Node.button - ~attrs:[ Vdom.Attr.on_click (fun _ -> inject_new_order) ] - [ Vdom.Node.text "New order" ] - ] -;; -``` - -```{=html} - -``` -However, as in the previous example, something weird starts to happen if -our trading UI happens to be nested in a model resetter. In the demo -below, try resetting the computation when there are open orders. - -```{=html} - -``` -As you may have noticed, we can still receive fills for orders that were -opened before resetting our model, even though resetting the model was -supposed to restore our state to `Model.default`, which has no open -orders. - -Resetting the model doesn't automatically cancel our open orders on the -exchange: we need to do this ourselves. We'll change the use of -`Bonsai.state_machine0`, providing a `reset` implementation that uses -the `Apply_action_context.t` parameter: - -```{=html} - -``` -``` ocaml - let model, inject_action = - Bonsai.state_machine0 - ~default_model:Model.empty - ~apply_action: - (fun - context { open_orders; next_order_id; filled_orders } (action : Action.t) -> - match action with - | Create_ui_order -> - let this_order_id = next_order_id in - let open_orders = Set.add open_orders this_order_id in - Apply_action_context.schedule_event - context - (Exchange.send_order exchange this_order_id); - { open_orders; next_order_id = this_order_id + 1; filled_orders } - | Received_fill order_id -> - let filled_orders = filled_orders @ [ order_id ] in - let open_orders = Set.remove open_orders order_id in - { open_orders; next_order_id; filled_orders }) - ~reset:(fun context (model : Model.t) -> - Set.iter model.open_orders ~f:(fun order_id -> - Apply_action_context.schedule_event - context - (Exchange.cancel_order exchange order_id)); - Model.empty) - graph - in -``` - -With this new reset behaviour, orders don't persist between model -resets. - -```{=html} - -``` -```{=html} - -``` diff --git a/docs/how_to/rpcs.md b/docs/how_to/rpcs.md deleted file mode 100644 index c48c6f23..00000000 --- a/docs/how_to/rpcs.md +++ /dev/null @@ -1,383 +0,0 @@ -# RPCs - -`Bonsai_web`'s `Rpc_effect` provides effects for dispatching RPCs and -tracking their results. - -### Example 1: Doubling a number - -In this example, we'll build a simple full-stack app: - -- The client stores an `int` state -- Whenever a button is clicked, it sends the current value to the - server -- The server sends back the input multiplied by 2 -- The client updates its state to the new, doubled number - -```{=html} - -``` -The first step is to define an RPC that is shared between the server and -client code. - -```{=html} - -``` -``` ocaml -let double_rpc = - Rpc.Rpc.create - ~name:"double" - ~version:0 - ~bin_query:[%bin_type_class: int] - ~bin_response:[%bin_type_class: int] - ~include_in_error_count:Only_on_exn -;; -``` - -Next, we provide the server's implementation of the RPC. - -```{=html} - -``` -``` ocaml -let double_implementation = - Rpc.Rpc.implement' double_rpc (fun _connection_state query -> Int.max 1 (query * 2)) -;; -``` - -On the client side, we start by picking a server to send our RPCs to. -Most users will pick `Self`, which causes RPCs to be sent to the same -server hosting the web page itself. - -```{=html} - -``` -``` ocaml -let where_to_connect : Rpc_effect.Where_to_connect.t = Self -``` - -Finally, we can build the client side of the app. - -```{=html} - -``` -``` ocaml -let double_number_app (local_ graph) = - let dispatch_double_rpc = - Rpc_effect.Rpc.dispatcher double_rpc ~where_to_connect graph - in - let number, set_number = Bonsai.state 1 graph in - let%arr dispatch_double_rpc = dispatch_double_rpc - and number = number - and set_number = set_number in - Vdom.Node.div - [ Vdom.Node.div [ Vdom.Node.text [%string "The number is: %{number#Int}"] ] - ; Vdom.Node.button - ~attrs: - [ Vdom.Attr.on_click (fun _ -> - match%bind.Effect dispatch_double_rpc number with - | Ok doubled_number -> set_number doubled_number - | Error error -> Effect.of_sync_fun eprint_s [%sexp (error : Error.t)]) - ] - [ Vdom.Node.text "Double the number" ] - ] -;; -``` - -It's worth noting that `dispatch_double_rpc` is a -`query -> 'response unit Effect.t`, so the RPC is only dispatched -whenever the effect is scheduled. Just instantiating -`dispatch_double_rpc` will not send anything. - -## Polling State RPC - -### Why not use `Pipe_rpc`? - -Bonsai apps often use a library called `polling_state_rpc` whenever they -want to keep up to date with a source of data. The library provides a -similar abstraction to the `Pipe_rpc` or `State_rpc` modules that come -built into `async_rpc_kernel`, but with tradeoffs that are more suitable -for web clients. - -Both `Pipe_rpc` and `State_rpc` operate by pushing streams of updates -through the connection; the client can then consume this stream of -events as appropriate. This approach works great if the client is able -to keep up with the stream of events, as is often the case with native -programs. - -Web UIs are different because they frequently get backgrounded: if the -user switches away from a browser tab, the Bonsai rendering loop, which -relies on the browser's `requestAnimationFrame` function, slows down to -a crawl. If the app uses `State_rpc`, then when the tab eventually comes -back to the foreground, it might have a large queue of events, which -will cause it to freeze while it catches up. - -Even more problematically, the server might run out of memory -accumulating massive pipes for backgrounded clients, and crash. - -The `polling_state_rpc` library solves this problem by having the client -explicitly poll the server, which responds with a diff from the -previously-requested state to the current data. - -### Using Polling State RPC - -This next example will build a UI that monitors the current time -reported by the server, in order to provide a concrete example of using -the `polling_state_rpc` library. - -```{=html} - -``` -Ten times a second, the client requests the current time for some -timezone. - -```{=html} - -``` -As in the previous example, we will begin by setting up the RPC -definition. The point of interest in this case is that we must provide a -module for the response type that satisfies the `Legacy_diffable.S` -module type (from the `legacy_diffable` library). - -```{=html} - -``` -``` ocaml -module Current_time = struct - include String - include Legacy_diffable.Atomic.Make (String) -end - -let current_time_rpc = - Polling_state_rpc.create - ~name:"current_time" - ~version:0 - ~query_equal:[%equal: string] - ~bin_query:[%bin_type_class: string] - (module Current_time) -;; -``` - -Next we provide a server implementation: - -```{=html} - -``` -``` ocaml -let current_time_implementation = - Polling_state_rpc.implement - ~on_client_and_server_out_of_sync:print_s - current_time_rpc - (fun _connection_state zone -> - Deferred.return - (Time_ns.to_string_trimmed ~zone:(Timezone.of_string zone) (Time_ns.now ()))) - |> Rpc.Implementation.lift ~f:(fun connection_state -> - connection_state, connection_state) -;; -``` - -```{=html} - -``` -Finally, we can write the UI code. There are a couple important ways in -which this example is different from the previous one: - -- Rather than explicitly dispatching the RPC, we merely instantiate - the poller and allow it to send requests as necessary. It sends - requests whenever the query (the timezone) changes, and also at - fixed intervals (every tenth of a second). -- Instead of a single `'response Or_error.t`, we get `Bonsai.t`s for - `last_ok_response` and `last_error`. These are packaged with their - corresponding query. - -The code omits the implementation of `zone_form`, since that is not our -focus in this chapter. - -```{=html} - -``` -``` ocaml -let current_time_app (local_ graph) = - let zone, zone_view = zone_form graph in - let poll = - Rpc_effect.Polling_state_rpc.poll - current_time_rpc - ~equal_query:[%equal: string] - ~equal_response:[%equal: Current_time.t] - ~where_to_connect - ~every:(Time_ns.Span.of_sec 0.1) - zone - graph - in - let%arr { last_ok_response; last_error; inflight_query = _; refresh = _ } = poll - and zone_view = zone_view in - let text = - match last_ok_response with - | Some (zone, current_time) -> - [%string "The current time in the zone '%{zone}' is %{current_time}"] - | None -> "Loading..." - in - let error_view = - match last_error with - | Some (zone, error) -> - Vdom.Node.div - ~attrs:[ Css.error_text ] - [ Vdom.Node.text [%string "Got error when requesting time in zone '%{zone}'"] - ; Vdom.Node.pre [ Vdom.Node.text (Error.to_string_hum error) ] - ] - | None -> Vdom.Node.none - in - Vdom.Node.div [ zone_view; Vdom.Node.div [ Vdom.Node.text text ]; error_view ] -;; -``` - -## Testing RPCs - -Let's try to test the "number doubler" from the first example. Using the -[testing tools](./testing.mdx) we are familiar with, we might write -something like this: - -```{=html} - -``` -``` ocaml -let%expect_test "Clicking the button should double the number" = - let handle = Handle.create (Result_spec.vdom Fn.id) double_number_app in - Handle.show handle; - [%expect - {| -
-
The number is: 1
- -
- |}]; - Handle.click_on handle ~get_vdom:Fn.id ~selector:"button"; - Handle.show handle; - [%expect - {| -
-
The number is: 1
- -
- |}] -;; -``` - -No dice. Since RPCs are dispatched asynchronously, the test must itself -be async. Native tests would accomplish this by adding `open Async` at -the top of the test file, but since we're running in javascript, we have -to use a different library. - -```{=html} - -``` -``` ocaml -open Async_kernel -open Bonsai_web_test_async -``` - -Here's what our attempt looks like now. In addition to the above module -imports, we also use `Handle.flush_async_and_bonsai` to force any -pending side-effects to happen before the test quits. If we don't -include that call, the output still gets printed, but after the -`return ()`, which means the test doesn't typecheck. - -```{=html} - -``` -``` ocaml -let%expect_test "Clicking the button should double the number" = - let handle = Handle.create (Result_spec.vdom Fn.id) double_number_app in - Handle.show handle; - [%expect - {| -
-
The number is: 1
- -
- |}]; - Handle.click_on handle ~get_vdom:Fn.id ~selector:"button"; - Handle.show handle; - [%expect - {| -
-
The number is: 1
- -
- |}]; - let%bind () = Handle.flush_async_and_bonsai handle in - [%expect {| (Failure "BUG: no bonsai-rpc handler installed") |}]; - return () -;; -``` - -We're close, but we need to provide an implementation for the RPC that -the button tried to invoke. Thankfully, `double_implementation` from -earlier is still lying around, so we can simply use that. (In some apps -you might want to include a mock implementation that differs from the -real one.) We should also yield to the scheduler *before* showing the -output, so that we can see the effect of the RPC completing. - -```{=html} - -``` -``` ocaml -let%expect_test "Clicking the button should double the number" = - let handle = - Handle.create - ~rpc_implementations:[ double_implementation ] - (Result_spec.vdom Fn.id) - double_number_app - in - Handle.show handle; - [%expect - {| -
-
The number is: 1
- -
- |}]; - Handle.click_on handle ~get_vdom:Fn.id ~selector:"button"; - let%bind () = Handle.flush_async_and_bonsai handle in - Handle.show handle; - [%expect - {| - ------ between bonsai frame ------ -
-
The number is: 2
- -
- |}]; - return () -;; -``` - -Hurrah! This test successfully demonstrates the effect of clicking the -button. - -```{=html} - -``` diff --git a/docs/how_to/state_per_key.md b/docs/how_to/state_per_key.md deleted file mode 100644 index b294dd89..00000000 --- a/docs/how_to/state_per_key.md +++ /dev/null @@ -1,119 +0,0 @@ -# State Per Key - -`Bonsai.scope_model` allows us to store multiple copies of state for -some `Bonsai.t`, each keyed by some value, while only displaying one at -a time. - -## Multiple copies with assoc - -Imagine we'd like to have a counter for each of many users. We could -show all the counters with an `assoc`: - -```{=html} - -``` -``` ocaml -let counters_for_users_assoc (local_ graph) : Vdom.Node.t Bonsai.t = - let users = - [ "Alice", (); "Bob", (); "Charlie", () ] |> String.Map.of_alist_exn |> Bonsai.return - in - let counters = - Bonsai.assoc - (module String) - users - ~f:(fun _ _ graph -> State_examples.counter_ui graph) - graph - in - let%arr counters = counters in - Vdom.Node.table - (counters - |> Map.to_alist - |> List.map ~f:(fun (key, vdom) -> - let open Vdom.Node in - let name = td [ Vdom.Node.text key ] in - let counter = td [ vdom ] in - Vdom.Node.tr [ name; counter ])) -;; -``` - -```{=html} - -``` -## One at a time - -But what if we wanted to show one counter at a time, but still maintain -separate counter state for each user? We can use `Bonsai.scope_model`: - -```{=html} - -``` -``` ocaml -module Form = Bonsai_web_ui_form.With_automatic_view - -let counters_for_users_scoped (local_ graph) : Vdom.Node.t Bonsai.t = - let form = - Form.Elements.Dropdown.list - (module String) - ~equal:[%equal: String.t] - (Bonsai.return [ "Alice"; "Bob"; "Charlie" ]) - graph - in - let active_user = - let%arr form = form in - Form.value_or_default form ~default:"Alice" - in - Bonsai.scope_model - (module String) - ~on:active_user - graph - ~for_:(fun graph -> - let%arr counter = State_examples.counter_ui graph - and name = active_user - and form = form in - Vdom.Node.div - [ Form.view_as_vdom form; Vdom.Node.p [ Vdom.Node.text name ]; counter ]) -;; -``` - -```{=html} - -``` -Bonsai will maintain a separate copy of all state created in `for_` for -every key that is passed into `on`. - -Of course, in this case, we could have centralized state for all -counters. But this would require explicitly threading through -getters/setters, and wouldn't allow us to use any code that creates and -owns its own state. - -## `Scope_model` and Lifecycles - -`scope_model` will run any `on_deactivate` and `on_activate` [lifecycle -handlers](./lifecycles.mdx) defined inside of `for_` whenever `on` -changes. `on_deactivate` will run first, and have access to state -corresponding to the old value of `on`. Then, `on_activate` will run -with access to state for the new `on`. - -```{=html} - -``` -## The Underlying Machinery - -Recall from the [state chapter](../guide/04-state.mdx) that Bonsai -aggregates and stores all state centrally. But `Bonsai.assoc`s need a -dynamic number state copies: one for each key. These states all have the -same static structure though, so we can just use a big `Map.t`. - -`scope_model` is implemented as a `Bonsai.assoc`, who's input map is -`Map.of_alist_exn [THE_CURRENT_KEY, ()]`: that's how we get state -retention and lifecycles on key change. diff --git a/docs/how_to/testing.md b/docs/how_to/testing.md deleted file mode 100644 index d81ffcdb..00000000 --- a/docs/how_to/testing.md +++ /dev/null @@ -1,334 +0,0 @@ -# Testing - -Traditional approaches for testing web applications can be infuriating. -With tools like selenium or puppeteer, there's an entire headless -browser running in the background, and not only do you need to find a -way to reconfigure the app or library for testing, slow test execution -and race condition-related bugs are a constant companion. - -Bonsai web apps compute a declarative `Vdom.Node.t`, which serves as the -source of truth for how the web UI should look at any given time. - -The `Bonsai_web_test` library provides tools to compute and test the -`Vdom.Node.t` generated by any `local_ graph -> Bonsai.t`, so we can use -`ppx_expect_test` and all the other OCaml testing tools we know and -love. - -## Getting Ready For Testing - -Testing a program built using Js_of_ocaml involves a few changes to your -normal workflow. - - (library ( - (name my_ui_test) - (js_of_ocaml ()) ; Test library must be marked with js_of_ocaml - (libraries (core my_ui)) - (inline_tests ( ; Native tests must be disabled - (native dont_build_dont_run) - (javascript build_and_run))))) - -Your jenga start-file also needs to specify the `javascript-runtest` -alias for the project. - - (alias ((name build) (deps ( - (alias %{root}/app/my-app/test/javascript-runtest) - ; ... your other build targets here... - )))) - -## Basics of testing: printing Vdom - -Let's say we have some constant Vdom we'd like to test: - -```{=html} - -``` -``` ocaml -let hello_world = Vdom.Node.span [ Vdom.Node.text "hello world" ] -``` - -There are a few tools we'll use: - -- `Result_spec.t` is a first class module containing: - - How to turn some `t` into a string "view" we can show in expect - tests - - How to convert an "incoming" value (passed in by testing code) - into an `Effect.t` how to display it, and any inputs needed. -- `Handle.t` wraps a `local_ graph -> 'a Bonsai.t`, and provides APIs - for: - - Evaluating it and printing the result (`Handle.show`). - - Injecting some value of type `incoming`, which the - `Result_spec.t` will convert into a `unit Effect.t` and run - (`Handle.do_actions`). - - Triggering event listeners (`Handle.click_on`, - `Handle.input_text`, etc.). - -`Result_spec.vdom` is a helper for generating -`Vdom.Node.t Result_spec.t`s: - -```{=html} - -``` -``` ocaml -module Handle = Bonsai_web_test.Handle -module Result_spec = Bonsai_web_test.Result_spec - -let%expect_test "it shows hello world" = - let handle = Handle.create (Result_spec.vdom Fn.id) (fun _ -> return hello_world) in - Handle.show handle; - [%expect {| hello world |}] -;; -``` - -Some `Bonsai.t`s return a `Vdom.Node.t` *and* something else. -`Result_spec.vdom`'s first argument is a function to pull out the -`Vdom.Node.t` part. - -We'll make our own `Result_spec.t`s for things other than `Vdom.Node.t` -later. - -```{=html} - -``` -## Testing dynamic inputs - -What if we want to test a `'a Bonsai.t -> 'b Bonsai.t`? - -```{=html} - -``` -``` ocaml -let hello_user (name : string Bonsai.t) : Vdom.Node.t Bonsai.t = - let%arr name = name in - Vdom.Node.span [ Vdom.Node.textf "hello %s" name ] -;; -``` - -```{=html} -``` -We can use `Bonsai.Var.t` to get a mutable handle on a `Bonsai.t`: - -```{=html} - -``` -``` ocaml -let%expect_test "shows hello to a user" = - let user_var = Bonsai.Expert.Var.create "Bob" in - let user = Bonsai.Expert.Var.value user_var in - let handle = Handle.create (Result_spec.vdom Fn.id) (fun _ -> hello_user user) in - Handle.show handle; - [%expect {| hello Bob |}]; - Bonsai.Expert.Var.set user_var "Alice"; - Handle.show handle; - [%expect {| hello Alice |}] -;; -``` - -As expected, after changing the `Var.t`, the contents in the DOM are -updated! - -## Testing with diffs - -If we only want to see what changed between two versions of the view, we -can use `Handle.show_diff`: - -```{=html} - -``` -``` ocaml -let%expect_test "shows hello to a user" = - let user_var = Bonsai.Expert.Var.create "Bob" in - let user = Bonsai.Expert.Var.value user_var in - let handle = Handle.create (Result_spec.vdom Fn.id) (fun _ -> hello_user user) in - Handle.show handle; - [%expect {| hello Bob |}]; - Bonsai.Expert.Var.set user_var "Alice"; - Handle.show_diff handle; - [%expect - {| - -| hello Bob - +| hello Alice - |}] -;; -``` - -While the diff in this instance isn't particularly illuminating, when -testing UI components that produce hundreds of lines of output, it can -be *much* easier to only review the diff. - -## Testing interactivity - -Most useful web UIs store internal state, which can be updated in -response to user interactions. - -Here, we actually use the `hello_user` we defined previously, but the -`string Bonsai.t` comes from internal state instead of being passed in -by the caller: - -```{=html} - -``` -``` ocaml -let hello_textbox (local_ graph) : Vdom.Node.t Bonsai.t = - let state, set = Bonsai.state "" graph in - let%arr message = hello_user state - and set = set in - Vdom.Node.div - [ Vdom.Node.input ~attrs:[ Vdom.Attr.on_input (fun _ text -> set text) ] (); message ] -;; -``` - -This is fully self-contained: its interior state is only changeable by -typing into the `` text-box. - -Event listeners added via `Vdom.Attr.*` are testable with Bonsai! We can -use `Handle.input_text` to simulate interacting with the `` DOM -element: - -```{=html} - -``` -``` ocaml -let%expect_test "shows hello to a specified user" = - let handle = Handle.create (Result_spec.vdom Fn.id) hello_textbox in - Handle.show handle; - [%expect - {| -
- - hello -
- |}]; - Handle.input_text handle ~get_vdom:Fn.id ~selector:"input" ~text:"Bob"; - Handle.show_diff handle; - [%expect - {| -
- - -| hello - +| hello Bob -
- |}]; - Handle.input_text handle ~get_vdom:Fn.id ~selector:"input" ~text:"Alice"; - Handle.show_diff handle; - [%expect - {| -
- - -| hello Bob - +| hello Alice -
- |}] -;; -``` - -We just have to provide a valid CSS selector via the `~selector` -argument. - -```{=html} - -``` -## Testing state transitions directly - -Many Bonsai functions expose a `'a -> unit Effect.t` that can be used to -set some internal state, or apply an action to a state machine. An -example is the second part of `Bonsai.state`'s output: - -```{=html} - -``` -``` ocaml -('model Bonsai.t * ('model -> unit Effect.t) Bonsai.t) -``` - -Testing `Bonsai.state` or anything that exposes an injection function -will usually require a custom `Result_spec.t` and a new `Handle` -function. - -We can use `val Handle.do_actions : Handle.t -> incoming list -> unit` -to inject actions: - -```{=html} - -``` -``` ocaml -module State_view_spec = struct - type t = string * (string -> unit Effect.t) - type incoming = string - - let view : t -> string = fun (view, _) -> view - let incoming : t -> incoming -> unit Effect.t = fun (_, incoming) -> incoming -end - -let%expect_test "test Bonsai.state" = - let state_single_bonsai (local_ graph) - : (string * (string -> unit Vdom.Effect.t)) Bonsai.t - = - let state, inject = Bonsai.state "hello" graph in - Bonsai.both state inject - in - let handle = Handle.create (module State_view_spec) state_single_bonsai in - Handle.show handle; - [%expect {| hello |}]; - Handle.do_actions handle [ "world" ]; - Handle.show handle; - [%expect {| world |}] -;; -``` - -`Bonsai_web_test` only supports creating handles for a single -`'a Bonsai.t`, so we need to combine the 2 outputs of `Bonsai.state`. - -Instead of using the `Result_spec.vdom` helper function like before, we -need to define our view-spec module that caters specifically to the type -`t` returned by `state`. We also define a `type incoming`, which -represents "input events" we can inject. - -## Mocking time in tests - -In the [how-to on time](./time.mdx), we wrote a UI that depends on time: - -```{=html} - -``` -``` ocaml -let current_time (local_ graph) = - let%arr now = Bonsai.Clock.now graph in - Vdom.Node.text (Time_ns.to_string_utc now) -;; -``` - -```{=html} - -``` -We can use `Handle.advance_clock_by` to mock time in tests: - -```{=html} - -``` -``` ocaml -let%expect_test "test clock" = - let handle = Handle.create (Result_spec.vdom Fn.id) Time_examples.current_time in - Handle.show handle; - [%expect {| 1970-01-01 00:00:00.000000000Z |}]; - Handle.advance_clock_by handle (Time_ns.Span.of_sec 2.0); - Handle.show handle; - [%expect {| 1970-01-01 00:00:02.000000000Z |}] -;; -``` - -```{=html} -``` diff --git a/docs/how_to/time.md b/docs/how_to/time.md deleted file mode 100644 index 18a6ee67..00000000 --- a/docs/how_to/time.md +++ /dev/null @@ -1,132 +0,0 @@ -# Time - -`Bonsai.Clock` contains some utils for dealing with time. You should use -this over any other time sources, because it is designed for the -browser, and can be controlled in tests. - -## Accessing Time - -We can access the current time as a `Time_ns.t Bonsai.t`: - -```{=html} - -``` -``` ocaml -let current_time (local_ graph) = - let%arr now = Bonsai.Clock.now graph in - Vdom.Node.text (Time_ns.to_string_utc now) -;; -``` - -```{=html} - -``` -There's also a `Bonsai.Clock.approx_now`, which will update at some -frequency: - -```{=html} - -``` -``` ocaml -let approx_current_time (local_ graph) = - let%arr now = Bonsai.Clock.approx_now ~tick_every:(Time_ns.Span.of_sec 1.) graph in - Vdom.Node.text (Time_ns.to_string_utc now) -;; -``` - -```{=html} - -``` -This can save unnecessary recomputations, if you don't need very precise -time. - -If we want to access the current time in effects without it [becoming -stale](./effects_and_stale_values.mdx), we can use -`Bonsai.Clock.get_current_time`: - -```{=html} - -``` -``` ocaml -let measure_time (local_ graph) = - let%arr get_time = Bonsai.Clock.get_current_time graph in - Vdom.Node.button - ~attrs: - [ Vdom.Attr.on_click (fun _ -> - let%bind.Effect start = get_time in - let%bind.Effect () = long_effect in - let%bind.Effect end_ = get_time in - let diff = Time_ns.diff end_ start |> Time_ns.Span.to_string_hum in - Effect.alert [%string "that took: %{diff}"]) - ] - [ Vdom.Node.text "Click to measure a long effect." ] -;; -``` - -```{=html} - -``` -## Delaying Effects - -`Bonsai.Clock.sleep` can be used to delay an effect (like `setTimeout`): - -```{=html} - -``` -``` ocaml -let clock_sleep_demo (local_ graph) = - let%arr sleep = Bonsai.Clock.sleep graph in - Vdom.Node.button - ~attrs: - [ Vdom.Attr.on_click (fun _ -> - let%bind.Effect () = sleep (Time_ns.Span.of_sec 2.) in - Effect.alert "... 2 seconds later...") - ] - [ Vdom.Node.text "delayed alert" ] -;; -``` - -```{=html} - -``` -## Scheduling Effects - -And `Bonsai.Clock.every` runs an effect repeatedly: - -```{=html} - -``` -``` ocaml -let clock_every_demo (local_ graph) = - let count, set_count = Bonsai.state 0 graph in - Bonsai.Clock.every - ~when_to_start_next_effect:`Every_multiple_of_period_blocking - ~trigger_on_activate:false - (Time_ns.Span.of_sec 1.0) - (let%arr count = count - and set_count = set_count in - set_count (count + 1)) - graph; - let%arr count = count in - Vdom.Node.text [%string "Seconds since you opened the page: %{count#Int}"] -;; -``` - -```{=html} - -``` diff --git a/docs/how_to/uri_parsing.md b/docs/how_to/uri_parsing.md deleted file mode 100644 index 0bbf18df..00000000 --- a/docs/how_to/uri_parsing.md +++ /dev/null @@ -1,534 +0,0 @@ -# URI Parsing - -`uri_parsing` is a library that helps generate an unambiguous set of -routes, and `parse_exn`/`unparse` functions for making a -[Url_var.t](./url_var.mdx), for any OCaml type. - -Most routers take a list of strings / regexes describing routes, match -the URL string against them, and extract data/params into a map/list. - -With `uri_parsing`, we start with an OCaml type, and define parsing -rules for it. The set of routes is generated automatically for us. - -## Building a Parser - -`Uri_parsing.Value_parser.*` provides utils for converting basic types -(`string`, `int`, `Time_ns.t`, sexpable types, etc) to/from strings. -There's also a `Value_parser.project`, which allows you to specify -custom conversion functions between types `'a` and `'b`. - -To `'a Parser.t`, you need to specify *how* to parse (using a -`Value_parser.t`), and *where* to parse data from. - -For instance, let's say we want to represent our navigation state as a -string, which we get from the path of the URL. Not practical, but here's -how we'd do it: - -```{=html} - -``` -``` ocaml -let (parser : string Parser.t) = Parser.from_path Value_parser.string - -let%expect_test _ = - Parser.check_ok_and_print_urls_or_errors parser; - [%expect - {| - URL parser looks good! - ┌───────────┐ - │ All urls │ - ├───────────┤ - │ / │ - └───────────┘ - |}] -;; -``` - -Or maybe we only care about an int, and we want to get it from the query -params: - -```{=html} - -``` -``` ocaml -let (parser : int Parser.t) = Parser.from_query_required ~key:"q" Value_parser.int - -let%expect_test _ = - Parser.check_ok_and_print_urls_or_errors parser; - [%expect - {| - URL parser looks good! - ┌───────────┐ - │ All urls │ - ├───────────┤ - │ /?q= │ - └───────────┘ - |}] -;; -``` - -Or maybe we want to use some abstract sexpable type (like `Id` from -before!), and we expect a list of them: - -```{=html} - -``` -``` ocaml -let (parser : Id.t list Parser.t) = - Parser.from_remaining_path (Value_parser.sexpable (module Id)) - |> Parser.with_prefix [ "With"; "a"; "Prefix" ] -;; - -let%expect_test _ = - Parser.check_ok_and_print_urls_or_errors parser; - [%expect - {| - URL parser looks good! - ┌─────────────────────────────────────┐ - │ All urls │ - ├─────────────────────────────────────┤ - │ /With/a/Prefix/> │ - └─────────────────────────────────────┘ - |}] -;; -``` - -We also snuck in a `Parser.with_prefix`. This is one of several utils -that allows customizing how the generated routes will look. You can read -about all the `Parser.t` constructors and combinators in [the -mli](https://github.com/janestreet/bonsai/blob/master/uri_parsing/src/uri_parsing.mli). - -None of these are remotely realistic URL structures. What we *really* -want is to parse some nested structure of variants/records, representing -pages/subpages and their data dependencies. - -## Choosing a Type - -A common app structure is a set of pages, where each page takes some -parameters. For instance, a forum might use the following type: - -```{=html} - -``` -``` ocaml -module Search_params = struct - type t = - { query : string - ; author_id : Id.t option - ; categories : Id.t list - } -end - -module Admin_page = struct - type t = - | Settings - | Edit_user of Id.t -end - -module User_page = struct - module Subpage = struct - type t = - | Profile - | Posts - end - - type t = { - user_id : Id.t; - subpage : Subpage.t - } -end - -module Url = struct - type t = - | Homepage - | Discussion of Id.t - | Search of Search_params.t - | User of User_page.t - | Admin of Admin_page.t -end -``` - -Where `Id.t` is sexpable. - -```{=html} - -``` -## Record = Set of Data - -Let's start with `Search_params.t`. We can generate a `Parser.t` for a -record type by specifying a `Parser.t` for each field. We also need to -declare the order of all fields whos data comes from the path. [Typed -fields](https://github.com/janestreet/ppx_typed_fields) enforces that -we've gotten all cases, with the correct field types: - -```{=html} - -``` -``` ocaml -module Search_params = struct - type t = - { query : string - ; author_id : Id.t option - ; categories : Id.t list - } - [@@deriving typed_fields, sexp, equal] - - let parser_for_field : type a. a Typed_field.t -> a Parser.t = function - | Query -> Parser.from_path Value_parser.string - | Author_id -> Parser.from_query_optional (Value_parser.sexpable (module Id)) - | Categories -> Parser.from_query_many (Value_parser.sexpable (module Id)) - ;; - - module Path_order = Parser.Record.Path_order (Typed_field) - - let path_order = Path_order.T [ Query ] -end - -let search_params_parser = Parser.Record.make (module Search_params) - -let%expect_test _ = - Parser.check_ok_and_print_urls_or_errors search_params_parser; - [%expect - {| - URL parser looks good! - ┌──────────────────────────────────────────────────────────────────────────┐ - │ All urls │ - ├──────────────────────────────────────────────────────────────────────────┤ - │ /?author_id=>&categories=> │ - └──────────────────────────────────────────────────────────────────────────┘ - |}] -;; -``` - -```{=html} - -``` -Query param keys are automatically derived from the record field name, -although they can be overriden by passing `~key` to -`Parser.from_query_*`. - -## Variant = Choice of Route - -Let's do the same with `Admin_page.t` to demonstrate `uri_parsing`'s -support for variants. It's nearly the same, except that we use -`typed_variants` instead of `typed_fields`, and don't need to specify a -`path_order`. - -`Parser.Variant.make` will generate a separate route for each variant -case: - -```{=html} - -``` -``` ocaml -module Admin_page = struct - type t = - | Settings - | Edit_user of Id.t - [@@deriving typed_variants, sexp, equal] - - let parser_for_variant : type a. a Typed_variant.t -> a Parser.t = function - | Settings -> Parser.unit - | Edit_user -> Parser.from_path (Value_parser.sexpable (module Id)) - ;; -end - -let admin_page_parser = Parser.Variant.make (module Admin_page) - -let%expect_test _ = - Parser.check_ok_and_print_urls_or_errors admin_page_parser; - [%expect - {| - URL parser looks good! - ┌───────────────────────┐ - │ All urls │ - ├───────────────────────┤ - │ /edit_user/ │ - │ /settings │ - └───────────────────────┘ - |}] -;; -``` - -`val : Parser.unit : Parser.t` is only really useful for variant cases -with no payload: it parses nothing. - -By default, `uri_parsing` will use the lowercased variant names as -prefixes for the routes. You can override this with -`Parser.with_prefix`, `end_of_path`, or `with_remaining_path`. - -## Nesting Variants and Records - -Our `Url.t` above contains records nested in variants, and vice-versa. -`uri_parsing` is designed for this! Let's take a look at a parser for -`User_page`: - -```{=html} - -``` -``` ocaml -module User_page = struct - module Subpage = struct - type t = - | Profile - | Posts - [@@deriving typed_variants, sexp, equal] - - let parser_for_variant : type a. a Typed_variant.t -> a Parser.t = function - | Profile -> Parser.unit - | Posts -> Parser.unit - ;; - end - - type t = - { user_id : Id.t - ; subpage : Subpage.t - } - [@@deriving typed_fields, sexp, equal] - - let parser_for_field : type a. a Typed_field.t -> a Parser.t = function - | User_id -> Parser.from_path (Value_parser.sexpable (module Id)) - | Subpage -> Parser.Variant.make (module Subpage) - ;; - - module Path_order = Parser.Record.Path_order (Typed_field) - - let path_order = Path_order.T [ User_id; Subpage ] -end - -let user_page_parser = Parser.Record.make (module User_page) - -let%expect_test _ = - Parser.check_ok_and_print_urls_or_errors user_page_parser; - [%expect - {| - URL parser looks good! - ┌─────────────────────┐ - │ All urls │ - ├─────────────────────┤ - │ //posts │ - │ //profile │ - └─────────────────────┘ - |}] -;; -``` - -```{=html} - -``` -So nesting variants in records produces a separate route for each -combination of variants, where each route includes data from the -remaining record fields. - -Let's take a look at "Variant of Record" and "Variant of Variant" by -writing a parser for `Url.t`, combining everything we've done so far: - -```{=html} - -``` -``` ocaml -module Url = struct - type t = - | Homepage - | Discussion of Id.t - | Search of Search_params.t - | User of User_page.t - | Admin of Admin_page.t - [@@deriving typed_variants, sexp, equal] - - let parser_for_variant : type a. a Typed_variant.t -> a Parser.t = function - | Homepage -> Parser.end_of_path Parser.unit - | Discussion -> Parser.from_path (Value_parser.sexpable (module Id)) - | Search -> search_params_parser - | User -> user_page_parser - | Admin -> admin_page_parser - ;; -end - -let parser = Parser.Variant.make (module Url) - -let%expect_test _ = - Parser.check_ok_and_print_urls_or_errors parser; - [%expect - {| - URL parser looks good! - ┌──────────────────────────────────────────────────────────────────────────────────────────┐ - │ All urls │ - ├──────────────────────────────────────────────────────────────────────────────────────────┤ - │ / │ - │ /admin/edit_user/ │ - │ /admin/settings │ - │ /discussion/ │ - │ /search/?search.author_id=>&search.categories=> │ - │ /user//posts │ - │ /user//profile │ - └──────────────────────────────────────────────────────────────────────────────────────────┘ - |}] -;; -``` - -The last new parsing util we'll introduce for now is -`Parser.end_of_path`, which declares the end of a route. There's also a -`Parser.with_remaining_path`, which requires that the path segments -match some constant list of strings. - -## Start With The Types - -We said this towards the start of this guide, but `uri_parsing` is -type-first, not route first. You'll get the best results if you: - -- Design a type that best fits the navigational needs of your web UI -- Build up a `Parser.t` for it, and get it to type-check and pass - tests -- Make small tweaks to the subparser definitions until the generated - routes match what you want. - -## Versioned Parsers - -You probably won't get your navigational state type right the first -time. Let's say the first version of your forum looked like this: - -```{=html} - -``` -``` ocaml -module Old_url = struct - type t = - | Homepage - | Post of Id.t - | Search of string - [@@deriving typed_variants, sexp, equal] - - let parser_for_variant : type a. a Typed_variant.t -> a Parser.t = function - | Homepage -> Parser.end_of_path Parser.unit - | Post -> Parser.from_path (Value_parser.sexpable (module Id)) - | Search -> Parser.from_path Value_parser.string - ;; -end - -let old_parser = Parser.Variant.make (module Old_url) - -let%expect_test _ = - Parser.check_ok_and_print_urls_or_errors old_parser; - [%expect - {| - URL parser looks good! - ┌──────────────────┐ - │ All urls │ - ├──────────────────┤ - │ / │ - │ /post/ │ - │ /search/ │ - └──────────────────┘ - |}] -;; -``` - -`Versioned_parser.t` allows you to update to the new `Url.t` without -breaking old URLs: - -```{=html} - -``` -``` ocaml -let v1_parser = Versioned_parser.first_parser old_parser - -let v2_parser = - Versioned_parser.new_parser parser ~previous:v1_parser ~f:(function - | Homepage -> Homepage - | Post id -> Discussion id - | Search query -> Search { query; author_id = None; categories = [] }) -;; - -let%expect_test _ = - Versioned_parser.check_ok_and_print_urls_or_errors v2_parser; - [%expect - {| - URL parser looks good! - ┌──────────────────────────────────────────────────────────────────────────────────────────┐ - │ All urls │ - ├──────────────────────────────────────────────────────────────────────────────────────────┤ - │ / │ - │ /admin/edit_user/ │ - │ /admin/settings │ - │ /discussion/ │ - │ /search/?search.author_id=>&search.categories=> │ - │ /user//posts │ - │ /user//profile │ - └──────────────────────────────────────────────────────────────────────────────────────────┘ - - | - falls back to - | - v - - URL parser looks good! - ┌──────────────────┐ - │ All urls │ - ├──────────────────┤ - │ / │ - │ /post/ │ - │ /search/ │ - └──────────────────┘ - |}] -;; -``` - -## Testing Your Parser - -In every example, we've included an expect test that calls -`Parser.check_ok_and_print_urls_or_errors`. This will analyze your -`Parser.t` for any ambiguous / duplicated invalid cases, and raise an -error if there are issues. If everything is ok, it will print the -generated routes. It's also great for spotting regressions / -unintentional URL changes. - -You should have such an inline expect test for your app's main parser, -and for any interesting subparsers you might want to debug. - -## Path vs Query Params - -`uri_parsing` can pull data from 2 [parts of the -URL](https://developer.mozilla.org/en-US/docs/Learn/Common_questions/What_is_a_URL): - -- Path, e.g. `path/to/my/file` -- Query, e.g. `?key1=value1&key2=value2&key3=value3` - -The path is parsed as a `string list` by splitting on `/`. The query is -a `string list String.Map.t`, because every query key can correspond to -multiple string, e.g. `"?a=1,2,3"`. Some browsers also support -`"?a=1&a=2&a=3"`, but this is -[inconsistent](https://stackoverflow.com/questions/32572264/what-might-be-shufflling-my-query-string-parameters-constructed-in-javascript), -so `uri_parsing` does not support it. - -When should you put data in the path vs. in the query? There's no right -and wrong answer, ultimately it's a matter of taste: a URL like -`website.com/profile/capybara_lover123` feels better than -`website.com?profile=capybara_lover123` and likewise `posts?after=dec-2` -is preferable to `posts/after/dec-2`. - -A good rule of thumb is that paths should be used for things that are -"resource-like", and query-parameters be used for optional or -configurable parameters to that resource. diff --git a/docs/how_to/url_var.md b/docs/how_to/url_var.md deleted file mode 100644 index 8610cbb4..00000000 --- a/docs/how_to/url_var.md +++ /dev/null @@ -1,192 +0,0 @@ -# URL Parsing / Routing - -Most web UIs use the URL to navigate between pages. This has a few -benefits over just storing "which page am I on" state: - -1. Users can save and share links to specific navigational states - within the app -2. The browser's forward and back buttons allow quick historical - navigation -3. Reading and editing URLs can be used (as a last resort maybe) for - precise navigation - -URLs are strings, but we usually want to use some OCaml data structure -to represent the navigational state. We'll need `parse_exn` and -`unparse` functions to convert between `Uri.t`s and our custom `t`. - -It's critical that `parse_exn` and `unparse` "roundtrip": -`unparse (parse_exn (x))` must equal `x`, and `parse_exn (unparse (y))` -must equal `y`. - -## Writing Parsers By Hand - -Imagine you have a site with the following URLs: - -- `/search?q=capybara` -- `/settings` - -We could represent this as an OCaml type: - -```{=html} - -``` -``` ocaml - type t = - | Homepage - | Search of string - [@@deriving sexp, equal] -``` - -Then, we need to write `parse_exn` and `unparse` functions. `Url_var` -will actually extract out the path and query from the URL into a -`Url_var.Components.t`: - -```{=html} - -``` -``` ocaml - module Components : sig - type t = - { path : string - ; query : string list String.Map.t - ; fragment : string option - } - end -``` - -So we need to write functions mapping between `Components.t` and our -custom `t`: - -```{=html} - -``` -``` ocaml - let parse_exn ({ path; query; _ } : Url_var.Components.t) : t = - let path = String.split path ~on:'/' in - match path with - | [ "home" ] -> Homepage - | [ "search" ] -> - (match Map.find (query : _ String.Map.t) "q" with - | Some [ query ] -> Search query - | None | Some [] -> failwith "search missing query param" - | Some (_ :: _ :: _) -> failwith "search with too many query params") - | _ -> failwith "unknown path" - ;; - - let unparse (t : t) : Url_var.Components.t = - match t with - | Homepage -> Url_var.Components.create ~path:"home" () - | Search query -> - Url_var.Components.create - ~path:"search" - ~query:(String.Map.singleton "q" [ query ]) - () - ;; -``` - -You'll want to write your own tests to verify that these functions -roundtrip correctly. - -## Generating Parsers with `uri_parsing` - -The [`uri_parsing` library](./uri_parsing.mdx) allows you to specify a -type, and provide rules for *how* it should be parsed. It generates -`parse_exn`/`unparse` functions that roundtrip, and expect test helper -functions that verify everything is unambiguous, and list the set of -routes. - -You give up some control over what your routes will look like though. - -## Creating a `Url_var.t` - -The `bonsai_web_ui_url_var` library provides a `Url_var.t` type, which -is a global variable that stores the URL. - -`Url_var.t`s can only be created in a web-browser. This means that if -you've factored your app out into a standard `lib/`, `test/` `bin/` -structure, then the `Url_var.Typed.make` function should be located in -`bin/`. If you stick it in the `lib/` section, then tests in `test/` may -fail because the url-var can't deal with being in an expect-test -environment. - -### From a Hand-Written Parser - -You can use `Url_var.create_exn`: - -```{=html} - -``` -``` ocaml - module type T = sig - type t [@@deriving sexp, equal] - end - - module type S = sig - include T - - val parse_exn : Components.t -> t - val unparse : t -> Components.t - end - - val create_exn : (module S with type t = 'a) -> fallback:'a -> 'a Url_var.t -``` - -### From a `Uri_parsing.Versioned_parser` - -You can use `Url_var.Typed.make`: - -```{=html} - -``` -``` ocaml - module Typed : sig - val make - : ?on_fallback_raises:'a - -> ?encoding_behavior:Uri_parsing.Percent_encoding_behavior.t - -> (module T with type t = 'a) - -> 'a Uri_parsing.Versioned_parser.t - -> fallback:(Exn.t -> Components.t -> 'a) - -> 'a Url_var.t - end -``` - -## Using `Url_Var.t` - -A `Url_var.t` is a mutable, global variable that provides a -Bonsai-friendly API for acccessing and modifying it: - -```{=html} - -``` -``` ocaml - val value : 'a Url_var.t -> 'a Bonsai.t - val set_effect : ?how:[ `Push | `Replace ] -> 'a Url_var.t -> 'a -> unit Effect.t -``` - -Once you have your `'a Bonsai.t`, all you need to do is [`match%sub` on -it](../guide/05-control_flow.mdx), and you have a router! - -Changing the `Url_var.t` will automatically update the [browser -history](https://developer.mozilla.org/en-US/docs/Web/API/History_API), -so that browser navigation controls work properly. - -```{=html} -``` -## Server Config Pitfalls - -Before client-side navigation via `Url_var.t` can kick in, the web -server needs to handle your request. If it has its own routing, this can -cause problems. Since Bonsai does not currently support server-side -rendering, you might need to disable server-side routing. - -If using `cohttp_static_handler`'s `Single_page_handler`, set -`` ~on_unknown_url:`Index ``: - - Single_page_handler.create_handler - ~title: - ~assets:<assets> - ~on_unknown_url:`Index - -```{=html} -</aside> -``` diff --git a/docs/upgrade/local-graph.md b/docs/upgrade/local-graph.md deleted file mode 100644 index 8812b93c..00000000 --- a/docs/upgrade/local-graph.md +++ /dev/null @@ -1,103 +0,0 @@ -Bonsai's new local\_ graph API is finally here! We recommend looking -through the[new mli -file](https://github.com/janestreet/bonsai/blob/master/src/bonsai.mli) -to see how types have changed. - -## Moving from `Value`/`Computation` to `local_ Bonsai.graph` and `Bonsai.t` - -You can convert your codebase to Bonsai's new API one file at a time! -The process is fairly mechanical and straightforward: - -1. Replace any `open! Bonsai_web` with `open! Bonsai_web.Cont` -2. Rename any `'a Value.t` to `'a Bonsai.t` -3. Rename any `'a Computation.t` to - `local_ Bonsai.graph -> 'a Bonsai.t` -4. For the most part, `let%sub` is no longer needed. Instead, - instantiate Bonsai components by passing `graph` to them. -5. `let%arr`s don't need to be modified at all. They work like fancy - `let%map`s. See below for more discussion on this topic. -6. Components can now return tuples / records of `Bonsai.t`s! If you - were previously returning `('a * 'b) Bonsai.t`, consider returning - `(a Bonsai.t * 'b Bonsai.t)` instead. This is because we no longer - need `let%sub`, so there's no longer anything enforcing that our - components must return a single `Computation.t`. - -Any Bonsai component that uses Bonsai primitives, either directly or -transitively, will need a `local_ graph` parameter. This is fine and -expected. - -If anything breaks or you're unsure of something, please reach out! - -## let%map vs let%arr - -You might have noticed that we have removed all of the `let%sub`s but -still have `let%arr`s. Previously `let%map`s were always considered -harmful, and in the `local_ graph` that's no longer the case! `let%map` -and `let%arr` have the same type and can be used interchangebly in the -simple case of `let%map_or_arr foo = foo in`. This is incredibly nice -for newcomers who are familiar with `let%map` from the various common -monads which support it. That said, `let%arr` still has some fancy -support for incremental computation built in! - -`let%arr` has performance benefits over `let%map` when you are pattern -matching: `let%arr { a; _ } = r` will only trigger incremental updates -when `a` changes, while `let%map` will trigger when any field in `r` -changes. As a guideline: if anything on the left side of your `let%arr`s -is a pattern with ignored parts (e.g. `let%arr a, _ = ...`; -`let%arr {a; _} = ...`), keep them as `let%arr`s for now. - -## Patterns - -We continue to recommend splitting non-trivial stateful components up -into modular pieces when possible. This should be even more flexible -with the `local_ graph` API. - -It's also often good practice to split the part of your component that -generates vdom view into a separate, pure OCaml function. - -More pattterns to come soon! - -## API Changes - -Alongside the new `local_ graph` API, we'd like to do some housekeeping -on the APIs Bonsai provides, and how they are organized. Some of these -are new features that are now possible because of `local_ graph`, others -are just cleanup we've been meaning to do for a while. - -### Changed - -- `Bonsai.yoink` -\> `Bonsai.peek`: still uses the same api (with - graph changes) - just a new name! Peek is a better representation of - what is happening and is more inline with what this operation is - called on other data structures (stacks, Deferred.t, Mvar.t, etc). -- Most `Bonsai.Value.t` combinators are now available directly in the - `Bonsai` module for use with `Bonsai.t`s. - -### Removed - -Many combinators for dealing with `Bonsai.Value.t` and -`Bonsai.Computation` are no longer needed, and are not included in the -new API. If you think we've forgotten something useful, please reach -out! - -### Known Issues - -#### Top-level `match%sub` - -Top-level `match%sub`'s raise an exception on app startup. For example: - -**Raises**: - -`ocaml skip let component = match%sub ... with | ... -> ...` - -**OK**: - -`ocaml skip let component (local_ graph) = match%sub ... with | ... -> ...` - -**OK**: (if only called from inside a Bonsai context) - -`ocaml skip let component () = match%map ... with | ... -> ...` - -**OK**: - -`ocaml skip let component = match%map ... with | ... -> ...` diff --git a/docs/upgrade/readme.md b/docs/upgrade/readme.md deleted file mode 100644 index f049441c..00000000 --- a/docs/upgrade/readme.md +++ /dev/null @@ -1,3 +0,0 @@ -# Bonsai Docs: Upgrade Guides - -Learn about how to upgrade between various versions of Bonsai's API. diff --git a/dune b/dune deleted file mode 100644 index e69de29b..00000000 diff --git a/examples/accordion/dune b/examples/accordion/dune deleted file mode 100644 index d4c61c86..00000000 --- a/examples/accordion/dune +++ /dev/null @@ -1,6 +0,0 @@ -(executables - (modes byte exe) - (names main) - (libraries async_js bonsai_web bonsai_web_ui_accordion bonsai_web_ui_gallery) - (preprocess - (pps ppx_jane js_of_ocaml-ppx ppx_bonsai ppx_css ppx_demo))) diff --git a/examples/accordion/index.html b/examples/accordion/index.html deleted file mode 100644 index 5d15f053..00000000 --- a/examples/accordion/index.html +++ /dev/null @@ -1,11 +0,0 @@ -<!DOCTYPE html> -<html> - <head> - <meta charset="UTF-8"> - <title>Hello, Accordion! - - - -
- - diff --git a/examples/accordion/main.ml b/examples/accordion/main.ml deleted file mode 100644 index d10bba60..00000000 --- a/examples/accordion/main.ml +++ /dev/null @@ -1,87 +0,0 @@ -open! Core -open! Bonsai_web.Cont -open! Bonsai.Let_syntax -module Gallery = Bonsai_web_ui_gallery - -module Basic_accordion = struct - let name = "Basic Accordion Usage" - let description = "Toggle the accordion by clicking its title bar" - - let view graph = - let vdom, demo = - [%demo - let%sub { view; is_open = _; open_ = _; toggle = _; close = _ } = - Bonsai_web_ui_accordion.component - ~starts_open:true - ~title: - (Bonsai.return (Vdom.Node.text "I am an accordion, click me to toggle!")) - ~content:(fun _graph -> Bonsai.return (Vdom.Node.text "I am the content!")) - () - graph - in - view] - in - Bonsai.map vdom ~f:(fun vdom -> vdom, demo) - ;; - - let selector = None - let filter_attrs = Some (fun k _ -> not (String.is_prefix k ~prefix:"style")) -end - -module Accordion_with_controls = struct - let name = "Accordion With External Controls" - - let description = - "You can also control the accordion programmatically. Try clicking the buttons below." - ;; - - let view graph = - let computation, demo = - let vbox = View.vbox ~gap:(`Em 1) in - let hbox = View.hbox ~gap:(`Em_float 0.5) in - [%demo - let theme = View.Theme.current graph in - let accordion = - Bonsai_web_ui_accordion.component - ~starts_open:false - ~title:(Bonsai.return (Vdom.Node.text "Important!")) - ~content:(fun _graph -> Bonsai.return (Vdom.Node.text "Wow, very important")) - () - graph - in - let%arr { view; is_open; open_; toggle; close } = accordion - and theme = theme in - let open_button = View.button theme ~disabled:is_open ~on_click:open_ "Open" in - let toggle_button = View.button theme ~on_click:toggle "Toggle" in - let close_button = - View.button theme ~disabled:(not is_open) ~on_click:close "Close" - in - vbox [ hbox [ open_button; toggle_button; close_button ]; view ]] - in - Bonsai.map computation ~f:(fun vdom -> vdom, demo) - ;; - - let selector = None - let filter_attrs = Some (fun k _ -> not (String.is_prefix k ~prefix:"style")) -end - -let component graph = - let%sub theme, theme_picker = Gallery.Theme_picker.component () graph in - View.Theme.set_for_app - theme - (Gallery.make_sections - ~theme_picker - [ ( "Accordion" - , {| Accordions can be used to toggle the visibility of different parts of your - UI, which can lead to more compact and user-friendly user experiences|} - , [ Gallery.make_demo (module Basic_accordion) - ; Gallery.make_demo (module Accordion_with_controls) - ] ) - ]) - graph -;; - -let () = - Async_js.init (); - Bonsai_web.Start.start component -;; diff --git a/examples/accordion/main.mli b/examples/accordion/main.mli deleted file mode 100644 index 8b434a57..00000000 --- a/examples/accordion/main.mli +++ /dev/null @@ -1 +0,0 @@ -(* This file intentionally left blank *) diff --git a/examples/animation/dune b/examples/animation/dune deleted file mode 100644 index 796d8545..00000000 --- a/examples/animation/dune +++ /dev/null @@ -1,6 +0,0 @@ -(executables - (modes byte exe) - (names main) - (libraries bonsai_web bonsai_experimental_animation bonsai_web_ui_form) - (preprocess - (pps ppx_bonsai ppx_jane))) diff --git a/examples/animation/index.html b/examples/animation/index.html deleted file mode 100644 index f648a4d2..00000000 --- a/examples/animation/index.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - - Hello, Animation! - - - -
- - diff --git a/examples/animation/main.ml b/examples/animation/main.ml deleted file mode 100644 index 8e648586..00000000 --- a/examples/animation/main.ml +++ /dev/null @@ -1,75 +0,0 @@ -open! Core -open! Bonsai_web.Cont -open Bonsai.Let_syntax -module Animation = Bonsai_experimental_animation -module Form = Bonsai_web_ui_form.With_automatic_view - -let component graph = - let interpolator_form = - Form.Elements.Dropdown.enumerable - (module Animation.Interpolator) - ~init:`First_item - graph - in - let text_picker = - Form.Elements.Textbox.string ~allow_updates_when_focused:`Never () graph - in - let text_picker = - Form.Dynamic.with_default (Bonsai.return "Hello Animation!") text_picker graph - in - let interpolator = - interpolator_form >>| Form.value_or_default ~default:Animation.Interpolator.Linear - in - let%sub { value; animate } = - Animation.Advanced.make - ~fallback:(Bonsai.return 0.0) - ~interpolate:Animation.Interpolatable.float - graph - in - let forward, set_forward = - Bonsai.state true ~sexp_of_model:[%sexp_of: Bool.t] ~equal:[%equal: Bool.t] graph - in - let get_forward = Bonsai.peek forward graph in - let get_interpolator = Bonsai.peek interpolator graph in - let get_things_started = - let%arr animate = animate - and get_forward = get_forward - and get_interpolator = get_interpolator - and set_forward = set_forward in - let rec switch_directions () = - let%bind.Effect forward = - match%bind.Effect get_forward with - | Active forward -> Effect.return forward - | Inactive -> Effect.never - in - let%bind.Effect interpolator = - match%bind.Effect get_interpolator with - | Active interpolator -> Effect.return interpolator - | Inactive -> Effect.never - in - let%bind.Effect () = set_forward (not forward) in - let target = if forward then 100.0 else 0.0 in - let duration = `For (Time_ns.Span.of_sec 0.5) in - animate ~with_:interpolator ~after_finished:(switch_directions ()) duration target - in - switch_directions () - in - let () = Bonsai.Edge.lifecycle ~on_activate:get_things_started graph in - let%arr value = value - and text_picker = text_picker - and interpolator_form = interpolator_form in - let margin = Vdom.Attr.style (Css_gen.margin_left (`Px_float value)) in - let color = - let v = Float.to_int (value /. 100.0 *. 255.0) in - Vdom.Attr.style (Css_gen.color (`RGBA (Css_gen.Color.RGBA.create ~r:v ~g:v ~b:v ()))) - in - let text = Form.value_or_default text_picker ~default:"Marquee" in - Vdom.Node.div - [ Form.view_as_vdom text_picker - ; Form.view_as_vdom interpolator_form - ; Vdom.Node.h1 ~attrs:[ margin ] [ Vdom.Node.text text ] - ; Vdom.Node.h1 ~attrs:[ color ] [ Vdom.Node.text text ] - ] -;; - -let () = Bonsai_web.Start.start component diff --git a/examples/beforeunload/dune b/examples/beforeunload/dune deleted file mode 100644 index 9349105a..00000000 --- a/examples/beforeunload/dune +++ /dev/null @@ -1,6 +0,0 @@ -(executables - (modes byte exe) - (names main) - (libraries bonsai_web virtual_dom) - (preprocess - (pps ppx_jane))) diff --git a/examples/beforeunload/index.html b/examples/beforeunload/index.html deleted file mode 100644 index 80875850..00000000 --- a/examples/beforeunload/index.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - - Hello, Beforeunload! - - - -
- - diff --git a/examples/beforeunload/main.ml b/examples/beforeunload/main.ml deleted file mode 100644 index c28acf18..00000000 --- a/examples/beforeunload/main.ml +++ /dev/null @@ -1,13 +0,0 @@ -open! Core -open! Bonsai_web.Cont - -let component _graph = - Bonsai.return - (Vdom.Node.div - ~attrs: - [ Vdom.Attr.Global_listeners.beforeunload (fun _ -> Effect.return `Show_warning) - ] - [ Vdom.Node.text "attempting to leave this page will show a warning" ]) -;; - -let () = Bonsai_web.Start.start component diff --git a/examples/beforeunload/main.mli b/examples/beforeunload/main.mli deleted file mode 100644 index 74bb7298..00000000 --- a/examples/beforeunload/main.mli +++ /dev/null @@ -1 +0,0 @@ -(*_ This signature is deliberately empty. *) diff --git a/examples/better_console_errors/dune b/examples/better_console_errors/dune deleted file mode 100644 index 7e432bb4..00000000 --- a/examples/better_console_errors/dune +++ /dev/null @@ -1,6 +0,0 @@ -(executables - (modes byte exe) - (names main) - (libraries bonsai_web async_js) - (preprocess - (pps ppx_jane))) diff --git a/examples/better_console_errors/index.html b/examples/better_console_errors/index.html deleted file mode 100644 index 55e92077..00000000 --- a/examples/better_console_errors/index.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - Hello, Bonsai! - - - -
- - Look in the dev console! - - diff --git a/examples/better_console_errors/main.ml b/examples/better_console_errors/main.ml deleted file mode 100644 index ec5f93e2..00000000 --- a/examples/better_console_errors/main.ml +++ /dev/null @@ -1,17 +0,0 @@ -open! Core -open! Bonsai_web.Cont - -let my_failing_function () = failwith "oh no" - -let component = - Bonsai.Expert.Var.create () - |> Bonsai.Expert.Var.value - |> Bonsai.map ~f:my_failing_function -;; - -let () = - let () = Async_js.init () in - Bonsai_web.Start.start (fun _graph -> component) -;; - -let () = Async_kernel.upon (Async_js.document_loaded ()) (fun () -> Ui_incr.stabilize ()) diff --git a/examples/better_console_errors/main.mli b/examples/better_console_errors/main.mli deleted file mode 100644 index 74bb7298..00000000 --- a/examples/better_console_errors/main.mli +++ /dev/null @@ -1 +0,0 @@ -(*_ This signature is deliberately empty. *) diff --git a/examples/bonsai_guide_code/bonsai_types.ml b/examples/bonsai_guide_code/bonsai_types.ml deleted file mode 100644 index 32043dd2..00000000 --- a/examples/bonsai_guide_code/bonsai_types.ml +++ /dev/null @@ -1,12 +0,0 @@ -open! Core -module Bonsai = Bonsai.Cont - -let assoc = Bonsai.assoc - -let state_machine0 ~default_model ~apply_action graph = - Bonsai.state_machine0 ~default_model ~apply_action graph -;; - -let peek = Bonsai.peek - -module Url_var = Bonsai_web_ui_url_var diff --git a/examples/bonsai_guide_code/bonsai_types.mli b/examples/bonsai_guide_code/bonsai_types.mli deleted file mode 100644 index 4af4c971..00000000 --- a/examples/bonsai_guide_code/bonsai_types.mli +++ /dev/null @@ -1,73 +0,0 @@ -(* A collection of bonsai type signatures that we'd like to stay up-to-date in the guide - code. *) - -open! Core -module Bonsai := Bonsai.Cont -module Effect := Bonsai.Effect -module Computation_status := Bonsai.Computation_status -module Url_var := Bonsai_web_ui_url_var - -(* $MDX part-begin=assoc *) -val assoc - : ('k, 'cmp) Bonsai.comparator - -> ('k, 'v, 'cmp) Map.t Bonsai.t - -> f:('k Bonsai.t -> 'v Bonsai.t -> Bonsai.graph -> 'result Bonsai.t) - -> Bonsai.graph - -> ('k, 'result, 'cmp) Map.t Bonsai.t -(* $MDX part-end *) - -(* $MDX part-begin=state_machine0 *) -val state_machine0 - : default_model:'model - -> apply_action:('action Bonsai.Apply_action_context.t -> 'model -> 'action -> 'model) - -> Bonsai.graph - -> 'model Bonsai.t * ('action -> unit Effect.t) Bonsai.t -(* $MDX part-end *) - -(* $MDX part-begin=peek *) -val peek : 'a Bonsai.t -> Bonsai.graph -> 'a Computation_status.t Effect.t Bonsai.t -(* $MDX part-end *) - -module Url_var : sig - (* $MDX part-begin=url_var_components *) - module Components : sig - type t = - { path : string - ; query : string list String.Map.t - ; fragment : string option - } - end - - (* $MDX part-end *) - (* $MDX part-begin=url_var_from_handwritten *) - module type T = sig - type t [@@deriving sexp, equal] - end - - module type S = sig - include T - - val parse_exn : Components.t -> t - val unparse : t -> Components.t - end - - val create_exn : (module S with type t = 'a) -> fallback:'a -> 'a Url_var.t - (* $MDX part-end *) - - (* $MDX part-begin=url_var_from_uri_parsing *) - module Typed : sig - val make - : ?on_fallback_raises:'a - -> ?encoding_behavior:Uri_parsing.Percent_encoding_behavior.t - -> (module T with type t = 'a) - -> 'a Uri_parsing.Versioned_parser.t - -> fallback:(Exn.t -> Components.t -> 'a) - -> 'a Url_var.t - end - (* $MDX part-end *) - - (* $MDX part-begin=url_var_usage_api *) - val value : 'a Url_var.t -> 'a Bonsai.t - val set_effect : ?how:[ `Push | `Replace ] -> 'a Url_var.t -> 'a -> unit Effect.t - (* $MDX part-end *) -end diff --git a/examples/bonsai_guide_code/control_flow_examples.ml b/examples/bonsai_guide_code/control_flow_examples.ml deleted file mode 100644 index 5dd242ed..00000000 --- a/examples/bonsai_guide_code/control_flow_examples.ml +++ /dev/null @@ -1,217 +0,0 @@ -open! Core -open! Bonsai_web.Cont -open Bonsai.Let_syntax -module Form = Bonsai_web_ui_form.With_automatic_view - -let counter ~step graph = State_examples.counter ~step graph |> Tuple2.get1 - -(* $MDX part-begin=maybe_show_naive *) -let maybe_show_naive show graph = - let counter = counter ~step:(return 1) graph in - let%arr counter = counter - and show = show in - match show with - | false -> Vdom.Node.none - | true -> counter -;; - -(* $MDX part-end *) - -let show_control f graph = - let show, toggle = Bonsai.toggle ~default_model:false graph in - let%arr toggle = toggle - and content = f show graph in - Vdom.Node.div - [ Vdom.Node.button - ~attrs:[ Vdom.Attr.on_click (fun _ -> toggle) ] - [ Vdom.Node.text "Toggle" ] - ; content - ] -;; - -let () = Util.run (show_control maybe_show_naive) ~id:"maybe_show_naive" - -(* $MDX part-begin=maybe_show *) -let maybe_show show graph = - let counter = counter ~step:(return 1) graph in - match%sub show with - | false -> Bonsai.return Vdom.Node.none - | true -> counter -;; - -(* $MDX part-end *) - -let () = Util.run (show_control maybe_show) ~id:"maybe_show" - -module Show_control_2 = struct - type t = - [ `Count_by_1 - | `Count_by_2 - | `No - ] - [@@deriving equal, sexp] -end - -(* $MDX part-begin=maybe_show_2 *) -let maybe_show_2 show graph = - match%sub show with - | `Count_by_1 -> counter ~step:(return 1) graph - | `Count_by_2 -> counter ~step:(return 2) graph - | `No -> Bonsai.return Vdom.Node.none -;; - -(* $MDX part-end *) - -let show_control_2 f graph = - let form = - Form.Elements.Dropdown.list - (module Show_control_2) - ~equal:[%equal: Show_control_2.t] - (Bonsai.return [ `Count_by_1; `Count_by_2; `No ]) - graph - in - let show = - let%arr form = form in - Form.value_or_default form ~default:`Count_by_1 - in - let%arr content = f show graph - and form = form in - Vdom.Node.div [ Form.view_as_vdom form; content ] -;; - -let () = Util.run (show_control_2 maybe_show_2) ~id:"maybe_show_2" - -(* $MDX part-begin=maybe_show_var *) -let maybe_show_var show graph = - match%sub show with - | `Count_by step -> counter ~step graph - | `No -> Bonsai.return Vdom.Node.none -;; - -(* $MDX part-end *) -(* $MDX part-begin=maybe_show_var_guard *) -let maybe_show_var_guard show graph = - match%sub show with - | `Count_by step when Int.equal step 1 -> counter ~step graph - | `Count_by step when Int.equal step 4 -> counter ~step graph - | `Count_by step -> counter ~step graph - | `No -> Bonsai.return Vdom.Node.none -;; - -(* $MDX part-end *) - -(* $MDX part-begin=maybe_show_var_scope_model *) -let maybe_show_var_scope_model show graph = - match%sub show with - | `Count_by step -> - Bonsai.scope_model - (module Int) - ~on:step - ~for_:(fun graph -> counter ~step graph) - graph - | `No -> Bonsai.return Vdom.Node.none -;; - -(* $MDX part-end *) - -module Show_control_var = struct - type t = - [ `Count_by of int - | `No - ] - [@@deriving equal, sexp] -end - -let show_control_var f graph = - let form = - Form.Elements.Dropdown.list - (module Show_control_var) - ~equal:[%equal: Show_control_var.t] - (Bonsai.return [ `Count_by 1; `Count_by 2; `Count_by 4; `Count_by 8; `No ]) - graph - in - let show = - let%arr form = form in - Form.value_or_default form ~default:(`Count_by 1) - in - let%arr content = f show graph - and form = form in - Vdom.Node.div [ Form.view_as_vdom form; content ] -;; - -let () = Util.run (show_control_var maybe_show_var) ~id:"maybe_show_var" -let () = Util.run (show_control_var maybe_show_var_guard) ~id:"maybe_show_var_guard" - -let () = - Util.run (show_control_var maybe_show_var_scope_model) ~id:"maybe_show_var_scope_model" -;; - -(* $MDX part-begin=maybe_show_dynamic_count *) -let maybe_show_dynamic_count show graph = - match%sub show with - | `Count_by_1 -> counter ~step:(return 1) graph - | `Count_by_2 -> counter ~step:(return 2) graph - | `No -> Bonsai.return Vdom.Node.none -;; - -(* $MDX part-end *) - -let show_control_2 f graph = - let form = - Form.Elements.Dropdown.list - (module Show_control_2) - ~equal:[%equal: Show_control_2.t] - (Bonsai.return [ `Count_by_1; `Count_by_2; `No ]) - graph - in - let show = - let%arr form = form in - Form.value_or_default form ~default:`Count_by_1 - in - let%arr content = f show graph - and form = form in - Vdom.Node.div [ Form.view_as_vdom form; content ] -;; - -let () = Util.run (show_control_2 maybe_show_dynamic_count) ~id:"maybe_show_2" - -(* $MDX part-begin=multiple_counters *) - -let multiple_counters (input : unit Int.Map.t Bonsai.t) graph = - let counters = - Bonsai.assoc - (module Int) - input - ~f:(fun key (_ : unit Bonsai.t) graph -> - let%arr key = key - and counter = State_examples.counter_ui graph in - Vdom.Node.tr - [ Vdom.Node.td [ Vdom.Node.textf "counter #%d:" key ] - ; Vdom.Node.td [ counter ] - ]) - graph - in - let%arr counters = counters in - Vdom.Node.table (Map.data counters) -;; - -(* $MDX part-end *) - -(* $MDX part-begin=multiple_counters_dynamic *) - -let multiple_counters_dynamic graph = - let counter_view, n = State_examples.counter ~step:(Bonsai.return 1) graph in - let map_containing_n_entries = - let%arr n = n in - if n <= 0 - then Int.Map.empty - else List.init n ~f:(fun i -> i, ()) |> Int.Map.of_alist_exn - in - let%arr counter_view = counter_view - and table = multiple_counters map_containing_n_entries graph in - Vdom.Node.div [ counter_view; table ] -;; - -(* $MDX part-end *) - -let () = Util.run multiple_counters_dynamic ~id:"multiple_counters_dynamic" diff --git a/examples/bonsai_guide_code/control_flow_examples.mli b/examples/bonsai_guide_code/control_flow_examples.mli deleted file mode 100644 index bc0db47b..00000000 --- a/examples/bonsai_guide_code/control_flow_examples.mli +++ /dev/null @@ -1 +0,0 @@ -(* this module is used only for side-effects *) diff --git a/examples/bonsai_guide_code/css_examples.ml b/examples/bonsai_guide_code/css_examples.ml deleted file mode 100644 index 2f9cfdbb..00000000 --- a/examples/bonsai_guide_code/css_examples.ml +++ /dev/null @@ -1,187 +0,0 @@ -open! Core -open! Bonsai_web.Cont -open! Bonsai.Let_syntax - -(* $MDX part-begin=ppx_css_inline *) -let view = - Vdom.Node.div - ~attrs: - [ [%css - {| - background-color: tomato; - min-width: 2rem; - min-height: 2rem; - |}] - ] - [ Vdom.Node.text "Very Red Background" ] -;; - -(* $MDX part-end *) - -let () = Util.run_vdom view ~id:"ppx_css_inline" - -(* $MDX part-begin=ppx_css_inline_interpol *) -let box_with_border (color : Css_gen.Color.t) (width : Css_gen.Length.t) = - Vdom.Node.div - ~attrs: - [ [%css - {| - border: %{width#Css_gen.Length} solid %{color#Css_gen.Color}; - |}] - ] - [ Vdom.Node.text "Nice Borders!" ] -;; - -(* $MDX part-end *) - -let () = - Util.run_vdom (box_with_border (`Name "purple") (`Px 2)) ~id:"ppx_css_inline_interpol" -;; - -(* $MDX part-begin=ppx_css_inline_nesting *) -let hoverable_blocks = - let block = - Vdom.Node.div - ~attrs: - [ [%css - {| - background-color: green; - min-width: 2rem; - min-height: 2rem; - border: 1px solid black; - - &:hover { - background-color: tomato; - } - - &:not(:nth-child(odd)):hover { - background-color: purple; - } - |}] - ] - [ Vdom.Node.text "Hoverable" ] - in - Vdom.Node.div (List.init 6 ~f:(fun _ -> block)) -;; - -(* $MDX part-end *) - -let () = Util.run_vdom hoverable_blocks ~id:"ppx_css_inline_nesting" - -(* $MDX part-begin=ppx_css_inline_multiple *) -let multiple_ppx_css = - Vdom.Node.div - ~attrs:[ [%css {|color: red|}] ] - [ Vdom.Node.text "Foo" - ; Vdom.Node.div ~attrs:[ [%css "color: blue"] ] [ Vdom.Node.text "Bar" ] - ] -;; - -(* $MDX part-end *) - -let () = Util.run_vdom multiple_ppx_css ~id:"ppx_css_inline_multiple" - -(* $MDX part-begin=ppx_css_stylesheet *) -module Style = -[%css -stylesheet - {| -@media only screen and (max-width: 300px) { - .container { - display: none; - } -} - -@media only screen and (min-width: 300px) { - .container { - font-size: 10px; - } -} - -@media only screen and (min-width: 600px) { - .container { - font-size: 20px; - } -} - |}] - -let stylesheet_demo = Vdom.Node.div ~attrs:[ Style.container ] [ Vdom.Node.text "Hello" ] - -(* $MDX part-end *) - -let () = Util.run_vdom stylesheet_demo ~id:"ppx_css_stylesheet" - -(* $MDX part-begin=ppx_css_stylesheet_interpol *) - -let stylesheet_interpol small_bg large_bg = - let module Style = - [%css - stylesheet - {| -@media only screen and (max-width: 1200px) { - .container { - background-color: %{small_bg#Css_gen.Color}; - } -} - -@media only screen and (min-width: 1200px) { - .container { - background-color: %{large_bg#Css_gen.Color}; - } -} - |}] - in - Vdom.Node.div ~attrs:[ Style.container ] [ Vdom.Node.text "Hello" ] -;; - -(* $MDX part-end *) - -let () = - Util.run_vdom - (stylesheet_interpol (`Name "purple") (`Name "green")) - ~id:"ppx_css_stylesheet_interpol" -;; - -module _ = struct - (* $MDX part-begin=ppx_css_stylesheet_vars *) - module Style = - [%css - stylesheet - {| -@media only screen and (max-width: 1200px) { - .container { - background-color: var(--small-bg); - } -} - -@media only screen and (min-width: 1200px) { - .container { - background-color: var(--large-bg); - } -} -|}] - - let stylesheet_vars = - Vdom.Node.div - ~attrs: - [ Style.container; Style.Variables.set_all ~large_bg:"green" ~small_bg:"purple" ] - [ Vdom.Node.text "Hello" ] - ;; - - (* $MDX part-end *) - - let () = Util.run_vdom stylesheet_vars ~id:"ppx_css_stylesheet_vars" -end - -(* $MDX part-begin=css_gen_inline *) -let css_gen_inline = - let style : Css_gen.t = - let open Css_gen in - font_size (`Px 15) @> background_color (`Name "red") - in - Vdom.Node.div ~attrs:[ Vdom.Attr.style style ] [ Vdom.Node.text "Hello" ] -;; - -(* $MDX part-end *) - -let () = Util.run_vdom css_gen_inline ~id:"css_gen_inline" diff --git a/examples/bonsai_guide_code/css_examples.mli b/examples/bonsai_guide_code/css_examples.mli deleted file mode 100644 index bc0db47b..00000000 --- a/examples/bonsai_guide_code/css_examples.mli +++ /dev/null @@ -1 +0,0 @@ -(* this module is used only for side-effects *) diff --git a/examples/bonsai_guide_code/dune b/examples/bonsai_guide_code/dune deleted file mode 100644 index 5f41289a..00000000 --- a/examples/bonsai_guide_code/dune +++ /dev/null @@ -1,9 +0,0 @@ -(executables - (modes byte exe) - (names main) - (libraries bonsai_web async_js core bonsai_web_ui_form legacy_diffable - timezone bonsai_guide_code_lib bonsai_web_ui_url_var) - (preprocess - (pps js_of_ocaml-ppx ppx_css ppx_bonsai ppx_jane ppx_typed_fields)) - (js_of_ocaml - (javascript_files ./resize_iframe.js))) diff --git a/examples/bonsai_guide_code/edge_triggered_examples.ml b/examples/bonsai_guide_code/edge_triggered_examples.ml deleted file mode 100644 index d420b2f3..00000000 --- a/examples/bonsai_guide_code/edge_triggered_examples.ml +++ /dev/null @@ -1,24 +0,0 @@ -open! Core -open! Bonsai_web.Cont -open! Bonsai.Let_syntax - -(* $MDX part-begin=on_change *) -let on_change_demo graph = - let view, value = State_examples.counter ~step:(Bonsai.return 1) graph in - Bonsai.Edge.on_change' - ~equal:Int.equal - ~callback: - (Bonsai.return (fun (prev_value : int option) (new_value : int) -> - match prev_value with - | None -> (* Do nothing on first render*) Effect.Ignore - | Some prev_value -> - Effect.alert - [%string "prev value: %{prev_value#Int}, new value: %{new_value#Int}"])) - value - graph; - view -;; - -(* $MDX part-end *) - -let () = Util.run on_change_demo ~id:"on_change" diff --git a/examples/bonsai_guide_code/edge_triggered_examples.mli b/examples/bonsai_guide_code/edge_triggered_examples.mli deleted file mode 100644 index bc0db47b..00000000 --- a/examples/bonsai_guide_code/edge_triggered_examples.mli +++ /dev/null @@ -1 +0,0 @@ -(* this module is used only for side-effects *) diff --git a/examples/bonsai_guide_code/effect_examples.ml b/examples/bonsai_guide_code/effect_examples.ml deleted file mode 100644 index bfca9379..00000000 --- a/examples/bonsai_guide_code/effect_examples.ml +++ /dev/null @@ -1,44 +0,0 @@ -open! Core -open! Async_kernel -open! Bonsai_web.Cont -open! Bonsai.Let_syntax - -type mouse_event = Js_of_ocaml.Dom_html.mouseEvent Js_of_ocaml.Js.t - -(* $MDX part-begin=clickies *) -let clickies : Vdom.Node.t = - (* This won't run until scheduled... - But it will run every time it is scheduled! *) - let greet_effect = Effect.alert "hello there!" in - Vdom.Node.div - [ Vdom.Node.button - ~attrs:[ Vdom.Attr.on_click (fun (_evt : mouse_event) -> greet_effect) ] - [ Vdom.Node.text "click me!" ] - ; Vdom.Node.button - ~attrs:[ Vdom.Attr.on_click (fun (_evt : mouse_event) -> greet_effect) ] - [ Vdom.Node.text "or me!" ] - ] -;; - -(* $MDX part-end *) -let () = Util.run_vdom clickies ~id:"clickies" - -(* $MDX part-begin=bind_chain *) -let chain_some_effects - (a : int Effect.t) - (b : int -> bool Effect.t) - (c : unit Effect.t) - (d : unit Effect.t) - : unit Effect.t - = - let%bind.Effect a_return = a in - (* Sometimes we don't care about the effect's return value; - we just want to execute it. *) - let%bind.Effect (_ : bool) = b a_return in - let%bind.Effect () = c in - d -;; - -(* $MDX part-end *) - -let () = ignore chain_some_effects diff --git a/examples/bonsai_guide_code/effect_examples.mli b/examples/bonsai_guide_code/effect_examples.mli deleted file mode 100644 index bc0db47b..00000000 --- a/examples/bonsai_guide_code/effect_examples.mli +++ /dev/null @@ -1 +0,0 @@ -(* this module is used only for side-effects *) diff --git a/examples/bonsai_guide_code/effect_stale_examples.ml b/examples/bonsai_guide_code/effect_stale_examples.ml deleted file mode 100644 index 33491024..00000000 --- a/examples/bonsai_guide_code/effect_stale_examples.ml +++ /dev/null @@ -1,74 +0,0 @@ -open! Core -open! Bonsai_web.Cont -open! Bonsai.Let_syntax - -let complicated_transformation str_input int_input = - let%arr str_input = str_input - and int_input = int_input in - let derived_value = - String.fold str_input ~init:0 ~f:(fun acc c -> acc + Char.to_int c) - |> fun x -> x * int_input mod 294 - in - [%string - "Input: %{int_input#Int}; Other Input: %{str_input#String}; Derived value: \ - %{derived_value#Int} "] -;; - -(* $MDX part-begin=stale_closed_naive *) -let set_and_run_effect_naive (other_input : string Bonsai.t) graph = - let count, set_state = Bonsai.state 0 graph in - let computed = complicated_transformation other_input count in - let set_and_alert = - let%arr computed = computed - and set_state = set_state in - fun new_state -> - let%bind.Effect () = set_state new_state in - Effect.alert computed - in - let%arr count = count - and set_and_alert = set_and_alert in - Vdom.Node.div - [ Vdom.Node.text [%string "Counter value: %{count#Int}"] - ; Vdom.Node.button - ~attrs:[ Vdom.Attr.on_click (fun _ -> set_and_alert (count + 1)) ] - [ Vdom.Node.text "increment count" ] - ] -;; - -(* $MDX part-end *) - -let () = - Util.run - (set_and_run_effect_naive (Bonsai.return "Other Input")) - ~id:"stale_closed_naive" -;; - -(* $MDX part-begin=stale_closed_peek *) -let set_and_run_effect_peek (other_input : string Bonsai.t) graph = - let count, set_state = Bonsai.state 0 graph in - let computed = complicated_transformation other_input count in - let peek_computed = Bonsai.peek computed graph in - let set_and_alert = - let%arr peek_computed = peek_computed - and set_state = set_state in - fun new_state -> - let%bind.Effect () = set_state new_state in - match%bind.Effect peek_computed with - | Active computed -> Effect.alert computed - | Inactive -> Effect.Ignore - in - let%arr count = count - and set_and_alert = set_and_alert in - Vdom.Node.div - [ Vdom.Node.text [%string "Counter value: %{count#Int}"] - ; Vdom.Node.button - ~attrs:[ Vdom.Attr.on_click (fun _ -> set_and_alert (count + 1)) ] - [ Vdom.Node.text "increment count" ] - ] -;; - -(* $MDX part-end *) - -let () = - Util.run (set_and_run_effect_peek (Bonsai.return "Other Input")) ~id:"stale_closed_peek" -;; diff --git a/examples/bonsai_guide_code/effect_stale_examples.mli b/examples/bonsai_guide_code/effect_stale_examples.mli deleted file mode 100644 index bc0db47b..00000000 --- a/examples/bonsai_guide_code/effect_stale_examples.mli +++ /dev/null @@ -1 +0,0 @@ -(* this module is used only for side-effects *) diff --git a/examples/bonsai_guide_code/form_examples.ml b/examples/bonsai_guide_code/form_examples.ml deleted file mode 100644 index 7b1c89f5..00000000 --- a/examples/bonsai_guide_code/form_examples.ml +++ /dev/null @@ -1,217 +0,0 @@ -open! Core -open! Async_kernel -open! Bonsai_web.Cont -open Bonsai.Let_syntax -module Form = Bonsai_web_ui_form.With_automatic_view - -(* $MDX part-begin=form_textbox_value *) -let textbox_value graph = - let textbox = - Form.Elements.Textbox.string ~allow_updates_when_focused:`Always () graph - in - let%arr textbox = textbox >>| Form.label "my textbox" in - let value = Form.value textbox in - Vdom.Node.div - [ Form.view_as_vdom textbox - ; Vdom.Node.sexp_for_debugging ([%sexp_of: string Or_error.t] value) - ] -;; - -(* $MDX part-end *) -let () = Util.run textbox_value ~id:"form_textbox_value" - -let alert = - let eff = - Effect.of_sync_fun (fun s -> - Js_of_ocaml.Dom_html.window##alert (Js_of_ocaml.Js.string s)) - in - fun s -> eff s -;; - -(* $MDX part-begin=view_with_submission *) - -let textbox_on_submit graph = - let textbox = - Form.Elements.Textbox.string ~allow_updates_when_focused:`Always () graph - in - let%arr textbox = textbox in - textbox - |> Form.label "text to alert" - |> Form.view_as_vdom ~on_submit:(Form.Submit.create () ~f:alert) -;; - -(* $MDX part-end *) - -let () = Util.run textbox_on_submit ~id:"form_textbox_on_submit" - -(* $MDX part-begin=form_set *) -let form_set graph = - let textbox = - Form.Elements.Textbox.string ~allow_updates_when_focused:`Always () graph - in - let%arr textbox = textbox >>| Form.label "my textbox" in - Vdom.Node.div - [ Form.view_as_vdom textbox - ; Vdom.Node.button - ~attrs:[ Vdom.Attr.on_click (fun _ -> Form.set textbox "hello world") ] - [ Vdom.Node.text "click me" ] - ] -;; - -(* $MDX part-end *) - -let () = Util.run form_set ~id:"form_set" - -(* $MDX part-begin=form_two_textboxes *) -let two_textboxes graph = - let textbox_a = - Form.Elements.Textbox.string ~allow_updates_when_focused:`Always () graph - in - let textbox_b = - Form.Elements.Textbox.string ~allow_updates_when_focused:`Always () graph - in - let both_textboxes = - Bonsai.map2 - ~f:Form.both - (textbox_a >>| Form.label "textbox a") - (textbox_b >>| Form.label "textbox b") - in - let%arr both_textboxes = both_textboxes in - let text_a, text_b = Form.value_or_default both_textboxes ~default:("", "") in - let display = Vdom.Node.textf "a: %s, b: %s" text_a text_b in - Vdom.Node.div - ~attrs:[ Vdom.Attr.style (Css_gen.display `Inline_grid) ] - [ Form.view_as_vdom both_textboxes; display ] -;; - -(* $MDX part-end *) - -let () = Util.run two_textboxes ~id:"form_two_textboxes" - -(* $MDX part-begin=record_form_type *) - -type t = - { some_string : string - ; an_int : int - ; on_or_off : bool - } -[@@deriving typed_fields, sexp_of] - -(* $MDX part-end *) - -(* $MDX part-begin=record_form *) -module Record = struct - type t = - { some_string : string - ; an_int : int - ; on_or_off : bool - } - [@@deriving typed_fields, sexp_of] - - let form : Bonsai.graph -> t Form.t Bonsai.t = - Form.Typed.Record.make - (module struct - (* reimport the module that typed_fields just derived *) - module Typed_field = Typed_field - - let label_for_field = `Inferred - - (* provide a form computation for each field in the record *) - let form_for_field : type a. a Typed_field.t -> Bonsai.graph -> a Form.t Bonsai.t = - fun typed_field graph -> - match typed_field with - | Some_string -> - Form.Elements.Textbox.string ~allow_updates_when_focused:`Always () graph - | An_int -> - Form.Elements.Number.int - ~default:0 - ~step:1 - ~allow_updates_when_focused:`Always - () - graph - | On_or_off -> Form.Elements.Checkbox.bool ~default:false () graph - ;; - end) - ;; -end - -(* $MDX part-end *) - -(* $MDX part-begin=variant_form *) -module Variant = struct - type t = - | A - | B of int - | C of string - [@@deriving typed_variants, sexp_of] - - let form : Bonsai.graph -> t Form.t Bonsai.t = - Form.Typed.Variant.make - (module struct - (* reimport the module that typed_fields just derived *) - module Typed_variant = Typed_variant - - let label_for_variant = `Inferred - let initial_choice = `First_constructor - - (* provide a form computation for constructor in the variant *) - let form_for_variant - : type a. a Typed_variant.t -> Bonsai.graph -> a Form.t Bonsai.t - = - fun typed_field graph -> - match typed_field with - | A -> Form.return () |> Bonsai.return - | B -> Form.Elements.Textbox.int ~allow_updates_when_focused:`Always () graph - | C -> Form.Elements.Textbox.string ~allow_updates_when_focused:`Always () graph - ;; - end) - ;; -end - -(* $MDX part-end *) - -let () = - Util.run - (fun graph -> - let%arr record_form = Record.form graph in - Form.view_as_vdom record_form) - ~id:"record_form" -;; - -(* $MDX part-begin=record_form_view *) -let view_for_form : Bonsai.graph -> Vdom.Node.t Bonsai.t = - fun graph -> - let%arr record_form = Record.form graph - and variant_form = Variant.form graph in - let form = Form.both record_form variant_form in - let value = Form.value form in - Vdom.Node.div - [ Form.view_as_vdom form - ; Vdom.Node.sexp_for_debugging ([%sexp_of: (Record.t * Variant.t) Or_error.t] value) - ] -;; - -(* $MDX part-end *) - -let () = Util.run view_for_form ~id:"record_form_view" - -(* $MDX part-begin=int_textbox *) -let int : Bonsai.graph -> int Form.t Bonsai.t = - fun graph -> - let form = Form.Elements.Textbox.string ~allow_updates_when_focused:`Always () graph in - let%arr form = form in - Form.project form ~parse_exn:Int.of_string ~unparse:Int.to_string -;; - -(* $MDX part-end *) - -let () = - Util.run - (fun graph -> - let%arr form = int graph in - Vdom.Node.div - [ Form.view_as_vdom form - ; Vdom.Node.sexp_for_debugging ([%sexp_of: int Or_error.t] (Form.value form)) - ]) - ~id:"int_textbox" -;; diff --git a/examples/bonsai_guide_code/form_examples.mli b/examples/bonsai_guide_code/form_examples.mli deleted file mode 100644 index bc0db47b..00000000 --- a/examples/bonsai_guide_code/form_examples.mli +++ /dev/null @@ -1 +0,0 @@ -(* this module is used only for side-effects *) diff --git a/examples/bonsai_guide_code/higher_order_examples.ml b/examples/bonsai_guide_code/higher_order_examples.ml deleted file mode 100644 index ea3878a8..00000000 --- a/examples/bonsai_guide_code/higher_order_examples.ml +++ /dev/null @@ -1,83 +0,0 @@ -open! Core -open! Bonsai_web.Cont -open! Bonsai.Let_syntax - -(* $MDX part-begin=hoc_modal *) - -type t = - { view : Vdom.Node.t Bonsai.t - ; open_modal : unit Effect.t Bonsai.t - } - -let modal - ~(title : Vdom.Node.t Bonsai.t) - ~(content : Bonsai.graph -> Vdom.Node.t Bonsai.t) - graph - : t - = - let is_open, set_is_open = Bonsai.state false graph in - let open_modal = - let%arr set_is_open = set_is_open in - set_is_open true - in - let view = - match%sub is_open with - | false -> Bonsai.return Vdom.Node.none - | true -> - (* only instantiate [content] here in the [true] branch *) - let%arr content = content graph - and title = title - and set_is_open = set_is_open in - let close_button = - Vdom.Node.button - ~attrs:[ Vdom.Attr.on_click (fun _ -> set_is_open false) ] - [ Vdom.Node.text "X" ] - in - Vdom.Node.div - ~attrs: - [ [%css - {| - position: fixed; - top: 0; bottom: 0; left: 0; right: 0; - height: fit-content; width: fit-content; - margin: auto; - border: 1px solid black; - background-color: white;|}] - ] - [ Vdom.Node.h1 [ title; close_button ]; content ] - in - { view; open_modal } -;; - -(* $MDX part-end *) - -(* $MDX part-begin=modal_example *) -let modal_example graph = - let title = Bonsai.return (Vdom.Node.text "Hi there!") in - let content graph = - let count, set_count = Bonsai.state 0 graph in - let on_activate = - let%arr count = count - and set_count = set_count in - set_count (count + 1) - in - let () = Bonsai.Edge.lifecycle ~on_activate graph in - let%arr count = count in - Vdom.Node.div - [ Vdom.Node.text [%string "This modal has been opened %{count#Int} times..."] ] - in - let { view = modal_view; open_modal } = modal ~title ~content graph in - let%arr modal_view = modal_view - and open_modal = open_modal in - Vdom.Node.div - ~attrs:[ [%css {|height: 400px;|}] ] - [ modal_view - ; Vdom.Node.button - ~attrs:[ Vdom.Attr.on_click (fun _ -> open_modal) ] - [ Vdom.Node.text "open modal" ] - ] -;; - -(* $MDX part-end *) - -let () = Util.run modal_example ~id:"modal_example" diff --git a/examples/bonsai_guide_code/higher_order_examples.mli b/examples/bonsai_guide_code/higher_order_examples.mli deleted file mode 100644 index bc0db47b..00000000 --- a/examples/bonsai_guide_code/higher_order_examples.mli +++ /dev/null @@ -1 +0,0 @@ -(* this module is used only for side-effects *) diff --git a/examples/bonsai_guide_code/incrementality_examples.ml b/examples/bonsai_guide_code/incrementality_examples.ml deleted file mode 100644 index 4929b8e6..00000000 --- a/examples/bonsai_guide_code/incrementality_examples.ml +++ /dev/null @@ -1,29 +0,0 @@ -open! Core -open! Async_kernel -open! Bonsai_web.Cont -open Bonsai.Let_syntax - -(* $MDX part-begin=int_view *) -let int_view (a : int Bonsai.t) : Vdom.Node.t Bonsai.t = - let%arr a = a in - Vdom.Node.div [ Vdom.Node.text (Int.to_string a) ] -;; - -(* $MDX part-end *) - -let () = Util.run_vdom_val (int_view (Bonsai.return 5)) ~id:"int_view" - -(* $MDX part-begin=sum_and_display *) -let sum_and_display (a : int Bonsai.t) (b : int Bonsai.t) : Vdom.Node.t Bonsai.t = - let%arr a = a - and b = b in - Vdom.Node.textf "%d + %d = %d" a b (a + b) -;; - -(* $MDX part-end *) - -let () = - Util.run_vdom_val - (sum_and_display (Bonsai.return 5) (Bonsai.return 8)) - ~id:"sum_and_display" -;; diff --git a/examples/bonsai_guide_code/incrementality_examples.mli b/examples/bonsai_guide_code/incrementality_examples.mli deleted file mode 100644 index bc0db47b..00000000 --- a/examples/bonsai_guide_code/incrementality_examples.mli +++ /dev/null @@ -1 +0,0 @@ -(* this module is used only for side-effects *) diff --git a/examples/bonsai_guide_code/index.html b/examples/bonsai_guide_code/index.html deleted file mode 100644 index 9cf3c1e3..00000000 --- a/examples/bonsai_guide_code/index.html +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - Hello, Bonsai! - - - - - - - -
- - - - diff --git a/examples/bonsai_guide_code/intro_examples.ml b/examples/bonsai_guide_code/intro_examples.ml deleted file mode 100644 index ae3aadc4..00000000 --- a/examples/bonsai_guide_code/intro_examples.ml +++ /dev/null @@ -1,83 +0,0 @@ -open! Bonsai_web.Cont - -let name_undefined = "Some User" -let new_emails_undefined = 412 -let on_click_undefined = Effect.alert "Not implemented" - -(* $MDX part-begin=message_vdom *) -open! Core -open Virtual_dom - -let message_vdom ~name ~new_emails = - Vdom.Node.div - ~attrs:[ [%css {|font-size: 16px;|}] ] - [ Vdom.Node.textf "hello %s! you have %d new emails" name new_emails ] -;; - -(* $MDX part-end *) -let () = - Util.run_vdom - (message_vdom ~name:name_undefined ~new_emails:new_emails_undefined) - ~id:"message_vdom" -;; - -(* $MDX part-begin=read_email_button *) -let read_email_button ~on_click = - Vdom.Node.button - ~attrs:[ Vdom.Attr.on_click (fun _ -> on_click) ] - [ Vdom.Node.text "Read an email!" ] -;; - -(* $MDX part-end *) -let () = - Util.run_vdom (read_email_button ~on_click:on_click_undefined) ~id:"read_email_button" -;; - -(* $MDX part-begin=emails_bonsai *) -open! Bonsai_web.Cont -open Bonsai.Let_syntax - -let emails_bonsai ~name ~new_emails ~read_email_effect = - let message = - let%arr name = name - and new_emails = new_emails in - message_vdom ~name ~new_emails - in - let%arr message = message - and read_email_effect = read_email_effect in - Vdom.Node.div [ message; read_email_button ~on_click:read_email_effect ] -;; - -(* $MDX part-end *) -let () = - Util.run - (fun _ -> - emails_bonsai - ~name:(return name_undefined) - ~new_emails:(return new_emails_undefined) - ~read_email_effect:(return on_click_undefined)) - ~id:"emails_bonsai" -;; - -(* $MDX part-begin=emails_stateful *) -let emails_stateful ~name graph = - let default_count = 999 in - let (count : int Bonsai.t), (set_count : (int -> unit Effect.t) Bonsai.t) = - Bonsai.state default_count graph - in - let read_email_effect = - let%arr count = count - and set_count = set_count in - set_count (count - 1) - in - emails_bonsai ~name ~new_emails:count ~read_email_effect -;; - -(* $MDX part-end *) -let () = Util.run (emails_stateful ~name:(return name_undefined)) ~id:"emails_stateful" - -(* $MDX part-begin=app *) -let app graph = emails_stateful ~name:(Bonsai.return "User") graph -(* $MDX part-end *) - -let () = Util.run app ~id:"app" diff --git a/examples/bonsai_guide_code/intro_examples.mli b/examples/bonsai_guide_code/intro_examples.mli deleted file mode 100644 index 39d14506..00000000 --- a/examples/bonsai_guide_code/intro_examples.mli +++ /dev/null @@ -1,26 +0,0 @@ -open! Core -open! Bonsai_web.Cont - -(* $MDX part-begin=message_vdom *) -val message_vdom : name:string -> new_emails:int -> Vdom.Node.t -(* $MDX part-end *) - -(* $MDX part-begin=read_email_button *) -val read_email_button : on_click:unit Effect.t -> Vdom.Node.t -(* $MDX part-end *) - -(* $MDX part-begin=emails_bonsai *) -val emails_bonsai - : name:string Bonsai.t - -> new_emails:int Bonsai.t - -> read_email_effect:unit Effect.t Bonsai.t - -> Vdom.Node.t Bonsai.t -(* $MDX part-end *) - -(* $MDX part-begin=emails_stateful *) -val emails_stateful : name:string Bonsai.t -> Bonsai.graph -> Vdom.Node.t Bonsai.t -(* $MDX part-end *) - -(* $MDX part-begin=app *) -val app : Bonsai.graph -> Vdom.Node.t Bonsai.t -(* $MDX part-end *) diff --git a/examples/bonsai_guide_code/lib/dune b/examples/bonsai_guide_code/lib/dune deleted file mode 100644 index 4334c111..00000000 --- a/examples/bonsai_guide_code/lib/dune +++ /dev/null @@ -1,6 +0,0 @@ -(library - (name bonsai_guide_code_lib) - (libraries bonsai_web bonsai_web_test_async bonsai_web_ui_form - async_js.async_test uri_parsing core legacy_diffable timezone) - (preprocess - (pps js_of_ocaml-ppx ppx_css ppx_bonsai ppx_jane ppx_typed_fields))) diff --git a/examples/bonsai_guide_code/lib/readme.md b/examples/bonsai_guide_code/lib/readme.md deleted file mode 100644 index 98fa6660..00000000 --- a/examples/bonsai_guide_code/lib/readme.md +++ /dev/null @@ -1,2 +0,0 @@ -Examples in this subdirectory are not included in the demo executable, so they cannot -produce interactive examples. But they can have expect tests! diff --git a/examples/bonsai_guide_code/lib/rpc_examples.ml b/examples/bonsai_guide_code/lib/rpc_examples.ml deleted file mode 100644 index 4c79d7c6..00000000 --- a/examples/bonsai_guide_code/lib/rpc_examples.ml +++ /dev/null @@ -1,170 +0,0 @@ -open! Core -open! Async_kernel -open! Bonsai_web.Cont -open! Bonsai_web_test -open Bonsai.Let_syntax -open Async_rpc_kernel - -(* $MDX part-begin=protocol-1 *) -let double_rpc = - Rpc.Rpc.create - ~name:"double" - ~version:0 - ~bin_query:[%bin_type_class: int] - ~bin_response:[%bin_type_class: int] - ~include_in_error_count:Only_on_exn -;; - -(* $MDX part-end *) - -(* $MDX part-begin=protocol-2 *) -module Current_time = struct - include String - include Legacy_diffable.Atomic.Make (String) -end - -let current_time_rpc = - Polling_state_rpc.create - ~name:"current_time" - ~version:0 - ~query_equal:[%equal: string] - ~bin_query:[%bin_type_class: string] - (module Current_time) -;; - -(* $MDX part-end *) - -(* $MDX part-begin=implementation-1 *) -let double_implementation = - Rpc.Rpc.implement' double_rpc (fun _connection_state query -> Int.max 1 (query * 2)) -;; - -(* $MDX part-end *) - -(* $MDX part-begin=implementation-2 *) -let current_time_implementation = - Polling_state_rpc.implement - ~on_client_and_server_out_of_sync:print_s - current_time_rpc - (fun _connection_state zone -> - Deferred.return - (Time_ns.to_string_trimmed ~zone:(Timezone.of_string zone) (Time_ns.now ()))) - |> Rpc.Implementation.lift ~f:(fun connection_state -> - connection_state, connection_state) -;; - -(* $MDX part-end *) - -type Rpc_effect.Where_to_connect.Custom.t += Connection - -(* Below is some sleight-of-hand. We want the readers of the guide to think that - we are using [Self], but we don't *actually* want to do that, since it would - require having a server to connect to. Thus, we shadow the text we want to - display with the value we want use. *) - -(* $MDX part-begin=where_to_connect *) -let where_to_connect : Rpc_effect.Where_to_connect.t = Self -(* $MDX part-end *) - -let () = ignore (where_to_connect : Rpc_effect.Where_to_connect.t) -let where_to_connect : Rpc_effect.Where_to_connect.t = Custom Connection - -let connector = - Rpc_effect.Connector.for_test - (Rpc.Implementations.create_exn - ~implementations:[ double_implementation; current_time_implementation ] - ~on_unknown_rpc:`Raise) - ~connection_state:Fn.id -;; - -let custom_connector = function - | Connection -> connector - | _ -> Rpc_effect.Connector.test_fallback -;; - -(* $MDX part-begin=client-1 *) -let double_number_app graph = - let dispatch_double_rpc = - Rpc_effect.Rpc.dispatcher double_rpc ~where_to_connect graph - in - let number, set_number = Bonsai.state 1 graph in - let%arr dispatch_double_rpc = dispatch_double_rpc - and number = number - and set_number = set_number in - Vdom.Node.div - [ Vdom.Node.div [ Vdom.Node.text [%string "The number is: %{number#Int}"] ] - ; Vdom.Node.button - ~attrs: - [ Vdom.Attr.on_click (fun _ -> - match%bind.Effect dispatch_double_rpc number with - | Ok doubled_number -> set_number doubled_number - | Error error -> Effect.of_sync_fun eprint_s [%sexp (error : Error.t)]) - ] - [ Vdom.Node.text "Double the number" ] - ] -;; - -(* $MDX part-end *) - -module Css = [%css stylesheet {| -.error_text { - color: red; -} -|}] - -let zone_form graph = - let module Form = Bonsai_web_ui_form.With_automatic_view in - let form = - Form.Elements.Textbox.string - ~placeholder:(Bonsai.return "timezone") - ~allow_updates_when_focused:`Always - () - graph - in - let form = Form.Dynamic.with_default (Bonsai.return "America/New_York") form graph in - let value = - let%arr form = form in - Form.value_or_default ~default:"America/New_York" form - in - let view = - let%arr form = form in - Form.view_as_vdom form - in - value, view -;; - -(* $MDX part-begin=client-2 *) -let current_time_app graph = - let zone, zone_view = zone_form graph in - let poll = - Rpc_effect.Polling_state_rpc.poll - current_time_rpc - ~equal_query:[%equal: string] - ~equal_response:[%equal: Current_time.t] - ~where_to_connect - ~every:(Time_ns.Span.of_sec 0.1) - zone - graph - in - let%arr { last_ok_response; last_error; inflight_query = _; refresh = _ } = poll - and zone_view = zone_view in - let text = - match last_ok_response with - | Some (zone, current_time) -> - [%string "The current time in the zone '%{zone}' is %{current_time}"] - | None -> "Loading..." - in - let error_view = - match last_error with - | Some (zone, error) -> - Vdom.Node.div - ~attrs:[ Css.error_text ] - [ Vdom.Node.text [%string "Got error when requesting time in zone '%{zone}'"] - ; Vdom.Node.pre [ Vdom.Node.text (Error.to_string_hum error) ] - ] - | None -> Vdom.Node.none - in - Vdom.Node.div [ zone_view; Vdom.Node.div [ Vdom.Node.text text ]; error_view ] -;; - -(* $MDX part-end *) diff --git a/examples/bonsai_guide_code/lib/rpc_examples.mli b/examples/bonsai_guide_code/lib/rpc_examples.mli deleted file mode 100644 index 89a94cdb..00000000 --- a/examples/bonsai_guide_code/lib/rpc_examples.mli +++ /dev/null @@ -1,8 +0,0 @@ -open! Core -open! Bonsai_web.Cont -open Async_rpc_kernel - -val custom_connector : Rpc_effect.Where_to_connect.Custom.t -> Rpc_effect.Connector.t -val double_implementation : Async_rpc_kernel_private.Connection.t Rpc.Implementation.t -val double_number_app : Bonsai.graph -> Vdom.Node.t Bonsai.t -val current_time_app : Bonsai.graph -> Vdom.Node.t Bonsai.t diff --git a/examples/bonsai_guide_code/lib/rpc_examples_test.ml b/examples/bonsai_guide_code/lib/rpc_examples_test.ml deleted file mode 100644 index c7a1a7be..00000000 --- a/examples/bonsai_guide_code/lib/rpc_examples_test.ml +++ /dev/null @@ -1,105 +0,0 @@ -open! Core -open! Async_kernel -open! Bonsai_web.Cont -open! Bonsai_web_test - -let double_number_app = Rpc_examples.double_number_app -let double_implementation = Rpc_examples.double_implementation - -(* $MDX part-begin=attempt-1 *) -let%expect_test "Clicking the button should double the number" = - let handle = Handle.create (Result_spec.vdom Fn.id) double_number_app in - Handle.show handle; - [%expect - {| -
-
The number is: 1
- -
- |}]; - Handle.click_on handle ~get_vdom:Fn.id ~selector:"button"; - Handle.show handle; - [%expect - {| -
-
The number is: 1
- -
- |}] -;; - -(* $MDX part-end *) - -(* $MDX part-begin=open-bonsai-web-test-async *) -open Async_kernel -open Bonsai_web_test_async -(* $MDX part-end *) - -let%expect_test "Allowing the async effect of the previous test to run." = - let handle = Handle.create (Result_spec.vdom Fn.id) double_number_app in - let%bind () = Handle.flush_async_and_bonsai handle in - [%expect - {| - (Failure "BUG: no bonsai-rpc handler installed") - ------ between bonsai frame ------ - |}]; - return () -;; - -(* $MDX part-begin=attempt-2 *) -let%expect_test "Clicking the button should double the number" = - let handle = Handle.create (Result_spec.vdom Fn.id) double_number_app in - Handle.show handle; - [%expect - {| -
-
The number is: 1
- -
- |}]; - Handle.click_on handle ~get_vdom:Fn.id ~selector:"button"; - Handle.show handle; - [%expect - {| -
-
The number is: 1
- -
- |}]; - let%bind () = Handle.flush_async_and_bonsai handle in - [%expect {| (Failure "BUG: no bonsai-rpc handler installed") |}]; - return () -;; - -(* $MDX part-end *) - -(* $MDX part-begin=attempt-3 *) -let%expect_test "Clicking the button should double the number" = - let handle = - Handle.create - ~rpc_implementations:[ double_implementation ] - (Result_spec.vdom Fn.id) - double_number_app - in - Handle.show handle; - [%expect - {| -
-
The number is: 1
- -
- |}]; - Handle.click_on handle ~get_vdom:Fn.id ~selector:"button"; - let%bind () = Handle.flush_async_and_bonsai handle in - Handle.show handle; - [%expect - {| - ------ between bonsai frame ------ -
-
The number is: 2
- -
- |}]; - return () -;; -(* $MDX part-end *) diff --git a/examples/bonsai_guide_code/lib/rpc_examples_test.mli b/examples/bonsai_guide_code/lib/rpc_examples_test.mli deleted file mode 100644 index bc0db47b..00000000 --- a/examples/bonsai_guide_code/lib/rpc_examples_test.mli +++ /dev/null @@ -1 +0,0 @@ -(* this module is used only for side-effects *) diff --git a/examples/bonsai_guide_code/lib/testing_examples.ml b/examples/bonsai_guide_code/lib/testing_examples.ml deleted file mode 100644 index f0106b8a..00000000 --- a/examples/bonsai_guide_code/lib/testing_examples.ml +++ /dev/null @@ -1,141 +0,0 @@ -open! Core -open! Bonsai_web.Cont -open Bonsai.Let_syntax - -(* $MDX part-begin=hello-world *) -let hello_world = Vdom.Node.span [ Vdom.Node.text "hello world" ] - -(* $MDX part-end *) - -(* $MDX part-begin=hello-user *) -let hello_user (name : string Bonsai.t) : Vdom.Node.t Bonsai.t = - let%arr name = name in - Vdom.Node.span [ Vdom.Node.textf "hello %s" name ] -;; - -(* $MDX part-end *) - -(* $MDX part-begin=hello-text-box *) -let hello_textbox graph : Vdom.Node.t Bonsai.t = - let state, set = Bonsai.state "" graph in - let%arr message = hello_user state - and set = set in - Vdom.Node.div - [ Vdom.Node.input ~attrs:[ Vdom.Attr.on_input (fun _ text -> set text) ] (); message ] -;; - -(* $MDX part-end *) - -(* $MDX part-begin=hello-world-test *) -module Handle = Bonsai_web_test.Handle -module Result_spec = Bonsai_web_test.Result_spec - -let%expect_test "it shows hello world" = - let handle = Handle.create (Result_spec.vdom Fn.id) (fun _ -> return hello_world) in - Handle.show handle; - [%expect {| hello world |}] -;; - -(* $MDX part-end *) - -(* $MDX part-begin=hello-user-test *) -let%expect_test "shows hello to a user" = - let user_var = Bonsai.Expert.Var.create "Bob" in - let user = Bonsai.Expert.Var.value user_var in - let handle = Handle.create (Result_spec.vdom Fn.id) (fun _ -> hello_user user) in - Handle.show handle; - [%expect {| hello Bob |}]; - Bonsai.Expert.Var.set user_var "Alice"; - Handle.show handle; - [%expect {| hello Alice |}] -;; - -(* $MDX part-end *) - -(* $MDX part-begin=hello-user-diff-test *) -let%expect_test "shows hello to a user" = - let user_var = Bonsai.Expert.Var.create "Bob" in - let user = Bonsai.Expert.Var.value user_var in - let handle = Handle.create (Result_spec.vdom Fn.id) (fun _ -> hello_user user) in - Handle.show handle; - [%expect {| hello Bob |}]; - Bonsai.Expert.Var.set user_var "Alice"; - Handle.show_diff handle; - [%expect {| - -| hello Bob - +| hello Alice - |}] -;; - -(* $MDX part-end *) - -(* $MDX part-begin=hello-text-box-diff-test *) -let%expect_test "shows hello to a specified user" = - let handle = Handle.create (Result_spec.vdom Fn.id) hello_textbox in - Handle.show handle; - [%expect - {| -
- - hello -
- |}]; - Handle.input_text handle ~get_vdom:Fn.id ~selector:"input" ~text:"Bob"; - Handle.show_diff handle; - [%expect - {| -
- - -| hello - +| hello Bob -
- |}]; - Handle.input_text handle ~get_vdom:Fn.id ~selector:"input" ~text:"Alice"; - Handle.show_diff handle; - [%expect - {| -
- - -| hello Bob - +| hello Alice -
- |}] -;; - -(* $MDX part-end *) - -(* $MDX part-begin=state-test *) -module State_view_spec = struct - type t = string * (string -> unit Effect.t) - type incoming = string - - let view : t -> string = fun (view, _) -> view - let incoming : t -> incoming -> unit Effect.t = fun (_, incoming) -> incoming -end - -let%expect_test "test Bonsai.state" = - let state_single_bonsai graph : (string * (string -> unit Vdom.Effect.t)) Bonsai.t = - let state, inject = Bonsai.state "hello" graph in - Bonsai.both state inject - in - let handle = Handle.create (module State_view_spec) state_single_bonsai in - Handle.show handle; - [%expect {| hello |}]; - Handle.do_actions handle [ "world" ]; - Handle.show handle; - [%expect {| world |}] -;; - -(* $MDX part-end *) - -(* $MDX part-begin=test-clock *) -let%expect_test "test clock" = - let handle = Handle.create (Result_spec.vdom Fn.id) Time_examples.current_time in - Handle.show handle; - [%expect {| 1970-01-01 00:00:00.000000000Z |}]; - Handle.advance_clock_by handle (Time_ns.Span.of_sec 2.0); - Handle.show handle; - [%expect {| 1970-01-01 00:00:02.000000000Z |}] -;; - -(* $MDX part-end *) diff --git a/examples/bonsai_guide_code/lib/testing_examples.mli b/examples/bonsai_guide_code/lib/testing_examples.mli deleted file mode 100644 index bc0db47b..00000000 --- a/examples/bonsai_guide_code/lib/testing_examples.mli +++ /dev/null @@ -1 +0,0 @@ -(* this module is used only for side-effects *) diff --git a/examples/bonsai_guide_code/lib/time_examples.ml b/examples/bonsai_guide_code/lib/time_examples.ml deleted file mode 100644 index e42d004e..00000000 --- a/examples/bonsai_guide_code/lib/time_examples.ml +++ /dev/null @@ -1,73 +0,0 @@ -open! Core -open! Bonsai_web.Cont -open! Bonsai.Let_syntax - -(* $MDX part-begin=clock_now *) -let current_time graph = - let%arr now = Bonsai.Clock.now graph in - Vdom.Node.text (Time_ns.to_string_utc now) -;; - -(* $MDX part-end *) - -(* $MDX part-begin=clock_approx_now *) -let approx_current_time graph = - let%arr now = Bonsai.Clock.approx_now ~tick_every:(Time_ns.Span.of_sec 1.) graph in - Vdom.Node.text (Time_ns.to_string_utc now) -;; - -(* $MDX part-end *) - -let long_effect = - Effect.of_deferred_thunk (fun () -> - Async_kernel.after (Time_ns.Span.of_int_ms (Random.int 2000))) -;; - -(* $MDX part-begin=current_time_effect *) - -let measure_time graph = - let%arr get_time = Bonsai.Clock.get_current_time graph in - Vdom.Node.button - ~attrs: - [ Vdom.Attr.on_click (fun _ -> - let%bind.Effect start = get_time in - let%bind.Effect () = long_effect in - let%bind.Effect end_ = get_time in - let diff = Time_ns.diff end_ start |> Time_ns.Span.to_string_hum in - Effect.alert [%string "that took: %{diff}"]) - ] - [ Vdom.Node.text "Click to measure a long effect." ] -;; - -(* $MDX part-end *) - -(* $MDX part-begin=clock_sleep *) -let clock_sleep_demo graph = - let%arr sleep = Bonsai.Clock.sleep graph in - Vdom.Node.button - ~attrs: - [ Vdom.Attr.on_click (fun _ -> - let%bind.Effect () = sleep (Time_ns.Span.of_sec 2.) in - Effect.alert "... 2 seconds later...") - ] - [ Vdom.Node.text "delayed alert" ] -;; - -(* $MDX part-end *) - -(* $MDX part-begin=clock_every *) -let clock_every_demo graph = - let count, set_count = Bonsai.state 0 graph in - Bonsai.Clock.every - ~when_to_start_next_effect:`Every_multiple_of_period_blocking - ~trigger_on_activate:false - (Time_ns.Span.of_sec 1.0) - (let%arr count = count - and set_count = set_count in - set_count (count + 1)) - graph; - let%arr count = count in - Vdom.Node.text [%string "Seconds since you opened the page: %{count#Int}"] -;; - -(* $MDX part-end *) diff --git a/examples/bonsai_guide_code/lib/time_examples.mli b/examples/bonsai_guide_code/lib/time_examples.mli deleted file mode 100644 index 22b0cf1f..00000000 --- a/examples/bonsai_guide_code/lib/time_examples.mli +++ /dev/null @@ -1,8 +0,0 @@ -open! Core -open! Bonsai_web.Cont - -val current_time : Bonsai.graph -> Vdom.Node.t Bonsai.t -val approx_current_time : Bonsai.graph -> Vdom.Node.t Bonsai.t -val measure_time : Bonsai.graph -> Vdom.Node.t Bonsai.t -val clock_sleep_demo : Bonsai.graph -> Vdom.Node.t Bonsai.t -val clock_every_demo : Bonsai.graph -> Vdom.Node.t Bonsai.t diff --git a/examples/bonsai_guide_code/lib/uri_parsing_examples.ml b/examples/bonsai_guide_code/lib/uri_parsing_examples.ml deleted file mode 100644 index 879d2cd6..00000000 --- a/examples/bonsai_guide_code/lib/uri_parsing_examples.ml +++ /dev/null @@ -1,298 +0,0 @@ -open! Core -open Uri_parsing -module Id = String - -(* $MDX part-begin=string_parser *) -let (parser : string Parser.t) = Parser.from_path Value_parser.string - -let%expect_test _ = - Parser.check_ok_and_print_urls_or_errors parser; - [%expect - {| - URL parser looks good! - ┌───────────┐ - │ All urls │ - ├───────────┤ - │ / │ - └───────────┘ - |}] -;; - -(* $MDX part-end *) -(* $MDX part-begin=int_parser *) -let (parser : int Parser.t) = Parser.from_query_required ~key:"q" Value_parser.int - -let%expect_test _ = - Parser.check_ok_and_print_urls_or_errors parser; - [%expect - {| - URL parser looks good! - ┌───────────┐ - │ All urls │ - ├───────────┤ - │ /?q= │ - └───────────┘ - |}] -;; - -(* $MDX part-end *) - -(* $MDX part-begin=many_id_parser *) -let (parser : Id.t list Parser.t) = - Parser.from_remaining_path (Value_parser.sexpable (module Id)) - |> Parser.with_prefix [ "With"; "a"; "Prefix" ] -;; - -let%expect_test _ = - Parser.check_ok_and_print_urls_or_errors parser; - [%expect - {| - URL parser looks good! - ┌─────────────────────────────────────┐ - │ All urls │ - ├─────────────────────────────────────┤ - │ /With/a/Prefix/> │ - └─────────────────────────────────────┘ - |}] -;; - -(* $MDX part-end *) - -(* $MDX part-begin=forum_search_params *) -module Search_params = struct - type t = - { query : string - ; author_id : Id.t option - ; categories : Id.t list - } - [@@deriving typed_fields, sexp, equal] - - let parser_for_field : type a. a Typed_field.t -> a Parser.t = function - | Query -> Parser.from_path Value_parser.string - | Author_id -> Parser.from_query_optional (Value_parser.sexpable (module Id)) - | Categories -> Parser.from_query_many (Value_parser.sexpable (module Id)) - ;; - - module Path_order = Parser.Record.Path_order (Typed_field) - - let path_order = Path_order.T [ Query ] -end - -let search_params_parser = Parser.Record.make (module Search_params) - -let%expect_test _ = - Parser.check_ok_and_print_urls_or_errors search_params_parser; - [%expect - {| - URL parser looks good! - ┌──────────────────────────────────────────────────────────────────────────┐ - │ All urls │ - ├──────────────────────────────────────────────────────────────────────────┤ - │ /?author_id=>&categories=> │ - └──────────────────────────────────────────────────────────────────────────┘ - |}] -;; - -(* $MDX part-end *) - -(* $MDX part-begin=forum_admin_page *) -module Admin_page = struct - type t = - | Settings - | Edit_user of Id.t - [@@deriving typed_variants, sexp, equal] - - let parser_for_variant : type a. a Typed_variant.t -> a Parser.t = function - | Settings -> Parser.unit - | Edit_user -> Parser.from_path (Value_parser.sexpable (module Id)) - ;; -end - -let admin_page_parser = Parser.Variant.make (module Admin_page) - -let%expect_test _ = - Parser.check_ok_and_print_urls_or_errors admin_page_parser; - [%expect - {| - URL parser looks good! - ┌───────────────────────┐ - │ All urls │ - ├───────────────────────┤ - │ /edit_user/ │ - │ /settings │ - └───────────────────────┘ - |}] -;; - -(* $MDX part-end *) - -(* $MDX part-begin=forum_user_page *) - -module User_page = struct - module Subpage = struct - type t = - | Profile - | Posts - [@@deriving typed_variants, sexp, equal] - - let parser_for_variant : type a. a Typed_variant.t -> a Parser.t = function - | Profile -> Parser.unit - | Posts -> Parser.unit - ;; - end - - type t = - { user_id : Id.t - ; subpage : Subpage.t - } - [@@deriving typed_fields, sexp, equal] - - let parser_for_field : type a. a Typed_field.t -> a Parser.t = function - | User_id -> Parser.from_path (Value_parser.sexpable (module Id)) - | Subpage -> Parser.Variant.make (module Subpage) - ;; - - module Path_order = Parser.Record.Path_order (Typed_field) - - let path_order = Path_order.T [ User_id; Subpage ] -end - -let user_page_parser = Parser.Record.make (module User_page) - -let%expect_test _ = - Parser.check_ok_and_print_urls_or_errors user_page_parser; - [%expect - {| - URL parser looks good! - ┌─────────────────────┐ - │ All urls │ - ├─────────────────────┤ - │ //posts │ - │ //profile │ - └─────────────────────┘ - |}] -;; - -(* $MDX part-end *) - -(* $MDX part-begin=forum_url *) -module Url = struct - type t = - | Homepage - | Discussion of Id.t - | Search of Search_params.t - | User of User_page.t - | Admin of Admin_page.t - [@@deriving typed_variants, sexp, equal] - - let parser_for_variant : type a. a Typed_variant.t -> a Parser.t = function - | Homepage -> Parser.end_of_path Parser.unit - | Discussion -> Parser.from_path (Value_parser.sexpable (module Id)) - | Search -> search_params_parser - | User -> user_page_parser - | Admin -> admin_page_parser - ;; -end - -let parser = Parser.Variant.make (module Url) - -let%expect_test _ = - Parser.check_ok_and_print_urls_or_errors parser; - [%expect - {| - URL parser looks good! - ┌──────────────────────────────────────────────────────────────────────────────────────────┐ - │ All urls │ - ├──────────────────────────────────────────────────────────────────────────────────────────┤ - │ / │ - │ /admin/edit_user/ │ - │ /admin/settings │ - │ /discussion/ │ - │ /search/?search.author_id=>&search.categories=> │ - │ /user//posts │ - │ /user//profile │ - └──────────────────────────────────────────────────────────────────────────────────────────┘ - |}] -;; - -(* $MDX part-end *) - -(* $MDX part-begin=forum_old *) -module Old_url = struct - type t = - | Homepage - | Post of Id.t - | Search of string - [@@deriving typed_variants, sexp, equal] - - let parser_for_variant : type a. a Typed_variant.t -> a Parser.t = function - | Homepage -> Parser.end_of_path Parser.unit - | Post -> Parser.from_path (Value_parser.sexpable (module Id)) - | Search -> Parser.from_path Value_parser.string - ;; -end - -let old_parser = Parser.Variant.make (module Old_url) - -let%expect_test _ = - Parser.check_ok_and_print_urls_or_errors old_parser; - [%expect - {| - URL parser looks good! - ┌──────────────────┐ - │ All urls │ - ├──────────────────┤ - │ / │ - │ /post/ │ - │ /search/ │ - └──────────────────┘ - |}] -;; - -(* $MDX part-end *) - -(* $MDX part-begin=forum_versioned *) -let v1_parser = Versioned_parser.first_parser old_parser - -let v2_parser = - Versioned_parser.new_parser parser ~previous:v1_parser ~f:(function - | Homepage -> Homepage - | Post id -> Discussion id - | Search query -> Search { query; author_id = None; categories = [] }) -;; - -let%expect_test _ = - Versioned_parser.check_ok_and_print_urls_or_errors v2_parser; - [%expect - {| - URL parser looks good! - ┌──────────────────────────────────────────────────────────────────────────────────────────┐ - │ All urls │ - ├──────────────────────────────────────────────────────────────────────────────────────────┤ - │ / │ - │ /admin/edit_user/ │ - │ /admin/settings │ - │ /discussion/ │ - │ /search/?search.author_id=>&search.categories=> │ - │ /user//posts │ - │ /user//profile │ - └──────────────────────────────────────────────────────────────────────────────────────────┘ - - | - falls back to - | - v - - URL parser looks good! - ┌──────────────────┐ - │ All urls │ - ├──────────────────┤ - │ / │ - │ /post/ │ - │ /search/ │ - └──────────────────┘ - |}] -;; -(* $MDX part-end *) diff --git a/examples/bonsai_guide_code/lib/uri_parsing_examples.mli b/examples/bonsai_guide_code/lib/uri_parsing_examples.mli deleted file mode 100644 index bc0db47b..00000000 --- a/examples/bonsai_guide_code/lib/uri_parsing_examples.mli +++ /dev/null @@ -1 +0,0 @@ -(* this module is used only for side-effects *) diff --git a/examples/bonsai_guide_code/lifecycle_examples.ml b/examples/bonsai_guide_code/lifecycle_examples.ml deleted file mode 100644 index d956d294..00000000 --- a/examples/bonsai_guide_code/lifecycle_examples.ml +++ /dev/null @@ -1,45 +0,0 @@ -open! Core -open! Bonsai_web.Cont -open! Bonsai.Let_syntax - -(* $MDX part-begin=lifecycle *) -let lifecycle_demo graph = - let log_val, log = - Bonsai.state_machine0 - ~default_model:"" - ~apply_action:(fun _ curr new_ -> curr ^ new_) - graph - in - let show, toggle_show = Bonsai.toggle ~default_model:false graph in - let main_view = - match%sub show with - | true -> - Bonsai.Edge.lifecycle - ~on_activate: - (let%arr log = log in - log "🚀") - ~on_deactivate: - (let%arr log = log in - log "🔥") - graph; - Vdom.Node.text [%string "Active!!!!"] |> Bonsai.return - | false -> Vdom.Node.text "Nothing to see here..." |> Bonsai.return - in - let%arr log_val = log_val - and toggle_show = toggle_show - and main_view = main_view in - Vdom.Node.( - div - [ div - [ button - ~attrs:[ Vdom.Attr.on_click (fun _ -> Effect.all_unit [ toggle_show ]) ] - [ text "toggle show" ] - ; text log_val - ] - ; main_view - ]) -;; - -(* $MDX part-end *) - -let () = Util.run lifecycle_demo ~id:"lifecycle" diff --git a/examples/bonsai_guide_code/lifecycle_examples.mli b/examples/bonsai_guide_code/lifecycle_examples.mli deleted file mode 100644 index bc0db47b..00000000 --- a/examples/bonsai_guide_code/lifecycle_examples.mli +++ /dev/null @@ -1 +0,0 @@ -(* this module is used only for side-effects *) diff --git a/examples/bonsai_guide_code/main.ml b/examples/bonsai_guide_code/main.ml deleted file mode 100644 index 95f8fd4e..00000000 --- a/examples/bonsai_guide_code/main.ml +++ /dev/null @@ -1,34 +0,0 @@ -open! Core -open! Bonsai_web - -(* !!!TURN AWAY!!! *) -(* This is not good code! It's made to interact in weird ways with docpub, and - you shouldn't be looking here for inspiration. - Of particular note: - - - The ui-components and call to "Bonsai.Start.start" are in the same file; - they should be in separate files to encourage testability - - Some code is written non-idiomatically because the ordering of the guide - makes it undesierable to use concepts that weren't explained yet *) - -module _ = Bonsai_types -module _ = Intro_examples -module _ = Vdom_examples -module _ = Incrementality_examples -module _ = State_examples -module _ = Effect_examples -module _ = Control_flow_examples - -(* How-tos *) - -module _ = Css_examples -module _ = Edge_triggered_examples -module _ = Effect_stale_examples -module _ = Form_examples -module _ = Higher_order_examples -module _ = Lifecycle_examples -module _ = Rpc_examples -module _ = Scope_model_examples -module _ = State_reset_examples -module _ = Time_examples -module _ = Url_var_examples diff --git a/examples/bonsai_guide_code/main.mli b/examples/bonsai_guide_code/main.mli deleted file mode 100644 index 74bb7298..00000000 --- a/examples/bonsai_guide_code/main.mli +++ /dev/null @@ -1 +0,0 @@ -(*_ This signature is deliberately empty. *) diff --git a/examples/bonsai_guide_code/resize_iframe.js b/examples/bonsai_guide_code/resize_iframe.js deleted file mode 100644 index ae08b5b4..00000000 --- a/examples/bonsai_guide_code/resize_iframe.js +++ /dev/null @@ -1,13 +0,0 @@ -window.addEventListener('DOMContentLoaded', function () { - var resizeObserver = new ResizeObserver(function () { - var height = document.body.scrollHeight; - if (height == 0) { return; } - var message = { - height: height, - hash: window.location.hash - }; - window.parent.postMessage(JSON.stringify(message), "*"); - }); - - resizeObserver.observe(document.body) -}); diff --git a/examples/bonsai_guide_code/rpc_examples.ml b/examples/bonsai_guide_code/rpc_examples.ml deleted file mode 100644 index 85929dd9..00000000 --- a/examples/bonsai_guide_code/rpc_examples.ml +++ /dev/null @@ -1,21 +0,0 @@ -open! Core -open! Async_kernel -open! Bonsai_web.Cont -open! Bonsai.Let_syntax -module Lib = Bonsai_guide_code_lib.Rpc_examples - -(* The code for these examples is defined inside `lib`, since we want to test it. *) - -let () = - Util.run - ~custom_connector:Lib.custom_connector - Lib.double_number_app - ~id:"double-the-number-rpc" -;; - -let () = - Util.run - ~custom_connector:Lib.custom_connector - Lib.current_time_app - ~id:"poll-the-current-time" -;; diff --git a/examples/bonsai_guide_code/rpc_examples.mli b/examples/bonsai_guide_code/rpc_examples.mli deleted file mode 100644 index bc0db47b..00000000 --- a/examples/bonsai_guide_code/rpc_examples.mli +++ /dev/null @@ -1 +0,0 @@ -(* this module is used only for side-effects *) diff --git a/examples/bonsai_guide_code/scope_model_examples.ml b/examples/bonsai_guide_code/scope_model_examples.ml deleted file mode 100644 index c642c2a4..00000000 --- a/examples/bonsai_guide_code/scope_model_examples.ml +++ /dev/null @@ -1,63 +0,0 @@ -open! Core -open! Async_kernel -open! Bonsai_web.Cont -open Bonsai.Let_syntax - -(* $MDX part-begin=counters_for_users_assoc *) - -let counters_for_users_assoc graph : Vdom.Node.t Bonsai.t = - let users = - [ "Alice", (); "Bob", (); "Charlie", () ] |> String.Map.of_alist_exn |> Bonsai.return - in - let counters = - Bonsai.assoc - (module String) - users - ~f:(fun _ _ graph -> State_examples.counter_ui graph) - graph - in - let%arr counters = counters in - Vdom.Node.table - (counters - |> Map.to_alist - |> List.map ~f:(fun (key, vdom) -> - let open Vdom.Node in - let name = td [ Vdom.Node.text key ] in - let counter = td [ vdom ] in - Vdom.Node.tr [ name; counter ])) -;; - -(* $MDX part-end *) - -let () = Util.run counters_for_users_assoc ~id:"counters_for_users_assoc" - -(* $MDX part-begin=counters_for_users_scoped *) -module Form = Bonsai_web_ui_form.With_automatic_view - -let counters_for_users_scoped graph : Vdom.Node.t Bonsai.t = - let form = - Form.Elements.Dropdown.list - (module String) - ~equal:[%equal: String.t] - (Bonsai.return [ "Alice"; "Bob"; "Charlie" ]) - graph - in - let active_user = - let%arr form = form in - Form.value_or_default form ~default:"Alice" - in - Bonsai.scope_model - (module String) - ~on:active_user - graph - ~for_:(fun graph -> - let%arr counter = State_examples.counter_ui graph - and name = active_user - and form = form in - Vdom.Node.div - [ Form.view_as_vdom form; Vdom.Node.p [ Vdom.Node.text name ]; counter ]) -;; - -(* $MDX part-end *) - -let () = Util.run counters_for_users_scoped ~id:"counters_for_users_scoped" diff --git a/examples/bonsai_guide_code/scope_model_examples.mli b/examples/bonsai_guide_code/scope_model_examples.mli deleted file mode 100644 index bc0db47b..00000000 --- a/examples/bonsai_guide_code/scope_model_examples.mli +++ /dev/null @@ -1 +0,0 @@ -(* this module is used only for side-effects *) diff --git a/examples/bonsai_guide_code/state_examples.ml b/examples/bonsai_guide_code/state_examples.ml deleted file mode 100644 index 1675034b..00000000 --- a/examples/bonsai_guide_code/state_examples.ml +++ /dev/null @@ -1,159 +0,0 @@ -open! Core -open! Async_kernel -open! Bonsai_web.Cont -open Bonsai.Let_syntax - -(* $MDX part-begin=counter *) -let counter graph : Vdom.Node.t Bonsai.t * int Bonsai.t = - let count, set_count = Bonsai.state 0 graph in - let view = - let%arr count = count - and set_count = set_count in - (* view-construction logic *) - Vdom.Node.div - [ Vdom.Node.button - ~attrs:[ Vdom.Attr.on_click (fun _ -> set_count (count - 1)) ] - [ Vdom.Node.text "-1" ] - ; Vdom.Node.text [%string "Counter value: %{count#Int}"] - ; Vdom.Node.button - ~attrs:[ Vdom.Attr.on_click (fun _ -> set_count (count + 1)) ] - [ Vdom.Node.text "+1" ] - ] - in - view, count -;; - -(* $MDX part-end *) - -let counter_ui graph = - let view, _ = counter graph in - view -;; - -let () = Util.run counter_ui ~id:"counter_ui" - -(* $MDX part-begin=two_counters_correct *) -let two_counters graph = - let counter1, _count1 = counter graph in - let counter2, _count2 = counter graph in - let%arr counter1 = counter1 - and counter2 = counter2 in - Vdom.Node.div [ counter1; counter2 ] -;; - -(* $MDX part-end *) - -let () = Util.run two_counters ~id:"two_counters_correct" - -(* $MDX part-begin=two_counters_wrong_1 *) -let two_counters_wrong_1 graph = - let counter, _count = counter graph in - let%arr counter1 = counter - and counter2 = counter in - Vdom.Node.div [ counter1; counter2 ] -;; - -(* $MDX part-end *) - -let () = Util.run two_counters_wrong_1 ~id:"two_counters_wrong_1" - -(* $MDX part-begin=two_counters_wrong_2 *) -let two_counters_wrong_2 graph = - let counter, _count = counter graph in - let%arr counter = counter in - Vdom.Node.div [ counter; counter ] -;; - -(* $MDX part-end *) - -let () = Util.run two_counters_wrong_2 ~id:"two_counters_wrong_2" - -(* $MDX part-begin=counter_state_machine *) - -let counter_state_machine graph : Vdom.Node.t Bonsai.t * int Bonsai.t = - let count, inject = - Bonsai.state_machine0 - ~default_model:0 - ~apply_action:(fun (_ : _ Bonsai.Apply_action_context.t) model action -> - match action with - | `Increment -> model + 1 - | `Decrement -> model - 1) - graph - in - let view = - let%arr count = count - and inject = inject in - Vdom.Node.div - [ Vdom.Node.button - ~attrs:[ Vdom.Attr.on_click (fun _ -> inject `Decrement) ] - [ Vdom.Node.text "-1" ] - ; Vdom.Node.text [%string "Counter value: %{count#Int}"] - ; Vdom.Node.button - ~attrs:[ Vdom.Attr.on_click (fun _ -> inject `Increment) ] - [ Vdom.Node.text "+1" ] - ] - in - view, count -;; - -(* $MDX part-end *) - -let () = - Util.run - (fun graph -> counter_state_machine graph |> Tuple2.get1) - ~id:"counter_state_machine" -;; - -(* $MDX part-begin=counter_state_machine1 *) - -let counter_state_machine1 ~(step : int Bonsai.t) graph = - let count, inject = - Bonsai.state_machine1 - ~default_model:0 - ~apply_action:(fun (_ : _ Bonsai.Apply_action_context.t) input model action -> - match input with - | Bonsai.Computation_status.Inactive -> - (* This state machine is inactive, so it can't access the current value of [input]. - Just keep the original model *) - model - | Active step -> - (match action with - | `Increment -> model + step - | `Decrement -> model - step)) - step - graph - in - let view = - let%arr step = step - and count = count - and inject = inject in - Vdom.Node.div - [ Vdom.Node.button - ~attrs:[ Vdom.Attr.on_click (fun _ -> inject `Decrement) ] - [ Vdom.Node.text [%string "-%{step#Int}"] ] - ; Vdom.Node.text [%string "Counter value: %{count#Int}"] - ; Vdom.Node.button - ~attrs:[ Vdom.Attr.on_click (fun _ -> inject `Increment) ] - [ Vdom.Node.text [%string "+%{step#Int}"] ] - ] - in - view, count -;; - -(* $MDX part-end *) - -(* $MDX part-begin=counter_state_machine_chained *) -let counter_state_machine_chained graph = - let counter1, count1 = counter_state_machine1 ~step:(Bonsai.return 1) graph in - let counter2, count2 = counter_state_machine1 ~step:count1 graph in - let counter3, _ = counter_state_machine1 ~step:count2 graph in - let%arr counter1 = counter1 - and counter2 = counter2 - and counter3 = counter3 in - Vdom.Node.div [ counter1; counter2; counter3 ] -;; - -(* $MDX part-end *) - -let () = Util.run counter_state_machine_chained ~id:"counter_state_machine_chained" -let counter = counter_state_machine1 diff --git a/examples/bonsai_guide_code/state_examples.mli b/examples/bonsai_guide_code/state_examples.mli deleted file mode 100644 index ca248cc4..00000000 --- a/examples/bonsai_guide_code/state_examples.mli +++ /dev/null @@ -1,6 +0,0 @@ -open! Core -open! Async_kernel -open! Bonsai_web.Cont - -val counter : step:int Bonsai.t -> Bonsai.graph -> Vdom.Node.t Bonsai.t * int Bonsai.t -val counter_ui : Bonsai.graph -> Vdom.Node.t Bonsai.t diff --git a/examples/bonsai_guide_code/state_reset_examples.ml b/examples/bonsai_guide_code/state_reset_examples.ml deleted file mode 100644 index 932fe305..00000000 --- a/examples/bonsai_guide_code/state_reset_examples.ml +++ /dev/null @@ -1,392 +0,0 @@ -open! Core -open! Bonsai_web.Cont -open! Bonsai.Let_syntax -module Apply_action_context = Bonsai.Apply_action_context - -(* $MDX part-begin=resettable_counters *) -let two_counters graph = - let%arr counter1 = State_examples.counter_ui graph - and counter2 = State_examples.counter_ui graph in - Vdom.Node.div - ~attrs:[ [%css {|border: 1px solid black; padding: 4px|}] ] - [ counter1; counter2 ] -;; - -let reset_ui graph ~f = - let view, reset = Bonsai.with_model_resetter ~f graph in - let%arr view = view - and reset = reset in - Vdom.Node.div - [ view - ; Vdom.Node.button - ~attrs:[ Vdom.Attr.on_click (fun _ -> reset) ] - [ Vdom.Node.text "Reset" ] - ] -;; - -let resettable_counters = reset_ui ~f:two_counters - -(* $MDX part-end *) - -let () = Util.run resettable_counters ~id:"resettable_counters" - -(* $MDX part-begin=resettable_counters_from_inside *) -let resettable_counters_from_inside graph = - Bonsai.with_model_resetter' - ~f:(fun ~reset graph -> - let%arr counter1 = State_examples.counter_ui graph - and counter2 = State_examples.counter_ui graph - and reset = reset in - Vdom.Node.div - ~attrs:[ [%css {|border: 1px solid black; padding: 4px|}] ] - [ counter1 - ; Vdom.Node.button - ~attrs:[ Vdom.Attr.on_click (fun _ -> reset) ] - [ Vdom.Node.text "Reset" ] - ; counter2 - ]) - graph -;; - -(* $MDX part-end *) - -let () = Util.run resettable_counters_from_inside ~id:"resettable_counters_from_inside" - -(* $MDX part-begin=connection_type *) -module Connection : sig - (* Some generic connection type that doesn't know of Bonsai *) - type t - - (* Connect to some resource *) - val create : uri:string -> t - - (* Notify subscribers whenever the connection status changes *) - val on_status_change - : t - -> ([ `Connected | `Connecting | `Disconnected ] -> unit Effect.t) - -> unit Effect.t -end -(* $MDX part-end *) -(*_ This comment used for spacing MDX comment above *) = struct - open Async_kernel - - type t = unit - - let create ~uri:_ = () - - let on_status_change () f = - Effect.of_thunk (fun () -> - Clock_ns.every' (Time_ns.Span.of_int_sec 1) (fun () -> - Effect.Expert.handle_non_dom_event_exn (f `Connecting); - let%bind.Deferred () = Clock_ns.after (Time_ns.Span.of_int_sec 1) in - Effect.Expert.handle_non_dom_event_exn (f `Connected); - let%bind.Deferred () = Clock_ns.after (Time_ns.Span.of_int_sec 3) in - Effect.Expert.handle_non_dom_event_exn (f `Disconnected); - return ())) - ;; -end - -(* $MDX part-begin=connection_status *) -let connection_status graph conn : [ `Connected | `Connecting | `Disconnected ] Bonsai.t = - let status, set_status = Bonsai.state `Disconnected graph in - let register_status_change = - let%arr set_status = set_status in - Connection.on_status_change conn set_status - in - let () = Bonsai.Edge.lifecycle ~on_activate:register_status_change graph in - status -;; - -(* $MDX part-end *) - -(* $MDX part-begin=connection_status_ui *) -let conn = Connection.create ~uri:"https://google.com" - -let connection_status_ui graph = - let connection_status = - match%sub connection_status graph conn with - | `Connected -> Bonsai.return (Vdom.Node.div [ Vdom.Node.text "Connected" ]) - | `Connecting -> Bonsai.return (Vdom.Node.div [ Vdom.Node.text "Connecting" ]) - | `Disconnected -> Bonsai.return (Vdom.Node.div [ Vdom.Node.text "Disconnected" ]) - in - let%arr connection_status = connection_status in - Vdom.Node.div [ connection_status ] -;; - -(* $MDX part-end *) -let () = Util.run connection_status_ui ~id:"connection_status_ui" - -(* $MDX part-begin=resettable_connection_and_counters *) -let connection_and_counters graph = - let%arr connection_status_ui = connection_status_ui graph - and counters = two_counters graph in - Vdom.Node.div [ connection_status_ui; counters ] -;; - -let resettable_ui = reset_ui ~f:connection_and_counters - -(* $MDX part-end *) - -let () = Util.run resettable_ui ~id:"resettable_connection_and_counters" - -(* $MDX part-begin=connection_status_reset *) -let connection_status graph conn : [ `Connected | `Connecting | `Disconnected ] Bonsai.t = - let status, set_status = - Bonsai.state ~reset:(fun model_when_reset -> model_when_reset) `Disconnected graph - in - let register_status_change = - let%arr set_status = set_status in - Connection.on_status_change conn set_status - in - let () = Bonsai.Edge.lifecycle ~on_activate:register_status_change graph in - status -;; - -(* $MDX part-end *) - -let connection_status_ui graph = - let connection_status = - match%sub connection_status graph conn with - | `Connected -> Bonsai.return (Vdom.Node.div [ Vdom.Node.text "Connected" ]) - | `Connecting -> Bonsai.return (Vdom.Node.div [ Vdom.Node.text "Connecting" ]) - | `Disconnected -> Bonsai.return (Vdom.Node.div [ Vdom.Node.text "Disconnected" ]) - in - let%arr connection_status = connection_status in - Vdom.Node.div [ connection_status ] -;; - -let connection_and_counters graph = - let%arr connection_status_ui = connection_status_ui graph - and counters = two_counters graph in - Vdom.Node.div [ connection_status_ui; counters ] -;; - -let () = - Util.run - (reset_ui ~f:connection_and_counters) - ~id:"proper_reset_ui_with_connection_status" -;; - -(* $MDX part-begin=exchange_type *) -module Exchange : sig - module Order_id = Int - - (* A connection to an exchange *) - type t - type event = Fill of Order_id.t - - val create : unit -> t - - (* Sends an order to the exchange *) - val send_order : t -> Order_id.t -> unit Effect.t - - (* Cancels an open order on the exchange *) - val cancel_order : t -> Order_id.t -> unit Effect.t - - (* Subscribe to notifications of which orders have been filled *) - val subscribe : t -> (event -> unit Effect.t) -> unit Effect.t -end -(* $MDX part-end *) -(* _ This comment used for spacing MDX comment above *) = struct - open Async_kernel - module Order_id = Int - - (* All of this code is pretty hacky and only to enable the demo *) - - type event = Fill of Order_id.t - - type t = - { orders : int Order_id.Table.t - ; mutable subscription : event -> unit Effect.t - } - - let create () = - { orders = Order_id.Table.create (); subscription = (fun _ -> Effect.Ignore) } - ;; - - let seq = ref 0 - - let next_seq () = - let res = !seq in - incr seq; - res - ;; - - let fill_in_3_seconds t (order_id, seq) = - upon - (Clock_ns.after (Time_ns.Span.of_int_sec 3)) - (fun () -> - match Hashtbl.find t.orders order_id with - | Some seq' when seq' = seq -> - Effect.Expert.handle_non_dom_event_exn (t.subscription (Fill order_id)) - | _ -> ()) - ;; - - (* No checking for duplicate order ids, seems fine. *) - let send_order t id = - Effect.of_thunk (fun () -> - let seq = next_seq () in - Hashtbl.set t.orders ~key:id ~data:seq; - fill_in_3_seconds t (id, seq)) - ;; - - let cancel_order t id = Effect.of_thunk (fun () -> Hashtbl.remove t.orders id) - let subscribe t f = Effect.of_thunk (fun () -> t.subscription <- f) -end - -open Exchange - -(* $MDX part-begin=order_manager_definition *) -module Model = struct - type t = - { open_orders : Order_id.Set.t - ; filled_orders : Order_id.t list - ; next_order_id : int - } - - let empty = { open_orders = Order_id.Set.empty; filled_orders = []; next_order_id = 0 } -end - -module Action = struct - type t = - | Create_ui_order - | Received_fill of Exchange.Order_id.t -end - -let order_manager (exchange : Exchange.t) graph = - let model, inject_action = - Bonsai.state_machine0 - ~default_model:Model.empty - ~apply_action: - (fun - context { open_orders; next_order_id; filled_orders } (action : Action.t) -> - match action with - | Create_ui_order -> - let this_order_id = next_order_id in - let open_orders = Set.add open_orders this_order_id in - Apply_action_context.schedule_event - context - (Exchange.send_order exchange this_order_id); - { open_orders; next_order_id = this_order_id + 1; filled_orders } - | Received_fill order_id -> - let filled_orders = filled_orders @ [ order_id ] in - let open_orders = Set.remove open_orders order_id in - { open_orders; next_order_id; filled_orders }) - graph - in - let subscribe_to_exchange = - let%arr inject_action = inject_action in - Exchange.subscribe exchange (fun (Fill order_id) -> - inject_action (Received_fill order_id)) - in - let () = Bonsai.Edge.lifecycle ~on_activate:subscribe_to_exchange graph in - let inject_new_order = - let%arr inject_action = inject_action in - inject_action Create_ui_order - in - let open_orders = - let%arr { open_orders; _ } = model in - open_orders - in - let filled_orders = - let%arr { filled_orders; _ } = model in - filled_orders - in - open_orders, filled_orders, inject_new_order -;; - -(* $MDX part-end *) - -(* $MDX part-begin=trading_ui *) -let trading_ui exchange graph = - let open_orders, filled_orders, inject_new_order = order_manager exchange graph in - let%arr open_orders = open_orders - and filled_orders = filled_orders - and inject_new_order = inject_new_order in - Vdom.Node.div - [ Vdom.Node.text [%string "Open orders:"] - ; Vdom.Node.sexp_for_debugging [%sexp (open_orders : Order_id.Set.t)] - ; Vdom.Node.text [%string "Filled orders:"] - ; Vdom.Node.sexp_for_debugging [%sexp (filled_orders : Order_id.t list)] - ; Vdom.Node.button - ~attrs:[ Vdom.Attr.on_click (fun _ -> inject_new_order) ] - [ Vdom.Node.text "New order" ] - ] -;; - -(* $MDX part-end *) - -let exchange = Exchange.create () -let () = Util.run (trading_ui exchange) ~id:"trading_ui" -let () = Util.run (reset_ui ~f:(trading_ui exchange)) ~id:"trading_ui_reset" - -let order_manager (exchange : Exchange.t) graph = - (* $MDX part-begin=order_manager_with_reset *) - let model, inject_action = - Bonsai.state_machine0 - ~default_model:Model.empty - ~apply_action: - (fun - context { open_orders; next_order_id; filled_orders } (action : Action.t) -> - match action with - | Create_ui_order -> - let this_order_id = next_order_id in - let open_orders = Set.add open_orders this_order_id in - Apply_action_context.schedule_event - context - (Exchange.send_order exchange this_order_id); - { open_orders; next_order_id = this_order_id + 1; filled_orders } - | Received_fill order_id -> - let filled_orders = filled_orders @ [ order_id ] in - let open_orders = Set.remove open_orders order_id in - { open_orders; next_order_id; filled_orders }) - ~reset:(fun context (model : Model.t) -> - Set.iter model.open_orders ~f:(fun order_id -> - Apply_action_context.schedule_event - context - (Exchange.cancel_order exchange order_id)); - Model.empty) - graph - in - (* $MDX part-end *) - let subscribe_to_exchange = - let%arr inject_action = inject_action in - Exchange.subscribe exchange (fun (Fill order_id) -> - inject_action (Received_fill order_id)) - in - let () = Bonsai.Edge.lifecycle ~on_activate:subscribe_to_exchange graph in - let inject_new_order = - let%arr inject_action = inject_action in - inject_action Create_ui_order - in - let open_orders = - let%arr { open_orders; _ } = model in - open_orders - in - let filled_orders = - let%arr { filled_orders; _ } = model in - filled_orders - in - open_orders, filled_orders, inject_new_order -;; - -(* $MDX part-begin=trading_ui *) -let trading_ui exchange graph = - let open_orders, filled_orders, inject_new_order = order_manager exchange graph in - let%arr open_orders = open_orders - and filled_orders = filled_orders - and inject_new_order = inject_new_order in - Vdom.Node.div - [ Vdom.Node.text [%string "Open orders:"] - ; Vdom.Node.sexp_for_debugging [%sexp (open_orders : Order_id.Set.t)] - ; Vdom.Node.text [%string "Filled orders:"] - ; Vdom.Node.sexp_for_debugging [%sexp (filled_orders : Order_id.t list)] - ; Vdom.Node.button - ~attrs:[ Vdom.Attr.on_click (fun _ -> inject_new_order) ] - [ Vdom.Node.text "New order" ] - ] -;; - -(* $MDX part-end *) - -let () = Util.run (reset_ui ~f:(trading_ui exchange)) ~id:"proper_trading_ui_reset" diff --git a/examples/bonsai_guide_code/state_reset_examples.mli b/examples/bonsai_guide_code/state_reset_examples.mli deleted file mode 100644 index bc0db47b..00000000 --- a/examples/bonsai_guide_code/state_reset_examples.mli +++ /dev/null @@ -1 +0,0 @@ -(* this module is used only for side-effects *) diff --git a/examples/bonsai_guide_code/time_examples.ml b/examples/bonsai_guide_code/time_examples.ml deleted file mode 100644 index 8f34d185..00000000 --- a/examples/bonsai_guide_code/time_examples.ml +++ /dev/null @@ -1,10 +0,0 @@ -open! Core -open! Bonsai_web.Cont -open! Bonsai.Let_syntax -module Lib = Bonsai_guide_code_lib.Time_examples - -let () = Util.run Lib.current_time ~id:"clock_now" -let () = Util.run Lib.approx_current_time ~id:"clock_approx_now" -let () = Util.run Lib.measure_time ~id:"current_time_effect" -let () = Util.run Lib.clock_sleep_demo ~id:"clock_sleep" -let () = Util.run Lib.clock_every_demo ~id:"clock_every" diff --git a/examples/bonsai_guide_code/time_examples.mli b/examples/bonsai_guide_code/time_examples.mli deleted file mode 100644 index bc0db47b..00000000 --- a/examples/bonsai_guide_code/time_examples.mli +++ /dev/null @@ -1 +0,0 @@ -(* this module is used only for side-effects *) diff --git a/examples/bonsai_guide_code/url_var_examples.ml b/examples/bonsai_guide_code/url_var_examples.ml deleted file mode 100644 index 80c0a42a..00000000 --- a/examples/bonsai_guide_code/url_var_examples.ml +++ /dev/null @@ -1,40 +0,0 @@ -open! Core -open! Bonsai_web.Cont -open! Bonsai.Let_syntax -module Url_var = Bonsai_web_ui_url_var - -module My_google_clone = struct - (* $MDX part-begin=type *) - type t = - | Homepage - | Search of string - [@@deriving sexp, equal] - (* $MDX part-end *) - - (* $MDX part-begin=parse_unparse *) - let parse_exn ({ path; query; _ } : Url_var.Components.t) : t = - let path = String.split path ~on:'/' in - match path with - | [ "home" ] -> Homepage - | [ "search" ] -> - (match Map.find (query : _ String.Map.t) "q" with - | Some [ query ] -> Search query - | None | Some [] -> failwith "search missing query param" - | Some (_ :: _ :: _) -> failwith "search with too many query params") - | _ -> failwith "unknown path" - ;; - - let unparse (t : t) : Url_var.Components.t = - match t with - | Homepage -> Url_var.Components.create ~path:"home" () - | Search query -> - Url_var.Components.create - ~path:"search" - ~query:(String.Map.singleton "q" [ query ]) - () - ;; - (* $MDX part-end *) -end - -let () = ignore My_google_clone.parse_exn -let () = ignore My_google_clone.unparse diff --git a/examples/bonsai_guide_code/url_var_examples.mli b/examples/bonsai_guide_code/url_var_examples.mli deleted file mode 100644 index bc0db47b..00000000 --- a/examples/bonsai_guide_code/url_var_examples.mli +++ /dev/null @@ -1 +0,0 @@ -(* this module is used only for side-effects *) diff --git a/examples/bonsai_guide_code/util.ml b/examples/bonsai_guide_code/util.ml deleted file mode 100644 index ca7d8d89..00000000 --- a/examples/bonsai_guide_code/util.ml +++ /dev/null @@ -1,18 +0,0 @@ -open! Core -open! Bonsai_web.Cont -open Js_of_ocaml - -let () = Async_js.init () - -let run ?custom_connector ~id computation = - (* Because we're iframing into this app from docpub, we look up what the - current url-hash is, and only run the requested example. *) - let current_hash = Dom_html.window##.location##.hash |> Js.to_string in - print_s [%message (current_hash : string) (id : string)]; - if String.equal current_hash ("#" ^ id) - then Start.start ?custom_connector computation - else () -;; - -let run_vdom_val vdom = run (fun _ -> vdom) -let run_vdom vdom = run (fun _ -> Bonsai.return vdom) diff --git a/examples/bonsai_guide_code/util.mli b/examples/bonsai_guide_code/util.mli deleted file mode 100644 index 5c11da93..00000000 --- a/examples/bonsai_guide_code/util.mli +++ /dev/null @@ -1,11 +0,0 @@ -open! Core -open! Bonsai_web.Cont - -val run - : ?custom_connector:(Rpc_effect.Where_to_connect.Custom.t -> Rpc_effect.Connector.t) - -> id:string - -> (Bonsai.graph -> Vdom.Node.t Bonsai.t) - -> unit - -val run_vdom_val : Vdom.Node.t Bonsai.t -> id:string -> unit -val run_vdom : Vdom.Node.t -> id:string -> unit diff --git a/examples/bonsai_guide_code/vdom_examples.ml b/examples/bonsai_guide_code/vdom_examples.ml deleted file mode 100644 index 9e21c536..00000000 --- a/examples/bonsai_guide_code/vdom_examples.ml +++ /dev/null @@ -1,59 +0,0 @@ -open! Core -open! Bonsai_web.Cont - -(* $MDX part-begin=hello_world *) -let hello_world : Vdom.Node.t = Vdom.Node.text "hello world!" - -(* $MDX part-end *) - -let () = Util.run_vdom hello_world ~id:"hello_world" - -(* $MDX part-begin=bulleted_list *) -let bulleted_list : Vdom.Node.t = - let open Vdom.Node in - div - [ h3 [ text "Norwegian Pancakes" ] - ; ul - [ li [ text "3 eggs" ] - ; li [ text "2 cups of milk" ] - ; li [ text "1 cup of flour" ] - ] - ] -;; - -(* $MDX part-end *) - -let () = Util.run_vdom bulleted_list ~id:"bulleted_list" - -(* $MDX part-begin=input_placeholder *) -let input_placeholder : Vdom.Node.t = - Vdom.Node.input ~attrs:[ Vdom.Attr.placeholder "placeholder text here" ] () -;; - -(* $MDX part-end *) -let () = Util.run_vdom input_placeholder ~id:"input_placeholder" - -(* $MDX part-begin=css *) -let css : Vdom.Node.t = - Vdom.Node.span ~attrs:[ [%css {|color: red;|}] ] [ Vdom.Node.text "this text is red" ] -;; - -(* $MDX part-end *) -let () = Util.run_vdom css ~id:"css" - -type mouse_event = Js_of_ocaml.Dom_html.mouseEvent Js_of_ocaml.Js.t - -(* $MDX part-begin=clicky_button *) -let clicky : Vdom.Node.t = - Vdom.Node.button - ~attrs: - [ Vdom.Attr.on_click (fun (_evt : mouse_event) -> - (* Alerts are generally bad UI; there's an `Effect.print_s` for logging *) - Effect.alert "hello there!") - ] - [ Vdom.Node.text "click me!" ] -;; - -(* $MDX part-end *) - -let () = Util.run_vdom clicky ~id:"clicky_button" diff --git a/examples/bonsai_guide_code/vdom_examples.mli b/examples/bonsai_guide_code/vdom_examples.mli deleted file mode 100644 index bc0db47b..00000000 --- a/examples/bonsai_guide_code/vdom_examples.mli +++ /dev/null @@ -1 +0,0 @@ -(* this module is used only for side-effects *) diff --git a/examples/bonsai_view/dune b/examples/bonsai_view/dune deleted file mode 100644 index 49064166..00000000 --- a/examples/bonsai_view/dune +++ /dev/null @@ -1,6 +0,0 @@ -(executables - (modes byte exe) - (names main) - (libraries async_js bonsai_web bonsai_web_ui_form bonsai_web_ui_gallery) - (preprocess - (pps ppx_jane js_of_ocaml-ppx ppx_bonsai ppx_css ppx_demo))) diff --git a/examples/bonsai_view/index.html b/examples/bonsai_view/index.html deleted file mode 100644 index 67bec598..00000000 --- a/examples/bonsai_view/index.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - - Bonsai View! - - - -
- - diff --git a/examples/bonsai_view/main.ml b/examples/bonsai_view/main.ml deleted file mode 100644 index bb8bbd0b..00000000 --- a/examples/bonsai_view/main.ml +++ /dev/null @@ -1,914 +0,0 @@ -open! Core -open! Bonsai_web.Cont -open! Bonsai.Let_syntax -module Form = Bonsai_web_ui_form.With_automatic_view -module Gallery = Bonsai_web_ui_gallery - -let vbox c = View.vbox ~cross_axis_alignment:Start ~gap:(`Px 5) c -let hbox c = View.hbox ~cross_axis_alignment:Start ~gap:(`Px 5) c - -module Text = struct - let name = "Plain Text" - - let description = - {| Strings are rendered to the page with View.text. - When invoked without any optional parameters, it will produce a dom Text node. |} - ;; - - let view graph = - let%map theme = View.Theme.current graph in - [%demo View.themed_text theme "hello world"] - ;; - - let selector = None - let filter_attrs = None -end - -module Text_with_style_and_size = struct - let name = "Text With Style And Size" - - let description = - {| You can use themed text to easily render text in a range of font styles and sizes - that are themable and consistent across different components. |} - ;; - - let view graph = - let%map theme = View.Theme.current graph in - [%demo - vbox - [ View.themed_text theme ?style:None ?size:None "default style and size" - ; View.themed_text theme ~style:Bold "bold text!" - ; View.themed_text theme ~style:Italic "italic text ..." - ; View.themed_text theme ~style:Underlined "this text is underlined" - ; View.themed_text theme ~size:Large "This text is large" - ; View.themed_text theme ~size:Small "This text is small" - ]] - ;; - - let selector = Some "span" - let filter_attrs k _ = not (String.is_substring k ~substring:"padding") - let filter_attrs = Some filter_attrs -end - -module Text_with_intent = struct - let name = "Text With Intent" - - let description = - {| When text conveys special meaning, like an error or a warning, it can be useful - to give the theme the ability to style it differently. |} - ;; - - let view graph = - let%map theme = View.Theme.current graph in - [%demo - vbox - [ View.themed_text theme ?intent:None "no intent" - ; View.themed_text theme ~intent:Info "informational" - ; View.themed_text theme ~intent:Success "success!!!" - ; View.themed_text theme ~intent:Warning "hmmmm" - ; View.themed_text theme ~intent:Error "oh no." - ]] - ;; - - let selector = Some "span" - let filter_attrs k _ = not (String.is_substring k ~substring:"padding") - let filter_attrs = Some filter_attrs -end - -module Table = struct - let name = "Plain Table" - - let description = - {| - Tables are created by specifying the columns for the table alongside a list of data that - will populate the table. Each column is defined with a name, a "getter" function that - extracts the cell-specific data, and a renderer for that cell-specific data. |} - ;; - - let view graph = - let%map theme = View.Theme.current graph in - let module T = - [%demo - [@@@ocamlformat "disable"] - - type t = { sym : string ; bid : float ; ask : float } [@@deriving fields ~getters] - - [@@@ocamlformat "enable"] - - let columns = - let render_text _ string = View.text string in - let render_float _ = Vdom.Node.textf "%.3f" in - [ View.Table.Col.make "sym" ~get:sym ~render:render_text - ; View.Table.Col.make "bid" ~get:bid ~render:render_float - ; View.Table.Col.make "ask" ~get:ask ~render:render_float - ] - ;; - - let data = - [ { sym = "aapl"; bid = 1.0; ask = 2.3 } - ; { sym = "msft"; bid = 8.2; ask = 9.8 } - ; { sym = "tsla"; bid = 3.3; ask = 7.2 } - ] - ;; - - let my_table = View.Table.render theme columns data] - in - let code = - (* Get rid of the lines that disable ocamlformat *) - T.ppx_demo_string - |> Core.String.split_lines - |> List.filter ~f:(fun s -> - not (Core.String.is_substring s ~substring:"ocamlformat")) - |> String.concat ~sep:"\n" - |> String.strip - |> String.substr_replace_first ~pattern:"\n\n" ~with_:"\n" - in - T.my_table, code - ;; - - let selector = None - let filter_attrs = None -end - -module Table_with_group = struct - let name = "Table with group" - - let description = - {| To make column groups, simply call the "group" function with a list of sub-columns. |} - ;; - - let view graph = - let%map theme = View.Theme.current graph in - let module T = struct - type t = - { sym : string - ; bid : float - ; ask : float - } - [@@deriving fields ~getters] - - include - [%demo - let columns = - let render_text _ string = View.text string in - let render_float _ = Vdom.Node.textf "%.3f" in - [ View.Table.Col.make "sym" ~get:sym ~render:render_text - ; View.Table.Col.group - "prices" - [ View.Table.Col.make "bid" ~get:bid ~render:render_float - ; View.Table.Col.make "ask" ~get:ask ~render:render_float - ] - ] - ;;] - - let data = - [ { sym = "aapl"; bid = 1.0; ask = 2.3 } - ; { sym = "msft"; bid = 8.2; ask = 9.8 } - ; { sym = "tsla"; bid = 3.3; ask = 7.2 } - ] - ;; - - let my_table = View.Table.render theme columns data - end - in - T.my_table, T.ppx_demo_string - ;; - - let selector = None - let filter_attrs = None -end - -module Table_with_empty_cells = struct - let name = "Table with empty cells" - - let description = - {| - Sometimes the cell contains some optional value. If you want to handle that case yourself, you can - use the "make" function like before. But you can also call "make_opt" and the table will get a line - through it indicating that it's properly empty (as opposed to containing - for example - the empty string)|} - ;; - - let view graph = - let%map theme = View.Theme.current graph in - let module T = struct - type t = - { sym : string - ; trader : string option - } - [@@deriving fields ~getters] - - include - [%demo - let columns = - let render_text _ string = View.text string in - [ View.Table.Col.make "symbol" ~get:sym ~render:render_text - ; View.Table.Col.make_opt "trader" ~get:trader ~render:render_text - ] - ;; - - let data = - [ { sym = "aapl"; trader = None } - ; { sym = "msft"; trader = None } - ; { sym = "tsla"; trader = Some "emusk" } - ] - ;;] - - let my_table = View.Table.render theme columns data - end - in - T.my_table, T.ppx_demo_string - ;; - - let selector = None - let filter_attrs = None -end - -module Tooltip = struct - let name = "Basic Tooltip" - - let description = - {| The standard tooltip function allows annotating some text with a tooltip containing another string |} - ;; - - let view graph = - let%map theme = View.Theme.current graph in - [%demo - View.tooltip - theme - ~tooltip:"saying 'hello world' every morning is a common programmer ritual" - "hello world"] - ;; - - let selector = None - let filter_attrs = None -end - -module Tooltip_directions = struct - let name = "Tooltip directions" - let description = {| Tooltips can be positioned relative to the annotated content |} - - let view graph = - let%map theme = View.Theme.current graph in - let vbox = View.vbox ~gap:(`Em 1) in - let hbox = View.hbox ~gap:(`Em 1) in - [%demo - let tooltip = "tooltip content" in - vbox - [ hbox - [ View.tooltip theme ~direction:Top ~tooltip "top" - ; View.tooltip theme ~direction:Bottom ~tooltip "bottom" - ] - ; hbox - [ View.tooltip theme ~direction:Left ~tooltip "left" - ; View.tooltip theme ~direction:Right ~tooltip "right" - ] - ]] - ;; - - let selector = None - let filter_attrs = None -end - -let rickroll = - Effect.of_sync_fun - (fun () -> - let url = Js_of_ocaml.Js.string "https://www.youtube.com/watch?v=dQw4w9WgXcQ" in - Js_of_ocaml.Dom_html.window##.location##assign url) - () -;; - -module Tooltip_with_arbitrary_content = struct - let name = "Tooltips with arbitrary content" - - let description = - {| With `tooltip'` you can add a tooltip to abitrary content, and the tooltip itself can contain arbitrary content |} - ;; - - let view graph = - let%map theme = View.Theme.current graph in - [%demo - let tooltip = - View.vbox - [ Vdom.Node.text "do not click this button" - ; View.button theme ~intent:Error ~on_click:rickroll "no clicky!" - ] - in - View.tooltip' theme ~tooltip ~direction:Right (Vdom.Node.text "cursed knowledge")] - ;; - - let selector = None - let filter_attrs = None -end - -module Button = struct - let name = "Regular Button" - - let description = - {| The button function requires an on_click handler, and takes the button label - as input in the form of a string |} - ;; - - let view graph = - let%map theme = View.Theme.current graph in - [%demo View.button theme ~on_click:Effect.Ignore "click me"] - ;; - - let selector = None - let filter_attrs = None -end - -module Disabled_button = struct - let name = "Disabled button" - let description = {| Buttons can be disabled with an optional parameter |} - - let view graph = - let%map theme = View.Theme.current graph in - [%demo - hbox - [ View.button theme ~disabled:false ~on_click:Effect.Ignore "not disabled" - ; View.button theme ~disabled:true ~on_click:Effect.Ignore "disabled" - ]] - ;; - - let selector = Some "button" - let filter_attrs = None -end - -module Buttons_with_intent = struct - let name = "Buttons with Intent" - let description = {| When given an intent, buttons can change their style |} - - let view graph = - let%map theme = View.Theme.current graph in - let on_click = Effect.Ignore in - [%demo - hbox - [ View.button theme ?intent:None ~on_click "no intent" - ; View.button theme ~intent:Info ~on_click "informational" - ; View.button theme ~intent:Success ~on_click "success!!!" - ; View.button theme ~intent:Warning ~on_click "hmmmm" - ; View.button theme ~intent:Error ~on_click "oh no." - ]] - ;; - - let selector = Some "button" - let filter_attrs = None -end - -module Disabled_buttons_with_intent = struct - let name = "Disabled buttons with Intent" - - let description = - {| When buttons with Intent are disabled, they are no longer clickable - and their visuals change. |} - ;; - - let view graph = - let%map theme = View.Theme.current graph in - [%demo - let on_click = Effect.Ignore - and disabled = true in - hbox - [ View.button theme ~disabled ~on_click ?intent:None "no intent" - ; View.button theme ~disabled ~on_click ~intent:Info "informational" - ; View.button theme ~disabled ~on_click ~intent:Success "success!!!" - ; View.button theme ~disabled ~on_click ~intent:Warning "hmmmm" - ; View.button theme ~disabled ~on_click ~intent:Error "oh. oh no." - ]] - ;; - - let selector = Some "button" - let filter_attrs = None -end - -module Badge = struct - let name = "Badge" - - let description = - {| Badges are small bits of text with a background, often used for labels, counters, - or minor status indicators. |} - ;; - - let view graph = - let%map theme = View.Theme.current graph in - [%demo - hbox - [ View.badge theme ?intent:None "0" - ; View.badge theme ~intent:Info "Informational Label" - ; View.badge theme ~intent:Success "5" - ; View.badge theme ~intent:Warning "Investigate" - ; View.badge theme ~intent:Error "System Meltdown" - ]] - ;; - - let selector = None - let filter_attrs = None -end - -module Dismissible_badge = struct - let name = "Dismissible Badge" - - let description = - {| If an optional `on_dismiss : unit Effect.t` argument is provided, the badge will - have a dismiss button. This is useful for multiselect forms. |} - ;; - - let view graph = - let%map theme = View.Theme.current graph in - [%demo - let on_dismiss = Effect.Ignore in - hbox - [ View.badge theme ~on_dismiss ?intent:None "0" - ; View.badge theme ~on_dismiss ~intent:Info "Informational Label" - ; View.badge theme ~on_dismiss ~intent:Success "5" - ; View.badge theme ~on_dismiss ~intent:Warning "Investigate" - ; View.badge theme ~on_dismiss ~intent:Error "System Meltdown" - ]] - ;; - - let selector = None - let filter_attrs = None -end - -module Basic_vbox = struct - let name = "Basic vbox" - let description = {| The vbox function builds a vertically stacking container. |} - - let make_box c w h = - Vdom.Node.div - ~attrs: - [ Vdom.Attr.style Css_gen.(background_color c @> min_width w @> min_height h) ] - [] - ;; - - let red = make_box (`Name "red") (`Px 20) (`Px 30) - let green = make_box (`Name "green") (`Px 30) (`Px 50) - let blue = make_box (`Name "blue") (`Px 50) (`Px 30) - let view _graph = Bonsai.return [%demo View.vbox [ red; green; blue ]] - let selector = None - - let filter_attrs = - Some - (fun k _ -> - (not (String.is_substring k ~substring:"width")) - && not (String.is_substring k ~substring:"height")) - ;; -end - -module Basic_hbox = struct - let name = "Basic hbox" - let description = {| The vbox function builds a horizontally stacking container. |} - let red, green, blue, filter_attrs = Basic_vbox.(red, green, blue, filter_attrs) - let view _graph = Bonsai.return [%demo View.hbox [ red; green; blue ]] - let selector = None -end - -module Interactive_vbox = struct - open Basic_vbox - open View.Flex - - let name = "Vbox and Hbox with Arguments" - - let description = - {| [vbox] and [hbox] take a number of optional parameters which you can use to control - the direction of the stacking as well as how the items are distributed in any space - that is left over in the container. |} - ;; - - let white_space_pre = Vdom.Attr.style Css_gen.(white_space `Pre) - - let dropdown (type a) ?init name (module M : Bonsai.Enum with type t = a) graph = - Bonsai.map - (Form.Elements.Dropdown.enumerable_opt ?init (module M) graph) - ~f:(fun form -> - let value = Or_error.ok_exn (Form.value form) in - let view = Form.View.to_vdom_plain (Form.view form) |> List.hd_exn in - let view = - View.hbox - ~main_axis_alignment:End - ~gap:(`Px 10) - [ Vdom.Node.label ~attrs:[ white_space_pre ] [ Vdom.Node.text name ] - ; Vdom.Node.div ~attrs:[ Vdom.Attr.style (Css_gen.width (`Px 200)) ] [ view ] - ] - in - let pattern = String.Search_pattern.create ("?" ^ name) in - let new_text = - match value with - | None -> sprintf "?%s:None" name - | Some value -> sprintf "~%s:%s" name (M.sexp_of_t value |> Sexp.to_string) - in - let modify_program s = - String.Search_pattern.replace_first pattern ~in_:s ~with_:new_text - in - view, value, modify_program) - ;; - - module Style = - [%css - stylesheet - {| - .controls { - border-radius: 3px; - padding: 1em; - border: 1px solid darkgrey; - } - - .target { - border: 1px dashed black; - padding: 5px; - width: 150px; - height: 150px; - } - |}] - - module Axis = struct - type t = - | Hbox - | Vbox - [@@deriving sexp, enumerate, equal, compare] - end - - let view graph = - let%map axis_v, axis, _ = dropdown "function" ~init:`First_item (module Axis) graph - and v_direction_v, v_direction, v_f1 = - dropdown "direction" (module Vertical_dir) graph - and h_direction_v, h_direction, h_f1 = - dropdown "direction" (module Horizontal_dir) graph - and main_axis_alignment_v, main_axis_alignment, f2 = - dropdown "main_axis_alignment" (module Main_axis_alignment) graph - and cross_axis_alignment_v, cross_axis_alignment, f3 = - dropdown "cross_axis_alignment" (module Cross_axis_alignment) graph - and theme = View.Theme.current graph in - let axis = Option.value axis ~default:Hbox in - let (view, text), direction_v, f1 = - match axis with - | Hbox -> - let direction = h_direction in - let demo = - [%demo - View.hbox - ~attrs:[ Vdom.Attr.(many [ id "target"; Style.target ]) ] - ?direction - ?main_axis_alignment - ?cross_axis_alignment - [ red; green; blue ]] - in - demo, h_direction_v, h_f1 - | Vbox -> - let direction = v_direction in - let demo = - [%demo - View.vbox - ~attrs:[ Vdom.Attr.(many [ id "target"; Style.target ]) ] - ?direction - ?main_axis_alignment - ?cross_axis_alignment - [ red; green; blue ]] - in - demo, v_direction_v, v_f1 - in - let controls_bg = (View.primary_colors theme).background in - let controls_border = View.extreme_primary_border_color theme in - let view = - View.hbox - ~attrs:[ Vdom.Attr.style (Css_gen.create ~field:"flex-grow" ~value:"1") ] - ~gap:(`Em 1) - ~main_axis_alignment:Space_between - ~cross_axis_alignment:Start - [ View.vbox - ~attrs: - [ Style.controls - ; Vdom.Attr.style (Css_gen.background_color controls_bg) - ; Vdom.Attr.style - (Css_gen.border ~width:(`Px 1) ~color:controls_border ~style:`Solid ()) - ] - ~gap:(`Px 3) - [ axis_v; direction_v; main_axis_alignment_v; cross_axis_alignment_v ] - ; view - ] - in - let text = - String.split_lines text - |> List.filter ~f:(Fn.non (String.is_substring ~substring:"target")) - |> String.concat_lines - |> fun init -> List.fold [ f1; f2; f3 ] ~init ~f:(fun s f -> f s) - in - view, text - ;; - - let selector = Some "#target" - - let filter_attrs = - Some - (fun k v -> - (not (String.equal k "id")) - && (not (String.is_substring k ~substring:"padding")) - && (not (String.is_substring k ~substring:"border")) - && (not (String.is_substring v ~substring:"target")) - && (not (String.is_substring k ~substring:"width")) - && not (String.is_substring k ~substring:"height")) - ;; -end - -module Basic_tabs = struct - let name = "Basic Tabs" - let description = "" - - let view graph = - let%map theme = View.Theme.current graph in - [%demo - let on_change ~from:_ ~to_:_ = Effect.Ignore in - View.tabs - theme - ~equal:[%equal: int] - ~active:0 - ~on_change - [ 0, Vdom.Node.text "home" - ; 1, Vdom.Node.text "about" - ; 2, Vdom.Node.text "user preferences" - ]] - ;; - - let selector = None - let filter_attrs = None -end - -module Enumerable_tabs = struct - let name = "Enumerable Tabs" - - let description = - {| If you have an enumerable type, tabs_enum can be used to generate tabs for it |} - ;; - - let view graph = - let%map theme = View.Theme.current graph in - let module T = - [%demo - module Pages = struct - type t = - | Home - | About - | User_preferences - [@@deriving enumerate, equal, sexp_of] - end - - let tabs = - let on_change ~from:_ ~to_:_ = Effect.Ignore in - View.tabs_enum theme (module Pages) ~active:Home ~on_change - ;;] - in - T.tabs, T.ppx_demo_string - ;; - - let selector = None - let filter_attrs = None -end - -module Devbar = struct - let name = "Basic Devbar" - let description = {| |} - - let view graph = - let%map theme = View.Theme.current graph in - let view, text = [%demo View.devbar theme "DEV"] in - Vdom.Node.div ~attrs:[ Vdom.Attr.style (Css_gen.max_width (`Px 500)) ] [ view ], text - ;; - - let selector = None - let filter_attrs = Some (fun k _ -> not (String.is_prefix k ~prefix:"style")) -end - -module Devbar_intent = struct - let name = "Devbar with Intent" - let description = {| |} - - let view graph = - let%map theme = View.Theme.current graph in - let view, text = - [%demo - View.vbox - ~gap:(`Em_float 0.5) - [ View.devbar theme ?intent:None "DEV" - ; View.devbar theme ~intent:Info "DEV" - ; View.devbar theme ~intent:Success "DEV" - ; View.devbar theme ~intent:Warning "DEV" - ; View.devbar theme ~intent:Error "DEV" - ]] - in - Vdom.Node.div ~attrs:[ Vdom.Attr.style (Css_gen.max_width (`Px 500)) ] [ view ], text - ;; - - let selector = None - let filter_attrs = Some (fun k _ -> not (String.is_prefix k ~prefix:"style")) -end - -module Basic_card = struct - let name = "Basic Card" - let description = {| |} - - let view graph = - let%map.Bonsai theme = View.Theme.current graph in - let view, text = [%demo View.card theme "The message is: You are great!"] in - Vdom.Node.div ~attrs:[ Vdom.Attr.style (Css_gen.max_width (`Px 500)) ] [ view ], text - ;; - - let selector = None - let filter_attrs = Some (fun k _ -> not (String.is_prefix k ~prefix:"style")) -end - -module Card_with_title = struct - let name = "Card with a title" - let description = {| |} - - let view graph = - let%map.Bonsai theme = View.Theme.current graph in - let view, text = - [%demo View.card theme ~title:"New message!!" "The message is: You are great!"] - in - Vdom.Node.div ~attrs:[ Vdom.Attr.style (Css_gen.max_width (`Px 500)) ] [ view ], text - ;; - - let selector = None - let filter_attrs = Some (fun k _ -> not (String.is_prefix k ~prefix:"style")) -end - -module Cards_with_intent = struct - let name = "Cards with intent" - let description = {| |} - - let view graph = - let%map.Bonsai theme = View.Theme.current graph in - let view, text = - [%demo - View.vbox - ~gap:(`Em_float 0.5) - [ View.card theme ?intent:None ~title:"no intent" "[message content here]" - ; View.card theme ~intent:Info ~title:"information" "FYI: some stuff" - ; View.card theme ~intent:Success ~title:"success!!!" "a good thing happened" - ; View.card theme ~intent:Warning ~title:"hmmm" "please be alerted" - ; View.card theme ~intent:Error ~title:"UH OH" "something bad happened!" - ]] - in - Vdom.Node.div ~attrs:[ Vdom.Attr.style (Css_gen.max_width (`Px 500)) ] [ view ], text - ;; - - let selector = None - let filter_attrs = Some (fun k _ -> not (String.is_prefix k ~prefix:"style")) -end - -module Cards_with_fieldset = struct - let name = "Cards with fieldset styling" - let description = {| |} - - let view graph = - let%map.Bonsai theme = View.Theme.current graph in - let view, text = - [%demo - View.vbox - ~gap:(`Em_float 0.5) - [ View.card - theme - ?intent:None - ~title:"no intent" - ~title_kind:Discreet - "[message content here]" - ; View.card - theme - ~intent:Info - ~title:"information" - ~title_kind:Discreet - "FYI: some stuff" - ; View.card - theme - ~intent:Success - ~title:"success!!!" - ~title_kind:Discreet - "a good thing happened" - ; View.card - theme - ~intent:Warning - ~title:"hmmm" - ~title_kind:Discreet - "please be alerted" - ; View.card - theme - ~intent:Error - ~title:"UH OH" - ~title_kind:Discreet - "something bad happened!" - ]] - in - Vdom.Node.div ~attrs:[ Vdom.Attr.style (Css_gen.max_width (`Px 500)) ] [ view ], text - ;; - - let selector = None - let filter_attrs = Some (fun k _ -> not (String.is_prefix k ~prefix:"style")) -end - -module Card_with_rows = struct - let name = "Card with rows" - - let description = - {| View.card' allows for the content to be a list of arbitrary vdom nodes which are separated vertically from one another. |} - ;; - - let view graph = - let%map.Bonsai theme = View.Theme.current graph in - let view, text = - [%demo - hbox - [ View.card' - theme - ~intent:Info - ~title:[ View.text "New message!!" ] - [ View.text "debug message 1 "; View.text "more debug message" ] - ; View.card' - theme - ~intent:Info - ~title_kind:Discreet - ~title:[ View.text "New message!!" ] - [ View.text "debug message 1 "; View.text "more debug message" ] - ]] - in - Vdom.Node.div ~attrs:[ Vdom.Attr.style (Css_gen.max_width (`Px 500)) ] [ view ], text - ;; - - let selector = None - let filter_attrs = Some (fun k _ -> not (String.is_prefix k ~prefix:"style")) -end - -let component graph = - let%sub theme, theme_picker = Gallery.Theme_picker.component () graph in - View.Theme.set_for_app - theme - (Gallery.make_sections - ~theme_picker - [ ( "Text" - , {| An amalgom of Vdom.Node.text and Vdom.Node.span, View.text is used to put - (optionally formatted) text on the screen. |} - , [ Gallery.make_demo (module Text) - ; Gallery.make_demo (module Text_with_style_and_size) - ; Gallery.make_demo (module Text_with_intent) - ] ) - ; ( "Tooltip" - , {|Tooltips are a great way to make extra information available to a user when they hover over an element.|} - , [ Gallery.make_demo (module Tooltip) - ; Gallery.make_demo (module Tooltip_directions) - ; Gallery.make_demo (module Tooltip_with_arbitrary_content) - ] ) - ; ( "Buttons" - , {| |} - , [ Gallery.make_demo (module Button) - ; Gallery.make_demo (module Disabled_button) - ; Gallery.make_demo (module Buttons_with_intent) - ; Gallery.make_demo (module Disabled_buttons_with_intent) - ] ) - ; ( "Badges" - , {| |} - , [ Gallery.make_demo (module Badge) - ; Gallery.make_demo (module Dismissible_badge) - ] ) - ; ( "Tables" - , {| |} - , [ Gallery.make_demo (module Table) - ; Gallery.make_demo (module Table_with_group) - ; Gallery.make_demo (module Table_with_empty_cells) - ] ) - ; ( "Flex Boxes" - , {| The vbox and hbox functions use CSS's flexbox layout algorithm to build vertically - and horizontally stacking containers. Because they are purely for layout, themes - do no have the ability to change their behavior. |} - , [ Gallery.make_demo (module Basic_hbox) - ; Gallery.make_demo (module Basic_vbox) - ; Gallery.make_demo (module Interactive_vbox) - ] ) - ; ( "Tabs" - , {| Used for navigating between pages within an application, the tabs component allows - for zero or one active tabs. |} - , [ Gallery.make_demo (module Basic_tabs) - ; Gallery.make_demo (module Enumerable_tabs) - ] ) - ; ( "Devbar" - , {| Used to convey that an application is in some kind of dev or test mode, the devbar - is intentionally large and bright and attention-grabbing |} - , [ Gallery.make_demo (module Devbar); Gallery.make_demo (module Devbar_intent) ] - ) - ; ( "Card" - , {| Cards are a great way of organizing the structure of your app or just - grabbing your users attention and letting them know things are happening, not - happening, happened, failed, kinda failed, kinda worked, and more! - |} - , [ Gallery.make_demo (module Basic_card) - ; Gallery.make_demo (module Card_with_title) - ; Gallery.make_demo (module Cards_with_intent) - ; Gallery.make_demo (module Cards_with_fieldset) - ; Gallery.make_demo (module Card_with_rows) - ] ) - ]) - graph -;; - -let () = - Async_js.init (); - Bonsai_web.Start.start component -;; diff --git a/examples/bonsai_view/main.mli b/examples/bonsai_view/main.mli deleted file mode 100644 index 74bb7298..00000000 --- a/examples/bonsai_view/main.mli +++ /dev/null @@ -1 +0,0 @@ -(*_ This signature is deliberately empty. *) diff --git a/examples/clipboard/dune b/examples/clipboard/dune deleted file mode 100644 index d76d2b18..00000000 --- a/examples/clipboard/dune +++ /dev/null @@ -1,6 +0,0 @@ -(executables - (modes byte exe) - (names main) - (libraries bonsai_web bonsai_web_ui_form js_clipboard) - (preprocess - (pps js_of_ocaml-ppx ppx_bonsai ppx_jane))) diff --git a/examples/clipboard/index.html b/examples/clipboard/index.html deleted file mode 100644 index cb657dfd..00000000 --- a/examples/clipboard/index.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - - Clipboard! - - - -
- - diff --git a/examples/clipboard/main.ml b/examples/clipboard/main.ml deleted file mode 100644 index cd4f642c..00000000 --- a/examples/clipboard/main.ml +++ /dev/null @@ -1,61 +0,0 @@ -open! Core -open! Bonsai_web.Cont -open Js_of_ocaml -open Bonsai.Let_syntax -module Form = Bonsai_web_ui_form.With_automatic_view - -let component graph = - let form = Form.Elements.Textbox.string ~allow_updates_when_focused:`Never () graph in - let copy_button = - let%arr form = form in - Vdom.Node.button - ~attrs: - [ Vdom.Attr.on_click (fun _ -> - match%bind.Effect - Js_clipboard.Asynchronous.copy_text - (Js.string (Form.value_or_default form ~default:"")) - with - | Ok () -> Effect.Ignore - | Error err -> - Effect.print_s [%message "Failed to copy to clipboard." (err : Error.t)]) - ] - [ Vdom.Node.text "copy to clipboard" ] - in - (* NOTE: This button copies from clipboard in a deprecated and non-recommended way. Use - the above button implementation as a reference if you'd like clipboard copying - behavior in your app. *) - let legacy_copy_button = - let%arr form = form in - Vdom.Node.button - ~attrs: - [ Vdom.Attr.on_click (fun _ -> - Ui_effect.Expert.handle - (Effect.ignore_m - (Js_clipboard.Asynchronous.copy_text - (Js.string (Form.value_or_default form ~default:"")))); - Effect.Ignore) - ] - [ Vdom.Node.text "copy to clipboard (legacy behaviour)" ] - in - let paste_button = - let%arr form = form in - Vdom.Node.button - ~attrs: - [ Vdom.Attr.on_click (fun _ -> - let%bind.Effect st = Js_clipboard.Asynchronous.read_text in - match st with - | Ok st -> Form.set form (Js.to_string st) - | Error error -> - Effect.print_s - [%message "Error reading from clipboard" ~_:(error : Error.t)]) - ] - [ Vdom.Node.text "paste from clipboard" ] - in - let%arr form = form - and copy_button = copy_button - and legacy_copy_button = legacy_copy_button - and paste_button = paste_button in - Vdom.Node.div [ Form.view_as_vdom form; copy_button; legacy_copy_button; paste_button ] -;; - -let () = Start.start component diff --git a/examples/clipboard/main.mli b/examples/clipboard/main.mli deleted file mode 100644 index 74bb7298..00000000 --- a/examples/clipboard/main.mli +++ /dev/null @@ -1 +0,0 @@ -(*_ This signature is deliberately empty. *) diff --git a/examples/clock_every/dune b/examples/clock_every/dune deleted file mode 100644 index f51922a9..00000000 --- a/examples/clock_every/dune +++ /dev/null @@ -1,6 +0,0 @@ -(executables - (modes byte exe) - (names main) - (libraries bonsai_web bonsai_extra bonsai_web_ui_form core virtual_dom.svg) - (preprocess - (pps ppx_jane ppx_bonsai ppx_css js_of_ocaml-ppx ppx_typed_fields))) diff --git a/examples/clock_every/index.html b/examples/clock_every/index.html deleted file mode 100644 index 94f7ae98..00000000 --- a/examples/clock_every/index.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - - Bonsai Clock.every - - - -
- - diff --git a/examples/clock_every/main.ml b/examples/clock_every/main.ml deleted file mode 100644 index d219dd8d..00000000 --- a/examples/clock_every/main.ml +++ /dev/null @@ -1,495 +0,0 @@ -open! Core -open! Bonsai_web.Cont -open Bonsai.Let_syntax -module Form = Bonsai_web_ui_form.With_automatic_view - -module Css = -[%css -stylesheet - {| -html, body { - font-family: 'Open Sans', sans-serif; -} - -.paper { - box-shadow: 0 0 8px rgba(0,0,0,0.2); - padding: 12px; - border-radius: 4px; - margin: 8px; - max-width: fit-content; - text-align: center; -} - -.timeline_frame { - box-shadow: 0 0 8px rgba(0,0,0,0.2); - padding: 12px; - border-radius: 4px; - margin: 8px; - width: 230px; - height: 100px; - background-color: black; -} - -.row { - display: flex; - flex-direction: row; - align-items: center; - align-content: center; -} - -.column { - display: flex; - flex-direction: column; - align-items: center; - align-content: center; -} - -:root { - box-sizing: border-box; -} -|}] - -let paper = Css.paper -let column = Css.column -let row = Css.row -let timeline_frame = Css.timeline_frame - -let colors = - [| "#ff355e" - ; "#fd5b78" - ; "#ff6037" - ; "#ff9966" - ; "#ff9933" - ; "#ffcc33" - ; "#ffff66" - ; "#ccff00" - ; "#66ff66" - ; "#aaf0d1" - ; "#50bfe6" - ; "#ff6eff" - ; "#ee34d2" - ; "#ff00cc" - |] -;; - -module Random_time_span = struct - type t = - { base_duration : Time_ns.Span.t - ; extra_duration : Time_ns.Span.t - ; chance_of_getting_extra_duration : float - } - [@@deriving typed_fields, sexp, equal] - - let default_base_duration = 0.5 - let default_extra_duration = 1.0 - let default_chance_of_getting_extra_duration = 0.5 - - let form graph = - Form.Typed.Record.make - (module struct - module Typed_field = Typed_field - - let time_span_form ~max ~default graph = - let form = - Form.Elements.Range.float - ~min:0.0 - ~max - ~step:0.1 - ~default - ~allow_updates_when_focused:`Never - () - graph - in - let%arr form = form in - Form.project form ~parse_exn:Time_ns.Span.of_sec ~unparse:Time_ns.Span.to_sec - ;; - - let form_for_field : type a. a Typed_field.t -> Bonsai.graph -> a Form.t Bonsai.t = - fun typed_field graph -> - match typed_field with - | Base_duration -> time_span_form ~max:1.0 ~default:default_base_duration graph - | Extra_duration -> - time_span_form ~max:2.0 ~default:default_extra_duration graph - | Chance_of_getting_extra_duration -> - Form.Elements.Range.float - ~allow_updates_when_focused:`Never - ~min:0.0 - ~max:1.0 - ~step:0.1 - ~default:default_chance_of_getting_extra_duration - () - graph - ;; - - let label_for_field = `Inferred - end) - graph - ;; - - let default = - { base_duration = Time_ns.Span.of_sec default_base_duration - ; extra_duration = Time_ns.Span.of_sec default_extra_duration - ; chance_of_getting_extra_duration = default_chance_of_getting_extra_duration - } - ;; - - let get_wait_time t = - if Float.(Random.float_range 0.0 1.0 < t.chance_of_getting_extra_duration) - then Time_ns.Span.(t.base_duration + t.extra_duration) - else t.base_duration - ;; -end - -module Bar_id = Bonsai_extra.Id_gen (Int) () - -let bar_id_to_color x = colors.(Bar_id.to_int_exn x % Array.length colors) - -module Bar = struct - type t = - { start_time : Time_ns.Alternate_sexp.t - ; end_time : Time_ns.Alternate_sexp.t option - ; id : Bar_id.t - } - [@@deriving sexp, equal] - - let create time bar_id = { start_time = time; id = bar_id; end_time = None } - let finish t ~now = { t with end_time = Some now } -end - -module Track_id : sig - type t [@@deriving sexp, equal] - - val of_int : int -> t - val to_int : t -> int - - include Comparable with type t := t -end = - Int - -module Tracks = struct - type t = - { tracks : Bar.t Fdeque.t Track_id.Map.t - ; track_ids_by_bar_id : Track_id.t Bar_id.Map.t - ; last_trigger_time : Time_ns.Alternate_sexp.t - } - [@@deriving sexp, equal] - - let empty = - let tracks = Track_id.Map.empty in - let track_ids_by_bar_id = Bar_id.Map.empty in - let last_trigger_time = Time_ns.epoch in - { tracks; track_ids_by_bar_id; last_trigger_time } - ;; - - let find_lowest_clear_track t = - Map.fold - (t.tracks : _ Track_id.Map.t) - ~init:None - ~f:(fun ~key ~data acc -> - match acc with - | Some _ -> acc - | None -> - let first = Fdeque.peek_front data in - (match first with - | None -> Some key - | Some x -> if Option.is_some x.end_time then Some key else None)) - ;; - - let add_bar t ~now bar_id = - let next_lowest_clear_track = find_lowest_clear_track t in - let track_id = - match next_lowest_clear_track with - | Some x -> x - | None -> Map.length (t.tracks : _ Track_id.Map.t) |> Track_id.of_int - in - let remove_id, tracks = - let remove_id, queue = - let queue = - Option.value - (Map.find (t.tracks : _ Track_id.Map.t) track_id) - ~default:Fdeque.empty - in - let queue = Fdeque.enqueue_front queue (Bar.create now bar_id) in - if Fdeque.length queue >= 10 - then ( - let remove, queue = Fdeque.dequeue_back_exn queue in - Some remove.id, queue) - else None, queue - in - remove_id, Map.set (t.tracks : _ Track_id.Map.t) ~key:track_id ~data:queue - in - let track_ids_by_bar_id = - Map.set (t.track_ids_by_bar_id : _ Bar_id.Map.t) ~key:bar_id ~data:track_id - in - let track_ids_by_bar_id = - match remove_id with - | None -> track_ids_by_bar_id - | Some id -> Map.remove (track_ids_by_bar_id : _ Bar_id.Map.t) id - in - { tracks; track_ids_by_bar_id; last_trigger_time = now } - ;; - - let finish_bar t ~now (bar_id : Bar_id.t) : t = - let updated_tracks = - let open Option.Let_syntax in - let%bind track_id = Map.find (t.track_ids_by_bar_id : _ Bar_id.Map.t) bar_id in - let%bind queue = Map.find (t.tracks : _ Track_id.Map.t) track_id in - let%map element = Fdeque.peek_front queue in - let updated_element = Bar.finish element ~now in - let updated_queue = - let q = Fdeque.drop_front_exn queue in - Fdeque.enqueue q `front updated_element - in - Map.set (t.tracks : _ Track_id.Map.t) ~key:track_id ~data:updated_queue - in - match updated_tracks with - | Some tracks -> { t with tracks } - | None -> t - ;; -end - -module Tracks_action = struct - type t = - | Add_bar of Bar_id.t - | Finish_bar of Bar_id.t - [@@deriving sexp, equal] -end - -let time_ns_to_string time = - Time_ns.to_string_abs_parts time ~zone:Time_float.Zone.utc |> List.last_exn -;; - -let time_span_to_pixels span = Int.to_float (Time_ns.Span.to_int_ms span) /. 25.0 - -let timeline ~now ~(tracks : Bar.t Fdeque.t Track_id.Map.t Bonsai.t) graph = - let timeline_width = 200.0 in - let timeline_height = 100.0 in - let starting_line = - Virtual_dom_svg.( - Node.line - ~attrs: - [ Attr.x1 200. - ; Attr.x2 200. - ; Attr.y1 (-10.) - ; Attr.y2 105. - ; Attr.stroke (`Name "white") - ; Attr.stroke_width 4.0 - ; Attr.stroke_dasharray [ 5.; 5. ] - ] - []) - in - let number_of_tracks = - let%arr tracks = tracks in - Map.length (tracks : _ Track_id.Map.t) - in - let tracks = - Bonsai.assoc - (module Track_id) - tracks - ~f:(fun track_id deque _graph -> - let%arr now = now - and number_of_tracks = number_of_tracks - and deque = deque - and track_id = track_id in - let deque = Fdeque.to_list deque in - List.map deque ~f:(fun { start_time; end_time; id } -> - let end_time = Option.value end_time ~default:now in - let start_time_pixels = time_span_to_pixels (Time_ns.diff now start_time) in - let x = timeline_width -. start_time_pixels in - let width = time_span_to_pixels (Time_ns.diff end_time start_time) in - let height = (timeline_height /. Int.to_float number_of_tracks) -. 5.0 in - let y = (height +. 5.0) *. Int.to_float (Track_id.to_int track_id) in - Virtual_dom_svg.( - Node.rect - ~attrs: - [ Attr.x x - ; Attr.y y - ; Attr.width width - ; Attr.height height - ; Attr.rx 3.0 - ; Attr.fill (`Name (bar_id_to_color id)) - ] - []))) - graph - in - let lines = - let%arr now = now in - let times_where_lines_should_be_on = - List.init 22 ~f:(fun i -> - Time_ns.prev_multiple - ~base:Time_ns.epoch - ~can_equal_before:false - ~before:(Time_ns.sub now (Time_ns.Span.of_sec (1.0 *. Int.to_float i))) - ~interval:(Time_ns.Span.of_sec 1.0) - ()) - in - List.map times_where_lines_should_be_on ~f:(fun time -> - let offset = time_span_to_pixels (Time_ns.diff now time) in - let x = timeline_width -. offset in - Virtual_dom_svg.( - Node.line - ~attrs: - [ Attr.x1 x - ; Attr.x2 x - ; Attr.y1 (-10.0) - ; Attr.y2 105. - ; Attr.stroke (`Name "white") - ; Attr.stroke_width 3.0 - ; Attr.stroke_dasharray [ 5.; 5. ] - ] - [])) - in - let%arr tracks = tracks - and lines = lines in - let bars = - Virtual_dom_svg.( - Node.g - ~attrs:[ Attr.transform [ Attr.Translate { dx = 0.0; dy = 5.0 } ] ] - (List.concat (Map.data (tracks : _ Track_id.Map.t)))) - in - Virtual_dom_svg.( - Node.svg - ~attrs:[ Attr.width timeline_width; Attr.height (timeline_height +. 5.0) ] - ([ bars; starting_line ] @ lines)) -;; - -let clock - ~trigger_on_activate - ~when_to_start_next_effect - ~title - ~description - ~wait_time - ~delta_time:now - graph - = - let next_bar_id = Bar_id.component graph in - let%sub { tracks; last_trigger_time; _ }, update_tracks = - Tuple2.uncurry Bonsai.both - @@ Bonsai.state_machine1 - ~sexp_of_model:[%sexp_of: Tracks.t] - ~equal:[%equal: Tracks.t] - ~sexp_of_action:[%sexp_of: Tracks_action.t] - ~default_model:Tracks.empty - ~apply_action:(fun (_ : _ Bonsai.Apply_action_context.t) now tracks action -> - match now with - | Active now -> - (match action with - | Add_bar bar_id -> Tracks.add_bar tracks ~now bar_id - | Finish_bar bar_id -> Tracks.finish_bar tracks ~now bar_id) - | Inactive -> - eprint_s - [%message - [%here] - "An action sent to a [state_machine1] has been dropped because its \ - input was not present. This happens when the [state_machine1] is \ - inactive when it receives a message." - (action : Tracks_action.t)]; - tracks) - now - graph - in - let timeline = timeline ~now ~tracks graph in - let clock_action = - let%arr wait_time = wait_time - and update_tracks = update_tracks - and next_bar_id = next_bar_id in - let open Effect.Let_syntax in - let%bind bar_id = next_bar_id in - let%bind wait_time = Effect.of_sync_fun Random_time_span.get_wait_time wait_time in - let%bind () = update_tracks (Add_bar bar_id) in - let%bind () = Effect.of_deferred_fun Async_kernel.Clock_ns.after wait_time in - update_tracks (Finish_bar bar_id) - in - let () = - Bonsai.Clock.every - ~trigger_on_activate - ~when_to_start_next_effect - (Time_ns.Span.of_sec 1.0) - clock_action - graph - in - let%arr last_trigger_time = last_trigger_time - and timeline = timeline in - Vdom.Node.div - ~attrs:[ paper; column ] - [ Vdom.Node.strong [ Vdom.Node.text title ] - ; Vdom.Node.div ~attrs:[ row ] [ Vdom.Node.text description ] - ; Vdom.Node.div - [ Vdom.Node.text ("Last triggered: " ^ time_ns_to_string last_trigger_time) ] - ; Vdom.Node.div ~attrs:[ timeline_frame; row ] [ timeline ] - ] -;; - -let all_clocks ~trigger_on_activate ~wait_time ~delta_time graph = - List.map - [ ( `Wait_period_after_previous_effect_finishes_blocking - , "`Wait_period_after_previous_effect_finishes_blocking" - , "Always waits clock span after action was finished." ) - ; ( `Wait_period_after_previous_effect_starts_blocking - , "`Wait_period_after_previous_effect_starts_blocking" - , "If action takes longer than span, then clock will trigger immediately after \ - current action is done." ) - ; ( `Every_multiple_of_period_blocking - , "`Every_multiple_of_period_blocking" - , "Keeps its rythmn even on missed beats, will always trigger on multiples of the \ - span." ) - ; ( `Every_multiple_of_period_non_blocking - , "`Every_multiple_of_period_non_blocking" - , "Does not wait for previous effect to finish. Effects can overlap." ) - ] - ~f:(fun (when_to_start_next_effect, title, description) -> - clock - ~trigger_on_activate - ~when_to_start_next_effect - ~title - ~description - ~wait_time - ~delta_time - graph) - |> Bonsai.all -;; - -let immediate_clocks ~wait_time = all_clocks ~trigger_on_activate:true ~wait_time - -let component graph = - let time_span_form = Random_time_span.form graph in - let wait_time = - let%arr time_span_form = time_span_form in - Form.value_or_default time_span_form ~default:Random_time_span.default - in - let now = Bonsai.Clock.now graph in - let initial_time = - Bonsai.freeze - ~sexp_of_model:[%sexp_of: Time_ns.Alternate_sexp.t] - now - ~equal:[%equal: Time_ns.Alternate_sexp.t] - graph - in - let delta_time = - let%arr initial_time = initial_time - and now = now in - Time_ns.sub now (Time_ns.to_span_since_epoch initial_time) - in - let immediate_clocks = immediate_clocks ~wait_time ~delta_time graph in - let%arr immediate_clocks = immediate_clocks - and wait_time_form = time_span_form - and wait_time = wait_time in - Vdom.Node.div - ~attrs:[ column ] - [ Vdom.Node.div - ~attrs:[ column ] - [ Vdom.Node.div ~attrs:[ row ] [ Vdom.Node.text "Clock ticks every 1s" ] - ; Vdom.Node.div ~attrs:[ paper; row ] [ Form.view_as_vdom wait_time_form ] - ; Vdom.Node.div - ~attrs:[ row ] - [ Vdom.Node.text - ("Current duration: " - ^ Sexp.to_string (Random_time_span.sexp_of_t wait_time) - ^ "s + some small (but non-zero) overhead.") - ] - ] - ; Vdom.Node.div ~attrs:[ paper; row ] immediate_clocks - ] -;; - -let () = Bonsai_web.Start.start component diff --git a/examples/clock_every/main.mli b/examples/clock_every/main.mli deleted file mode 100644 index 74bb7298..00000000 --- a/examples/clock_every/main.mli +++ /dev/null @@ -1 +0,0 @@ -(*_ This signature is deliberately empty. *) diff --git a/examples/codemirror/dune b/examples/codemirror/dune deleted file mode 100644 index 9a0cc14d..00000000 --- a/examples/codemirror/dune +++ /dev/null @@ -1,7 +0,0 @@ -(executables - (modes byte exe) - (names main) - (libraries bonsai_web bonsai_web_ui_codemirror bonsai_web_ui_form codemirror - codemirror_rainbow_parentheses ppx_css.inline_css codemirror_themes) - (preprocess - (pps ppx_bonsai ppx_jane))) diff --git a/examples/codemirror/index.html b/examples/codemirror/index.html deleted file mode 100644 index b4e5badc..00000000 --- a/examples/codemirror/index.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - - Codemirror with autocomplete - - - -
- - diff --git a/examples/codemirror/main.ml b/examples/codemirror/main.ml deleted file mode 100644 index 295cef1b..00000000 --- a/examples/codemirror/main.ml +++ /dev/null @@ -1,517 +0,0 @@ -open! Core -open! Bonsai_web.Cont -open Bonsai.Let_syntax -open Codemirror -open Virtual_dom -module Form = Bonsai_web_ui_form.With_automatic_view -module Codemirror = Bonsai_web_ui_codemirror - -(* Make the codemirror editor take up most of the view *) -let () = - Inline_css.Private.append - {| - .cm-editor { - height: 80vh; - } - - label { - font-weight: normal; - } -|} -;; - -module Fruit_sexp_grammar_auto_complete = struct - module Fruit = struct - type t = - | Apple - | Blueberry - | Banana - | Pineapple - | Custom of string - [@@deriving sexp, equal, sexp_grammar] - end - - module Query = struct - type t = Fruit.t Blang.t [@@deriving sexp, equal, sexp_grammar] - end - - let codemirror_editor = - Codemirror.with_sexp_grammar_autocompletion - ~include_non_exhaustive_hint:false - ~extra_extension: - (State.Extension.of_list - [ Basic_setup.basic_setup; Codemirror_rainbow_parentheses.extension () ]) - (Bonsai.return Query.t_sexp_grammar) - ;; -end - -module Ocaml_syntax_highlighting = struct - let doc = - {|open! Core - -(* Syntax highlight for ocaml *) - -let x = List.map [ 1; 2; 3; 4; 5 ] ~f:(fun x -> x + 1) - -let y = - let z = 3 in - let a = 4 in - z + a -;; -|} - ;; - - let codemirror_editor ~theme = - let create_extensions state = - let theme = Codemirror_themes.get state in - [ Basic_setup.basic_setup - ; Mllike.ocaml - |> Stream_parser.Stream_language.define - |> Stream_parser.Stream_language.to_language - |> Language.extension - ; theme - ] - in - let create_state extensions = - State.Editor_state.create (State.Editor_state_config.create ~doc ~extensions ()) - in - Codemirror.with_dynamic_extensions - (module Codemirror_themes) - ~equal:[%equal: Codemirror_themes.t] - ~initial_state:(create_state (create_extensions Codemirror_themes.Material_dark)) - ~compute_extensions:(Bonsai.return create_extensions) - theme - ;; -end - -module Fsharp_syntax_highlighting = struct - let codemirror_editor = - Codemirror.of_initial_state - (State.Editor_state.create - (State.Editor_state_config.create - ~extensions: - [ Basic_setup.basic_setup - ; Mllike.fsharp - |> Stream_parser.Stream_language.define - |> Stream_parser.Stream_language.to_language - |> Language.extension - ] - ())) - ;; -end - -module Sml_syntax_highlighting = struct - let codemirror_editor = - Codemirror.of_initial_state - (State.Editor_state.create - (State.Editor_state_config.create - ~extensions: - [ Basic_setup.basic_setup - ; Mllike.sml - |> Stream_parser.Stream_language.define - |> Stream_parser.Stream_language.to_language - |> Language.extension - ] - ())) - ;; -end - -module Markdown_syntax_highlighting = struct - let codemirror_editor = - Codemirror.of_initial_state - (State.Editor_state.create - (State.Editor_state_config.create - ~extensions: - [ Basic_setup.basic_setup; Lang_markdown.markdown () |> Language.extension ] - ())) - ;; -end - -module Sql_syntax_highlighting = struct - let codemirror_editor = - Codemirror.of_initial_state - (State.Editor_state.create - (State.Editor_state_config.create - ~extensions:[ Basic_setup.basic_setup; Lang_sql.sql () |> Language.extension ] - ())) - ;; -end - -module Diff_syntax_highlighting = struct - let doc = - {|diff --git a/index.html b/index.html -index c1d9156..7764744 100644 ---- a/index.html -+++ b/index.html -@@ -95,7 +95,8 @@ StringStream.prototype = { - - -diff --git a/lib/codemirror.js b/lib/codemirror.js -index 04646a9..9a39cc7 100644 ---- a/lib/codemirror.js -+++ b/lib/codemirror.js -@@ -1420,6 +1425,7 @@ var CodeMirror = (function() { - readOnly: false, - onChange: null, - onCursorActivity: null, -+ onGutterClick: null, - autoMatchBrackets: false, - workTime: 200, - workDelay: 300, - |} - ;; - - let codemirror_editor = - Codemirror.of_initial_state - (State.Editor_state.create - (State.Editor_state_config.create - ~doc - ~extensions: - [ Basic_setup.basic_setup - ; Diff.diff - |> Stream_parser.Stream_language.define - |> Stream_parser.Stream_language.to_language - |> Language.extension - ] - ())) - ;; -end - -module Html_syntax_highlighting = struct - let doc = - {| - - My website! - -
- Hello! -
- - |} - ;; - - let codemirror_editor = - Codemirror.of_initial_state - (State.Editor_state.create - (State.Editor_state_config.create - ~doc - ~extensions: - [ Basic_setup.basic_setup; Lang_html.html () |> Language.extension ] - ())) - ;; -end - -module Css_syntax_highlighting = struct - let doc = - {|:root { - margin: 0; -} - -html { - background-color: #ffc700; - font-family: "Verdana", sans-serif; -} -|} - ;; - - let codemirror_editor = - Codemirror.of_initial_state - (State.Editor_state.create - (State.Editor_state_config.create - ~doc - ~extensions:[ Basic_setup.basic_setup; Lang_css.css () |> Language.extension ] - ())) - ;; -end - -module Javascript_syntax_highlighting = struct - let doc = - {|const http = require('http'); - -const hostname = '127.0.0.1'; -const port = 3000; - -const server = http.createServer((req, res) => { - res.statusCode = 200; - res.setHeader('Content-Type', 'text/plain'); - res.end('Hello World'); -}); - -server.listen(port, hostname, () => { - console.log(`Server running at http://${hostname}:${port}/`); -});|} - ;; - - let codemirror_editor = - Codemirror.of_initial_state - (State.Editor_state.create - (State.Editor_state_config.create - ~doc - ~extensions: - [ Basic_setup.basic_setup - ; Lang_javascript.javascript () |> Language.extension - ] - ())) - ;; -end - -module Php_syntax_highlighting = struct - let doc = {||} - - let codemirror_editor = - Codemirror.of_initial_state - (State.Editor_state.create - (State.Editor_state_config.create - ~doc - ~extensions:[ Basic_setup.basic_setup; Lang_php.php () |> Language.extension ] - ())) - ;; -end - -module Rust_syntax_highlighting = struct - let doc = - {|use yew::prelude::*; - -#[function_component(App)] -fn app() -> Html { - html! { -

{ "Hello World" }

- } -} - -fn main() { - yew::start_app::(); -}|} - ;; - - let codemirror_editor = - Codemirror.of_initial_state - (State.Editor_state.create - (State.Editor_state_config.create - ~doc - ~extensions: - [ Basic_setup.basic_setup; Lang_rust.rust () |> Language.extension ] - ())) - ;; -end - -module Common_lisp_syntax_highlighting = struct - let doc = - {|(in-package :cl-postgres) - -;; These are used to synthesize reader and writer names for integer -;; reading/writing functions when the amount of bytes and the -;; signedness is known. Both the macro that creates the functions and -;; some macros that use them create names this way. -(eval-when (:compile-toplevel :load-toplevel :execute) - (defun integer-reader-name (bytes signed) - (intern (with-standard-io-syntax - (format nil "~a~a~a~a" '#:read- (if signed "" '#:u) '#:int bytes)))) - (defun integer-writer-name (bytes signed) - (intern (with-standard-io-syntax - (format nil "~a~a~a~a" '#:write- (if signed "" '#:u) '#:int bytes))))) |} - ;; - - let codemirror_editor = - Codemirror.of_initial_state - (State.Editor_state.create - (State.Editor_state_config.create - ~doc - ~extensions: - [ Basic_setup.basic_setup - ; Commonlisp.common_lisp - |> Stream_parser.Stream_language.define - |> Stream_parser.Stream_language.to_language - |> Language.extension - ] - ())) - ;; -end - -module Scheme_syntax_highlighting = struct - let doc = - {|;; Building a list of squares from 0 to 9: -;; Note: loop is simply an arbitrary symbol used as a label. Any symbol will do. - -(define (list-of-squares n) - (let loop ((i n) (res '())) - (if (< i 0) - res - (loop (- i 1) (cons (* i i) res)))))|} - ;; - - let codemirror_editor = - Codemirror.of_initial_state - (State.Editor_state.create - (State.Editor_state_config.create - ~doc - ~extensions: - [ Basic_setup.basic_setup - ; Scheme.scheme - |> Stream_parser.Stream_language.define - |> Stream_parser.Stream_language.to_language - |> Language.extension - ] - ())) - ;; -end - -module Xml_syntax_highlighting = struct - let doc = - {| -|} - ;; - - let codemirror_editor = - Codemirror.of_initial_state - (State.Editor_state.create - (State.Editor_state_config.create - ~doc - ~extensions:[ Basic_setup.basic_setup; Lang_xml.xml () |> Language.extension ] - ())) - ;; -end - -module Which_language = struct - type t = - (* Fruit is the blang language defined above *) - | Ocaml - | Fruit - | Fsharp - | Markdown - | Sml - | Sql - | Common_lisp - | Scheme - | Diff - | Html - | Css - | Javascript - | Php - | Rust - | Xml - [@@deriving enumerate, sexp, equal, compare] - - let to_string = function - | Fruit -> "Fruit-based blang with autocomplete" - | Fsharp -> "F# syntax highlighting" - | Markdown -> "Markdown syntax highlighting" - | Ocaml -> "OCaml syntax highlighting" - | Sml -> "SML syntax highlighting" - | Sql -> "SQL syntax highlighting" - | Common_lisp -> "Common Lisp syntax highlighting" - | Scheme -> "Scheme syntax highlighting" - | Diff -> "Diff syntax highlighting" - | Html -> "HTML syntax highlighting" - | Css -> "CSS syntax highlighting" - | Javascript -> "JavaScript syntax highlighting" - | Php -> "PHP syntax highlighting" - | Rust -> "Rust syntax highlighting" - | Xml -> "Xml syntax highlighting" - ;; -end - -let no_theme_picker x = - let%arr x = x in - None, x -;; - -let component graph = - let language_picker = - Form.Elements.Dropdown.enumerable - ~to_string:Which_language.to_string - (module Which_language) - graph - in - let chosen_language = - let%arr language_picker = language_picker in - Form.value language_picker |> Or_error.ok_exn - in - let%sub theme_picker, codemirror = - (* Note: [Codemirror.with_dynamic_extensions] is generally preferred to [match%sub]ing - and choosing a codemirror editor instance. For the purposes of this demo, the code - is optimized for showing off the ease with which people can create different - codemirror editors, so we do the less-preferred option. *) - match%sub chosen_language with - | Which_language.Fruit -> - no_theme_picker - (Fruit_sexp_grammar_auto_complete.codemirror_editor ~name:"fruit" graph) - | Fsharp -> - no_theme_picker (Fsharp_syntax_highlighting.codemirror_editor ~name:"fsharp" graph) - | Markdown -> - no_theme_picker - (Markdown_syntax_highlighting.codemirror_editor ~name:"markdown" graph) - | Ocaml -> - let theme_picker = - Form.Elements.Dropdown.enumerable - ~to_string:Codemirror_themes.to_string - (module Codemirror_themes) - graph - |> Bonsai.map ~f:(Form.label "theme") - in - let chosen_theme = - let%arr theme_picker = theme_picker in - Form.value theme_picker |> Or_error.ok_exn - in - let c = - Ocaml_syntax_highlighting.codemirror_editor - ~name:"ocaml" - ~theme:chosen_theme - graph - in - let%arr c = c - and theme_picker = theme_picker in - Some theme_picker, c - | Sml -> - no_theme_picker @@ Sml_syntax_highlighting.codemirror_editor ~name:"sml" graph - | Sql -> - no_theme_picker @@ Sql_syntax_highlighting.codemirror_editor ~name:"sql" graph - | Common_lisp -> - no_theme_picker - @@ Common_lisp_syntax_highlighting.codemirror_editor ~name:"common lisp" graph - | Scheme -> - no_theme_picker @@ Scheme_syntax_highlighting.codemirror_editor ~name:"scheme" graph - | Diff -> - no_theme_picker @@ Diff_syntax_highlighting.codemirror_editor ~name:"diff" graph - | Html -> - no_theme_picker @@ Html_syntax_highlighting.codemirror_editor ~name:"html" graph - | Css -> - no_theme_picker @@ Css_syntax_highlighting.codemirror_editor ~name:"css" graph - | Javascript -> - no_theme_picker - @@ Javascript_syntax_highlighting.codemirror_editor ~name:"javascript" graph - | Php -> - no_theme_picker @@ Php_syntax_highlighting.codemirror_editor ~name:"php" graph - | Rust -> - no_theme_picker @@ Rust_syntax_highlighting.codemirror_editor ~name:"rust" graph - | Xml -> - no_theme_picker @@ Xml_syntax_highlighting.codemirror_editor ~name:"xml" graph - in - let codemirror_view = - let%arr codemirror = codemirror in - Codemirror.view codemirror - in - let%arr codemirror_view = codemirror_view - and language_picker = language_picker - and theme_picker = theme_picker in - Vdom.Node.div - [ Vdom.Node.text "Choose your editor extension:" - ; Vdom.Node.div - ~attrs:[ Vdom.Attr.style (Css_gen.flex_container ~direction:`Row ()) ] - [ Form.view_as_vdom language_picker - ; Option.value_map ~default:Vdom.Node.none ~f:Form.view_as_vdom theme_picker - ] - ; codemirror_view - ] -;; - -let () = Bonsai_web.Start.start component diff --git a/examples/codemirror/main.mli b/examples/codemirror/main.mli deleted file mode 100644 index 74bb7298..00000000 --- a/examples/codemirror/main.mli +++ /dev/null @@ -1 +0,0 @@ -(*_ This signature is deliberately empty. *) diff --git a/examples/codemirror_readonly/dune b/examples/codemirror_readonly/dune deleted file mode 100644 index 1444501b..00000000 --- a/examples/codemirror_readonly/dune +++ /dev/null @@ -1,7 +0,0 @@ -(executables - (modes byte exe) - (names main) - (libraries bonsai_web bonsai_web_ui_codemirror_read_only - bonsai_experimental_animation) - (preprocess - (pps ppx_jane ppx_bonsai ppx_embed_file ppx_css))) diff --git a/examples/codemirror_readonly/index.html b/examples/codemirror_readonly/index.html deleted file mode 100644 index 8cefd1b0..00000000 --- a/examples/codemirror_readonly/index.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - - Hello, Codemirror_readonly! - - - -
- - diff --git a/examples/codemirror_readonly/main.ml b/examples/codemirror_readonly/main.ml deleted file mode 100644 index 79d7882a..00000000 --- a/examples/codemirror_readonly/main.ml +++ /dev/null @@ -1,81 +0,0 @@ -open! Core -open! Bonsai_web.Cont -open Bonsai.Let_syntax -module Cm_ro = Bonsai_web_ui_codemirror_read_only -module Animation = Bonsai_experimental_animation - -let this_source_code = [%embed_file_as_string "./main.ml"] - -module _ = [%css stylesheet {| body { background: black } |}] - -let back_and_forth graph = - let%sub { value; animate } = - Animation.Advanced.make - ~fallback:(Bonsai.return 0) - ~interpolate:Animation.Interpolatable.int - graph - in - let forward, set_forward = Bonsai.state true graph in - let get_forward = Bonsai.peek forward graph in - let get_things_started = - let%map animate = animate - and get_forward = get_forward - and set_forward = set_forward in - let rec switch_directions () = - let%bind.Effect forward = - match%bind.Effect get_forward with - | Active forward -> Effect.return forward - | Inactive -> Effect.return false - in - let%bind.Effect () = set_forward (not forward) in - let target = if forward then String.length this_source_code else 0 in - let duration = `For (Time_ns.Span.of_sec 10.0) in - animate - ~with_:Animation.Interpolator.Linear - ~after_finished:(switch_directions ()) - duration - target - in - switch_directions () - in - Bonsai.Edge.lifecycle ~on_activate:get_things_started graph; - let%map value = value in - String.sub this_source_code ~pos:0 ~len:value -;; - -let pausable ~equal ~f graph = - let is_paused, toggle_pause = Bonsai.toggle ~default_model:false graph in - let previous_value, set_previous_value = Bonsai.state_opt graph in - let result = - match%sub is_paused, previous_value with - | true, Some previous -> previous - | _ -> - let to_return = f graph in - let callback = - let%map set_previous_value = set_previous_value in - fun v -> set_previous_value (Some v) - in - Bonsai.Edge.on_change ~equal to_return graph ~callback; - to_return - in - let view = - let%map is_paused = is_paused - and toggle_pause = toggle_pause - and theme = View.Theme.current graph in - View.button theme ~on_click:toggle_pause (if is_paused then "play" else "pause") - in - result, view -;; - -let component graph = - let source_code, pause_controlls = - pausable ~equal:[%equal: string] graph ~f:back_and_forth - in - let%map source_code = source_code - and pause_controlls = pause_controlls in - View.vbox - ~attrs:[ {%css| background: white; width : fit-content; |} ] - [ pause_controlls; Cm_ro.make ~language:OCaml ~theme:Basic_light source_code ] -;; - -let () = Bonsai_web.Start.start component diff --git a/examples/codemirror_readonly/main.mli b/examples/codemirror_readonly/main.mli deleted file mode 100644 index 74bb7298..00000000 --- a/examples/codemirror_readonly/main.mli +++ /dev/null @@ -1 +0,0 @@ -(*_ This signature is deliberately empty. *) diff --git a/examples/codicons/dune b/examples/codicons/dune deleted file mode 100644 index 2ff8627e..00000000 --- a/examples/codicons/dune +++ /dev/null @@ -1,7 +0,0 @@ -(executables - (modes byte exe) - (names main) - (libraries bonsai_web bonsai_web_ui_form codicons fuzzy_match.match - js_clipboard) - (preprocess - (pps ppx_jane ppx_bonsai ppx_css))) diff --git a/examples/codicons/index.html b/examples/codicons/index.html deleted file mode 100644 index 4e96c34a..00000000 --- a/examples/codicons/index.html +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - Codicons - - - - -
- - - \ No newline at end of file diff --git a/examples/codicons/main.ml b/examples/codicons/main.ml deleted file mode 100644 index ae599661..00000000 --- a/examples/codicons/main.ml +++ /dev/null @@ -1,207 +0,0 @@ -open! Core -open! Bonsai_web.Cont -open! Bonsai.Let_syntax -module Form = Bonsai_web_ui_form.With_automatic_view - -module Style = struct - include - [%css - stylesheet - {| - .main { - padding: 24px; - display: flex; - flex-direction: column; - gap: 24px; - } - - .grid { - display: flex; - flex-wrap: wrap; - justify-content: flex-start; - gap: 12px; - } - - .icon { - width: 96px; - height: 96px; - padding: 4px; - box-sizing: border-box; - background: #fff; - - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - - border-radius: 2px; - border: 1px solid #ddd; - cursor: pointer; - overflow: hidden; - } - - .icon:hover { - background: #ddd; - } - - .icon svg { - margin: 8px; - flex-shrink: none; - } - - .icon p { - width: 100%; - height: 24px; - margin: 0; - overflow: hidden; - - font-family: roboto, sans-serif; - text-align: center; - font-size: 12px; - line-height: 12px; - text-overflow: ellipsis; - } - - .search { - display: flex; - align-items: center; - gap: 8px; - } - - .search input { - padding: 4px 8px; - border-radius: 0; - border: 1px solid #000; - } - .search input:active, .search input:focus { - border: 1px solid #5555fe; - outline: none; - } - |}] -end - -module Temporary_toggle = struct - let state ~base ~temporary timeout graph = - let state = - Tuple2.uncurry Bonsai.both - @@ Bonsai.state - Time_ns.min_value_representable - ~sexp_of_model:[%sexp_of: Time_ns.Alternate_sexp.t] - ~equal:[%equal: Time_ns.Alternate_sexp.t] - graph - in - let toggle_back_time = - Bonsai.map state ~f:(fun (last_set_time, _) -> Time_ns.add last_set_time timeout) - in - let toggle = Bonsai.Clock.at toggle_back_time graph in - let get_now = Bonsai.Clock.get_current_time graph in - let%arr _, set_time = state - and toggle = toggle - and get_now = get_now in - let output = - match toggle with - | Before -> temporary - | After -> base - in - let turn_on = - let%bind.Effect now = get_now in - set_time now - in - output, turn_on - ;; -end - -module Icon_grid = struct - let icon_card icon graph = - let copied = - Temporary_toggle.state - ~base:`Show_icon - ~temporary:`Show_copied - Time_ns.Span.second - graph - in - let%arr copied, set_copied = copied - and icon = icon in - let name = Codicons.name icon in - let variant_name = - String.map name ~f:(function - | '-' -> '_' - | c -> c) - |> String.capitalize - in - let on_click = - Vdom.Attr.on_click (fun _ -> - let%bind.Effect (_ : unit Or_error.t) = - Js_clipboard.Asynchronous.copy_text (Js_of_ocaml.Js.string variant_name) - in - set_copied) - in - let style = Style.icon in - match copied with - | `Show_icon -> - Vdom.( - Node.button - ~attrs:[ Vdom.Attr.combine on_click style ] - [ Codicons.svg icon; Node.p [ Node.text name ] ]) - | `Show_copied -> - Vdom.( - Node.div - ~attrs:[ style ] - [ Codicons.svg Copy; Node.p [ Node.text [%string "Copied %{variant_name}!"] ] ]) - ;; - - let component icons graph = - let icons = - Bonsai.map icons ~f:(String.Map.of_list_with_key_exn ~get_key:Codicons.name) - in - let cards = Bonsai.assoc (module String) icons ~f:(fun _ -> icon_card) graph in - let%arr cards = cards in - Vdom.Node.div ~attrs:[ Style.grid ] (Map.data cards) - ;; -end - -module Search = struct - let component () graph = - let input = - Form.Elements.Textbox.string - ~placeholder:(Bonsai.return "Filter icons") - ~allow_updates_when_focused:`Never - () - graph - in - let%arr input = input in - let search = - Form.value input - |> Result.ok - |> Option.value ~default:"" - |> String.filter ~f:Char.is_alpha - in - let filter_icon = - Codicons.svg (if String.is_empty search then Filter else Filter_filled) - in - let filtered_icon_list = - List.filter Codicons.all ~f:(fun icon -> - Fuzzy_match.is_match - (Codicons.name icon) - ~pattern:search - ~char_equal:Char.Caseless.equal) - in - let view = - Vdom.Node.div - ~attrs:[ Style.search ] - (filter_icon :: (Form.view input |> Form.View.to_vdom_plain)) - in - filtered_icon_list, view - ;; -end - -let app graph = - let search = Search.component () graph in - let icons = Bonsai.map ~f:fst search in - let grid = Icon_grid.component icons graph in - let%arr grid = grid - and _, search = search in - Vdom.Node.div ~attrs:[ Style.main ] [ search; grid ] -;; - -let () = Bonsai_web.Start.start app diff --git a/examples/codicons/main.mli b/examples/codicons/main.mli deleted file mode 100644 index 835446f0..00000000 --- a/examples/codicons/main.mli +++ /dev/null @@ -1 +0,0 @@ -(*_ This signature is deliberately left empty. *) diff --git a/examples/counters/bin/dune b/examples/counters/bin/dune deleted file mode 100644 index c213223e..00000000 --- a/examples/counters/bin/dune +++ /dev/null @@ -1,6 +0,0 @@ -(executables - (modes byte exe) - (names main) - (libraries bonsai_web bonsai_web_counters_example) - (preprocess - (pps ppx_jane))) diff --git a/examples/counters/bin/index.html b/examples/counters/bin/index.html deleted file mode 100644 index a65ab927..00000000 --- a/examples/counters/bin/index.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - - Counters! - - - -
- - diff --git a/examples/counters/bin/main.ml b/examples/counters/bin/main.ml deleted file mode 100644 index 09827a82..00000000 --- a/examples/counters/bin/main.ml +++ /dev/null @@ -1,3 +0,0 @@ -open Bonsai_web_counters_example - -let () = Bonsai_web.Start.start application diff --git a/examples/counters/bin/main.mli b/examples/counters/bin/main.mli deleted file mode 100644 index 472b62cf..00000000 --- a/examples/counters/bin/main.mli +++ /dev/null @@ -1 +0,0 @@ -(* this file intentionally left blank *) diff --git a/examples/counters/lib/bonsai_web_counters_example.ml b/examples/counters/lib/bonsai_web_counters_example.ml deleted file mode 100644 index 80eb7131..00000000 --- a/examples/counters/lib/bonsai_web_counters_example.ml +++ /dev/null @@ -1,95 +0,0 @@ -open! Core -open Bonsai_web.Cont -open Bonsai.Let_syntax - -(* [CODE_EXCERPT_BEGIN 2] *) -module Model = struct - type t = unit Int.Map.t [@@deriving sexp, equal] -end - -let add_counter_component graph = - let add_counter_state = - Tuple2.uncurry Bonsai.both - @@ Bonsai.state_machine0 - graph - ~sexp_of_model:[%sexp_of: Model.t] - ~equal:[%equal: Model.t] - ~sexp_of_action:[%sexp_of: Unit.t] - ~default_model:Int.Map.empty - ~apply_action:(fun (_ : _ Bonsai.Apply_action_context.t) model () -> - let key = Map.length model in - Map.add_exn model ~key ~data:()) - in - let%arr state, inject = add_counter_state in - let view = - Vdom.Node.button - ~attrs:[ Vdom.Attr.on_click (fun _ -> inject ()) ] - [ Vdom.Node.text "Add Another Counter" ] - in - state, view -;; - -(* [CODE_EXCERPT_END 2] *) - -(* [CODE_EXCERPT_BEGIN 1] *) - -module Action = struct - type t = - | Increment - | Decrement - [@@deriving sexp_of] -end - -let single_counter graph = - let counter_state = - Tuple2.uncurry Bonsai.both - @@ Bonsai.state_machine0 - graph - ~sexp_of_model:[%sexp_of: Int.t] - ~equal:[%equal: Int.t] - ~sexp_of_action:[%sexp_of: Action.t] - ~default_model:0 - ~apply_action:(fun (_ : _ Bonsai.Apply_action_context.t) model -> function - | Action.Increment -> model + 1 - | Action.Decrement -> model - 1) - in - let%arr state, inject = counter_state in - let button label action = - Vdom.Node.button - ~attrs:[ Vdom.Attr.on_click (fun _ -> inject action) ] - [ Vdom.Node.text label ] - in - Vdom.Node.div - [ button "-1" Action.Decrement - ; Vdom.Node.text (Int.to_string state) - ; button "+1" Action.Increment - ] -;; - -(* [CODE_EXCERPT_END 1] *) - -(* [CODE_EXCERPT_BEGIN 3] *) -let application graph = - let open Bonsai.Let_syntax in - let%sub map, add_button = add_counter_component graph in - let counters = - Bonsai.assoc (module Int) map ~f:(fun _key _data -> single_counter) graph - in - let%arr add_button = add_button - and counters = counters in - Vdom.Node.div [ add_button; Vdom.Node.div (Map.data counters) ] -;; - -(* [CODE_EXCERPT_END 3] *) - -let _application_sugar_free graph = - let open Bonsai.Let_syntax in - Let_syntax.sub (add_counter_component graph) ~f:(fun add_counter -> - let map = Bonsai.map ~f:fst add_counter in - let add_button = Bonsai.map ~f:snd add_counter in - let counters = - Bonsai.assoc (module Int) map ~f:(fun _key _data -> single_counter) graph - in - Bonsai.map2 add_button counters ~f:(fun add_button counters -> - Vdom.Node.div [ add_button; Vdom.Node.div (Map.data counters) ])) [@nontail] -;; diff --git a/examples/counters/lib/bonsai_web_counters_example.mli b/examples/counters/lib/bonsai_web_counters_example.mli deleted file mode 100644 index a27e228b..00000000 --- a/examples/counters/lib/bonsai_web_counters_example.mli +++ /dev/null @@ -1,5 +0,0 @@ -open! Core -open Bonsai_web.Cont - -val single_counter : Bonsai.graph -> Vdom.Node.t Bonsai.t -val application : Bonsai.graph -> Vdom.Node.t Bonsai.t diff --git a/examples/counters/lib/dune b/examples/counters/lib/dune deleted file mode 100644 index 14e59fcf..00000000 --- a/examples/counters/lib/dune +++ /dev/null @@ -1,5 +0,0 @@ -(library - (name bonsai_web_counters_example) - (libraries bonsai_web) - (preprocess - (pps ppx_bonsai ppx_jane ppx_pattern_bind))) diff --git a/examples/counters_condensed/dune b/examples/counters_condensed/dune deleted file mode 100644 index 3ccf9db2..00000000 --- a/examples/counters_condensed/dune +++ /dev/null @@ -1,6 +0,0 @@ -(executables - (modes byte exe) - (names main) - (libraries bonsai_web) - (preprocess - (pps ppx_bonsai ppx_jane))) diff --git a/examples/counters_condensed/index.html b/examples/counters_condensed/index.html deleted file mode 100644 index 676d6a1f..00000000 --- a/examples/counters_condensed/index.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - - Hello, Counters Condensed! - - - -
- - diff --git a/examples/counters_condensed/main.ml b/examples/counters_condensed/main.ml deleted file mode 100644 index 83491155..00000000 --- a/examples/counters_condensed/main.ml +++ /dev/null @@ -1,49 +0,0 @@ -open! Core -open! Bonsai_web.Cont -open Bonsai.Let_syntax -open Vdom - -module Model = struct - type t = int Int.Map.t [@@deriving sexp, equal] -end - -module Action = struct - type t = - | New - | Update of int * int - [@@deriving sexp] -end - -let default_model = Int.Map.empty - -let apply_action (_ : _ Bonsai.Apply_action_context.t) model = function - | Action.New -> Map.add_exn model ~key:(Map.length model) ~data:0 - | Update (location, diff) -> - Map.update model location ~f:(Option.value_map ~default:0 ~f:(( + ) diff)) -;; - -let component graph = - let state = - Tuple2.uncurry Bonsai.both - @@ Bonsai.state_machine0 - graph - ~sexp_of_model:[%sexp_of: Model.t] - ~equal:[%equal: Model.t] - ~sexp_of_action:[%sexp_of: Action.t] - ~default_model - ~apply_action - in - let%arr state, inject = state in - let button text action = - Node.button ~attrs:[ Attr.on_click (fun _ -> inject action) ] [ Node.text text ] - in - let add_button = button "add" New in - let for_each i c = - Node.div - [ button "-1" (Update (i, -1)); Node.textf "%d" c; button "+1" (Update (i, 1)) ] - in - let counters = state |> Map.data |> List.mapi ~f:for_each in - Node.div (add_button :: counters) -;; - -let () = Bonsai_web.Start.start component diff --git a/examples/counters_condensed/main.mli b/examples/counters_condensed/main.mli deleted file mode 100644 index 74bb7298..00000000 --- a/examples/counters_condensed/main.mli +++ /dev/null @@ -1 +0,0 @@ -(*_ This signature is deliberately empty. *) diff --git a/examples/counters_cont/dune b/examples/counters_cont/dune deleted file mode 100644 index 3804f179..00000000 --- a/examples/counters_cont/dune +++ /dev/null @@ -1,6 +0,0 @@ -(executables - (modes byte exe) - (names main) - (libraries bonsai_web) - (preprocess - (pps ppx_jane))) diff --git a/examples/counters_cont/index.html b/examples/counters_cont/index.html deleted file mode 100644 index a65ab927..00000000 --- a/examples/counters_cont/index.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - - Counters! - - - -
- - diff --git a/examples/counters_cont/main.ml b/examples/counters_cont/main.ml deleted file mode 100644 index 53d931d4..00000000 --- a/examples/counters_cont/main.ml +++ /dev/null @@ -1,60 +0,0 @@ -open! Core -open! Bonsai_web.Cont -open Bonsai.Let_syntax - -let add_counter_component graph = - let state, inject = - Bonsai.state_machine0 - graph - ~default_model:Int.Map.empty - ~apply_action:(fun _ctx model () -> - let key = Map.length model in - Map.add_exn model ~key ~data:()) - in - let view = - let%map inject = inject in - Vdom.Node.button - ~attrs:[ Vdom.Attr.on_click (fun _ -> inject ()) ] - [ Vdom.Node.text "Add Another Counter" ] - in - state, view -;; - -module Action = struct - type t = - | Increment - | Decrement - [@@deriving sexp_of] -end - -let single_counter graph = - let state, inject = - Bonsai.state_machine0 graph ~default_model:0 ~apply_action:(fun _ctx model -> function - | Action.Increment -> model + 1 - | Action.Decrement -> model - 1) - in - let%map state = state - and inject = inject in - let button label action = - Vdom.Node.button - ~attrs:[ Vdom.Attr.on_click (fun _ -> inject action) ] - [ Vdom.Node.text label ] - in - Vdom.Node.div - [ button "-1" Action.Decrement - ; Vdom.Node.text (Int.to_string state) - ; button "+1" Action.Increment - ] -;; - -let application graph = - let map, add_button = add_counter_component graph in - let counters = - Bonsai.assoc (module Int) map graph ~f:(fun _key _data graph -> single_counter graph) - in - let%map add_button = add_button - and counters = counters in - Vdom.Node.div [ add_button; Vdom.Node.div (Map.data counters) ] -;; - -let () = Bonsai_web.Start.start application diff --git a/examples/counters_cont/main.mli b/examples/counters_cont/main.mli deleted file mode 100644 index 472b62cf..00000000 --- a/examples/counters_cont/main.mli +++ /dev/null @@ -1 +0,0 @@ -(* this file intentionally left blank *) diff --git a/examples/dagviz/dune b/examples/dagviz/dune deleted file mode 100644 index a60bc9c3..00000000 --- a/examples/dagviz/dune +++ /dev/null @@ -1,7 +0,0 @@ -(executables - (modes byte exe) - (names main) - (libraries virtual_dom.svg bonsai_web_ui_element_size_hooks - bonsai_experimental_dagviz bonsai_web tailwind_colors feather_icon) - (preprocess - (pps js_of_ocaml-ppx ppx_typed_fields ppx_jane ppx_css ppx_demo ppx_bonsai))) diff --git a/examples/dagviz/index.html b/examples/dagviz/index.html deleted file mode 100644 index 19ab0493..00000000 --- a/examples/dagviz/index.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - - Hello Dagviz! - - - -
- - diff --git a/examples/dagviz/main.ml b/examples/dagviz/main.ml deleted file mode 100644 index 0d8a92b2..00000000 --- a/examples/dagviz/main.ml +++ /dev/null @@ -1,458 +0,0 @@ -open! Core -open! Bonsai_web.Cont -open Bonsai.Let_syntax -module Position = Bonsai_web_ui_element_size_hooks.Position_tracker.Position - -module Progress = struct - type t = - { complete : int - ; remaining : int - } - [@@deriving sexp, compare, quickcheck] - - let to_message { complete; remaining } = - [%string "%{complete#Int}/%{(complete+remaining)#Int}"] - ;; -end - -module Styles = -[%css -stylesheet - {| -.paper { - box-shadow: 0 0 8px rgba(0,0,0,0.2); - padding: 12px; - border-radius: 4px; - margin: 8px; - max-width: fit-content; - text-align: center; -} - - .header { - display: flex; - direction: column; - align-items: center; - justify-content: center; - } - - @keyframes linear-progress-bar-stripes{0% {background-position:0 0}to{background-position:30px 0}} - - .spinning_progress_bar { - animation-duration: 0.3s; - animation-timing-function: linear; - animation-delay: 0s; - animation-iteration-count: infinite; - animation-direction: reverse; - animation-fill-mode: none; - animation-play-state: running; - animation-name: linear-progress-bar-stripes; - } - - - .progress_bar_meter { - background: linear-gradient(-45deg,hsla(0,0%,100%,.2) 25%,transparent 0,transparent 50%,hsla(0,0%,100%,.2) 0,hsla(0,0%,100%,.2) 75%,transparent 0); - background-size: 30px 30px; - height: 6px; - position: absolute; - transition: width .2s cubic-bezier(.4,1,.75,.9); - } - - .progress_bar { - background: rgba(95,107,124,.2); - display: block; - height: 6px; - overflow: hidden; - position: relative; - width: 100%; - } - - .node { - border-style: solid; - border-color: #D3D3D3; - border-width: 1px; - display: flex; - flex-direction: column; - align-items: center; - width: fit-content; - transition: width 2s cubic-bezier(.4, 1, .75, .9); - } - - .node_contents { - display: flex; - flex-direction: column; - align-items: center; - width: fit-content; - padding: 4px; - } - - .status_message { - color: #636363; - } - - .person { - transition: all 0.5s; - } - - .cursor { - cursor: pointer; - } - |}] - -module Status = struct - type t = - | Starting - | Running of Progress.t - | Failed of Progress.t - | Done of { total_atoms : int } - [@@deriving sexp, compare, quickcheck] - - let to_color = function - | Starting -> Tailwind_colors.amber500 - | Running _ -> Tailwind_colors.blue500 - | Failed _ -> Tailwind_colors.red500 - | Done _ -> Tailwind_colors.emerald500 - ;; - - let to_percent_complete = function - | Done _ -> Percent.one_hundred_percent - | Starting -> Percent.one_hundred_percent - | Running p | Failed p -> - let total = p.remaining + p.complete in - Percent.of_percentage (100. *. (Int.to_float p.complete /. Int.to_float total)) - ;; - - let is_not_finished = function - | Starting | Running _ -> true - | Failed _ | Done _ -> false - ;; - - let to_status_message = function - | Starting -> "Starting" - | Failed p -> [%string "Failed (%{Progress.to_message p} atoms)"] - | Running p -> [%string "Running (%{Progress.to_message p} atoms)"] - | Done { total_atoms } -> [%string "Done (%{total_atoms#Int} atoms)"] - ;; - - let to_vdom t = - let color = to_color t in - let width = to_percent_complete t in - let maybe_spin = - match is_not_finished t with - | true -> [ Styles.spinning_progress_bar ] - | false -> [] - in - Vdom.Node.div - ~attrs:[ Styles.progress_bar ] - [ Vdom.Node.div - ~attrs: - [ Vdom.Attr.many ([ Styles.progress_bar_meter ] @ maybe_spin) - ; Vdom.Attr.style - (Css_gen.combine - (Css_gen.background_color color) - (Css_gen.width (`Percent width))) - ] - [] - ] - ;; -end - -module Id = Bonsai_experimental_dagviz.Default_id - -module Node_data = struct - module Person = struct - type t = unit [@@deriving sexp, compare, equal, quickcheck] - - let to_vdom _ = - Vdom.Node.div - ~attrs:[ Styles.node; Styles.person ] - [ Vdom.Node.text "I am a human BEEP BOOP!" ] - ;; - end - - type t = - { status : Status.t - ; name : string - ; people : Person.t list - } - [@@deriving sexp, compare, quickcheck, fields ~iterators:create] - - let to_vdom (id : Id.t Bonsai.t) (t : t Bonsai.t) : Bonsai.graph -> Vdom.Node.t Bonsai.t - = - fun graph -> - let collapsed, collapse = Bonsai.toggle ~default_model:true graph in - let%arr collapsed = collapsed - and id = id - and t = t - and collapse = collapse in - let status = Status.to_vdom t.status in - let status_message = - Vdom.Node.div - ~attrs:[ Styles.status_message ] - [ Vdom.Node.text @@ Status.to_status_message t.status ] - in - let name = - Vdom.Node.div - ~attrs:[ Vdom.Attr.style (Css_gen.flex_container ~direction:`Row ()) ] - [ Vdom.Node.strong [ Vdom.Node.text t.name ] ] - in - let people = - match collapsed with - | true -> - let number_of_people = List.length t.people in - Vdom.Node.div [ Vdom.Node.text [%string "%{number_of_people#Int} people"] ] - | false -> - Vdom.Node.div - ~attrs:[ Vdom.Attr.style (Css_gen.flex_container ~direction:`Row ()) ] - (List.map t.people ~f:Person.to_vdom) - in - Vdom.Node.div - ~attrs:[ Styles.node ] - [ status - ; Vdom.Node.div - ~attrs:[ Styles.node_contents ] - [ Vdom.Node.div - ~attrs:[ Vdom.Attr.style (Css_gen.flex_container ~direction:`Row ()) ] - [ name - ; Feather_icon.svg - ~extra_attrs:[ Styles.cursor; Vdom.Attr.on_click @@ Fn.const collapse ] - (if collapsed - then Feather_icon.Chevron_down - else Feather_icon.Chevron_up) - ] - ; status_message - ] - ; people - ; Vdom.Node.sexp_for_debugging [%message (id : Id.t)] - ] - ;; -end - -module To_vdom = Bonsai_experimental_dagviz.To_vdom.Make (Id) - -let people n = List.init n ~f:(Fn.const ()) -let capybara_finder_id = Id.create () - -let capybara_finder = - Node_data.Fields.create - ~status:(Done { total_atoms = 126 }) - ~name:"Capybara finder" - ~people:(people 2) -;; - -let capybara_feeder_id = Id.create () - -let capybara_feeder = - Node_data.Fields.create ~status:Starting ~name:"Capybara feeder" ~people:(people 3) -;; - -let capybara_cleaner_id = Id.create () - -let capybara_cleaner = - Node_data.Fields.create ~status:Starting ~name:"Capybara cleaner" ~people:(people 4) -;; - -let capybara_petter_id = Id.create () - -let capybara_petter = - Node_data.Fields.create - ~status:(Running { complete = 50; remaining = 76 }) - ~name:"Capybara petter" - ~people:(people 5) -;; - -let capybara_herder_id = Id.create () - -let capybara_herder = - Node_data.Fields.create - ~status:(Running { complete = 20; remaining = 96 }) - ~name:"Capybara herder" - ~people:[ () ] -;; - -let capybara_photographer_id = Id.create () - -let capybara_photographer = - Node_data.Fields.create - ~status:(Running { complete = 10; remaining = 116 }) - ~name:"Capybara photographer" - ~people:[ () ] -;; - -let capybara_fan_id = Id.create () - -let capybara_fan = - Node_data.Fields.create - ~status:(Running { complete = 30; remaining = 116 }) - ~name:"Capybara fan" - ~people:[ (); () ] -;; - -let nodes = - [ capybara_finder_id, capybara_finder - ; capybara_feeder_id, capybara_feeder - ; capybara_cleaner_id, capybara_cleaner - ; capybara_petter_id, capybara_petter - ; capybara_herder_id, capybara_herder - ; capybara_photographer_id, capybara_photographer - ; capybara_fan_id, capybara_fan - ] - |> Id.Map.of_alist_exn -;; - -open To_vdom - -let edges = - [ { Edge.from = capybara_finder_id; to_ = capybara_feeder_id } - ; { Edge.from = capybara_finder_id; to_ = capybara_petter_id } - ; { Edge.from = capybara_finder_id; to_ = capybara_herder_id } - ; { Edge.from = capybara_finder_id; to_ = capybara_photographer_id } - ; { Edge.from = capybara_feeder_id; to_ = capybara_cleaner_id } - ; { Edge.from = capybara_cleaner_id; to_ = capybara_herder_id } - ; { Edge.from = capybara_petter_id; to_ = capybara_herder_id } - ; { Edge.from = capybara_herder_id; to_ = capybara_photographer_id } - ; { Edge.from = capybara_fan_id; to_ = capybara_photographer_id } - ] - |> To_vdom.Edge.Set.of_list -;; - -module Point = struct - type t = - { x : int - ; y : int - } -end - -module Line = struct - type t = - { from : Point.t - ; to_ : Point.t - } -end - -let face_point : Position.t -> [ `Top | `Left | `Bottom | `Right ] -> Point.t = - fun position direction -> - let { Position.top; left; width; height } = position in - let x = - match direction with - | `Top | `Bottom -> left + (width / 2) - | `Right -> left - | `Left -> left + width - in - let y = - match direction with - | `Right | `Left -> top + (height / 2) - | `Top -> top + height - | `Bottom -> top - in - { Point.x; y } -;; - -let edge_to_svg - ~(direction : [ `Top_down | `Left_to_right ]) - ~(edge : Edge.t Bonsai.t) - ~(from : Position.t Bonsai.t) - ~(to_ : Position.t Bonsai.t) - _graph - = - let%arr edge = edge - and from = from - and to_ = to_ in - let href = - let from = edge.from |> Id.to_string in - let to_ = edge.to_ |> Id.to_string in - [%string "bee_path%{from}_%{to_}"] - in - let get_from_direction = function - | `Top_down -> `Top - | `Left_to_right -> `Left - in - let get_to_direction = function - | `Top_down -> `Bottom - | `Left_to_right -> `Right - in - let { Line.from = { x = x1; y = y1 }; to_ = { x = x2; y = y2 } } = - let from = face_point from (get_from_direction direction) in - let to_ = face_point to_ (get_to_direction direction) in - { Line.from; to_ } - in - let midx, midy = Int.to_float ((x1 + x2) / 2), Int.to_float ((y1 + y2) / 2) in - let attr = - match direction with - | `Top_down -> - Virtual_dom_svg.Attr.d - [ Virtual_dom_svg.Attr.Move_to_abs { x = Int.to_float x1; y = Int.to_float y1 } - ; Virtual_dom_svg.Attr.Cubic_smooth_abs - { x = midx; y = midy; x2 = Int.to_float x1; y2 = midy } - ; Virtual_dom_svg.Attr.Cubic_smooth_abs - { x = Int.to_float x2; y = Int.to_float y2; x2 = Int.to_float x2; y2 = midy } - ] - | `Left_to_right -> - Virtual_dom_svg.Attr.d - [ Virtual_dom_svg.Attr.Move_to_abs { x = Int.to_float x1; y = Int.to_float y1 } - ; Virtual_dom_svg.Attr.Cubic_smooth_abs - { x = midx; y = midy; x2 = midx; y2 = Int.to_float y1 } - ; Virtual_dom_svg.Attr.Cubic_smooth_abs - { x = Int.to_float x2; y = Int.to_float y2; x2 = midx; y2 = Int.to_float y2 } - ] - in - let attr = - Vdom.Attr.many - [ attr - ; Vdom.Attr.create "fill-opacity" "0.0" - ; Virtual_dom_svg.Attr.stroke_width 3.0 - ; Virtual_dom_svg.Attr.stroke (`Hex "#939393") - ; Vdom.Attr.id href - ] - in - let bee_href = href ^ "actual_bee" in - Virtual_dom_svg.Node.g - [ Virtual_dom_svg.Node.path ~attrs:[ attr ] [] - ; Virtual_dom_svg.Node.text - ~attrs:[ Vdom.Attr.create "rotate" "180" ] - [ Virtual_dom_svg.Node.text_path - ~attrs:[ Vdom.Attr.id bee_href; Virtual_dom_svg.Attr.href ("#" ^ href) ] - [ Vdom.Node.text "🐝" ] - ] - ; Vdom.Node.create_svg - "animate" - ~attrs: - [ Vdom.Attr.href ("#" ^ bee_href) - ; Vdom.Attr.create "attributeName" "startOffset" - ; Vdom.Attr.create "from" "0%" - ; Vdom.Attr.create "to" "100%" - ; Vdom.Attr.create "dur" "2s" - ; Vdom.Attr.create "repeatCount" "indefinite" - ] - [] - ] -;; - -let component graph = - let curr_id = Bonsai.return Id.Count.zero in - let dag_data = Bonsai.return { To_vdom.nodes; edges } in - let%sub dag, _curr_id = - To_vdom.create - ~curr_id - ~direction:`Top_to_bottom - ~node_to_vdom:Node_data.to_vdom - ~edge_to_svg:(edge_to_svg ~direction:`Top_down) - dag_data - graph - in - let dag = - match%sub dag with - | Ok dag -> dag - | Error error -> - let%arr error = error in - Vdom.Node.sexp_for_debugging [%message "" ~_:(error : Error.t)] - in - let%arr dag = dag in - Vdom.Node.div - ~attrs:[ Styles.header ] - [ Vdom.Node.div - ~attrs:[ Styles.paper ] - [ Vdom.Node.strong [ Vdom.Node.text "Capybara Petting Zoo" ]; dag ] - ] -;; - -let () = Bonsai_web.Start.start component diff --git a/examples/dagviz/main.mli b/examples/dagviz/main.mli deleted file mode 100644 index e69de29b..00000000 diff --git a/examples/drag_and_drop/bin/dune b/examples/drag_and_drop/bin/dune deleted file mode 100644 index c6b8ab82..00000000 --- a/examples/drag_and_drop/bin/dune +++ /dev/null @@ -1,6 +0,0 @@ -(executables - (modes byte exe) - (names main) - (libraries bonsai_web bonsai_drag_and_drop_example) - (preprocess - (pps ppx_jane))) diff --git a/examples/drag_and_drop/bin/index.html b/examples/drag_and_drop/bin/index.html deleted file mode 100644 index 3dbd27c7..00000000 --- a/examples/drag_and_drop/bin/index.html +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - Drag and Drop Example - - - -
- - diff --git a/examples/drag_and_drop/bin/main.ml b/examples/drag_and_drop/bin/main.ml deleted file mode 100644 index 303874c9..00000000 --- a/examples/drag_and_drop/bin/main.ml +++ /dev/null @@ -1,4 +0,0 @@ -open! Core -open! Bonsai_web.Cont - -let () = Bonsai_web.Start.start Bonsai_drag_and_drop_example.app diff --git a/examples/drag_and_drop/bin/main.mli b/examples/drag_and_drop/bin/main.mli deleted file mode 100644 index 74bb7298..00000000 --- a/examples/drag_and_drop/bin/main.mli +++ /dev/null @@ -1 +0,0 @@ -(*_ This signature is deliberately empty. *) diff --git a/examples/drag_and_drop/lib/bonsai_drag_and_drop_example.ml b/examples/drag_and_drop/lib/bonsai_drag_and_drop_example.ml deleted file mode 100644 index 4de56ba7..00000000 --- a/examples/drag_and_drop/lib/bonsai_drag_and_drop_example.ml +++ /dev/null @@ -1,276 +0,0 @@ -open! Core -open! Bonsai_web.Cont -open Bonsai.Let_syntax -module Drag_and_drop = Bonsai_web_ui_drag_and_drop -module Node = Vdom.Node - -module Style = -[%css -stylesheet - {| - .centered { - text-align: center; - } - - .kanban_container { - display: flex; - flex-direction: row; - } - - .kanban_column { - padding: 10px; - margin: 10px; - flex: 1; - min-height : 200px; - border: 3px solid white; - } - - .kanban_column_active { - border: 3px dashed black; - } - - .kanban_column_todo { - background-color: brown; - } - - .kanban_column_in_progress { - background-color: lightblue; - } - - .kanban_column_finished { - background-color: lightgreen; - } - - .kanban_item { - background-color: purple; - color: white; - margin: 10px; - padding: 10px; - } - - .being_dragged { - opacity: 0.5; - background-color: #990033; - } - - .dragged_on_self { - background-color: black; - } - - .disable_child_pointer_events * { - pointer-events: none; - } - |}] - -module Column = struct - type t = - | Todo - | In_progress - | Finished - [@@deriving sexp, equal] - - include Stringable -end - -module Item_id : sig - include Identifiable.S - - val of_int : int -> t -end = - Int - -module Kanban_board = struct - type t = (string * Column.t) Item_id.Map.t [@@deriving sexp, equal] -end - -module Action = struct - type t = - | Move of - { item_id : Item_id.t - ; new_column : Column.t - } - [@@deriving sexp] -end - -let kanban_column ~extra_dnd ~dnd ~items ~column ~title graph = - let map = - let%map items = items - and model = dnd >>| Drag_and_drop.model in - (* Only display items from this column or items that are being hovered - over this column; exclude any items that have been dragged away and - are hovered over a different column *) - Map.filteri items ~f:(fun ~key ~data:(_, item_column) -> - match model with - | Not_dragging | Dragging { target = None; _ } -> - [%equal: Column.t] item_column column - | Dragging { source = item_id; target = Some target_column; _ } -> - let from_this_column = [%equal: Column.t] item_column column in - let is_the_dragged_item = [%equal: Item_id.t] item_id key in - let from_target_column = [%equal: Column.t] column target_column in - (from_this_column && not is_the_dragged_item) - || (is_the_dragged_item && from_target_column)) - in - let extra_source = - match extra_dnd with - | Some dnd -> Bonsai.map ~f:Drag_and_drop.source dnd - | None -> Bonsai.return (fun ~id:_ -> Vdom.Attr.empty) - in - let items = - Bonsai.assoc - (module Item_id) - map - ~f:(fun item_id item _graph -> - let%arr item = item - and item_id = item_id - and source = dnd >>| Drag_and_drop.source - and model = dnd >>| Drag_and_drop.model - and extra_source = extra_source in - let contents, _ = item in - let extra = - match model with - | Not_dragging -> Vdom.Attr.empty - | Dragging { source = source_item_id; target = None; _ } -> - if Item_id.equal item_id source_item_id - then Style.being_dragged - else Vdom.Attr.empty - | Dragging { source = source_item_id; target = Some target_column; _ } -> - if Item_id.equal item_id source_item_id - then - if [%equal: Column.t] target_column column - then Style.dragged_on_self - else Style.being_dragged - else Vdom.Attr.empty - in - Node.div - ~key:(Item_id.to_string item_id) - ~attrs: - [ Vdom.Attr.( - source ~id:item_id @ Style.kanban_item @ extra @ extra_source ~id:item_id) - ] - [ Node.text contents ]) - graph - in - let%arr items = items - and drop_target = dnd >>| Drag_and_drop.drop_target - and model = dnd >>| Drag_and_drop.model in - let is_active = - match model with - | Dragging { target = Some target_column; _ } - when [%equal: Column.t] column target_column -> true - | _ -> false - in - let column_class = - match column with - | Todo -> Style.kanban_column_todo - | In_progress -> Style.kanban_column_in_progress - | Finished -> Style.kanban_column_finished - in - Node.div - ~attrs: - [ Vdom.Attr.( - drop_target ~id:column - @ Style.kanban_column - @ column_class - @ if is_active then Style.kanban_column_active else empty) - ] - [ Node.h3 ~attrs:[ Style.centered ] [ Node.text title ]; Node.div (Map.data items) ] -;; - -let board ?extra_dnd name graph = - let items, inject = - Bonsai.state_machine0 - graph - ~sexp_of_model:[%sexp_of: Kanban_board.t] - ~equal:[%equal: Kanban_board.t] - ~sexp_of_action:[%sexp_of: Action.t] - ~default_model: - ([ "todo 1", Column.Todo - ; "todo 2", Todo - ; "in progress 1", In_progress - ; "in progress 2", In_progress - ; "in progress 3", In_progress - ; "finished 1", Finished - ; "finished 2", Finished - ; "finished 3", Finished - ] - |> List.mapi ~f:(fun i item -> Item_id.of_int i, item) - |> Map.of_alist_exn (module Item_id)) - ~apply_action: - (fun - (_ : _ Bonsai.Apply_action_context.t) model (Move { item_id; new_column }) -> - let change_col (contents, _) ~new_column = contents, new_column in - Map.change model item_id ~f:(Option.map ~f:(change_col ~new_column))) - in - let dnd = - Drag_and_drop.create - ~source_id:(module Item_id) - ~target_id:(module Column) - ~on_drop: - (let%map inject = inject in - fun item_id new_column -> inject (Move { item_id; new_column })) - graph - in - let todo = kanban_column ~extra_dnd ~dnd ~items ~column:Todo ~title:"Todo" graph in - let in_progress = - kanban_column ~extra_dnd ~dnd ~items ~column:In_progress ~title:"In Progress" graph - in - let finished = - kanban_column ~extra_dnd ~dnd ~items ~column:Finished ~title:"Done" graph - in - let dragged_element = - Drag_and_drop.dragged_element - dnd - ~f:(fun item_id _graph -> - let text = - match%sub - let%map item_id = item_id - and items = items in - Map.find items item_id - with - | Some (contents, _) -> contents - | None -> Bonsai.return "No item exists with that id" - in - let%arr text = text in - Node.div ~attrs:[ Style.kanban_item ] [ Node.text text ]) - graph - in - let sentinel = Bonsai.map ~f:Drag_and_drop.sentinel dnd in - let view = - let%arr todo = todo - and in_progress = in_progress - and finished = finished - and dragged_element = dragged_element - and sentinel = sentinel in - let sentinel = sentinel ~name in - Node.div - ~attrs:[ Vdom.Attr.(Style.kanban_container @ sentinel) ] - [ todo; in_progress; finished; dragged_element ] - in - Bonsai.both view dnd -;; - -let app graph = - let%sub board1, dnd = board "board1" graph in - let%sub board2, _ = board ~extra_dnd:dnd "board2" graph in - let%arr board1 = board1 - and board2 = board2 in - Node.div - [ Node.p - [ Node.text - {| - You can drag items between each of the columns. When dropped, an item gets - placed in a location determined by some ordering within the column, rather - than the position at which it is dropped (This was more straightforward to - implement). There are two drag-and-drop universes corresponding to each of - the kanban boards. Interestingly, the items in the bottom board are - draggable items of both universes; while the resulting behavior is not - intuitive for a kanban board, it does demonstrate how multiple universes - interact. - |} - ] - ; Node.div [ board1 ] - ; Node.div [ board2 ] - ] -;; - -let board name graph = Bonsai.map (board ?extra_dnd:None name graph) ~f:fst diff --git a/examples/drag_and_drop/lib/bonsai_drag_and_drop_example.mli b/examples/drag_and_drop/lib/bonsai_drag_and_drop_example.mli deleted file mode 100644 index 75e80280..00000000 --- a/examples/drag_and_drop/lib/bonsai_drag_and_drop_example.mli +++ /dev/null @@ -1,4 +0,0 @@ -open Bonsai_web.Cont - -val app : Bonsai.graph -> Vdom.Node.t Bonsai.t -val board : string -> Bonsai.graph -> Vdom.Node.t Bonsai.t diff --git a/examples/drag_and_drop/lib/dune b/examples/drag_and_drop/lib/dune deleted file mode 100644 index 3dd13d36..00000000 --- a/examples/drag_and_drop/lib/dune +++ /dev/null @@ -1,5 +0,0 @@ -(library - (name bonsai_drag_and_drop_example) - (libraries bonsai_web bonsai_web_ui_drag_and_drop) - (preprocess - (pps ppx_bonsai ppx_css ppx_jane))) diff --git a/examples/drag_and_drop/test/app_test.ml b/examples/drag_and_drop/test/app_test.ml deleted file mode 100644 index 89fec7ee..00000000 --- a/examples/drag_and_drop/test/app_test.ml +++ /dev/null @@ -1,345 +0,0 @@ -open! Core -open! Bonsai_web_test -open! Bonsai_web.Cont -module Handle = Bonsai_web_test.Handle -module Result_spec = Bonsai_web_test.Result_spec -module Example = Bonsai_drag_and_drop_example - -let board = Example.board "board" -let run = Handle.Drag_and_drop.run ~get_vdom:Fn.id ~name:"board" - -let%expect_test "drag between containers" = - let handle = Handle.create (Result_spec.vdom Fn.id) board in - Handle.show handle; - [%expect - {| -
> -
-

Todo

-
-
todo 1
-
todo 2
-
-
-
-

In Progress

-
-
in progress 1
-
in progress 2
-
in progress 3
-
-
-
-

Done

-
-
finished 1
-
finished 2
-
finished 3
-
-
-
- adding window event listener - adding window event listener - |}]; - run handle (Start_drag "0"); - run handle (Set_target (Some "finished")); - run handle Finish_drag; - Handle.show_diff handle; - [%expect - {| -
> -
-

Todo

-
- -|
todo 1
-
todo 2
-
-
-
-

In Progress

-
-
in progress 1
-
in progress 2
-
in progress 3
-
-
-
-

Done

-
- +|
todo 1
-
finished 1
-
finished 2
-
finished 3
-
-
-
- |}] -;; - -let%expect_test "drag to same container" = - let handle = Handle.create (Result_spec.vdom Fn.id) board in - Handle.store_view handle; - [%expect {| - adding window event listener - adding window event listener - |}]; - run handle (Start_drag "0"); - run handle (Set_target (Some "todo")); - run handle Finish_drag; - (* No diff, since the item was dragged into the same column *) - Handle.show_diff handle; - [%expect {| |}] -;; - -let%expect_test "appearance of dragged item and preview item while drag is happening" = - let handle = Handle.create (Result_spec.vdom Fn.id) board in - Handle.store_view handle; - [%expect {| - adding window event listener - adding window event listener - |}]; - run handle (Start_drag "0"); - Handle.show_diff handle; - [%expect - {| -
> -
-

Todo

-
-
todo 1
-
todo 2
-
-
-
-

In Progress

-
-
in progress 1
-
-

Done

-
-
finished 1
-
finished 2
-
finished 3
-
-
- +|
- +|
todo 1
- +|
-
- |}]; - run handle (Set_target (Some "todo")); - Handle.show_diff handle; - [%expect - {| -
> -
-

Todo

-
-
todo 1
-
todo 2
-
-
-
-

In Progress

-
-
in progress 1
-
> -
-

Todo

-
- -|
todo 1
-
todo 2
-
-
-
-

In Progress

-
-
in progress 1
-
in progress 2
-
in progress 3
-
-
-
-

Done

-
- +|
todo 1
-
finished 1
-
finished 2
-
finished 3
-
-
-
-

In Progress

-
-
in progress 1
-
in progress 2
-
in progress 3
-
-
-
-

Done

-
-
todo 1
-
finished 1
-
finished 2
-
finished 3
-
-
- -|
- -|
todo 1
- -|
-
- |}] -;; diff --git a/examples/drag_and_drop/test/app_test.mli b/examples/drag_and_drop/test/app_test.mli deleted file mode 100644 index 537e8f54..00000000 --- a/examples/drag_and_drop/test/app_test.mli +++ /dev/null @@ -1 +0,0 @@ -(*_ This file intentionally left blank *) diff --git a/examples/drag_and_drop/test/dune b/examples/drag_and_drop/test/dune deleted file mode 100644 index c4bef45f..00000000 --- a/examples/drag_and_drop/test/dune +++ /dev/null @@ -1,5 +0,0 @@ -(library - (name bonsai_drag_and_drop_example_test) - (libraries bonsai_drag_and_drop_example bonsai_web bonsai_web_test core) - (preprocess - (pps js_of_ocaml-ppx ppx_jane))) diff --git a/examples/drag_and_drop_list/dune b/examples/drag_and_drop_list/dune deleted file mode 100644 index f4ae6750..00000000 --- a/examples/drag_and_drop_list/dune +++ /dev/null @@ -1,7 +0,0 @@ -(executables - (modes byte exe) - (names main) - (libraries bonsai_web bonsai_web_ui_form bonsai_web_ui_reorderable_list - virtual_dom.input_widgets) - (preprocess - (pps ppx_jane ppx_bonsai ppx_css))) diff --git a/examples/drag_and_drop_list/index.html b/examples/drag_and_drop_list/index.html deleted file mode 100644 index 4e7f12a3..00000000 --- a/examples/drag_and_drop_list/index.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - - Reorderable List - - - -
- - diff --git a/examples/drag_and_drop_list/main.ml b/examples/drag_and_drop_list/main.ml deleted file mode 100644 index 0fce6d39..00000000 --- a/examples/drag_and_drop_list/main.ml +++ /dev/null @@ -1,116 +0,0 @@ -open! Core -open! Bonsai_web.Cont -open Bonsai.Let_syntax -module Reorderable_list = Bonsai_web_ui_reorderable_list -module Form = Bonsai_web_ui_form.With_automatic_view - -module S = -[%css -stylesheet - {| - .item { - background-color: green; - color: white; - font-size:20px; - padding: 5px; - margin: 5px; - } - - .text_input { - flex: 1; - } - - .list { - flex: 1; - } - - .transition_transform { - transition: transform 0.3s, opacity 0.1s, background-color 0.3s; - } - |}] - -let item ~index:_ ~source _which _data graph = - let text, set_text = Bonsai.state_opt ~equal:[%equal: string] graph in - let%arr source = source - and text = text - and set_text = set_text in - let view = - View.hbox - ~attrs:[ S.item; source ] - [ Vdom_input_widgets.Entry.text - ~extra_attrs:[ S.text_input ] - ~value:text - ~on_input:set_text - ~allow_updates_when_focused:`Never - () - ] - in - (), view -;; - -let component graph = - let input, extend_input = - Bonsai.state_machine0 - graph - ~sexp_of_model:[%sexp_of: Int.Set.t] - ~equal:[%equal: Int.Set.t] - ~sexp_of_action:[%sexp_of: Unit.t] - ~default_model:(Int.Set.of_list [ 0; 1; 2 ]) - ~apply_action:(fun (_ : _ Bonsai.Apply_action_context.t) model () -> - Set.add model (Set.length model)) - in - let () = - Bonsai.Clock.every - ~when_to_start_next_effect:`Every_multiple_of_period_blocking - ~trigger_on_activate:true - (Time_ns.Span.of_sec 1.0) - (let%map extend_input = extend_input in - extend_input ()) - graph - in - let num_lists = - Form.Elements.Number.int - ~default:1 - ~step:1 - ~allow_updates_when_focused:`Never - () - graph - in - let whiches = - let%arr num_lists = num_lists in - let length = Int.max 0 (Form.value_or_default num_lists ~default:1) in - Int.Set.of_list (List.range 0 length) - in - let%sub lists, dragged_element = - Reorderable_list.Multi.simple - (module Int) - (module Int) - ~extra_item_attrs:(Bonsai.return S.transition_transform) - ~default_item_height:40 - ~render:item - ~lists:whiches - ~default_list:(Bonsai.return 0) - input - graph - in - let lists = - Bonsai.assoc - (module Int) - lists - ~f:(fun which data _graph -> - let%sub _, view = data in - let%arr view = view - and which = which in - Vdom.Node.div - ~attrs:[ S.list ] - [ Vdom.Node.h3 [ Vdom.Node.text [%string "List %{which#Int}"] ]; view ]) - graph - in - let%arr lists = lists - and dragged_element = dragged_element - and num_lists = num_lists in - Vdom.Node.div - [ Form.view_as_vdom num_lists; View.hbox (Map.data lists); dragged_element ] -;; - -let () = Bonsai_web.Start.start component diff --git a/examples/drag_and_drop_list/main.mli b/examples/drag_and_drop_list/main.mli deleted file mode 100644 index 74bb7298..00000000 --- a/examples/drag_and_drop_list/main.mli +++ /dev/null @@ -1 +0,0 @@ -(*_ This signature is deliberately empty. *) diff --git a/examples/drilldown/dune b/examples/drilldown/dune deleted file mode 100644 index 6a7f21ee..00000000 --- a/examples/drilldown/dune +++ /dev/null @@ -1,7 +0,0 @@ -(executables - (modes byte exe) - (names main) - (libraries bonsai_web virtual_dom bonsai_web_ui_drilldown - bonsai_web_ui_tree_layout) - (preprocess - (pps ppx_jane ppx_css ppx_bonsai))) diff --git a/examples/drilldown/index.html b/examples/drilldown/index.html deleted file mode 100644 index 2bf96b28..00000000 --- a/examples/drilldown/index.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - - Hello, Drilldown! - - - -
- - diff --git a/examples/drilldown/main.ml b/examples/drilldown/main.ml deleted file mode 100644 index 7eabc1c3..00000000 --- a/examples/drilldown/main.ml +++ /dev/null @@ -1,97 +0,0 @@ -open! Core -open! Bonsai_web.Cont -open! Bonsai.Let_syntax -open! Bonsai_web_ui_drilldown - -module Style = [%css stylesheet {| -.app { - width: 600px; -} -|}] - -let example : Tree.String.t = - let open Tree.String in - let a = { name = "a"; children = Leaf 3 } in - let b = - { name = "b" - ; children = - Branch [ { name = "ba"; children = Leaf 4 }; { name = "bb"; children = Leaf 1 } ] - } - in - let c = - { name = "c" - ; children = - Branch - [ { name = "ca" - ; children = - Branch - [ { name = "caa"; children = Leaf 2 } - ; { name = "cab"; children = Leaf 4 } - ] - } - ; { name = "cb"; children = Branch [ { name = "cba"; children = Leaf 1 } ] } - ; { name = "cc"; children = Leaf 1 } - ] - } - in - { name = "all"; children = Branch [ a; b; c ] } -;; - -let tree_layout : Vdom.Node.t = - let open Bonsai_web_ui_tree_layout in - let rec loop (tree : Tree.String.t) = - let name = Vdom.Node.text tree.name in - match tree.children with - | Leaf _ -> leaf name - | Branch children -> branch name (List.map children ~f:loop) - in - loop example |> to_vdom -;; - -let app : Bonsai.graph -> Vdom.Node.t Bonsai.t = - fun graph -> - let module N = Vdom.Node in - let%map drilldown_with_breadcrumbs = - Drilldown_with_breadcrumbs.component (Bonsai.return example) graph - in - let explanation1 = - N.div - [ N.text - "This demo shows off an interactive drilldown component. Click around to drill \ - in." - ] - in - let explanation2 = - N.div - [ N.text - "The key feature is that each child takes up space proportional to the sum of \ - the values of that child's leaves." - ] - in - let sexp = - N.div - [ N.text - "Below, you can see (a sexp of) the tree-like data structure that we're \ - visualizing in the drilldown." - ; [%sexp_of: Tree.String.t] example |> N.sexp_for_debugging - ] - in - let tree_layout = - N.div - [ N.text "And here is a different (static) tree layout to visualize the same thing:" - ; tree_layout - ] - in - let add_breaks l = List.intersperse ~sep:(N.br ()) l in - N.div - ~attrs:[ Style.app ] - (add_breaks - [ explanation1 - ; explanation2 - ; N.div [ drilldown_with_breadcrumbs ] - ; sexp - ; tree_layout - ]) -;; - -let () = Bonsai_web.Start.start app diff --git a/examples/drilldown/main.mli b/examples/drilldown/main.mli deleted file mode 100644 index 74bb7298..00000000 --- a/examples/drilldown/main.mli +++ /dev/null @@ -1 +0,0 @@ -(*_ This signature is deliberately empty. *) diff --git a/examples/dune b/examples/dune deleted file mode 100644 index e69de29b..00000000 diff --git a/examples/dygraph/custom_points.ml b/examples/dygraph/custom_points.ml deleted file mode 100644 index 50e465a6..00000000 --- a/examples/dygraph/custom_points.ml +++ /dev/null @@ -1,93 +0,0 @@ -open Core -open Js_of_ocaml -open Bonsai_web.Cont - -let side_lengths = Array.init 12 ~f:(fun i -> float i) - -let data : Dygraph.Data.t = - Array.map side_lengths ~f:(fun side_length -> - let radius = side_length /. 2. in - [| side_length; Float.pi *. (radius **. 2.); side_length ** 2. |]) - |> Dygraph.Data.create -;; - -let draw_square - ~(context : Dom_html.canvasRenderingContext2D Js.t) - ~stroke_or_fill - ~cx - ~cy - ~side_length - = - let top_left_x_coord = cx -. (side_length /. 2.) in - let top_left_y_coord = cy -. (side_length /. 2.) in - context##beginPath; - match stroke_or_fill with - | `Stroke -> - context##strokeRect top_left_x_coord top_left_y_coord side_length side_length - | `Fill -> context##fillRect top_left_x_coord top_left_y_coord side_length side_length -;; - -let draw_circle ~(context : Dom_html.canvasRenderingContext2D Js.t) ~cx ~cy ~radius = - context##beginPath; - context##arc cx cy radius 0. (2. *. Float.pi) (Js.bool false); - context##stroke -;; - -let options = - let circle_series_options = - Dygraph.Options.Series_options.create - ~drawHighlightPointCallback: - (fun - ~graph:_ ~seriesName:_ ~context ~cx ~cy ~color:_ ~pointSize:_ ~idx -> - let radius = side_lengths.(idx) /. 2. in - draw_circle ~context ~cx ~cy ~radius; - context##fill) - ~drawPointCallback: - (fun - ~graph:_ ~seriesName:_ ~context ~cx ~cy ~color:_ ~pointSize:_ ~idx -> - let radius = side_lengths.(idx) /. 2. in - draw_circle ~context ~cx ~cy ~radius) - () - in - let square_series_options = - Dygraph.Options.Series_options.create - ~drawHighlightPointCallback: - (fun - ~graph:_ ~seriesName:_ ~context ~cx ~cy ~color:_ ~pointSize:_ ~idx -> - let side_length = side_lengths.(idx) in - draw_square ~context ~stroke_or_fill:`Fill ~cx ~cy ~side_length) - ~drawPointCallback: - (fun - ~graph:_ ~seriesName:_ ~context ~cx ~cy ~color:_ ~pointSize:_ ~idx -> - let side_length = side_lengths.(idx) in - draw_square ~context ~stroke_or_fill:`Stroke ~cx ~cy ~side_length) - () - in - let series = - Dygraph.Options.Series.create - [ "circles", circle_series_options; "squares", square_series_options ] - in - Dygraph.Options.create - () - ~drawPoints:true - ~strokeWidth:0. - ~series - ~title:"Custom Drawn Points Example" -;; - -let app graph = - let%sub.Bonsai { graph_view; _ } = - Dygraph.With_bonsai.create - () - ~key:("custom-drawn-points-graph" |> Bonsai.return) - ~x_label:("diameter/side length" |> Bonsai.return) - ~per_series_info: - ([ "circles"; "squares" ] - |> Dygraph.Per_series_info.create_all_visible - |> Bonsai.return) - ~options:(options |> Bonsai.return) - ~data:(data |> Bonsai.return) - graph - in - graph_view -;; diff --git a/examples/dygraph/custom_points.mli b/examples/dygraph/custom_points.mli deleted file mode 100644 index 2b7088b1..00000000 --- a/examples/dygraph/custom_points.mli +++ /dev/null @@ -1,4 +0,0 @@ -open! Core -open! Bonsai_web.Cont - -val app : Bonsai.graph -> Vdom.Node.t Bonsai.t diff --git a/examples/dygraph/dune b/examples/dygraph/dune deleted file mode 100644 index 339e6491..00000000 --- a/examples/dygraph/dune +++ /dev/null @@ -1,7 +0,0 @@ -(executables - (modes byte exe) - (names main) - (preprocess - (pps js_of_ocaml-ppx ppx_bonsai ppx_jane)) - (libraries core dygraph dygraph_jane bonsai_web virtual_dom.input_widgets - timezone)) diff --git a/examples/dygraph/hide_overnights.ml b/examples/dygraph/hide_overnights.ml deleted file mode 100644 index f9c3cd13..00000000 --- a/examples/dygraph/hide_overnights.ml +++ /dev/null @@ -1,112 +0,0 @@ -open! Core -open Import -open! Gen_js_api - -let start_time = Time_ns.of_string_with_utc_offset "2020-10-06 09:30:00-04" -let end_time = Time_ns.of_string_with_utc_offset "2020-10-08 16:00:00-04" -let zone = Lazy.force Timezone.local - -(* these times will span multiple days, but will not contain overnights *) -let times : Time_ns.t list = - let start_ofday = Time_ns.Ofday.create ~hr:9 ~min:30 () in - let stop_ofday = Time_ns.Ofday.create ~hr:16 ~min:00 () in - List.range' - ~stop:`inclusive - start_time - end_time - ~compare:Time_ns.compare - ~stride:(fun t -> Time_ns.add t (Time_ns.Span.of_min 1.)) - |> List.filter ~f:(fun t -> - let ofday = Time_ns.to_ofday t ~zone in - Time_ns.Ofday.( >= ) ofday start_ofday && Time_ns.Ofday.( <= ) ofday stop_ofday) -;; - -(* a brownian motion *) -let raw_data = - let last = ref 0. in - Array.of_list_map times ~f:(fun time -> - let noise = Random.float 1. -. 0.5 in - last := !last +. noise; - time, [| !last |]) -;; - -let axes ~value_formatter ~axis_label_formatter = - let axis_options = - Dygraph.Options.Axis_options.create - ?valueFormatter:value_formatter - ?axisLabelFormatter:axis_label_formatter - () - in - Dygraph.Options.Axes.create ~x:axis_options () -;; - -let options ~title ?value_formatter ?axis_label_formatter () = - Dygraph.Options.create - () - ~title - ~width:800 - ~axes:(axes ~value_formatter ~axis_label_formatter) - ~highlightSeriesOpts: - (Dygraph.Options.Highlight_series_options.create () ~strokeWidth:1.5) -;; - -let app graph = - let x_label = "time" in - let y_labels = [ "brownian motion" ] in - let make_graph ~name ~title ~data ?value_formatter ?axis_label_formatter () graph = - let options = options ~title ?value_formatter ?axis_label_formatter () in - let%sub { graph_view; _ } = - Dygraph.With_bonsai.create - ~key:(Bonsai.return name) - ~x_label:(Bonsai.return x_label) - ~per_series_info: - (y_labels |> Dygraph.Per_series_info.create_all_visible |> Bonsai.return) - ~options:(Bonsai.return options) - ~data:(Bonsai.return data) - ~with_graph:(fun graph -> - Js.Unsafe.set Dom_html.window (sprintf "g_%s" name) graph) - () - graph - in - graph_view - in - let hide_overnights_graph = - let { Dygraph.X_axis_mapping.time_to_x_value - ; x_value_to_time = _ - ; value_formatter - ; axis_label_formatter - } - = - Dygraph_jane.X_axis_mapping.only_display_market_hours - () - ~start_time - ~end_time - ~view_zone:zone - |> Or_error.ok_exn - in - let data = - Array.map raw_data ~f:(fun (time, values) -> time_to_x_value time, values) - |> Dygraph.Data.create_time_ns - in - make_graph - ~name:"with_overnights_hidden" - ~title:"With overnights hidden" - ~value_formatter - ~axis_label_formatter - ~data - () - graph - in - let visible_overnights_graphs = - let data = Dygraph.Data.create_time_ns raw_data in - make_graph - ~name:"with_overnights_visible" - ~title:"With overnights visible" - ~data - () - graph - in - let%arr hide_overnights_graph = hide_overnights_graph - and visible_overnights_graphs = visible_overnights_graphs in - Vdom.Node.div [ hide_overnights_graph; visible_overnights_graphs ] -;; diff --git a/examples/dygraph/hide_overnights.mli b/examples/dygraph/hide_overnights.mli deleted file mode 100644 index 2b7088b1..00000000 --- a/examples/dygraph/hide_overnights.mli +++ /dev/null @@ -1,4 +0,0 @@ -open! Core -open! Bonsai_web.Cont - -val app : Bonsai.graph -> Vdom.Node.t Bonsai.t diff --git a/examples/dygraph/import.ml b/examples/dygraph/import.ml deleted file mode 100644 index 701017a7..00000000 --- a/examples/dygraph/import.ml +++ /dev/null @@ -1,3 +0,0 @@ -include Js_of_ocaml -include Bonsai_web.Cont -include Bonsai.Let_syntax diff --git a/examples/dygraph/index.html b/examples/dygraph/index.html deleted file mode 100644 index 4283fbe2..00000000 --- a/examples/dygraph/index.html +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - -
- - diff --git a/examples/dygraph/main.ml b/examples/dygraph/main.ml deleted file mode 100644 index 1cbd18cd..00000000 --- a/examples/dygraph/main.ml +++ /dev/null @@ -1,26 +0,0 @@ -open! Core -open! Bonsai_web.Cont -open Bonsai.Let_syntax - -let app graph = - let hide_overnight = Hide_overnights.app graph in - let simple = Simple.app graph in - let stock_chart = Stock_chart.app graph in - let custom_points = Custom_points.app graph in - let%arr hide_overnight = hide_overnight - and simple = simple - and stock_chart = stock_chart - and custom_points = custom_points in - Vdom.Node.div - [ Vdom.Node.h1 [ Vdom.Node.text "Simple" ] - ; simple - ; Vdom.Node.h1 [ Vdom.Node.text "Stock" ] - ; stock_chart - ; Vdom.Node.h1 [ Vdom.Node.text "Hide Overnight" ] - ; hide_overnight - ; Vdom.Node.h1 [ Vdom.Node.text "Custom points" ] - ; custom_points - ] -;; - -let () = Bonsai_web.Start.start app diff --git a/examples/dygraph/main.mli b/examples/dygraph/main.mli deleted file mode 100644 index 537e8f54..00000000 --- a/examples/dygraph/main.mli +++ /dev/null @@ -1 +0,0 @@ -(*_ This file intentionally left blank *) diff --git a/examples/dygraph/simple.ml b/examples/dygraph/simple.ml deleted file mode 100644 index d2351597..00000000 --- a/examples/dygraph/simple.ml +++ /dev/null @@ -1,56 +0,0 @@ -open Core -open Js_of_ocaml -open Bonsai_web.Cont - -(** x, x^2, x^3 *) -let data : Dygraph.Data.t = - Array.init 10 ~f:(fun i -> - let x = float i in - [| x; x ** 2.; x ** 3. |]) - |> Dygraph.Data.create -;; - -let x_label = "x" - -let options = - let cubed_series_options = - Dygraph.Options.Series_options.create () ~axis:`y2 ~color:(`Name "purple") - in - let squared_series_otions = - Dygraph.Options.Series_options.create () ~axis:`y1 ~color:(`Hex "#70ba70") - in - let axes = - let y2_axis_options = Dygraph.Options.Axis_options.create () ~independentTicks:true in - Dygraph.Options.Axes.create () ~y2:y2_axis_options - in - let series = - Dygraph.Options.Series.create - [ "x^2", squared_series_otions; "x^3", cubed_series_options ] - in - Dygraph.Options.create - () - ~drawPoints:true - ~strokeWidth:0. - ~series - ~title:"Simple example" - ~xlabel:x_label - ~ylabel:"x^2" - ~y2label:"x^3" - ~axes -;; - -let app graph = - let%sub.Bonsai { graph_view; _ } = - Dygraph.With_bonsai.create - () - ~key:("graph" |> Bonsai.return) - ~x_label:("x" |> Bonsai.return) - ~per_series_info: - ([ "x^2"; "x^3" ] |> Dygraph.Per_series_info.create_all_visible |> Bonsai.return) - ~options:(options |> Bonsai.return) - ~data:(data |> Bonsai.return) - ~with_graph:(fun graph -> Js.Unsafe.set Dom_html.window "g" graph) - graph - in - graph_view -;; diff --git a/examples/dygraph/simple.mli b/examples/dygraph/simple.mli deleted file mode 100644 index 2b7088b1..00000000 --- a/examples/dygraph/simple.mli +++ /dev/null @@ -1,4 +0,0 @@ -open! Core -open! Bonsai_web.Cont - -val app : Bonsai.graph -> Vdom.Node.t Bonsai.t diff --git a/examples/dygraph/stock_chart.ml b/examples/dygraph/stock_chart.ml deleted file mode 100644 index d3a2e498..00000000 --- a/examples/dygraph/stock_chart.ml +++ /dev/null @@ -1,81 +0,0 @@ -open! Core -open Import - -module Scale = struct - type t = - [ `log - | `linear - ] - [@@deriving enumerate, equal, sexp] - - let to_string t = sexp_of_t t |> Sexp.to_string -end - -let scale : Bonsai.graph -> (Scale.t * Vdom.Node.t) Bonsai.t = - fun graph -> - let scale_state = - Tuple2.uncurry Bonsai.both - @@ Bonsai.state `log ~sexp_of_model:[%sexp_of: Scale.t] ~equal:[%equal: Scale.t] graph - in - let%arr scale, set_scale = scale_state in - let view = - Vdom_input_widgets.Dropdown.of_enum - ~merge_behavior:Legacy_dont_merge - (module Scale) - ~selected:scale - ~on_change:set_scale - in - scale, view -;; - -let data = - let local_tz = Lazy.force Timezone.local in - Dygraph.Data.create_date - ~zone:local_tz - (Array.of_list (List.map Stock_data.data ~f:(fun (date, a, b) -> date, [| a; b |]))) - |> Bonsai.return -;; - -let options ~logscale = - let series_options = - Dygraph.Options.Series_options.create - () - ~strokePattern:Dygraph.Options.Line_pattern.dashed - in - Dygraph.Options.create - () - ~width:800 - ~logscale - ~series:(Dygraph.Options.Series.create [ "Nominal", series_options ]) - ~highlightSeriesOpts: - (Dygraph.Options.Highlight_series_options.create () ~strokeWidth:1.5) -;; - -let app graph = - let x_label = "Month" in - let y_labels = [ "Nominal"; "Real" ] in - let%sub scale, scale_view = scale graph in - let options = - match%map scale with - | `log -> options ~logscale:true - | `linear -> options ~logscale:false - in - let%sub { graph_view; _ } = - Dygraph.With_bonsai.create - ~key:("graph" |> Bonsai.return) - ~x_label:(x_label |> Bonsai.return) - ~per_series_info: - (y_labels |> Dygraph.Per_series_info.create_all_visible |> Bonsai.return) - ~options - ~data - (* By setting the graph to global variable "g", I'm able to access the graph in the - chrome console and look at things, e.g. g.getOptions("series"). This is purely - for debugging/convenience. *) - ~with_graph:(fun graph -> Js.Unsafe.set Dom_html.window "g" graph) - () - graph - in - let%arr graph_view = graph_view - and scale_view = scale_view in - Vdom.Node.div [ graph_view; Vdom.Node.textf "y-axis scale: "; scale_view ] -;; diff --git a/examples/dygraph/stock_chart.mli b/examples/dygraph/stock_chart.mli deleted file mode 100644 index 2b7088b1..00000000 --- a/examples/dygraph/stock_chart.mli +++ /dev/null @@ -1,4 +0,0 @@ -open! Core -open! Bonsai_web.Cont - -val app : Bonsai.graph -> Vdom.Node.t Bonsai.t diff --git a/examples/dygraph/stock_data.ml b/examples/dygraph/stock_data.ml deleted file mode 100644 index c07d49c7..00000000 --- a/examples/dygraph/stock_data.ml +++ /dev/null @@ -1,1175 +0,0 @@ -open Core - -let data : (Date.t * float * float) list = - let parse_custom_bars s = - (* I'm ignoring error bars for now *) - match String.split s ~on:';' with - | [ _low; middle; _high ] -> Float.of_string middle - | _ -> raise_s [%message " bug"] - in - List.map - ~f:(fun s -> - match String.split s ~on:',' with - | [ date; nominal; real ] -> - Date.of_string date, parse_custom_bars nominal, parse_custom_bars real - | _ -> raise_s [%message " bug"]) - [ "1913-01-15,59.740;61.330;64.880,609.591836734694;625.816326530612;662.04081632653" - ; "1913-02-15,57.670;58.840;61.140,588.469387755102;600.408163265306;623.877551020408" - ; "1913-03-15,57.320;59.280;59.840,584.897959183673;604.897959183673;610.612244897959" - ; "1913-04-15,57.430;57.540;60.940,586.020408163265;587.142857142857;621.836734693877" - ; "1913-05-15,57.420;57.420;58.570,591.958762886598;591.958762886598;603.814432989691" - ; "1913-06-15,52.830;54.860;56.610,539.081632653061;559.795918367347;577.65306122449" - ; "1913-07-15,55.110;57.490;57.920,556.666666666667;580.707070707071;585.050505050505" - ; "1913-08-15,57.290;59.930;59.930,578.686868686869;605.353535353535;605.353535353535" - ; "1913-09-15,58.800;58.880;60.830,588;588.8;608.3" - ; "1913-10-15,56.470;57.360;59.650,564.7;573.6;596.5" - ; "1913-11-15,55.630;55.830;57.220,550.792079207921;552.772277227723;566.534653465347" - ; "1913-12-15,55.140;57.710;57.760,551.4;577.1;577.6" - ; "1914-01-15,57.570;60.660;60.710,575.7;606.6;607.1" - ; "1914-02-15,59.560;60.070;60.940,601.616161616162;606.767676767677;615.555555555556" - ; "1914-03-15,59.430;60.360;61.120,600.30303030303;609.69696969697;617.373737373737" - ; "1914-04-15,56.790;57.960;60.410,579.489795918367;591.428571428571;616.428571428571" - ; "1914-05-15,57.990;59.760;59.820,585.757575757576;603.636363636364;604.242424242424" - ; "1914-06-15,58.090;59.090;59.950,586.767676767677;596.868686868687;605.555555555556" - ; "1914-07-15,52.320;52.320;59.920,523.2;523.2;599.2" - ; "1914-12-15,53.170;54.580;56.760,526.435643564356;540.39603960396;561.980198019802" - ; "1915-01-15,55.400;56.540;58.510,548.514851485149;559.80198019802;579.306930693069" - ; "1915-02-15,54.220;55.020;57.830,542.2;550.2;578.3" - ; "1915-03-15,55.290;60.830;61.300,558.484848484848;614.444444444444;619.191919191919" - ; "1915-04-15,61.050;71.780;71.780,610.5;717.8;717.8" - ; "1915-05-15,60.380;65.010;69.580,597.821782178218;643.663366336634;688.910891089109" - ; "1915-06-15,64.860;70.060;71.900,642.178217821782;693.663366336634;711.881188118812" - ; "1915-07-15,67.880;75.530;75.790,672.079207920792;747.821782178218;750.396039603961" - ; "1915-08-15,76.460;81.200;81.880,757.029702970297;803.960396039604;810.693069306931" - ; "1915-09-15,80.700;90.580;90.580,799.009900990099;896.831683168317;896.831683168317" - ; "1915-10-15,88.230;95.340;96.460,865;934.705882352941;945.686274509804" - ; "1915-11-15,91.080;96.710;97.560,884.271844660194;938.932038834951;947.184466019417" - ; "1915-12-15,94.780;99.150;99.210,920.194174757282;962.621359223301;963.203883495146" - ; "1916-01-15,90.580;90.580;98.810,870.961538461538;870.961538461538;950.096153846154" - ; "1916-02-15,90.890;91.030;96.150,873.942307692308;875.288461538461;924.519230769231" - ; "1916-03-15,90.520;93.250;96.080,862.095238095238;888.095238095238;915.047619047619" - ; "1916-04-15,87.000;89.650;94.460,820.754716981132;845.754716981132;891.132075471698" - ; "1916-05-15,87.710;91.800;92.620,819.719626168224;857.943925233645;865.607476635514" - ; "1916-06-15,87.680;89.580;93.610,811.851851851852;829.444444444444;866.759259259259" - ; "1916-07-15,86.420;89.250;90.530,800.185185185185;826.388888888889;838.240740740741" - ; "1916-08-15,88.150;92.250;93.830,808.715596330275;846.330275229358;860.825688073395" - ; "1916-09-15,91.190;103.730;103.730,821.531531531532;934.504504504505;934.504504504505" - ; "1916-10-15,98.940;104.610;105.280,875.575221238938;925.75221238938;931.681415929203" - ; "1916-11-15,105.630;105.970;110.150,918.521739130435;921.478260869565;957.826086956522" - ; "1916-12-15,90.160;95.000;106.760,777.241379310345;818.965517241379;920.344827586207" - ; "1917-01-15,95.430;95.430;99.180,815.641025641026;815.641025641026;847.692307692308" - ; "1917-02-15,87.010;91.560;94.910,725.083333333333;763;790.916666666667" - ; "1917-03-15,91.100;96.720;98.200,759.166666666667;806;818.333333333333" - ; "1917-04-15,90.660;93.230;97.060,719.52380952381;739.920634920635;770.31746031746" - ; "1917-05-15,89.080;97.380;97.410,695.9375;760.78125;761.015625" - ; "1917-06-15,94.780;95.380;98.580,729.076923076923;733.692307692308;758.307692307692" - ; "1917-07-15,90.480;91.750;95.310,706.875;716.796875;744.609375" - ; "1917-08-15,83.400;83.400;93.850,641.538461538462;641.538461538462;721.923076923077" - ; "1917-09-15,81.200;83.460;86.020,610.526315789474;627.518796992481;646.766917293233" - ; "1917-10-15,74.500;74.500;83.580,551.851851851852;551.851851851852;619.111111111111" - ; "1917-11-15,68.580;72.650;74.230,508;538.148148148148;549.851851851852" - ; "1917-12-15,65.950;74.380;74.380,481.386861313869;542.919708029197;542.919708029197" - ; "1918-01-15,73.380;79.800;79.800,524.142857142857;570;570" - ; "1918-02-15,77.780;80.390;82.080,551.631205673759;570.141843971631;582.127659574468" - ; "1918-03-15,76.410;76.410;79.930,545.785714285714;545.785714285714;570.928571428571" - ; "1918-04-15,75.580;77.510;79.420,532.253521126761;545.845070422535;559.295774647887" - ; "1918-05-15,78.080;78.080;84.040,538.48275862069;538.48275862069;579.586206896552" - ; "1918-06-15,78.530;82.580;83.020,534.21768707483;561.768707482993;564.761904761905" - ; "1918-07-15,80.510;81.230;82.960,533.17880794702;537.94701986755;549.403973509934" - ; "1918-08-15,80.710;82.460;83.180,524.090909090909;535.454545454545;540.12987012987" - ; "1918-09-15,80.290;84.680;84.680,511.40127388535;539.363057324841;539.363057324841" - ; "1918-10-15,83.360;85.510;89.070,521;534.4375;556.6875" - ; "1918-11-15,79.870;80.930;87.660,490;496.503067484663;537.791411042945" - ; "1918-12-15,80.440;82.200;84.500,487.515151515152;498.181818181818;512.121212121212" - ; "1919-01-15,79.880;80.610;83.350,484.121212121212;488.545454545455;505.151515151515" - ; "1919-02-15,79.350;84.810;85.680,489.814814814815;523.518518518518;528.888888888889" - ; "1919-03-15,84.240;88.850;89.050,513.658536585366;541.768292682927;542.987804878049" - ; "1919-04-15,88.840;92.880;93.510,531.976047904192;556.167664670659;559.940119760479" - ; "1919-05-15,93.260;105.500;105.500,551.834319526627;624.260355029586;624.260355029586" - ; "1919-06-15,99.560;106.980;107.550,589.112426035503;633.01775147929;636.390532544379" - ; "1919-07-15,107.160;107.160;112.230,615.862068965517;615.862068965517;645" - ; "1919-08-15,98.460;104.750;107.990,556.271186440678;591.80790960452;610.112994350283" - ; "1919-09-15,105.840;111.420;111.420,594.606741573034;625.955056179775;625.955056179775" - ; "1919-10-15,108.900;118.920;118.920,601.657458563536;657.016574585635;657.016574585635" - ; "1919-11-15,103.720;103.720;119.620,560.648648648649;560.648648648649;646.594594594595" - ; "1919-12-15,103.550;107.230;107.970,547.883597883598;567.354497354497;571.269841269841" - ; "1920-01-15,101.900;104.210;108.850,527.979274611399;539.948186528497;563.989637305699" - ; "1920-02-15,89.980;91.180;103.010,461.435897435897;467.589743589744;528.25641025641" - ; "1920-03-15,91.680;102.810;104.170,465.380710659899;521.878172588833;528.781725888325" - ; "1920-04-15,93.160;93.540;105.650,458.916256157635;460.788177339901;520.443349753695" - ; "1920-05-15,87.360;91.810;94.410,424.077669902913;445.679611650485;458.300970873786" - ; "1920-06-15,90.160;90.760;93.060,431.387559808612;434.258373205742;445.263157894737" - ; "1920-07-15,86.860;86.860;94.510,417.596153846154;417.596153846154;454.375" - ; "1920-08-15,83.200;86.160;87.290,409.852216748768;424.433497536946;430" - ; "1920-09-15,82.950;82.950;89.950,414.75;414.75;449.75" - ; "1920-10-15,84.000;85.080;85.730,422.110552763819;427.537688442211;430.804020100503" - ; "1920-11-15,73.120;76.040;85.480,369.292929292929;384.040404040404;431.717171717172" - ; "1920-12-15,66.750;71.950;77.300,344.072164948454;370.876288659794;398.453608247423" - ; "1921-01-15,72.670;76.130;76.760,382.473684210526;400.684210526316;404" - ; "1921-02-15,74.340;74.980;77.140,404.021739130435;407.5;419.239130434783" - ; "1921-03-15,72.250;75.760;77.780,394.808743169399;413.989071038251;425.027322404372" - ; "1921-04-15,75.060;78.570;78.860,414.696132596685;434.088397790055;435.690607734807" - ; "1921-05-15,73.440;73.440;80.030,414.915254237288;414.915254237288;452.146892655367" - ; "1921-06-15,64.900;68.450;73.510,368.75;388.920454545455;417.670454545455" - ; "1921-07-15,67.250;68.370;69.860,379.943502824859;386.271186440678;394.689265536723" - ; "1921-08-15,63.900;67.110;69.950,361.016949152542;379.152542372881;395.197740112994" - ; "1921-09-15,66.830;71.080;71.720,381.885714285714;406.171428571429;409.828571428571" - ; "1921-10-15,69.460;73.210;73.800,396.914285714286;418.342857142857;421.714285714286" - ; "1921-11-15,73.440;77.300;78.010,422.068965517241;444.252873563218;448.333333333333" - ; "1921-12-15,78.120;80.800;81.500,451.560693641618;467.052023121387;471.098265895954" - ; "1922-01-15,78.590;81.300;82.950,465.029585798817;481.065088757396;490.828402366864" - ; "1922-02-15,81.680;85.460;85.810,483.313609467456;505.680473372781;507.751479289941" - ; "1922-03-15,85.330;89.050;89.050,510.958083832335;533.233532934132;533.233532934132" - ; "1922-04-15,89.300;91.930;93.210,534.730538922156;550.479041916168;558.14371257485" - ; "1922-05-15,91.500;95.630;96.410,547.904191616767;572.634730538922;577.305389221557" - ; "1922-06-15,90.730;92.930;96.360,543.293413173653;556.467065868264;577.005988023952" - ; "1922-07-15,92.920;97.050;97.050,553.095238095238;577.678571428571;577.678571428571" - ; "1922-08-15,96.210;100.780;100.780,579.578313253012;607.10843373494;607.10843373494" - ; "1922-09-15,96.580;97.120;102.050,581.807228915663;585.060240963855;614.759036144578" - ; "1922-10-15,96.110;96.110;102.760,575.508982035928;575.508982035928;615.329341317365" - ; "1922-11-15,92.030;94.650;99.530,547.797619047619;563.392857142857;592.440476190476" - ; "1922-12-15,95.030;98.170;99.220,562.307692307692;580.887573964497;587.100591715976" - ; "1923-01-15,96.960;97.430;99.420,577.142857142857;579.940476190476;591.785714285714" - ; "1923-02-15,97.710;103.900;103.900,581.607142857143;618.452380952381;618.452380952381" - ; "1923-03-15,102.360;102.770;105.380,609.285714285714;611.72619047619;627.261904761905" - ; "1923-04-15,98.380;98.380;102.700,582.130177514793;582.130177514793;607.692307692308" - ; "1923-05-15,92.770;97.530;98.190,548.934911242604;577.100591715976;581.005917159763" - ; "1923-06-15,88.400;88.400;97.240,520;520;572" - ; "1923-07-15,86.910;86.910;91.720,505.290697674419;505.290697674419;533.255813953488" - ; "1923-08-15,87.960;93.460;93.700,514.385964912281;546.549707602339;547.953216374269" - ; "1923-09-15,87.940;87.970;93.610,511.279069767442;511.453488372093;544.244186046512" - ; "1923-10-15,85.910;88.530;90.450,496.589595375722;511.734104046243;522.832369942196" - ; "1923-11-15,88.410;92.340;92.880,511.040462427746;533.757225433526;536.878612716763" - ; "1923-12-15,92.640;95.520;95.610,535.491329479769;552.138728323699;552.658959537572" - ; "1924-01-15,94.880;100.660;100.660,548.439306358381;581.849710982659;581.849710982659" - ; "1924-02-15,96.330;97.220;101.310,560.058139534884;565.232558139535;589.011627906977" - ; "1924-03-15,92.540;93.010;98.860,541.169590643275;543.918128654971;578.12865497076" - ; "1924-04-15,89.180;90.630;94.690,524.588235294118;533.117647058823;557" - ; "1924-05-15,88.330;89.900;92.470,519.588235294118;528.823529411765;543.941176470588" - ; "1924-06-15,89.180;96.370;96.370,524.588235294118;566.882352941176;566.882352941176" - ; "1924-07-15,96.380;102.140;102.140,563.625730994152;597.309941520468;597.309941520468" - ; "1924-08-15,101.510;104.140;105.570,597.117647058824;612.588235294118;621" - ; "1924-09-15,101.070;103.160;104.950,591.052631578947;603.27485380117;613.74269005848" - ; "1924-10-15,99.180;104.060;104.080,576.627906976744;605;605.116279069768" - ; "1924-11-15,103.890;111.100;111.100,604.011627906977;645.93023255814;645.93023255814" - ; "1924-12-15,110.440;120.510;120.510,638.381502890173;696.589595375723;696.589595375723" - ; "1925-01-15,119.460;123.490;123.600,690.520231213873;713.815028901734;714.450867052023" - ; "1925-02-15,117.960;122.240;122.860,685.813953488372;710.697674418605;714.302325581395" - ; "1925-03-15,115.000;116.750;125.680,664.739884393064;674.85549132948;726.473988439306" - ; "1925-04-15,117.400;120.010;121.540,682.558139534884;697.732558139535;706.627906976744" - ; "1925-05-15,121.100;129.950;129.950,700;751.156069364162;751.156069364162" - ; "1925-06-15,126.750;131.010;131.010,724.285714285714;748.628571428571;748.628571428571" - ; "1925-07-15,131.330;133.810;136.500,741.977401129944;755.988700564972;771.186440677966" - ; "1925-08-15,135.710;141.180;143.180,766.723163841808;797.627118644068;808.926553672316" - ; "1925-09-15,137.220;143.460;147.160,775.254237288136;810.508474576271;831.412429378531" - ; "1925-10-15,144.770;155.650;155.650,817.909604519774;879.378531073446;879.378531073446" - ; "1925-11-15,148.180;151.080;159.390,823.222222222222;839.333333333333;885.5" - ; "1925-12-15,152.110;156.660;156.870,849.776536312849;875.195530726257;876.368715083799" - ; "1926-01-15,153.200;157.350;158.930,855.865921787709;879.050279329609;887.877094972067" - ; "1926-02-15,154.680;154.680;162.310,864.134078212291;864.134078212291;906.759776536313" - ; "1926-03-15,135.200;140.460;153.130,759.550561797753;789.101123595506;860.280898876404" - ; "1926-04-15,136.270;143.710;144.550,761.284916201117;802.849162011173;807.541899441341" - ; "1926-05-15,137.160;143.430;143.430,770.561797752809;805.786516853933;805.786516853933" - ; "1926-06-15,142.300;153.040;154.030,803.954802259887;864.632768361582;870.225988700565" - ; "1926-07-15,153.010;160.180;160.580,874.342857142857;915.314285714286;917.6" - ; "1926-08-15,160.410;162.510;166.140,921.896551724138;933.965517241379;954.827586206897" - ; "1926-09-15,156.260;158.190;166.100,892.914285714286;903.942857142857;949.142857142857" - ; "1926-10-15,145.660;150.760;159.690,827.613636363636;856.590909090909;907.329545454545" - ; "1926-11-15,150.510;156.550;157.370,850.338983050847;884.463276836158;889.09604519774" - ; "1926-12-15,156.650;157.200;160.750,885.028248587571;888.135593220339;908.19209039548" - ; "1927-01-15,152.730;156.410;156.560,872.742857142857;893.771428571429;894.628571428571" - ; "1927-02-15,154.310;161.960;161.960,886.83908045977;930.80459770115;930.80459770115" - ; "1927-03-15,158.410;160.080;161.780,915.664739884393;925.317919075144;935.14450867052" - ; "1927-04-15,160.710;164.640;167.360,928.959537572254;951.676300578035;967.398843930636" - ; "1927-05-15,164.550;172.960;172.960,945.689655172414;994.022988505747;994.022988505747" - ; "1927-06-15,165.730;166.230;171.980,941.647727272727;944.488636363636;977.159090909091" - ; "1927-07-15,168.060;181.400;181.400,971.445086705202;1048.5549132948;1048.5549132948" - ; "1927-08-15,177.130;189.790;190.630,1029.82558139535;1103.43023255814;1108.31395348837" - ; "1927-09-15,191.560;197.590;198.970,1107.28323699422;1142.1387283237;1150.11560693642" - ; "1927-10-15,181.430;181.730;199.780,1042.70114942529;1044.42528735632;1148.16091954023" - ; "1927-11-15,181.650;198.210;198.210,1050;1145.7225433526;1145.7225433526" - ; "1927-12-15,193.580;200.700;200.930,1118.95953757225;1160.11560693642;1161.4450867052" - ; "1928-01-15,194.500;198.590;203.350,1124.2774566474;1147.91907514451;1175.43352601156" - ; "1928-02-15,191.330;194.780;199.350,1118.88888888889;1139.06432748538;1165.78947368421" - ; "1928-03-15,194.530;214.450;214.450,1137.60233918129;1254.09356725146;1254.09356725146" - ; "1928-04-15,207.940;211.630;216.930,1216.02339181287;1237.60233918129;1268.59649122807" - ; "1928-05-15,211.730;219.810;220.880,1230.98837209302;1277.96511627907;1284.18604651163" - ; "1928-06-15,201.960;210.370;220.270,1181.05263157895;1230.23391812865;1288.12865497076" - ; "1928-07-15,205.100;216.000;216.620,1199.41520467836;1263.15789473684;1266.78362573099" - ; "1928-08-15,214.080;240.410;240.410,1251.9298245614;1405.90643274854;1405.90643274854" - ; "1928-09-15,236.860;237.380;241.720,1369.13294797688;1372.1387283237;1397.22543352601" - ; "1928-10-15,237.750;252.160;257.130,1382.26744186047;1466.04651162791;1494.94186046512" - ; "1928-11-15,254.380;293.380;295.620,1478.95348837209;1705.6976744186;1718.72093023256" - ; "1928-12-15,263.950;300.000;300.000,1543.56725146199;1754.38596491228;1754.38596491228" - ; "1929-01-15,296.980;317.510;317.510,1736.72514619883;1856.78362573099;1856.78362573099" - ; "1929-02-15,300.410;317.410;322.060,1756.78362573099;1856.19883040936;1883.3918128655" - ; "1929-03-15,296.510;308.850;321.180,1744.17647058824;1816.76470588235;1889.29411764706" - ; "1929-04-15,299.130;319.290;319.290,1770;1889.2899408284;1889.2899408284" - ; "1929-05-15,293.420;297.410;326.160,1726;1749.47058823529;1918.58823529412" - ; "1929-06-15,303.270;331.650;331.650,1773.50877192982;1939.47368421053;1939.47368421053" - ; "1929-07-15,335.220;347.700;347.700,1937.68786127168;2009.82658959538;2009.82658959538" - ; "1929-08-15,337.990;380.330;380.330,1953.69942196532;2198.43930635838;2198.43930635838" - ; "1929-09-15,343.450;343.450;381.170,1985.26011560694;1985.26011560694;2203.29479768786" - ; "1929-10-15,230.070;273.510;352.860,1329.88439306358;1580.98265895954;2039.65317919075" - ; "1929-11-15,198.690;238.950;257.680,1148.49710982659;1381.21387283237;1489.47976878613" - ; "1929-12-15,230.890;248.480;262.200,1342.38372093023;1444.6511627907;1524.41860465116" - ; "1930-01-15,244.200;267.140;267.140,1428.0701754386;1562.22222222222;1562.22222222222" - ; "1930-02-15,262.470;271.110;272.270,1543.94117647059;1594.76470588235;1601.58823529412" - ; "1930-03-15,270.590;286.100;286.100,1601.12426035503;1692.89940828402;1692.89940828402" - ; "1930-04-15,276.940;279.230;294.070,1629.05882352941;1642.52941176471;1729.82352941176" - ; "1930-05-15,259.680;275.070;275.070,1536.56804733728;1627.63313609467;1627.63313609467" - ; "1930-06-15,211.840;226.340;274.450,1260.95238095238;1347.2619047619;1633.63095238095" - ; "1930-07-15,218.330;233.990;240.810,1315.24096385542;1409.57831325301;1450.66265060241" - ; "1930-08-15,217.240;240.420;240.420,1316.60606060606;1457.09090909091;1457.09090909091" - ; "1930-09-15,204.900;204.900;245.090,1234.33734939759;1234.33734939759;1476.44578313253" - ; "1930-10-15,183.350;183.350;214.180,1111.21212121212;1111.21212121212;1298.06060606061" - ; "1930-11-15,171.600;180.910;190.300,1046.34146341463;1103.10975609756;1160.36585365854" - ; "1930-12-15,157.510;164.580;186.820,978.32298136646;1022.23602484472;1160.37267080745" - ; "1931-01-15,161.450;169.340;173.040,1015.40880503145;1065.03144654088;1088.30188679245" - ; "1931-02-15,168.710;190.340;194.360,1074.58598726115;1212.35668789809;1237.96178343949" - ; "1931-03-15,172.360;172.360;187.720,1104.87179487179;1104.87179487179;1203.33333333333" - ; "1931-04-15,143.610;151.190;171.070,926.516129032258;975.41935483871;1103.67741935484" - ; "1931-05-15,128.460;128.460;154.410,839.607843137255;839.607843137255;1009.21568627451" - ; "1931-06-15,121.700;150.180;154.040,805.960264900662;994.569536423841;1020.13245033113" - ; "1931-07-15,135.390;135.390;155.260,896.622516556291;896.622516556291;1028.2119205298" - ; "1931-08-15,133.770;139.410;144.150,885.894039735099;923.245033112583;954.635761589404" - ; "1931-09-15,96.610;96.610;140.130,644.066666666667;644.066666666667;934.2" - ; "1931-10-15,86.480;103.970;108.880,580.402684563758;697.785234899329;730.738255033557" - ; "1931-11-15,91.550;93.870;116.790,622.789115646259;638.571428571429;794.489795918367" - ; "1931-12-15,73.790;77.900;91.170,505.41095890411;533.561643835617;624.452054794521" - ; "1932-01-15,71.240;76.550;85.880,498.181818181818;535.314685314685;600.55944055944" - ; "1932-02-15,71.800;81.440;85.980,509.219858156028;577.58865248227;609.787234042553" - ; "1932-03-15,73.280;73.280;88.780,523.428571428571;523.428571428571;634.142857142857" - ; "1932-04-15,55.930;55.930;72.180,402.374100719424;402.374100719424;519.280575539568" - ; "1932-05-15,44.740;44.740;59.010,326.569343065693;326.569343065693;430.729927007299" - ; "1932-06-15,42.840;42.840;50.620,315;315;372.205882352941" - ; "1932-07-15,41.220;53.890;53.890,303.088235294118;396.25;396.25" - ; "1932-08-15,53.160;73.160;75.220,393.777777777778;541.925925925926;557.185185185185" - ; "1932-09-15,65.060;71.560;79.930,485.522388059701;534.029850746269;596.492537313433" - ; "1932-10-15,58.470;61.900;71.210,439.624060150376;465.413533834586;535.413533834586" - ; "1932-11-15,56.350;56.350;68.030,426.893939393939;426.893939393939;515.378787878788" - ; "1932-12-15,55.910;60.260;61.930,426.793893129771;460;472.748091603053" - ; "1933-01-15,59.290;60.900;64.350,459.612403100775;472.093023255814;498.837209302326" - ; "1933-02-15,50.160;51.390;60.090,394.96062992126;404.645669291339;473.149606299213" - ; "1933-03-15,52.540;55.400;62.950,416.984126984127;439.68253968254;499.603174603175" - ; "1933-04-15,55.690;73.100;73.690,441.984126984127;580.15873015873;584.84126984127" - ; "1933-05-15,76.630;88.110;90.020,608.174603174603;699.285714285714;714.444444444444" - ; "1933-06-15,88.870;98.140;98.740,699.763779527559;772.755905511811;777.48031496063" - ; "1933-07-15,88.710;90.770;108.670,677.175572519084;692.900763358779;829.541984732824" - ; "1933-08-15,92.550;102.410;105.070,701.136363636364;775.833333333333;795.984848484848" - ; "1933-09-15,93.180;94.240;105.740,705.909090909091;713.939393939394;801.060606060606" - ; "1933-10-15,84.380;88.160;99.720,639.242424242424;667.878787878788;755.454545454545" - ; "1933-11-15,89.620;98.140;101.280,678.939393939394;743.484848484848;767.272727272727" - ; "1933-12-15,95.280;98.670;102.040,721.818181818182;747.5;773.030303030303" - ; "1934-01-15,96.730;107.220;108.990,732.80303030303;812.272727272727;825.681818181818" - ; "1934-02-15,103.120;103.460;110.740,775.338345864662;777.894736842105;832.631578947368" - ; "1934-03-15,98.760;100.310;105.790,742.556390977444;754.210526315789;795.413533834586" - ; "1934-04-15,100.490;100.490;106.550,755.563909774436;755.563909774436;801.127819548872" - ; "1934-05-15,91.810;94.000;100.620,690.300751879699;706.766917293233;756.541353383459" - ; "1934-06-15,91.790;95.750;100.420,685;714.55223880597;749.402985074627" - ; "1934-07-15,85.510;88.050;98.820,638.134328358209;657.089552238806;737.462686567164" - ; "1934-08-15,87.470;92.860;95.480,652.761194029851;692.985074626866;712.537313432836" - ; "1934-09-15,86.690;92.490;93.650,637.426470588235;680.073529411765;688.602941176471" - ; "1934-10-15,90.410;93.360;95.600,669.703703703704;691.555555555556;708.148148148148" - ; "1934-11-15,93.460;102.940;103.080,692.296296296296;762.518518518518;763.555555555556" - ; "1934-12-15,99.590;104.040;104.040,743.208955223881;776.417910447761;776.417910447761" - ; "1935-01-15,100.490;101.690;105.880,738.897058823529;747.720588235294;778.529411764706" - ; "1935-02-15,100.230;102.380;107.170,731.605839416059;747.299270072993;782.262773722628" - ; "1935-03-15,96.710;100.780;103.270,705.912408759124;735.620437956204;753.795620437956" - ; "1935-04-15,100.390;109.450;110.470,727.463768115942;793.115942028986;800.507246376812" - ; "1935-05-15,108.710;110.640;116.810,787.753623188406;801.739130434783;846.449275362319" - ; "1935-06-15,111.450;118.360;120.040,813.503649635037;863.941605839416;876.204379562044" - ; "1935-07-15,118.690;126.230;126.560,866.350364963504;921.386861313869;923.795620437956" - ; "1935-08-15,124.930;127.350;128.990,911.897810218978;929.562043795621;941.532846715329" - ; "1935-09-15,127.270;131.920;134.110,928.978102189781;962.919708029197;978.905109489051" - ; "1935-10-15,128.060;139.740;140.780,934.744525547445;1020;1027.59124087591" - ; "1935-11-15,141.070;142.340;148.440,1022.24637681159;1031.44927536232;1075.65217391304" - ; "1935-12-15,138.940;144.130;144.130,1006.8115942029;1044.42028985507;1044.42028985507" - ; "1936-01-15,143.110;149.490;149.490,1037.02898550725;1083.26086956522;1083.26086956522" - ; "1936-02-15,149.810;152.530;154.430,1085.57971014493;1105.28985507246;1119.05797101449" - ; "1936-03-15,150.420;156.340;158.750,1097.95620437956;1141.16788321168;1158.75912408759" - ; "1936-04-15,143.650;145.670;161.990,1048.5401459854;1063.28467153285;1182.40875912409" - ; "1936-05-15,146.700;152.640;152.640,1070.80291970803;1114.16058394161;1114.16058394161" - ; "1936-06-15,149.260;157.690;160.660,1081.59420289855;1142.68115942029;1164.20289855072" - ; "1936-07-15,155.600;164.860;167.010,1119.42446043165;1186.04316546763;1201.51079136691" - ; "1936-08-15,160.800;166.290;169.050,1148.57142857143;1187.78571428571;1207.5" - ; "1936-09-15,165.160;167.820;169.550,1179.71428571429;1198.71428571429;1211.07142857143" - ; "1936-10-15,168.260;177.150;177.420,1201.85714285714;1265.35714285714;1267.28571428571" - ; "1936-11-15,176.670;183.220;184.900,1261.92857142857;1308.71428571429;1320.71428571429" - ; "1936-12-15,175.850;179.900;182.180,1256.07142857143;1285;1301.28571428571" - ; "1937-01-15,177.720;184.740;186.900,1260.42553191489;1310.21276595745;1325.53191489362" - ; "1937-02-15,186.010;187.170;190.290,1319.21985815603;1327.44680851064;1349.57446808511" - ; "1937-03-15,179.820;186.410;194.400,1266.33802816901;1312.74647887324;1369.01408450704" - ; "1937-04-15,170.130;174.270;185.190,1189.72027972028;1218.67132867133;1295.03496503496" - ; "1937-05-15,167.460;174.710;176.300,1162.91666666667;1213.26388888889;1224.30555555556" - ; "1937-06-15,165.510;169.320;175.140,1149.375;1175.83333333333;1216.25" - ; "1937-07-15,170.130;184.010;184.420,1173.31034482759;1269.03448275862;1271.86206896552" - ; "1937-08-15,175.910;177.410;189.340,1213.1724137931;1223.51724137931;1305.79310344828" - ; "1937-09-15,147.380;154.570;173.080,1009.45205479452;1058.69863013699;1185.47945205479" - ; "1937-10-15,125.730;138.480;153.890,861.164383561644;948.493150684931;1054.04109589041" - ; "1937-11-15,113.640;123.480;135.940,783.724137931035;851.586206896552;937.51724137931" - ; "1937-12-15,118.930;120.850;129.980,825.902777777778;839.236111111111;902.638888888889" - ; "1938-01-15,120.570;121.870;134.350,849.084507042254;858.239436619718;946.12676056338" - ; "1938-02-15,118.490;129.640;132.410,840.354609929078;919.432624113475;939.078014184397" - ; "1938-03-15,98.950;98.950;130.470,701.77304964539;701.77304964539;925.31914893617" - ; "1938-04-15,103.020;111.660;118.990,725.492957746479;786.338028169014;837.957746478873" - ; "1938-05-15,107.740;107.740;119.430,764.113475177305;764.113475177305;847.021276595745" - ; "1938-06-15,109.710;133.880;135.870,778.085106382979;949.503546099291;963.617021276596" - ; "1938-07-15,134.560;141.200;144.910,954.326241134752;1001.41843971631;1027.7304964539" - ; "1938-08-15,136.510;139.270;144.470,968.156028368794;987.730496453901;1024.60992907801" - ; "1938-09-15,129.910;141.450;143.080,921.347517730497;1003.1914893617;1014.75177304965" - ; "1938-10-15,144.230;151.730;154.170,1030.21428571429;1083.78571428571;1101.21428571429" - ; "1938-11-15,146.140;149.820;158.080,1043.85714285714;1070.14285714286;1129.14285714286" - ; "1938-12-15,147.390;154.360;154.360,1052.78571428571;1102.57142857143;1102.57142857143" - ; "1939-01-15,136.420;143.760;154.850,974.428571428571;1026.85714285714;1106.07142857143" - ; "1939-02-15,142.430;147.300;147.300,1024.67625899281;1059.71223021583;1059.71223021583" - ; "1939-03-15,131.840;131.840;152.280,948.489208633093;948.489208633093;1095.53956834532" - ; "1939-04-15,123.750;128.380;132.250,896.739130434783;930.289855072464;958.333333333333" - ; "1939-05-15,127.830;138.180;138.180,926.304347826087;1001.30434782609;1001.30434782609" - ; "1939-06-15,130.050;130.630;140.090,942.391304347826;946.594202898551;1015.14492753623" - ; "1939-07-15,131.930;143.260;144.510,956.014492753623;1038.11594202899;1047.17391304348" - ; "1939-08-15,131.330;134.410;144.260,951.666666666667;973.985507246377;1045.36231884058" - ; "1939-09-15,135.250;150.160;155.920,959.219858156028;1064.96453900709;1105.81560283688" - ; "1939-10-15,149.890;151.880;155.480,1070.64285714286;1084.85714285714;1110.57142857143" - ; "1939-11-15,145.690;145.690;152.640,1040.64285714286;1040.64285714286;1090.28571428571" - ; "1939-12-15,146.340;149.990;149.990,1045.28571428571;1071.35714285714;1071.35714285714" - ; "1940-01-15,144.650;145.330;152.800,1040.64748201439;1045.53956834532;1099.28057553957" - ; "1940-02-15,145.000;146.540;148.940,1035.71428571429;1046.71428571429;1063.85714285714" - ; "1940-03-15,145.590;147.540;148.370,1039.92857142857;1053.85714285714;1059.78571428571" - ; "1940-04-15,146.800;148.430;151.290,1048.57142857143;1060.21428571429;1080.64285714286" - ; "1940-05-15,113.940;116.220;148.170,813.857142857143;830.142857142857;1058.35714285714" - ; "1940-06-15,111.840;122.060;123.860,793.191489361702;865.673758865248;878.439716312057" - ; "1940-07-15,120.960;126.140;126.140,864;901;901" - ; "1940-08-15,121.280;128.880;128.880,866.285714285714;920.571428571428;920.571428571428" - ; "1940-09-15,127.740;132.640;135.100,912.428571428571;947.428571428571;965" - ; "1940-10-15,130.390;134.610;135.090,931.357142857143;961.5;964.928571428571" - ; "1940-11-15,129.780;130.030;137.750,927;928.785714285714;983.928571428571" - ; "1940-12-15,128.410;131.130;132.350,910.709219858156;930;938.652482269504" - ; "1941-01-15,124.050;124.130;133.590,879.787234042553;880.354609929078;947.446808510638" - ; "1941-02-15,117.660;121.970;124.760,834.468085106383;865.035460992908;884.822695035461" - ; "1941-03-15,120.300;122.720;123.920,847.183098591549;864.225352112676;872.676056338028" - ; "1941-04-15,115.540;115.540;124.650,807.972027972028;807.972027972028;871.678321678322" - ; "1941-05-15,115.300;116.230;117.820,800.694444444444;807.152777777778;818.194444444444" - ; "1941-06-15,116.180;123.140;123.970,790.340136054422;837.687074829932;843.333333333333" - ; "1941-07-15,122.850;128.790;130.060,835.714285714286;876.122448979592;884.761904761905" - ; "1941-08-15,124.900;127.430;128.220,838.255033557047;855.234899328859;860.536912751678" - ; "1941-09-15,125.810;126.820;129.320,833.17880794702;839.867549668874;856.423841059603" - ; "1941-10-15,117.820;117.820;126.850,770.065359477124;770.065359477124;829.084967320261" - ; "1941-11-15,114.660;114.660;119.850,744.545454545455;744.545454545455;778.246753246753" - ; "1941-12-15,106.340;110.960;116.650,686.064516129032;715.870967741935;752.58064516129" - ; "1942-01-15,108.940;109.410;114.220,693.885350318471;696.87898089172;727.515923566879" - ; "1942-02-15,105.100;106.580;110.800,665.189873417722;674.556962025316;701.26582278481" - ; "1942-03-15,99.210;99.530;106.970,620.0625;622.0625;668.5625" - ; "1942-04-15,92.920;95.350;102.500,577.142857142857;592.23602484472;636.645962732919" - ; "1942-05-15,95.830;100.880;101.090,587.914110429448;618.895705521472;620.184049079755" - ; "1942-06-15,101.300;103.340;106.290,621.472392638037;633.98773006135;652.085889570552" - ; "1942-07-15,102.690;105.720;108.910,626.158536585366;644.634146341464;664.085365853659" - ; "1942-08-15,104.800;106.330;107.550,635.151515151515;644.424242424242;651.818181818182" - ; "1942-09-15,106.030;109.110;109.560,642.606060606061;661.272727272727;664" - ; "1942-10-15,109.650;113.500;115.290,656.586826347306;679.640718562874;690.359281437126" - ; "1942-11-15,114.100;114.500;117.300,679.166666666667;681.547619047619;698.214285714286" - ; "1942-12-15,114.610;119.400;119.560,678.165680473373;706.508875739645;707.455621301775" - ; "1943-01-15,119.260;125.410;125.410,705.680473372781;742.07100591716;742.07100591716" - ; "1943-02-15,125.070;129.710;130.040,740.059171597633;767.514792899408;769.467455621302" - ; "1943-03-15,128.600;136.570;136.820,747.674418604651;794.011627906977;795.46511627907" - ; "1943-04-15,131.180;135.480;136.930,753.908045977012;778.620689655172;786.954022988506" - ; "1943-05-15,136.820;141.180;141.180,781.828571428571;806.742857142857;806.742857142857" - ; "1943-06-15,138.790;143.380;143.380,793.085714285714;819.314285714286;819.314285714286" - ; "1943-07-15,137.250;137.250;145.820,788.793103448276;788.793103448276;838.045977011494" - ; "1943-08-15,134.000;136.620;138.450,774.566473988439;789.71098265896;800.28901734104" - ; "1943-09-15,136.910;140.120;141.750,786.83908045977;805.287356321839;814.655172413793" - ; "1943-10-15,136.390;138.290;140.330,783.850574712644;794.770114942529;806.494252873563" - ; "1943-11-15,129.570;129.570;138.500,744.655172413793;744.655172413793;795.977011494253" - ; "1943-12-15,130.680;135.890;136.240,751.034482758621;780.977011494253;782.988505747127" - ; "1944-01-15,135.920;137.400;138.650,781.149425287356;789.655172413793;796.83908045977" - ; "1944-02-15,134.220;136.300;137.450,771.379310344828;783.333333333333;789.942528735632" - ; "1944-03-15,136.440;138.840;141.000,784.137931034483;797.931034482759;810.344827586207" - ; "1944-04-15,135.000;136.210;139.110,771.428571428571;778.342857142857;794.914285714286" - ; "1944-05-15,137.060;142.240;142.240,783.2;812.8;812.8" - ; "1944-06-15,141.620;148.380;148.630,804.659090909091;843.068181818182;844.488636363636" - ; "1944-07-15,145.770;146.110;150.500,823.559322033898;825.480225988701;850.282485875706" - ; "1944-08-15,144.900;146.990;148.960,818.64406779661;830.45197740113;841.581920903955" - ; "1944-09-15,142.960;146.310;147.160,807.683615819209;826.610169491525;831.412429378531" - ; "1944-10-15,145.830;146.530;148.870,823.898305084746;827.853107344633;841.073446327684" - ; "1944-11-15,145.600;147.330;148.080,822.598870056497;832.372881355932;836.610169491526" - ; "1944-12-15,147.300;151.930;152.280,827.52808988764;853.539325842697;855.505617977528" - ; "1945-01-15,151.350;153.670;155.850,850.280898876404;863.314606741573;875.561797752809" - ; "1945-02-15,153.790;160.400;160.400,863.988764044944;901.123595505618;901.123595505618" - ; "1945-03-15,152.270;154.060;161.520,855.449438202247;865.505617977528;907.415730337079" - ; "1945-04-15,154.990;165.440;165.440,870.730337078652;929.438202247191;929.438202247191" - ; "1945-05-15,163.090;168.300;169.080,911.117318435754;940.223463687151;944.581005586592" - ; "1945-06-15,164.570;164.570;168.920,909.226519337016;909.226519337016;933.259668508287" - ; "1945-07-15,160.910;162.880;167.090,889.005524861878;899.889502762431;923.149171270718" - ; "1945-08-15,161.550;174.290;174.290,892.541436464088;962.92817679558;962.92817679558" - ; "1945-09-15,173.900;180.110;180.220,960.773480662983;995.082872928177;995.690607734806" - ; "1945-10-15,183.060;186.600;187.060,1011.38121546961;1030.93922651934;1033.48066298343" - ; "1945-11-15,187.820;191.460;192.130,1037.67955801105;1057.79005524862;1061.49171270718" - ; "1945-12-15,189.070;192.910;195.820,1038.84615384615;1059.94505494506;1075.93406593407" - ; "1946-01-15,190.900;204.670;205.350,1048.9010989011;1124.56043956044;1128.2967032967" - ; "1946-02-15,186.020;190.090;206.610,1027.73480662983;1050.22099447514;1141.49171270718" - ; "1946-03-15,188.460;199.560;200.560,1029.83606557377;1090.49180327869;1095.95628415301" - ; "1946-04-15,199.190;206.770;208.310,1082.55434782609;1123.75;1132.11956521739" - ; "1946-05-15,200.650;212.280;212.500,1084.59459459459;1147.45945945946;1148.64864864865" - ; "1946-06-15,200.520;205.620;211.470,1072.29946524064;1099.57219251337;1130.85561497326" - ; "1946-07-15,195.220;201.560;207.560,985.959595959596;1017.9797979798;1048.28282828283" - ; "1946-08-15,189.190;189.190;204.520,936.584158415842;936.584158415842;1012.47524752475" - ; "1946-09-15,165.170;172.420;181.180,809.656862745098;845.196078431373;888.137254901961" - ; "1946-10-15,163.120;169.150;175.940,784.230769230769;813.221153846154;845.865384615385" - ; "1946-11-15,163.550;169.780;174.400,767.840375586855;797.089201877934;818.779342723005" - ; "1946-12-15,167.500;177.200;177.850,779.06976744186;824.186046511628;827.209302325581" - ; "1947-01-15,171.950;180.440;180.440,799.767441860465;839.255813953488;839.255813953488" - ; "1947-02-15,177.220;178.900;184.060,824.279069767442;832.093023255814;856.093023255814" - ; "1947-03-15,172.580;177.200;181.880,788.036529680365;809.132420091324;830.502283105023" - ; "1947-04-15,166.690;170.640;177.450,761.141552511416;779.178082191781;810.27397260274" - ; "1947-05-15,163.550;169.250;174.210,746.803652968037;772.831050228311;795.479452054795" - ; "1947-06-15,168.000;177.300;177.440,763.636363636364;805.909090909091;806.545454545455" - ; "1947-07-15,179.880;183.180;186.850,810.27027027027;825.135135135135;841.666666666667" - ; "1947-08-15,177.570;178.850;183.810,789.2;794.888888888889;816.933333333333" - ; "1947-09-15,174.860;177.490;179.810,760.260869565217;771.695652173913;781.782608695652" - ; "1947-10-15,178.100;181.810;185.290,774.347826086957;790.478260869565;805.608695652174" - ; "1947-11-15,179.510;179.510;183.170,777.099567099567;777.099567099567;792.943722943723" - ; "1947-12-15,176.100;181.160;181.160,752.564102564103;774.188034188034;774.188034188034" - ; "1948-01-15,171.180;174.760;181.040,722.278481012658;737.383966244726;763.881856540084" - ; "1948-02-15,165.650;166.800;174.920,704.893617021277;709.787234042553;744.340425531915" - ; "1948-03-15,165.390;177.200;177.200,706.794871794872;757.264957264957;757.264957264957" - ; "1948-04-15,177.320;180.510;183.780,745.042016806723;758.44537815126;772.18487394958" - ; "1948-05-15,180.940;190.740;191.060,757.071129707113;798.075313807531;799.414225941423" - ; "1948-06-15,187.900;189.460;193.160,779.668049792531;786.141078838174;801.49377593361" - ; "1948-07-15,181.200;181.330;191.620,742.622950819672;743.155737704918;785.327868852459" - ; "1948-08-15,179.270;181.710;183.600,731.714285714286;741.673469387755;749.387755102041" - ; "1948-09-15,175.990;178.300;185.360,718.326530612245;727.755102040816;756.571428571429" - ; "1948-10-15,179.870;188.280;189.760,737.172131147541;771.639344262295;777.704918032787" - ; "1948-11-15,171.200;171.200;189.760,707.438016528926;707.438016528926;784.132231404959" - ; "1948-12-15,173.220;177.300;177.920,718.755186721992;735.684647302905;738.257261410788" - ; "1949-01-15,175.030;179.120;181.430,729.291666666667;746.333333333333;755.958333333333" - ; "1949-02-15,171.100;173.060;180.390,718.90756302521;727.142857142857;757.941176470588" - ; "1949-03-15,173.660;177.100;178.450,729.663865546218;744.117647058823;749.789915966387" - ; "1949-04-15,173.240;174.060;177.160,724.853556485356;728.284518828452;741.255230125523" - ; "1949-05-15,168.360;168.360;176.630,707.394957983193;707.394957983193;742.142857142857" - ; "1949-06-15,161.600;167.420;168.150,676.150627615063;700.502092050209;703.556485355649" - ; "1949-07-15,168.080;175.920;176.460,709.198312236287;742.278481012658;744.556962025317" - ; "1949-08-15,176.840;178.660;182.020,743.025210084034;750.672268907563;764.789915966387" - ; "1949-09-15,178.040;182.510;183.290,744.937238493724;763.640167364017;766.903765690377" - ; "1949-10-15,182.670;189.540;190.360,770.759493670886;799.746835443038;803.206751054852" - ; "1949-11-15,187.980;191.550;193.520,789.831932773109;804.831932773109;813.109243697479" - ; "1949-12-15,192.710;200.520;200.520,816.567796610169;849.661016949153;849.661016949153" - ; "1950-01-15,196.810;201.790;201.980,837.489361702128;858.68085106383;859.489361702128" - ; "1950-02-15,201.690;203.440;204.590,858.255319148936;865.702127659575;870.595744680851" - ; "1950-03-15,202.330;206.050;209.780,857.330508474576;873.093220338983;888.898305084746" - ; "1950-04-15,208.440;213.560;215.310,883.220338983051;904.915254237288;912.330508474576" - ; "1950-05-15,214.870;223.420;223.420,906.62447257384;942.700421940928;942.700421940928" - ; "1950-06-15,206.720;209.110;228.380,868.571428571429;878.613445378151;959.579831932773" - ; "1950-07-15,197.460;209.400;210.850,819.336099585062;868.879668049792;874.896265560166" - ; "1950-08-15,211.260;216.870;221.510,869.382716049383;892.469135802469;911.56378600823" - ; "1950-09-15,218.100;226.360;226.780,893.852459016393;927.704918032787;929.426229508197" - ; "1950-10-15,225.010;225.010;231.740,914.674796747967;914.674796747967;942.032520325203" - ; "1950-11-15,222.520;227.600;235.470,900.890688259109;921.457489878542;953.31983805668" - ; "1950-12-15,222.330;235.420;235.420,889.32;941.68;941.68" - ; "1951-01-15,238.990;248.830;249.580,940.905511811024;979.645669291339;982.59842519685" - ; "1951-02-15,250.760;252.050;255.710,975.719844357977;980.739299610895;994.980544747082" - ; "1951-03-15,243.950;248.530;253.610,945.542635658915;963.294573643411;982.984496124031" - ; "1951-04-15,246.020;259.130;259.130,953.565891472868;1004.37984496124;1004.37984496124" - ; "1951-05-15,245.270;249.650;263.130,946.988416988417;963.899613899614;1015.94594594595" - ; "1951-06-15,242.640;242.640;254.030,936.833976833977;936.833976833977;980.810810810811" - ; "1951-07-15,243.980;257.860;260.700,942.007722007722;995.598455598456;1006.56370656371" - ; "1951-08-15,259.890;270.250;270.250,1003.43629343629;1043.43629343629;1043.43629343629" - ; "1951-09-15,270.630;271.160;276.370,1036.89655172414;1038.92720306513;1058.88888888889" - ; "1951-10-15,260.430;262.350;275.870,994.007633587786;1001.3358778626;1052.93893129771" - ; "1951-11-15,256.950;261.270;264.060,973.295454545455;989.659090909091;1000.22727272727" - ; "1951-12-15,263.240;269.230;269.230,993.358490566038;1015.96226415094;1015.96226415094" - ; "1952-01-15,268.080;270.690;275.400,1011.62264150943;1021.47169811321;1039.24528301887" - ; "1952-02-15,258.490;260.080;271.680,982.851711026616;988.897338403042;1033.00380228137" - ; "1952-03-15,260.080;269.460;269.460,988.897338403042;1024.56273764259;1024.56273764259" - ; "1952-04-15,257.630;257.630;267.220,975.871212121212;975.871212121212;1012.19696969697" - ; "1952-05-15,256.350;262.940;264.220,971.022727272727;995.984848484848;1000.83333333333" - ; "1952-06-15,262.090;274.260;274.260,989.018867924528;1034.94339622641;1034.94339622641" - ; "1952-07-15,272.580;279.560;279.560,1020.89887640449;1047.04119850187;1047.04119850187" - ; "1952-08-15,273.170;275.040;280.290,1023.10861423221;1030.11235955056;1049.77528089888" - ; "1952-09-15,268.380;270.610;277.150,1005.16853932584;1013.52059925094;1038.01498127341" - ; "1952-10-15,263.060;269.230;271.400,985.243445692884;1008.35205992509;1016.47940074906" - ; "1952-11-15,270.230;283.660;283.660,1012.09737827715;1062.39700374532;1062.39700374532" - ; "1952-12-15,281.630;291.900;292.000,1054.79400749064;1093.25842696629;1093.6329588015" - ; "1953-01-15,285.240;289.770;293.790,1072.33082706767;1089.36090225564;1104.47368421053" - ; "1953-02-15,281.140;284.270;290.190,1060.90566037736;1072.71698113208;1095.05660377358" - ; "1953-03-15,279.870;279.870;290.640,1052.14285714286;1052.14285714286;1092.63157894737" - ; "1953-04-15,270.730;274.750;280.090,1017.78195488722;1032.89473684211;1052.96992481203" - ; "1953-05-15,271.480;272.280;278.790,1016.77902621723;1019.77528089888;1044.15730337079" - ; "1953-06-15,262.880;268.260;269.840,980.89552238806;1000.97014925373;1006.86567164179" - ; "1953-07-15,268.060;275.380;275.380,1000.22388059701;1027.53731343284;1027.53731343284" - ; "1953-08-15,261.220;261.220;276.740,971.078066914498;971.078066914498;1028.77323420074" - ; "1953-09-15,255.490;264.040;265.480,949.776951672863;981.561338289963;986.914498141264" - ; "1953-10-15,264.260;275.810;276.310,978.740740740741;1021.51851851852;1023.37037037037" - ; "1953-11-15,273.880;281.370;281.370,1018.14126394052;1045.98513011152;1045.98513011152" - ; "1953-12-15,278.300;280.900;283.540,1034.57249070632;1044.23791821561;1054.05204460967" - ; "1954-01-15,279.870;292.390;292.850,1040.40892193309;1086.95167286245;1088.66171003717" - ; "1954-02-15,289.540;294.540;294.540,1076.35687732342;1094.94423791822;1094.94423791822" - ; "1954-03-15,296.400;303.510;303.510,1101.85873605948;1128.28996282528;1128.28996282528" - ; "1954-04-15,304.260;319.330;319.330,1135.29850746269;1191.52985074627;1191.52985074627" - ; "1954-05-15,317.930;327.490;327.490,1181.89591078067;1217.43494423792;1217.43494423792" - ; "1954-06-15,319.270;333.530;336.900,1186.87732342007;1239.88847583643;1252.41635687732" - ; "1954-07-15,334.120;347.920;347.920,1242.08178438662;1293.38289962825;1293.38289962825" - ; "1954-08-15,335.800;335.800;350.380,1248.32713754647;1248.32713754647;1302.52788104089" - ; "1954-09-15,338.130;360.460;363.320,1261.67910447761;1345;1355.67164179104" - ; "1954-10-15,352.140;352.140;364.430,1313.9552238806;1313.9552238806;1359.81343283582" - ; "1954-11-15,353.960;386.770;388.510,1320.74626865672;1443.17164179104;1449.66417910448" - ; "1954-12-15,384.040;404.390;404.390,1438.35205992509;1514.56928838951;1514.56928838951" - ; "1955-01-15,388.200;408.830;408.890,1453.93258426966;1531.19850187266;1531.42322097378" - ; "1955-02-15,405.700;411.870;413.990,1519.47565543071;1542.58426966292;1550.52434456929" - ; "1955-03-15,391.360;409.700;419.680,1465.76779026217;1534.45692883895;1571.83520599251" - ; "1955-04-15,412.970;425.650;430.640,1546.70411985019;1594.19475655431;1612.88389513109" - ; "1955-05-15,414.120;424.860;426.300,1551.01123595506;1591.23595505618;1596.62921348315" - ; "1955-06-15,424.880;451.380;451.380,1591.31086142322;1690.56179775281;1690.56179775281" - ; "1955-07-15,453.820;465.850;468.450,1693.35820895522;1738.24626865672;1747.94776119403" - ; "1955-08-15,448.840;468.180;468.180,1674.77611940298;1746.94029850746;1746.94029850746" - ; "1955-09-15,455.560;466.620;487.450,1693.53159851301;1734.6468401487;1812.08178438662" - ; "1955-10-15,438.590;454.870;461.140,1630.44609665428;1690.96654275093;1714.2750929368" - ; "1955-11-15,454.890;483.260;487.380,1691.04089219331;1796.50557620818;1811.82156133829" - ; "1955-12-15,480.720;488.400;488.400,1793.73134328358;1822.38805970149;1822.38805970149" - ; "1956-01-15,462.350;470.740;485.780,1725.18656716418;1756.49253731343;1812.61194029851" - ; "1956-02-15,465.720;483.650;485.710,1737.76119402985;1804.66417910448;1812.35074626866" - ; "1956-03-15,486.690;511.790;513.030,1816.00746268657;1909.66417910448;1914.29104477612" - ; "1956-04-15,503.020;516.120;521.050,1869.96282527881;1918.66171003717;1936.98884758364" - ; "1956-05-15,468.810;478.050;516.440,1736.33333333333;1770.55555555556;1912.74074074074" - ; "1956-06-15,475.290;492.780;492.780,1747.38970588235;1811.69117647059;1811.69117647059" - ; "1956-07-15,491.920;517.810;517.810,1795.32846715328;1889.81751824818;1889.81751824818" - ; "1956-08-15,495.960;502.040;520.950,1816.7032967033;1838.97435897436;1908.24175824176" - ; "1956-09-15,475.250;475.250;509.820,1734.48905109489;1734.48905109489;1860.65693430657" - ; "1956-10-15,468.700;479.850;490.190,1704.36363636364;1744.90909090909;1782.50909090909" - ; "1956-11-15,466.100;472.780;495.370,1694.90909090909;1719.2;1801.34545454545" - ; "1956-12-15,480.610;499.470;499.470,1741.34057971014;1809.67391304348;1809.67391304348" - ; "1957-01-15,474.590;479.160;499.200,1719.52898550725;1736.08695652174;1808.69565217391" - ; "1957-02-15,454.820;464.620;477.220,1641.94945848375;1677.3285198556;1722.81588447653" - ; "1957-03-15,468.910;474.810;475.010,1686.72661870504;1707.94964028777;1708.6690647482" - ; "1957-04-15,474.980;494.360;494.360,1702.43727598566;1771.89964157706;1771.89964157706" - ; "1957-05-15,494.680;504.930;506.040,1766.71428571429;1803.32142857143;1807.28571428571" - ; "1957-06-15,497.080;503.290;513.190,1768.96797153025;1791.06761565836;1826.29893238434" - ; "1957-07-15,503.290;508.520;520.770,1778.40989399293;1796.89045936396;1840.17667844523" - ; "1957-08-15,470.140;484.350;506.210,1661.27208480565;1711.48409893993;1788.72791519435" - ; "1957-09-15,456.300;456.300;486.130,1612.36749116608;1612.36749116608;1717.77385159011" - ; "1957-10-15,419.790;441.040;465.820,1483.35689045936;1558.44522968198;1646.00706713781" - ; "1957-11-15,427.940;449.870;449.870,1506.83098591549;1584.04929577465;1584.04929577465" - ; "1957-12-15,425.650;435.690;449.550,1498.7676056338;1534.11971830986;1582.92253521127" - ; "1958-01-15,438.680;450.020;451.160,1533.84615384615;1573.4965034965;1577.48251748252" - ; "1958-02-15,436.890;439.920;458.650,1527.58741258741;1538.18181818182;1603.67132867133" - ; "1958-03-15,443.380;446.760;455.920,1539.51388888889;1551.25;1583.05555555556" - ; "1958-04-15,440.090;455.860;455.860,1522.80276816609;1577.37024221453;1577.37024221453" - ; "1958-05-15,455.450;462.700;463.670,1575.95155709343;1601.03806228374;1604.39446366782" - ; "1958-06-15,466.110;478.180;478.970,1612.83737024221;1654.60207612457;1657.33564013841" - ; "1958-07-15,476.890;502.990;504.370,1644.44827586207;1734.44827586207;1739.20689655172" - ; "1958-08-15,502.670;508.630;512.420,1739.34256055363;1759.96539792388;1773.07958477509" - ; "1958-09-15,511.770;532.090;532.090,1770.83044982699;1841.14186851211;1841.14186851211" - ; "1958-10-15,530.940;543.220;546.360,1837.16262975779;1879.65397923875;1890.51903114187" - ; "1958-11-15,540.520;557.460;567.440,1863.86206896552;1922.27586206897;1956.68965517241" - ; "1958-12-15,556.080;583.650;583.650,1924.15224913495;2019.55017301038;2019.55017301038" - ; "1959-01-15,583.150;593.960;597.660,2010.86206896552;2048.13793103448;2060.89655172414" - ; "1959-02-15,574.460;603.500;603.500,1987.7508650519;2088.23529411765;2088.23529411765" - ; "1959-03-15,601.710;601.710;614.690,2082.04152249135;2082.04152249135;2126.95501730104" - ; "1959-04-15,602.940;623.750;629.870,2079.10344827586;2150.86206896552;2171.96551724138" - ; "1959-05-15,615.640;643.790;643.790,2122.89655172414;2219.96551724138;2219.96551724138" - ; "1959-06-15,617.620;643.600;643.600,2122.40549828179;2211.68384879725;2211.68384879725" - ; "1959-07-15,650.880;674.880;674.880,2229.04109589041;2311.23287671233;2311.23287671233" - ; "1959-08-15,646.530;664.410;678.100,2214.14383561644;2275.37671232877;2322.2602739726" - ; "1959-09-15,616.450;631.680;655.900,2103.92491467577;2155.90443686007;2238.56655290102" - ; "1959-10-15,625.590;646.600;646.600,2127.85714285714;2199.31972789116;2199.31972789116" - ; "1959-11-15,634.460;659.180;659.180,2158.02721088435;2242.10884353741;2242.10884353741" - ; "1959-12-15,661.290;679.360;679.360,2249.28571428571;2310.74829931973;2310.74829931973" - ; "1960-01-15,622.620;622.620;685.470,2124.98293515358;2124.98293515358;2339.48805460751" - ; "1960-02-15,611.330;630.120;636.920,2079.3537414966;2143.26530612245;2166.39455782313" - ; "1960-03-15,599.100;616.590;626.870,2037.75510204082;2097.24489795918;2132.21088435374" - ; "1960-04-15,601.700;601.700;630.770,2039.66101694915;2039.66101694915;2138.20338983051" - ; "1960-05-15,599.610;625.500;625.500,2032.57627118644;2120.33898305085;2120.33898305085" - ; "1960-06-15,624.890;640.620;656.420,2111.11486486486;2164.25675675676;2217.63513513513" - ; "1960-07-15,601.680;616.730;646.910,2032.7027027027;2083.5472972973;2185.50675675676" - ; "1960-08-15,608.690;625.990;641.560,2056.38513513513;2114.83108108108;2167.43243243243" - ; "1960-09-15,569.080;580.140;626.100,1922.56756756757;1959.93243243243;2115.2027027027" - ; "1960-10-15,566.050;580.360;596.480,1899.4966442953;1947.51677852349;2001.61073825503" - ; "1960-11-15,585.240;597.220;612.010,1963.89261744966;2004.09395973154;2053.72483221477" - ; "1960-12-15,593.490;615.890;617.780,1991.57718120805;2066.74496644295;2073.08724832215" - ; "1961-01-15,610.250;648.200;650.640,2047.81879194631;2175.1677852349;2183.35570469799" - ; "1961-02-15,637.040;662.080;662.080,2137.71812080537;2221.74496644295;2221.74496644295" - ; "1961-03-15,661.080;676.630;679.380,2218.38926174497;2270.57046979866;2279.79865771812" - ; "1961-04-15,672.660;678.710;696.720,2257.24832214765;2277.55033557047;2337.98657718121" - ; "1961-05-15,677.050;696.720;705.960,2271.97986577181;2337.98657718121;2368.9932885906" - ; "1961-06-15,680.680;683.960;703.790,2284.1610738255;2295.1677852349;2361.71140939597" - ; "1961-07-15,679.300;705.370;705.370,2264.33333333333;2351.23333333333;2351.23333333333" - ; "1961-08-15,710.460;719.940;725.760,2376.12040133779;2407.82608695652;2427.29096989967" - ; "1961-09-15,691.860;701.210;726.530,2306.2;2337.36666666667;2421.76666666667" - ; "1961-10-15,697.240;703.920;708.490,2324.13333333333;2346.4;2361.63333333333" - ; "1961-11-15,703.840;721.600;734.340,2346.13333333333;2405.33333333333;2447.8" - ; "1961-12-15,720.100;731.140;734.910,2400.33333333333;2437.13333333333;2449.7" - ; "1962-01-15,689.920;700.000;726.010,2299.73333333333;2333.33333333333;2420.03333333333" - ; "1962-02-15,702.540;708.050;717.550,2334.01993355482;2352.32558139535;2383.88704318937" - ; "1962-03-15,706.630;706.950;723.540,2347.60797342193;2348.67109634552;2403.78737541528" - ; "1962-04-15,665.330;665.330;705.420,2203.07947019868;2203.07947019868;2335.82781456954" - ; "1962-05-15,576.930;613.360;675.490,1910.3642384106;2030.99337748344;2236.72185430464" - ; "1962-06-15,535.760;561.280;611.050,1774.03973509934;1858.54304635762;2023.34437086093" - ; "1962-07-15,571.240;597.930;597.930,1885.28052805281;1973.36633663366;1973.36633663366" - ; "1962-08-15,588.350;609.180;616.000,1941.74917491749;2010.49504950495;2033.00330033003" - ; "1962-09-15,574.120;578.980;607.630,1888.55263157895;1904.53947368421;1998.78289473684" - ; "1962-10-15,558.060;589.770;589.770,1835.72368421053;1940.03289473684;1940.03289473684" - ; "1962-11-15,597.130;649.300;652.610,1964.24342105263;2135.85526315789;2146.74342105263" - ; "1962-12-15,640.140;652.100;653.990,2105.72368421053;2145.06578947368;2151.28289473684" - ; "1963-01-15,646.790;682.850;683.730,2127.59868421053;2246.21710526316;2249.11184210526" - ; "1963-02-15,662.940;662.940;688.960,2180.72368421053;2180.72368421053;2266.31578947368" - ; "1963-03-15,659.720;682.520;684.730,2163.01639344262;2237.77049180328;2245.01639344262" - ; "1963-04-15,684.270;717.700;718.330,2243.50819672131;2353.11475409836;2355.18032786885" - ; "1963-05-15,712.550;726.960;726.960,2336.22950819672;2383.47540983607;2383.47540983607" - ; "1963-06-15,706.030;706.880;726.870,2307.28758169935;2310.06535947712;2375.39215686275" - ; "1963-07-15,687.710;695.430;716.450,2240.09771986971;2265.24429967427;2333.71335504886" - ; "1963-08-15,694.870;729.320;729.320,2263.42019543974;2375.63517915309;2375.63517915309" - ; "1963-09-15,732.020;732.790;745.960,2384.42996742671;2386.93811074919;2429.83713355049" - ; "1963-10-15,737.940;755.230;760.500,2395.90909090909;2452.04545454545;2469.15584415584" - ; "1963-11-15,711.490;750.520;753.770,2310.03246753247;2436.75324675325;2447.30519480519" - ; "1963-12-15,751.820;762.950;767.210,2433.07443365696;2469.09385113269;2482.88025889968" - ; "1964-01-15,766.080;785.340;787.780,2479.22330097087;2541.55339805825;2549.4498381877" - ; "1964-02-15,783.040;800.140;800.140,2534.11003236246;2589.4498381877;2589.4498381877" - ; "1964-03-15,802.750;813.290;820.250,2597.89644012945;2632.00647249191;2654.53074433657" - ; "1964-04-15,810.770;810.770;827.330,2623.85113268608;2623.85113268608;2677.44336569579" - ; "1964-05-15,817.100;820.560;830.170,2644.33656957929;2655.53398058252;2686.63430420712" - ; "1964-06-15,800.310;831.500;831.500,2581.64516129032;2682.25806451613;2682.25806451613" - ; "1964-07-15,837.350;841.100;851.350,2692.44372990354;2704.50160771704;2737.45980707396" - ; "1964-08-15,823.400;838.480;842.830,2656.12903225806;2704.77419354839;2718.8064516129" - ; "1964-09-15,844.000;875.370;875.740,2713.82636655949;2814.69453376206;2815.88424437299" - ; "1964-10-15,868.440;873.080;881.500,2792.4115755627;2807.33118971061;2834.40514469453" - ; "1964-11-15,870.640;875.430;891.710,2790.51282051282;2805.86538461538;2858.04487179487" - ; "1964-12-15,857.450;874.130;874.130,2748.23717948718;2801.69871794872;2801.69871794872" - ; "1965-01-15,869.780;902.860;902.860,2787.75641025641;2893.78205128205;2893.78205128205" - ; "1965-02-15,881.350;903.480;906.300,2824.83974358974;2895.76923076923;2904.80769230769" - ; "1965-03-15,887.820;889.050;901.910,2836.4856230032;2840.41533546326;2881.50159744409" - ; "1965-04-15,890.330;922.310;922.310,2835.44585987261;2937.29299363057;2937.29299363057" - ; "1965-05-15,913.220;918.040;939.620,2908.34394904459;2923.69426751592;2992.42038216561" - ; "1965-06-15,840.590;868.030;908.530,2660.09493670886;2746.93037974684;2875.09493670886" - ; "1965-07-15,861.770;881.740;883.230,2727.12025316456;2790.3164556962;2795.03164556962" - ; "1965-08-15,878.890;893.100;896.180,2781.29746835443;2826.26582278481;2836.01265822785" - ; "1965-09-15,893.600;930.580;937.880,2827.84810126582;2944.87341772152;2967.9746835443" - ; "1965-10-15,929.650;960.820;960.820,2932.64984227129;3030.97791798107;3030.97791798107" - ; "1965-11-15,946.380;946.710;961.850,2985.42586750789;2986.46687697161;3034.22712933754" - ; "1965-12-15,939.530;969.260;969.260,2954.49685534591;3047.98742138365;3047.98742138365" - ; "1966-01-15,968.540;983.510;994.200,3045.72327044025;3092.79874213836;3126.41509433962" - ; "1966-02-15,950.660;951.890;995.150,2970.8125;2974.65625;3109.84375" - ; "1966-03-15,911.080;924.770;938.190,2838.2554517134;2880.90342679128;2922.71028037383" - ; "1966-04-15,931.290;933.680;954.730,2883.25077399381;2890.65015479876;2955.82043343653" - ; "1966-05-15,864.140;884.070;931.950,2675.3560371517;2737.05882352941;2885.29411764706" - ; "1966-06-15,870.100;870.100;903.170,2685.49382716049;2685.49382716049;2787.56172839506" - ; "1966-07-15,847.380;847.380;894.040,2607.32307692308;2607.32307692308;2750.89230769231" - ; "1966-08-15,767.030;788.410;852.390,2345.65749235474;2411.03975535168;2606.69724770642" - ; "1966-09-15,772.660;774.220;814.300,2362.874617737;2367.64525993884;2490.21406727829" - ; "1966-10-15,744.320;807.070;809.570,2262.37082066869;2453.10030395137;2460.6990881459" - ; "1966-11-15,791.590;791.590;820.870,2406.04863221885;2406.04863221885;2495.04559270517" - ; "1966-12-15,785.690;785.690;820.540,2388.11550151976;2388.11550151976;2494.04255319149" - ; "1967-01-15,786.410;849.890;849.890,2390.30395136778;2583.25227963526;2583.25227963526" - ; "1967-02-15,836.640;839.370;860.970,2542.97872340426;2551.27659574468;2616.93009118541" - ; "1967-03-15,841.760;865.980;876.670,2550.78787878788;2624.18181818182;2656.57575757576" - ; "1967-04-15,842.430;897.050;897.050,2545.10574018127;2710.12084592145;2710.12084592145" - ; "1967-05-15,852.560;852.560;909.630,2567.95180722892;2567.95180722892;2739.84939759036" - ; "1967-06-15,847.770;860.260;886.150,2545.85585585586;2583.36336336336;2661.11111111111" - ; "1967-07-15,859.690;904.240;909.560,2573.92215568862;2707.30538922156;2723.23353293413" - ; "1967-08-15,893.720;901.290;926.720,2667.82089552239;2690.41791044776;2766.32835820896" - ; "1967-09-15,901.180;926.660;943.080,2682.08333333333;2757.91666666667;2806.78571428571" - ; "1967-10-15,879.740;879.740;933.310,2610.50445103858;2610.50445103858;2769.46587537092" - ; "1967-11-15,849.570;875.810;884.880,2513.52071005917;2591.15384615385;2617.98816568047" - ; "1967-12-15,879.160;905.110;905.110,2593.39233038348;2669.94100294985;2669.94100294985" - ; "1968-01-15,855.470;855.470;908.920,2508.70967741935;2508.70967741935;2665.45454545455" - ; "1968-02-15,831.770;840.500;863.560,2432.07602339181;2457.60233918129;2525.02923976608" - ; "1968-03-15,825.130;840.670;843.220,2405.62682215743;2450.93294460641;2458.36734693878" - ; "1968-04-15,861.250;912.220;912.220,2503.63372093023;2651.8023255814;2651.8023255814" - ; "1968-05-15,891.600;899.000;919.900,2584.34782608696;2605.79710144928;2666.3768115942" - ; "1968-06-15,897.800;897.800;917.950,2587.31988472622;2587.31988472622;2645.38904899135" - ; "1968-07-15,883.000;883.000;923.720,2530.08595988539;2530.08595988539;2646.76217765043" - ; "1968-08-15,869.650;896.010;896.130,2484.71428571429;2560.02857142857;2560.37142857143" - ; "1968-09-15,900.360;935.790;938.280,2565.12820512821;2666.06837606838;2673.16239316239" - ; "1968-10-15,942.320;952.390;967.490,2669.46175637394;2697.98866855524;2740.76487252125" - ; "1968-11-15,946.230;985.080;985.080,2672.96610169492;2782.71186440678;2782.71186440678" - ; "1968-12-15,943.750;943.750;985.210,2658.45070422535;2658.45070422535;2775.23943661972" - ; "1969-01-15,921.250;946.050;951.890,2587.7808988764;2657.44382022472;2673.84831460674" - ; "1969-02-15,899.800;905.210;952.700,2513.40782122905;2528.51955307263;2661.17318435754" - ; "1969-03-15,904.030;935.480;935.480,2504.23822714681;2591.35734072022;2591.35734072022" - ; "1969-04-15,917.510;950.180;950.180,2527.57575757576;2617.57575757576;2617.57575757576" - ; "1969-05-15,936.920;937.560;968.850,2573.95604395604;2575.71428571429;2661.67582417582" - ; "1969-06-15,869.760;873.190;933.170,2376.39344262295;2385.7650273224;2549.64480874317" - ; "1969-07-15,801.960;815.470;886.120,2179.23913043478;2215.95108695652;2407.9347826087" - ; "1969-08-15,809.130;836.720;837.250,2186.83783783784;2261.40540540541;2262.83783783784" - ; "1969-09-15,811.840;813.090;837.780,2188.24797843666;2191.61725067385;2258.16711590296" - ; "1969-10-15,802.200;855.990;862.260,2150.67024128686;2294.87935656836;2311.6890080429" - ; "1969-11-15,807.290;812.300;863.050,2152.77333333333;2166.13333333333;2301.46666666667" - ; "1969-12-15,769.930;800.360;805.040,2042.25464190981;2122.97082228117;2135.38461538462" - ; "1970-01-15,744.060;744.060;811.310,1968.4126984127;1968.4126984127;2146.32275132275" - ; "1970-02-15,746.440;777.590;777.590,1964.31578947368;2046.28947368421;2046.28947368421" - ; "1970-03-15,763.600;785.570;791.050,1998.95287958115;2056.46596858639;2070.81151832461" - ; "1970-04-15,724.330;736.070;792.500,1881.37662337662;1911.87012987013;2058.44155844156" - ; "1970-05-15,631.160;700.440;733.630,1635.12953367876;1814.61139896373;1900.59585492228" - ; "1970-06-15,682.910;683.530;720.430,1760.07731958763;1761.67525773196;1856.77835051546" - ; "1970-07-15,669.360;734.120;735.560,1716.30769230769;1882.35897435897;1886.05128205128" - ; "1970-08-15,707.350;764.580;765.810,1813.71794871795;1960.46153846154;1963.61538461538" - ; "1970-09-15,747.470;760.680;773.140,1906.8112244898;1940.51020408163;1972.29591836735" - ; "1970-10-15,753.560;755.610;783.680,1912.58883248731;1917.79187817259;1989.03553299492" - ; "1970-11-15,754.240;794.090;794.090,1904.64646464646;2005.27777777778;2005.27777777778" - ; "1970-12-15,794.290;838.920;842.000,1995.70351758794;2107.8391959799;2115.57788944724" - ; "1971-01-15,830.570;868.500;868.500,2086.85929648241;2182.1608040201;2182.1608040201" - ; "1971-02-15,868.980;878.830;890.060,2177.89473684211;2202.58145363409;2230.72681704261" - ; "1971-03-15,882.390;904.370;916.830,2205.975;2260.925;2292.075" - ; "1971-04-15,903.040;941.750;950.820,2251.97007481297;2348.50374064838;2371.12219451372" - ; "1971-05-15,905.780;907.810;939.920,2247.59305210918;2252.63027295285;2332.30769230769" - ; "1971-06-15,873.100;891.140;923.060,2150.49261083744;2194.92610837438;2273.54679802956" - ; "1971-07-15,858.430;858.430;903.400,2109.16461916462;2109.16461916462;2219.65601965602" - ; "1971-08-15,839.590;898.070;908.370,2057.81862745098;2201.15196078431;2226.39705882353" - ; "1971-09-15,883.470;887.190;920.930,2165.36764705882;2174.48529411765;2257.18137254902" - ; "1971-10-15,836.380;839.000;901.800,2044.93887530562;2051.34474327628;2204.88997555012" - ; "1971-11-15,797.970;831.340;843.170,1951.02689486553;2032.61613691932;2061.54034229829" - ; "1971-12-15,846.010;890.200;893.660,2058.41849148418;2165.93673965937;2174.35523114355" - ; "1972-01-15,889.150;902.170;917.220,2163.38199513382;2195.06082725061;2231.67883211679" - ; "1972-02-15,901.790;928.130;928.130,2183.51089588378;2247.28813559322;2247.28813559322" - ; "1972-03-15,928.660;940.700;950.180,2243.14009661836;2272.22222222222;2295.12077294686" - ; "1972-04-15,940.920;954.170;968.920,2267.27710843374;2299.20481927711;2334.74698795181" - ; "1972-05-15,925.120;960.720;971.250,2223.84615384615;2309.42307692308;2334.73557692308" - ; "1972-06-15,926.250;929.030;961.390,2221.22302158273;2227.8896882494;2305.49160671463" - ; "1972-07-15,910.450;924.740;942.130,2172.91169451074;2207.01670644391;2248.52028639618" - ; "1972-08-15,930.460;963.730;973.510,2215.38095238095;2294.59523809524;2317.88095238095" - ; "1972-09-15,935.730;953.270;970.050,2222.63657957245;2264.29928741093;2304.1567695962" - ; "1972-10-15,921.660;955.520;955.520,2178.86524822695;2258.91252955083;2258.91252955083" - ; "1972-11-15,968.540;1018.21;1025.21,2284.29245283019;2401.43867924528;2417.94811320755" - ; "1972-12-15,1000.00;1020.02;1036.27,2352.94117647059;2400.04705882353;2438.28235294118" - ; "1973-01-15,992.930;999.020;1051.70,2330.82159624413;2345.11737089202;2468.779342723" - ; "1973-02-15,947.920;955.070;996.760,2209.60372960373;2226.2703962704;2323.44988344988" - ; "1973-03-15,922.710;951.010;979.980,2130.96997690531;2196.32794457275;2263.23325635104" - ; "1973-04-15,921.430;921.430;967.410,2113.37155963303;2113.37155963303;2218.83027522936" - ; "1973-05-15,886.510;901.410;956.580,2019.38496583144;2053.32574031891;2178.99772209567" - ; "1973-06-15,869.130;891.710;927.000,1966.35746606335;2017.44343891403;2097.2850678733" - ; "1973-07-15,870.110;926.400;936.710,1964.1309255079;2091.19638826185;2114.46952595937" - ; "1973-08-15,851.900;887.570;912.780,1888.91352549889;1968.0044345898;2023.90243902439" - ; "1973-09-15,880.570;947.100;953.270,1948.16371681416;2095.35398230089;2109.00442477876" - ; "1973-10-15,948.830;956.580;987.060,2080.76754385965;2097.76315789474;2164.60526315789" - ; "1973-11-15,817.730;822.250;948.830,1781.54684095861;1791.39433551198;2067.16775599129" - ; "1973-12-15,788.310;850.860;851.140,1706.2987012987;1841.68831168831;1842.29437229437" - ; "1974-01-15,823.110;855.550;880.690,1766.330472103;1835.94420600858;1889.89270386266" - ; "1974-02-15,803.900;860.530;863.420,1703.17796610169;1823.15677966102;1829.27966101695" - ; "1974-03-15,846.680;846.680;891.660,1771.29707112971;1771.29707112971;1865.39748953975" - ; "1974-04-15,827.680;836.750;869.920,1724.33333333333;1743.22916666667;1812.33333333333" - ; "1974-05-15,795.370;802.170;865.770,1636.56378600823;1650.55555555556;1781.41975308642" - ; "1974-06-15,802.410;802.410;859.670,1637.57142857143;1637.57142857143;1754.42857142857" - ; "1974-07-15,757.430;757.430;806.240,1533.25910931174;1533.25910931174;1632.06477732794" - ; "1974-08-15,656.840;678.580;797.560,1313.68;1357.16;1595.12" - ; "1974-09-15,607.870;607.870;677.880,1201.32411067194;1201.32411067194;1339.6837944664" - ; "1974-10-15,584.560;665.520;673.500,1143.9530332681;1302.38747553816;1318.00391389432" - ; "1974-11-15,608.570;618.660;674.750,1181.68932038835;1201.28155339806;1310.19417475728" - ; "1974-12-15,577.600;616.240;616.240,1112.90944123314;1187.36030828516;1187.36030828516" - ; "1975-01-15,632.040;703.690;705.960,1213.12859884837;1350.65259117083;1355.00959692898" - ; "1975-02-15,707.600;739.050;749.770,1347.80952380952;1407.71428571429;1428.13333333333" - ; "1975-03-15,743.430;768.150;786.530,1410.68311195446;1457.59013282732;1492.46679316888" - ; "1975-04-15,742.880;821.340;821.340,1404.31001890359;1552.62759924386;1552.62759924386" - ; "1975-05-15,815.000;832.290;858.730,1531.95488721805;1564.45488721804;1614.15413533835" - ; "1975-06-15,819.310;878.990;878.990,1528.56343283582;1639.90671641791;1639.90671641791" - ; "1975-07-15,824.860;831.510;881.810,1521.88191881919;1534.15129151291;1626.9557195572" - ; "1975-08-15,791.690;835.340;835.340,1457.9926335175;1538.37937384899;1538.37937384899" - ; "1975-09-15,793.880;793.880;840.110,1453.99267399267;1453.99267399267;1538.663003663" - ; "1975-10-15,784.160;836.040;855.160,1428.34244080146;1522.84153005464;1557.66848816029" - ; "1975-11-15,825.720;860.670;860.670,1493.16455696203;1556.36528028933;1556.36528028933" - ; "1975-12-15,818.800;852.410;859.810,1475.31531531532;1535.87387387387;1549.20720720721" - ; "1976-01-15,858.710;975.280;975.280,1544.44244604317;1754.10071942446;1754.10071942446" - ; "1976-02-15,950.570;972.610;994.570,1703.53046594982;1743.02867383513;1782.3835125448" - ; "1976-03-15,970.640;999.450;1009.21,1736.38640429338;1787.92486583184;1805.38461538462" - ; "1976-04-15,968.280;996.850;1011.02,1725.98930481283;1776.91622103387;1802.17468805704" - ; "1976-05-15,965.570;975.230;1007.48,1708.97345132743;1726.07079646018;1783.15044247788" - ; "1976-06-15,958.090;1002.78;1007.45,1686.77816901408;1765.45774647887;1773.67957746479" - ; "1976-07-15,979.290;984.640;1011.21,1715.04378283713;1724.41330998249;1770.94570928196" - ; "1976-08-15,960.440;973.740;999.340,1673.24041811847;1696.41114982578;1741.01045296167" - ; "1976-09-15,978.640;990.190;1014.79,1699.02777777778;1719.07986111111;1761.78819444444" - ; "1976-10-15,932.350;964.930;979.890,1610.27633851468;1666.54576856649;1692.38341968912" - ; "1976-11-15,924.040;947.220;966.090,1593.1724137931;1633.13793103448;1665.6724137931" - ; "1976-12-15,946.640;1004.65;1004.65,1626.52920962199;1726.20274914089;1726.20274914089" - ; "1977-01-15,954.370;954.370;999.750,1631.40170940171;1631.40170940171;1708.97435897436" - ; "1977-02-15,931.520;936.420;958.360,1576.17597292724;1584.46700507614;1621.59052453469" - ; "1977-03-15,919.130;919.130;968.000,1544.75630252101;1544.75630252101;1626.89075630252" - ; "1977-04-15,914.600;926.900;947.760,1524.33333333333;1544.83333333333;1579.6" - ; "1977-05-15,898.660;898.660;943.440,1490.31509121061;1490.31509121061;1564.57711442786" - ; "1977-06-15,903.040;916.300;929.700,1487.71004942339;1509.55518945634;1531.63097199341" - ; "1977-07-15,888.430;890.070;923.420,1456.44262295082;1459.13114754098;1513.80327868852" - ; "1977-08-15,854.120;861.490;891.810,1395.62091503268;1407.66339869281;1457.20588235294" - ; "1977-09-15,834.720;847.110;876.390,1359.47882736156;1379.65798045603;1427.34527687296" - ; "1977-10-15,801.540;818.350;851.960,1301.2012987013;1328.49025974026;1383.05194805195" - ; "1977-11-15,800.850;829.700;845.890,1293.7802907916;1340.38772213247;1366.54281098546" - ; "1977-12-15,806.220;831.170;831.170,1298.26086956522;1338.43800322061;1338.43800322061" - ; "1978-01-15,763.340;769.920;817.740,1221.344;1231.872;1308.384" - ; "1978-02-15,742.120;742.120;782.660,1179.84101748808;1179.84101748808;1244.29252782194" - ; "1978-03-15,742.720;757.360;773.820,1171.48264984227;1194.57413249211;1220.53627760252" - ; "1978-04-15,751.040;837.320;837.320,1175.33646322379;1310.35993740219;1310.35993740219" - ; "1978-05-15,822.070;840.610;858.370,1274.52713178295;1303.27131782946;1330.80620155039" - ; "1978-06-15,812.280;818.950;866.510,1245.8282208589;1256.05828220859;1329.00306748466" - ; "1978-07-15,805.790;862.270;862.270,1226.46879756469;1312.43531202435;1312.43531202435" - ; "1978-08-15,860.710;876.820;900.120,1304.10606060606;1328.51515151515;1363.81818181818" - ; "1978-09-15,857.160;865.820;907.740,1288.96240601504;1301.98496240602;1365.02255639098" - ; "1978-10-15,792.450;792.450;901.420,1180.99850968703;1180.99850968703;1343.39791356185" - ; "1978-11-15,785.260;799.030;827.790,1165.07418397626;1185.50445103858;1228.17507418398" - ; "1978-12-15,787.510;805.010;821.900,1163.23485967504;1189.08419497784;1214.03249630724" - ; "1979-01-15,811.420;839.220;859.750,1188.02342606149;1228.7262079063;1258.78477306003" - ; "1979-02-15,807.000;808.820;840.870,1167.87264833575;1170.50651230101;1216.88856729378" - ; "1979-03-15,815.750;862.180;871.360,1168.69627507163;1235.21489971347;1248.36676217765" - ; "1979-04-15,854.900;854.900;878.720,1210.90651558074;1210.90651558074;1244.64589235127" - ; "1979-05-15,822.160;822.330;857.590,1149.87412587413;1150.11188811189;1199.42657342657" - ; "1979-06-15,821.210;841.980;849.100,1135.83679114799;1164.5643153527;1174.41217150761" - ; "1979-07-15,825.510;846.420;852.990,1129.28864569083;1157.89329685363;1166.88098495212" - ; "1979-08-15,846.160;887.630;887.630,1146.55826558266;1202.75067750677;1202.75067750677" - ; "1979-09-15,866.130;878.580;893.940,1161.03217158177;1177.72117962466;1198.3109919571" - ; "1979-10-15,806.830;815.700;897.610,1072.91223404255;1084.70744680851;1193.63031914894" - ; "1979-11-15,796.670;822.350;831.740,1049.63109354414;1083.465085639;1095.83662714097" - ; "1979-12-15,819.620;838.740;844.620,1068.60495436767;1093.5332464146;1101.19947848761" - ; "1980-01-15,820.310;875.850;881.910,1054.38303341902;1125.77120822622;1133.56041131105" - ; "1980-02-15,854.440;863.140;903.840,1082.94043092522;1093.9670468948;1145.55133079848" - ; "1980-03-15,759.980;785.750;856.480,948.789013732834;980.961298377029;1069.26342072409" - ; "1980-04-15,759.130;817.060;817.060,937.197530864197;1008.71604938272;1008.71604938272" - ; "1980-05-15,805.200;850.850;860.320,984.352078239609;1040.15892420538;1051.73594132029" - ; "1980-06-15,843.770;867.920;887.540,1020.27811366385;1049.48004836759;1073.20435308343" - ; "1980-07-15,872.270;935.320;936.180,1054.7400241838;1130.97944377267;1132.01934703748" - ; "1980-08-15,929.780;932.590;966.720,1116.1824729892;1119.55582232893;1160.52821128451" - ; "1980-09-15,921.930;932.420;974.570,1097.53571428571;1110.02380952381;1160.20238095238" - ; "1980-10-15,917.750;924.490;972.440,1082.25235849057;1090.20047169811;1146.74528301887" - ; "1980-11-15,932.420;993.340;1000.17,1090.54970760234;1161.80116959064;1169.78947368421" - ; "1980-12-15,908.450;963.990;974.400,1052.6651216686;1117.02201622248;1129.08458864426" - ; "1981-01-15,938.910;947.270;1004.69,1079.20689655172;1088.81609195402;1154.81609195402" - ; "1981-02-15,931.570;974.580;974.580,1059.80659840728;1108.73720136519;1108.73720136519" - ; "1981-03-15,964.620;1003.87;1015.22,1089.96610169492;1134.31638418079;1147.14124293785" - ; "1981-04-15,989.100;997.750;1024.05,1110.10101010101;1119.80920314254;1149.3265993266" - ; "1981-05-15,963.440;991.750;995.590,1072.87305122494;1104.3986636971;1108.67483296214" - ; "1981-06-15,976.880;976.880;1011.99,1078.23399558499;1078.23399558499;1116.98675496689" - ; "1981-07-15,924.660;952.340;967.660,1009.45414847162;1039.67248908297;1056.39737991266" - ; "1981-08-15,881.470;881.470;953.580,955.005417118093;955.005417118093;1033.13109425786" - ; "1981-09-15,824.010;849.980;884.230,884.130901287554;911.995708154506;948.744635193133" - ; "1981-10-15,830.960;852.550;878.140,889.678800856531;912.79443254818;940.192719486081" - ; "1981-11-15,844.080;888.980;888.980,900.832443970117;948.751334044824;948.751334044824" - ; "1981-12-15,868.250;875.000;892.690,923.670212765957;930.851063829787;949.670212765958" - ; "1982-01-15,838.950;871.100;882.520,889.66065747614;923.753976670202;935.864262990456" - ; "1982-02-15,811.260;824.390;852.550,857.568710359408;871.448202959831;901.215644820296" - ; "1982-03-15,795.470;822.770;828.390,841.767195767196;870.656084656085;876.603174603175" - ; "1982-04-15,833.240;848.360;865.580,878.018967334036;893.951527924131;912.096944151739" - ; "1982-05-15,819.540;819.540;869.200,855.469728601253;855.469728601253;907.306889352818" - ; "1982-06-15,788.620;811.930;816.880,813.010309278351;837.041237113402;842.144329896907" - ; "1982-07-15,796.990;808.600;833.430,817.425641025641;829.333333333333;854.8" - ; "1982-08-15,776.920;901.310;901.310,795.209825997953;922.528147389969;922.528147389969" - ; "1982-09-15,895.050;896.250;934.790,914.249233912155;915.474974463738;954.841675178754" - ; "1982-10-15,903.610;991.720;1036.98,920.173116089613;1009.89816700611;1055.98778004073" - ; "1982-11-15,990.990;1039.28;1065.49,1011.21428571429;1060.48979591837;1087.23469387755" - ; "1982-12-15,990.250;1046.54;1070.55,1014.60040983607;1072.27459016393;1096.875" - ; "1983-01-15,1027.04;1075.70;1092.35,1050.14314928425;1099.89775051125;1116.92229038855" - ; "1983-02-15,1059.79;1112.16;1121.81,1082.52298263534;1136.01634320735;1145.873340143" - ; "1983-03-15,1114.45;1130.03;1145.90,1138.35546475996;1154.26966292135;1170.48008171604" - ; "1983-04-15,1113.49;1226.20;1226.20,1129.30020283976;1243.61054766734;1243.61054766734" - ; "1983-05-15,1190.02;1199.98;1232.59,1199.61693548387;1209.65725806452;1242.53024193548" - ; "1983-06-15,1185.50;1221.96;1248.30,1191.45728643216;1228.10050251256;1254.57286432161" - ; "1983-07-15,1189.90;1199.22;1243.69,1191.09109109109;1200.42042042042;1244.93493493493" - ; "1983-08-15,1163.06;1216.16;1216.16,1160.73852295409;1213.73253493014;1213.73253493014" - ; "1983-09-15,1206.81;1233.13;1260.77,1198.42105263158;1224.55809334657;1252.00595829196" - ; "1983-10-15,1223.48;1225.20;1284.65,1211.36633663366;1213.06930693069;1271.93069306931" - ; "1983-11-15,1214.84;1276.02;1287.19,1200.4347826087;1260.88932806324;1271.92687747036" - ; "1983-12-15,1236.79;1258.64;1275.10,1220.91806515301;1242.48766041461;1258.73642645607" - ; "1984-01-15,1220.58;1220.58;1286.64,1197.82139352306;1197.82139352306;1262.64965652601" - ; "1984-02-15,1134.21;1154.63;1213.88,1107.626953125;1127.568359375;1185.4296875" - ; "1984-03-15,1139.76;1164.89;1184.36,1110.87719298246;1135.37037037037;1154.3469785575" - ; "1984-04-15,1130.55;1170.75;1175.25,1096.55674102813;1135.54801163919;1139.91270611057" - ; "1984-05-15,1101.24;1104.85;1186.56,1065.02901353965;1068.52030947776;1147.54352030948" - ; "1984-06-15,1086.90;1132.40;1133.84,1048.11957569913;1091.99614271938;1093.38476374156" - ; "1984-07-15,1086.57;1115.28;1134.28,1043.77521613833;1071.35446685879;1089.60614793468" - ; "1984-08-15,1134.61;1224.38;1239.73,1085.75119617225;1171.65550239234;1186.34449760766" - ; "1984-09-15,1197.99;1206.71;1237.52,1140.94285714286;1149.24761904762;1178.59047619048" - ; "1984-10-15,1175.13;1207.38;1225.93,1115.98290598291;1146.60968660969;1164.22602089269" - ; "1984-11-15,1185.29;1188.94;1244.15,1125.63152896486;1129.09781576448;1181.5289648623" - ; "1984-12-15,1163.21;1211.57;1211.57,1104.6628679962;1150.58879392213;1150.58879392213" - ; "1985-01-15,1184.96;1286.77;1292.62,1123.18483412322;1219.68720379147;1225.23222748815" - ; "1985-02-15,1275.84;1284.01;1297.92,1203.62264150943;1211.33018867925;1224.45283018868" - ; "1985-03-15,1247.35;1266.78;1299.36,1172.32142857143;1190.58270676692;1221.2030075188" - ; "1985-04-15,1252.98;1258.06;1284.78,1172.10477081384;1176.85687558466;1201.85219831618" - ; "1985-05-15,1242.05;1315.41;1315.41,1157.54892823858;1225.91798695247;1225.91798695247" - ; "1985-06-15,1290.10;1335.46;1335.46,1198.97769516729;1241.13382899628;1241.13382899628" - ; "1985-07-15,1321.91;1347.45;1359.54,1226.26159554731;1249.95361781076;1261.16883116883" - ; "1985-08-15,1312.50;1334.01;1355.62,1215.27777777778;1235.19444444444;1255.2037037037" - ; "1985-09-15,1297.94;1328.63;1339.27,1198.46722068329;1226.80517082179;1236.6297322253" - ; "1985-10-15,1324.37;1374.31;1375.57,1218.37166513339;1264.3146274149;1265.47378104876" - ; "1985-11-15,1389.68;1472.13;1475.69,1274.93577981651;1350.57798165138;1353.84403669725" - ; "1985-12-15,1457.91;1546.67;1553.10,1333.86093321135;1415.06861848124;1420.95150960659" - ; "1986-01-15,1502.29;1570.99;1570.99,1370.70255474453;1433.38503649635;1433.38503649635" - ; "1986-02-15,1593.12;1709.06;1713.99,1457.56633119854;1563.64135407136;1568.15187557182" - ; "1986-03-15,1686.42;1818.61;1821.72,1550.01838235294;1671.51654411765;1674.375" - ; "1986-04-15,1735.51;1783.98;1855.90,1598.07550644567;1642.70718232044;1708.93186003683" - ; "1986-05-15,1758.18;1876.71;1882.35,1614.49035812672;1723.33333333333;1728.51239669421" - ; "1986-06-15,1837.19;1892.72;1892.72,1677.79908675799;1728.51141552511;1728.51141552511" - ; "1986-07-15,1766.87;1775.31;1909.03,1613.5799086758;1621.28767123288;1743.40639269406" - ; "1986-08-15,1763.64;1898.34;1904.53,1607.69371011851;1730.48313582498;1736.1257976299" - ; "1986-09-15,1755.20;1767.58;1919.71,1592.74047186933;1603.97459165154;1742.02359346642" - ; "1986-10-15,1774.18;1877.71;1878.37,1608.50407978241;1702.36627379873;1702.96464188577" - ; "1986-11-15,1817.21;1914.23;1916.76,1646.02355072464;1733.90398550725;1736.19565217391" - ; "1986-12-15,1895.95;1895.95;1955.57,1715.79185520362;1715.79185520362;1769.74660633484" - ; "1987-01-15,1927.31;2158.04;2163.39,1733.19244604317;1940.68345323741;1945.49460431655" - ; "1987-02-15,2158.04;2223.99;2244.09,1933.72759856631;1992.82258064516;2010.83333333333" - ; "1987-03-15,2220.47;2304.69;2372.59,1980.79393398751;2055.92328278323;2116.49420160571" - ; "1987-04-15,2230.54;2286.36;2405.54,1979.18367346939;2028.71339840284;2134.46317657498" - ; "1987-05-15,2215.87;2291.57;2342.19,1959.21308576481;2026.14500442087;2070.90185676393" - ; "1987-06-15,2278.22;2418.53;2451.05,2007.2422907489;2130.86343612335;2159.5154185022" - ; "1987-07-15,2409.76;2572.07;2572.07,2117.539543058;2260.16695957821;2260.16695957821" - ; "1987-08-15,2546.72;2662.95;2722.42,2226.15384615385;2327.7534965035;2379.73776223776" - ; "1987-09-15,2492.82;2596.28;2613.04,2167.66956521739;2257.6347826087;2272.20869565217" - ; "1987-10-15,1738.74;1993.53;2640.99,1508.01387684302;1728.99392888118;2290.53772766696" - ; "1987-11-15,1833.55;1833.55;2014.09,1588.86481802426;1588.86481802426;1745.31195840555" - ; "1987-12-15,1766.74;1938.83;2005.64,1530.9705372617;1680.09532062392;1737.98960138648" - ; "1988-01-15,1879.14;1958.22;2051.89,1624.14866032844;1692.49783923941;1773.45721694036" - ; "1988-02-15,1895.72;2071.62;2071.62,1634.24137931034;1785.87931034483;1785.87931034483" - ; "1988-03-15,1978.12;1988.06;2087.37,1697.95708154506;1706.48927038627;1791.7339055794" - ; "1988-04-15,1980.60;2032.33;2110.08,1691.37489325363;1735.55081127242;1801.94705380017" - ; "1988-05-15,1941.48;2031.12;2058.36,1652.32340425532;1728.61276595745;1751.79574468085" - ; "1988-06-15,2052.45;2141.71;2152.20,1739.36440677966;1815.00847457627;1823.89830508475" - ; "1988-07-15,2053.70;2128.73;2158.61,1733.08016877637;1796.39662447257;1821.61181434599" - ; "1988-08-15,1989.33;2031.65;2134.07,1671.70588235294;1707.26890756303;1793.33613445378" - ; "1988-09-15,2002.31;2112.91;2119.31,1671.37729549249;1763.69782971619;1769.04006677796" - ; "1988-10-15,2102.06;2148.65;2183.50,1748.80199667221;1787.56239600666;1816.55574043261" - ; "1988-11-15,2038.58;2114.51;2170.34,1694.58021612635;1757.69742310889;1804.106400665" - ; "1988-12-15,2092.28;2168.57;2182.68,1736.33195020747;1799.64315352697;1811.35269709544" - ; "1989-01-15,2144.64;2342.32;2342.32,1770.96614368291;1934.20313790256;1934.20313790256" - ; "1989-02-15,2245.54;2258.39;2347.14,1846.66118421053;1857.22861842105;1930.21381578947" - ; "1989-03-15,2243.04;2293.62;2340.71,1834.04742436631;1875.40474243663;1913.90842191333" - ; "1989-04-15,2291.97;2418.80;2418.99,1861.87652315191;1964.90658001625;1965.06092607636" - ; "1989-05-15,2371.33;2480.15;2502.02,1915.45234248788;2003.352180937;2021.01777059774" - ; "1989-06-15,2440.06;2440.06;2531.87,1966.20467365028;1966.20467365028;2040.18533440774" - ; "1989-07-15,2452.77;2660.66;2660.66,1971.68006430868;2138.79421221865;2138.79421221865" - ; "1989-08-15,2641.12;2737.27;2743.36,2119.67897271268;2196.84590690209;2201.73354735152" - ; "1989-09-15,2659.19;2692.82;2752.09,2127.352;2154.256;2201.672" - ; "1989-10-15,2569.26;2645.08;2791.41,2045.58917197452;2105.95541401274;2222.4601910828" - ; "1989-11-15,2582.17;2706.27;2706.27,2050.96902303415;2149.53931691819;2149.53931691819" - ; "1989-12-15,2687.93;2753.20;2761.09,2131.58604282316;2183.34655035686;2189.60348929421" - ; "1990-01-15,2543.24;2590.54;2810.15,1996.26373626374;2033.39089481947;2205.76923076923" - ; "1990-02-15,2564.19;2627.25;2649.55,2003.2734375;2052.5390625;2069.9609375" - ; "1990-03-15,2635.59;2707.21;2755.63,2047.85547785548;2103.50427350427;2141.12665112665" - ; "1990-04-15,2645.05;2656.76;2765.77,2052.01706749418;2061.1016291699;2145.67106283941" - ; "1990-05-15,2668.92;2876.66;2878.56,2065.72755417957;2226.51702786378;2227.98761609907" - ; "1990-06-15,2842.33;2880.69;2935.89,2188.09083910701;2217.62124711316;2260.11547344111" - ; "1990-07-15,2879.21;2905.20;2999.75,2207.98312883436;2227.91411042945;2300.4217791411" - ; "1990-08-15,2483.42;2614.36;2899.26,1887.09726443769;1986.59574468085;2203.08510638298" - ; "1990-09-15,2427.48;2452.48;2628.22,1829.29917106255;1848.13865862849;1980.572720422" - ; "1990-10-15,2365.10;2442.33;2523.76,1771.61048689139;1829.4606741573;1890.45692883895" - ; "1990-11-15,2440.84;2559.65;2565.35,1824.24514200299;1913.04185351271;1917.3019431988" - ; "1990-12-15,2565.59;2633.66;2637.13,1917.48131539611;1968.355754858;1970.94917787743" - ; "1991-01-15,2470.30;2736.39;2736.39,1835.2897473997;2032.97919762259;2032.97919762259" - ; "1991-02-15,2730.69;2882.18;2934.65,2025.73442136499;2138.11572700297;2177.04005934718" - ; "1991-03-15,2855.45;2913.86;2973.27,2115.14814814815;2158.41481481481;2202.42222222222" - ; "1991-04-15,2873.02;2887.87;3004.46,2125.01479289941;2135.99852071006;2222.23372781065" - ; "1991-05-15,2865.38;3027.50;3027.50,2113.11209439528;2232.66961651917;2232.66961651917" - ; "1991-06-15,2906.75;2906.75;3035.33,2137.31617647059;2137.31617647059;2231.86029411765" - ; "1991-07-15,2932.47;3024.82;3024.82,2153.06167400881;2220.86637298091;2220.86637298091" - ; "1991-08-15,2898.03;3043.60;3055.23,2121.54465592972;2228.11127379209;2236.62518301611" - ; "1991-09-15,2982.56;3016.77;3029.07,2173.87755102041;2198.81195335277;2207.77696793003" - ; "1991-10-15,2942.75;3069.10;3077.15,2141.73944687045;2233.69723435226;2239.55604075691" - ; "1991-11-15,2894.68;2894.68;3065.30,2100.63860667634;2100.63860667634;2224.4557329463" - ; "1991-12-15,2863.82;3168.83;3168.83,2076.7367657723;2297.91878172589;2297.91878172589" - ; "1992-01-15,3172.41;3223.39;3272.14,2297.18320057929;2334.09847936278;2369.39898624185" - ; "1992-02-15,3224.73;3267.67;3283.32,2326.64502164502;2357.62626262626;2368.91774891775" - ; "1992-03-15,3208.63;3235.47;3290.25,2303.39554917444;2322.66331658291;2361.98851399856" - ; "1992-04-15,3181.35;3359.12;3366.50,2280.5376344086;2407.97132616487;2413.26164874552" - ; "1992-05-15,3336.09;3396.88;3398.43,2388.03865425913;2431.5533285612;2432.66284896206" - ; "1992-06-15,3274.12;3318.52;3413.21,2335.3209700428;2366.99001426534;2434.52924393723" - ; "1992-07-15,3277.61;3393.78;3393.78,2332.81850533808;2415.50177935943;2415.50177935943" - ; "1992-08-15,3228.17;3257.35;3395.40,2291.1071682044;2311.81689141235;2409.79418026969" - ; "1992-09-15,3250.32;3271.66;3376.22,2300.29723991507;2315.39985845718;2389.39844302902" - ; "1992-10-15,3136.58;3226.28;3254.37,2211.97461212976;2275.23272214386;2295.04231311707" - ; "1992-11-15,3193.32;3305.16;3305.16,2248.81690140845;2327.57746478873;2327.57746478873" - ; "1992-12-15,3255.18;3301.11;3333.26,2293.99577167019;2326.36363636364;2349.02043692741" - ; "1993-01-15,3241.95;3310.03;3310.03,2273.4572230014;2321.19915848527;2321.19915848527" - ; "1993-02-15,3302.19;3370.81;3442.14,2307.61006289308;2355.56254367575;2405.40880503145" - ; "1993-03-15,3355.41;3435.11;3478.34,2336.6364902507;2392.13788300836;2422.24233983287" - ; "1993-04-15,3370.81;3427.55;3478.61,2340.84027777778;2380.24305555556;2415.70138888889" - ; "1993-05-15,3437.19;3527.43;3554.83,2383.62690707351;2446.20665742025;2465.2080443828" - ; "1993-06-15,3466.81;3516.08;3553.45,2400.8379501385;2434.95844875346;2460.8379501385" - ; "1993-07-15,3449.93;3539.47;3567.70,2389.14819944598;2451.15650969529;2470.70637119114" - ; "1993-08-15,3548.97;3651.25;3652.09,2450.94613259668;2521.58149171271;2522.16160220994" - ; "1993-09-15,3537.24;3555.12;3645.10,2437.79462439697;2450.11716057891;2512.12956581668" - ; "1993-10-15,3577.76;3680.59;3687.86,2455.56623198353;2526.14275909403;2531.13246396706" - ; "1993-11-15,3624.98;3683.95;3710.77,2486.26886145405;2526.7146776406;2545.109739369" - ; "1993-12-15,3697.08;3754.09;3794.33,2535.72016460905;2574.82167352538;2602.42112482853" - ; "1994-01-15,3756.60;3978.36;3978.36,2569.49384404925;2721.17647058824;2721.17647058824" - ; "1994-02-15,3832.02;3832.02;3975.54,2612.1472392638;2612.1472392638;2709.97955010225" - ; "1994-03-15,3626.75;3635.96;3895.65,2463.82472826087;2470.08152173913;2646.50135869565" - ; "1994-04-15,3593.35;3681.69;3705.78,2437.82225237449;2497.75440976934;2514.09769335142" - ; "1994-05-15,3629.04;3758.37;3766.35,2460.36610169491;2548.04745762712;2553.45762711864" - ; "1994-06-15,3624.96;3624.96;3814.83,2449.2972972973;2449.2972972973;2577.58783783784" - ; "1994-07-15,3646.65;3764.50;3764.50,2457.31132075472;2536.72506738544;2536.72506738544" - ; "1994-08-15,3747.02;3913.42;3917.30,2514.77852348993;2626.45637583893;2629.06040268456" - ; "1994-09-15,3831.75;3843.19;3953.88,2564.75903614458;2572.41633199465;2646.50602409639" - ; "1994-10-15,3775.56;3908.12;3936.04,2525.45819397993;2614.127090301;2632.80267558528" - ; "1994-11-15,3674.63;3739.23;3863.37,2454.66265865063;2497.81563126253;2580.74148296593" - ; "1994-12-15,3685.73;3834.44;3861.69,2462.07748830995;2561.41616566466;2579.61923847695" - ; "1995-01-15,3832.08;3843.86;3932.34,2549.62075848303;2557.45841650033;2616.32734530938" - ; "1995-02-15,3847.56;4011.05;4011.74,2549.74155069583;2658.08482438701;2658.54208084824" - ; "1995-03-15,3962.63;4157.69;4172.56,2617.3249669749;2746.16248348745;2755.98414795244" - ; "1995-04-15,4168.41;4321.27;4321.27,2744.18038183015;2844.81237656353;2844.81237656353" - ; "1995-05-15,4316.08;4465.14;4465.14,2835.7950065703;2933.73193166886;2933.73193166886" - ; "1995-06-15,4423.99;4556.10;4589.64,2900.97704918033;2987.60655737705;3009.6" - ; "1995-07-15,4585.15;4708.47;4736.29,3006.65573770492;3087.52131147541;3105.76393442623" - ; "1995-08-15,4580.62;4610.56;4701.42,2995.8273381295;3015.40876389797;3074.83322432963" - ; "1995-09-15,4647.54;4789.08;4801.80,3033.64229765013;3126.03133159269;3134.33420365535" - ; "1995-10-15,4703.82;4755.48;4802.45,3060.39037085231;3094.00130123617;3124.56083279115" - ; "1995-11-15,4766.68;5074.49;5105.56,3103.30729166667;3303.70442708333;3323.93229166667" - ; "1995-12-15,5059.32;5117.12;5216.47,3295.97394136808;3333.62866449511;3398.35179153094" - ; "1996-01-15,5032.94;5395.30;5395.30,3259.67616580311;3494.36528497409;3494.36528497409" - ; "1996-02-15,5373.99;5485.62;5630.49,3469.32859909619;3541.39444803099;3634.91930277598" - ; "1996-03-15,5470.45;5587.14;5683.60,3513.45536287733;3588.40077071291;3650.35324341683" - ; "1996-04-15,5485.98;5569.08;5689.74,3509.90403071017;3563.07101727447;3640.26871401152" - ; "1996-05-15,5420.95;5643.18;5778.00,3461.65389527459;3603.56321839081;3689.65517241379" - ; "1996-06-15,5624.71;5654.63;5719.27,3589.4767070836;3608.5705169113;3649.82131461391" - ; "1996-07-15,5346.55;5528.91;5729.98,3405.44585987261;3521.59872611465;3649.66878980892" - ; "1996-08-15,5594.75;5616.21;5733.47,3556.73871582962;3570.38143674507;3644.92689129053" - ; "1996-09-15,5606.96;5882.17;5894.74,3553.20659062104;3727.61089987326;3735.57667934094" - ; "1996-10-15,5904.90;6029.38;6094.23,3730.1958307012;3808.83133291219;3849.79785217941" - ; "1996-11-15,6021.93;6521.70;6547.79,3796.92938209332;4112.04287515763;4128.49306431274" - ; "1996-12-15,6268.35;6448.27;6560.91,3952.30138713745;4065.74401008827;4136.76544766709" - ; "1997-01-15,6442.49;6813.09;6883.90,4049.33375235701;4282.26901319925;4326.77561282212" - ; "1997-02-15,6746.90;6877.74;7067.46,4227.38095238095;4309.36090225564;4428.23308270677" - ; "1997-03-15,6583.48;6583.48;7085.16,4114.675;4114.675;4428.225" - ; "1997-04-15,6391.69;7008.99;7008.99,3989.81897627965;4375.14981273408;4375.14981273408" - ; "1997-05-15,6976.48;7331.04;7383.41,4357.57651467833;4579.03810118676;4611.74890693317" - ; "1997-06-15,7269.66;7672.79;7796.51,4535.0343106675;4786.5190268247;4863.69931378665" - ; "1997-07-15,7722.33;8222.61;8254.89,4811.42056074766;5123.1214953271;5143.23364485981" - ; "1997-08-15,7622.42;7622.42;8259.31,4740.31094527363;4740.31094527363;5136.3868159204" - ; "1997-09-15,7660.98;7945.26;7996.83,4752.46898263027;4928.82133995037;4960.81265508685" - ; "1997-10-15,7161.15;7442.08;8178.31,4431.4047029703;4605.24752475248;5060.8353960396" - ; "1997-11-15,7401.32;7823.13;7881.07,4582.86068111455;4844.04334365325;4879.91950464396" - ; "1997-12-15,7660.13;7908.25;8149.13,4748.99566026038;4902.82083075015;5052.15747055177" - ; "1998-01-15,7580.42;7906.50;7978.99,4690.85396039604;4892.63613861386;4937.49381188119" - ; "1998-02-15,8107.78;8545.72;8545.72,5007.89376158122;5278.39407041383;5278.39407041383" - ; "1998-03-15,8444.33;8799.81;8906.43,5206.12207151665;5425.28360049322;5491.01726263872" - ; "1998-04-15,8868.32;9063.37;9184.94,5457.42769230769;5577.45846153846;5652.27076923077" - ; "1998-05-15,8899.95;8899.95;9211.84,5466.79975429975;5466.79975429975;5658.37837837838" - ; "1998-06-15,8627.93;8952.02;9069.60,5293.20858895706;5492.03680981595;5564.1717791411" - ; "1998-07-15,8883.29;8883.29;9337.97,5443.19240196078;5443.19240196078;5721.79534313726" - ; "1998-08-15,7539.07;7539.07;8786.74,4613.87392900857;4613.87392900857;5377.44186046512" - ; "1998-09-15,7615.54;7842.62;8154.41,4654.97555012225;4793.77750611247;4984.35819070905" - ; "1998-10-15,7632.53;8592.10;8592.10,4653.98170731707;5239.08536585366;5239.08536585366" - ; "1998-11-15,8706.15;9116.55;9374.27,5308.62804878049;5558.87195121951;5716.01829268293" - ; "1998-12-15,8695.60;9181.43;9320.98,5305.43014032947;5601.84868822453;5686.99206833435" - ; "1999-01-15,9120.67;9358.83;9643.32,5551.22945830797;5696.18381010347;5869.33657942788" - ; "1999-02-15,9133.03;9306.58;9552.68,5551.99392097264;5657.49544072948;5807.10030395137" - ; "1999-03-15,9275.88;9786.16;10006.8,5621.74545454545;5931.00606060606;6064.72727272727" - ; "1999-04-15,9832.51;10789.0;10878.4,5916.07099879663;6491.57641395909;6545.3670276775" - ; "1999-05-15,10466.9;10559.7;11107.2,6297.77376654633;6353.61010830325;6683.03249097473" - ; "1999-06-15,10490.5;10970.8;10970.8,6311.97352587244;6600.96269554753;6600.96269554753" - ; "1999-07-15,10655.2;10655.2;11209.8,6391.84163167367;6391.84163167367;6724.5350929814" - ; "1999-08-15,10646.0;10829.3;11326.0,6371.03530819868;6480.73010173549;6777.97725912627" - ; "1999-09-15,10213.5;10337.0;11079.4,6083.08516974389;6156.64085765336;6598.8088147707" - ; "1999-10-15,10019.7;10729.9;10729.9,5957.01545778835;6379.25089179548;6379.25089179548" - ; "1999-11-15,10581.8;10877.8;11089.5,6287.46286393345;6463.33927510398;6589.12655971479" - ; "1999-12-15,10998.4;11497.1;11497.1,6534.99702911468;6831.31313131313;6831.31313131313" - ; "2000-01-15,10738.9;10940.5;11723.0,6361.90758293839;6481.33886255924;6944.90521327014" - ; "2000-02-15,9862.12;10128.3;11041.1,5808.0800942285;5964.84098939929;6502.41460541814" - ; "2000-03-15,9796.03;10921.9;11119.9,5721.98014018692;6379.61448598131;6495.26869158879" - ; "2000-04-15,10305.8;10733.9;11287.1,6016.22883829539;6266.14127262113;6589.08347927612" - ; "2000-05-15,10299.2;10522.3;10934.6,6005.36443148688;6135.45189504373;6375.86005830904" - ; "2000-06-15,10376.1;10447.9;10815.3,6018.61948955917;6060.26682134571;6273.37587006961" - ; "2000-07-15,10481.5;10522.0;10843.9,6065.68287037037;6089.12037037037;6275.40509259259" - ; "2000-08-15,10607.0;11215.1;11252.8,6138.31018518518;6490.21990740741;6512.03703703704" - ; "2000-09-15,10628.4;10650.9;11310.6,6118.82556131261;6131.77892918826;6511.57167530225" - ; "2000-10-15,9975.02;10971.1;10971.1,5732.77011494253;6305.22988505747;6305.22988505747" - ; "2000-11-15,10399.3;10414.5;10977.2,5973.1763354394;5981.90695002872;6305.11200459506" - ; "2000-12-15,10318.9;10788.0;10898.7,5930.40229885057;6200;6263.62068965517" - ; "2001-01-15,10525.4;10887.4;10945.8,6011.0793832096;6217.81838949172;6251.17075956596" - ; "2001-02-15,10441.9;10495.3;10983.6,5939.64732650739;5970.02275312855;6247.78156996587" - ; "2001-03-15,9389.48;9878.78;10858.3,5328.876276958;5606.57207718502;6162.48581157775" - ; "2001-04-15,9485.71;10735.0;10810.1,5362.18767665348;6068.40022611645;6110.85358959864" - ; "2001-05-15,10796.7;10911.9;11337.9,6075.80191333709;6140.63027574564;6380.36015756894" - ; "2001-06-15,10434.8;10502.4;11175.8,5862.24719101124;5900.22471910112;6278.5393258427" - ; "2001-07-15,10175.6;10522.8;10610.0,5732.7323943662;5928.33802816901;5977.46478873239" - ; "2001-08-15,9919.58;9949.75;10551.2,5588.49577464789;5605.49295774648;5944.33802816902" - ; "2001-09-15,8235.81;8847.56;10033.3,4619.07459338194;4962.17610768368;5627.20134604599" - ; "2001-10-15,8836.83;9075.14;9545.17,4972.89251547552;5107.0005627462;5371.50815981992" - ; "2001-11-15,9263.90;9851.56;9982.75,5222.04058624577;5553.30326944758;5627.25479143179" - ; "2001-12-15,9763.96;10021.6;10137.0,5525.727221279;5671.53367289191;5736.84210526316" - ; "2002-01-15,9618.24;9920.00;10259.7,5430.96555618295;5601.35516657256;5793.16770186335" - ; "2002-02-15,9625.44;10106.1;10145.7,5413.63329583802;5683.97075365579;5706.2429696288" - ; "2002-03-15,10281.7;10403.9;10635.3,5750.39149888143;5818.73601789709;5948.15436241611" - ; "2002-04-15,9819.87;9946.22;10381.7,5461.55172413793;5531.82424916574;5774.02669632926" - ; "2002-05-15,9808.04;9925.25;10353.1,5454.97219132369;5520.16129032258;5758.12013348165" - ; "2002-06-15,9120.11;9243.26;9796.80,5069.54419121734;5137.99888827126;5445.69205113952" - ; "2002-07-15,7702.34;8736.59;9379.50,4276.70183231538;4850.96612992782;5207.94003331483" - ; "2002-08-15,8043.63;8663.50;9053.64,4451.37244050913;4794.41062534588;5010.31543995573" - ; "2002-09-15,7591.93;7591.93;8602.61,4194.4364640884;4194.4364640884;4752.82320441989" - ; "2002-10-15,7286.27;8397.03;8538.24,4018.90237175951;4631.56646442361;4709.45394373966" - ; "2002-11-15,8358.95;8896.09;8931.68,4610.56260341975;4906.83397683398;4926.46442360728" - ; "2002-12-15,8303.78;8341.63;8862.57,4590.25981205086;4611.18297401879;4899.15422885572" - ; "2003-01-15,7945.13;8053.81;8842.62,4372.66373142543;4432.47660979637;4866.60429279031" - ; "2003-02-15,7749.87;7891.08;8109.82,4232.58874931731;4309.71054068815;4429.17531403605" - ; "2003-03-15,7524.06;7992.13;8521.97,4084.72312703583;4338.83279044517;4626.4766558089" - ; "2003-04-15,8069.86;8480.09;8515.66,4390.56583242655;4613.75952121872;4633.11207834603" - ; "2003-05-15,8454.25;8850.26;8850.26,4607.22070844687;4823.02997275204;4823.02997275204" - ; "2003-06-15,8897.81;8985.44;9323.02,4843.66358192706;4891.36635819271;5075.13336962439" - ; "2003-07-15,9036.04;9233.80;9284.57,4913.56171832518;5021.09842305601;5048.70581837955" - ; "2003-08-15,9036.32;9415.82;9428.90,4895.0812567714;5100.66088840737;5107.74647887324" - ; "2003-09-15,9275.06;9275.06;9659.13,5008.13174946004;5008.13174946004;5215.51295896328" - ; "2003-10-15,9469.20;9801.12;9812.98,5118.48648648649;5297.9027027027;5304.31351351351" - ; "2003-11-15,9619.42;9782.46;9858.46,5213.77777777778;5302.14634146341;5343.33875338753" - ; "2003-12-15,9853.64;10453.9;10453.9,5346.52197504069;5672.21920781335;5672.21920781335" - ; "2004-01-15,10409.9;10488.1;10702.5,5620.89632829374;5663.12095032397;5778.88768898488" - ; "2004-02-15,10470.7;10583.9;10737.7,5623.3619763695;5684.15682062299;5766.75617615467" - ; "2004-03-15,10048.2;10357.7;10678.1,5361.89967982924;5527.05442902882;5698.02561366062" - ; "2004-04-15,10225.6;10225.6;10570.8,5439.14893617021;5439.14893617021;5622.76595744681" - ; "2004-05-15,9906.91;10188.5;10317.2,5238.97937599154;5387.89000528821;5455.94923320994" - ; "2004-06-15,10195.9;10435.5;10479.6,5374.7496046389;5501.05429625725;5524.30152872957" - ; "2004-07-15,9961.92;10139.7;10334.2,5259.72544878564;5353.59028511088;5456.28299894403" - ; "2004-08-15,9814.59;10173.9;10195.0,5179.20316622691;5368.81266490765;5379.94722955145" - ; "2004-09-15,9988.54;10080.3;10341.2,5259.89468141127;5308.21484992101;5445.60294892048" - ; "2004-10-15,9749.99;10027.5;10239.9,5107.38082765846;5252.75013095862;5364.01257202724" - ; "2004-11-15,10035.7;10428.0;10572.6,5254.29319371728;5459.68586387435;5535.39267015707" - ; "2004-12-15,10440.6;10783.0;10854.5,5486.38991066737;5666.31634261692;5703.88859695218" - ; "2005-01-15,10368.6;10489.9;10729.4,5437.1263765076;5500.73413738857;5626.32406921867" - ; "2005-02-15,10551.9;10766.2;10841.6,5501.51199165798;5613.24296141814;5652.55474452555" - ; "2005-03-15,10405.7;10503.8;10940.6,5383.1867563373;5433.93688566994;5659.90688049664" - ; "2005-04-15,10012.4;10192.5;10546.3,5145.11819116136;5237.66700924974;5419.47584789311" - ; "2005-05-15,10140.1;10467.5;10542.6,5216.10082304527;5384.51646090535;5423.14814814815" - ; "2005-06-15,10275.0;10275.0;10623.1,5282.7763496144;5282.7763496144;5461.74807197943" - ; "2005-07-15,10270.7;10640.9;10705.6,5256.24360286592;5445.7011258956;5478.81269191402" - ; "2005-08-15,10397.3;10481.6;10697.6,5293.94093686354;5336.86354378819;5446.84317718941" - ; "2005-09-15,10378.0;10568.7;10682.9,5220.32193158954;5316.24748490946;5373.6921529175" - ; "2005-10-15,10215.2;10440.1;10535.5,5128.1124497992;5241.0140562249;5288.90562248996" - ; "2005-11-15,10406.8;10805.9;10931.6,5266.5991902834;5468.57287449393;5532.18623481781" - ; "2005-12-15,10717.5;10717.5;10912.6,5445.88414634146;5445.88414634146;5545.02032520325" - ; "2006-01-15,10667.4;10864.9;11043.4,5379.42511346445;5479.02168431669;5569.03681290973" - ; "2006-02-15,10749.8;10993.4;11137.2,5410.06542526422;5532.66230498239;5605.03271263211" - ; "2006-03-15,10958.6;11109.3;11317.4,5484.78478478478;5560.21021021021;5664.36436436436" - ; "2006-04-15,11073.8;11367.1;11382.5,5495.68238213399;5641.24069478908;5648.88337468983" - ; "2006-05-15,11094.4;11168.3;11642.7,5478.71604938272;5515.20987654321;5749.48148148148" - ; "2006-06-15,10706.1;11150.2;11260.3,5276.54016757023;5495.41646131099;5549.67964514539" - ; "2006-07-15,10739.4;11185.7;11228.0,5277.34643734644;5496.65847665848;5517.44471744472" - ; "2006-08-15,11076.2;11381.2;11382.9,5432.17263364394;5581.75576262874;5582.58950465915" - ; "2006-09-15,11331.4;11679.1;11718.5,5584.7215377033;5756.08674223756;5775.50517496304" - ; "2006-10-15,11670.4;12080.7;12163.7,5783.15163528246;5986.47175421209;6027.60158572844" - ; "2006-11-15,11986.0;12221.9;12342.6,5948.38709677419;6065.45905707196;6125.35980148883" - ; "2006-12-15,12194.1;12463.2;12510.6,6042.66600594648;6176.01585728444;6199.50445986125" - ; "2007-01-15,12398.0;12621.7;12621.8,6125.00988064185;6235.52485969489;6235.57426290412" - ; "2007-02-15,12216.2;12268.6;12786.6,6003.07618219254;6028.82569447516;6283.37239986437" - ; "2007-03-15,12050.4;12354.4;12481.0,5868.16782889867;6016.20631890607;6077.85655849468" - ; "2007-04-15,12382.3;13062.9;13120.9,5990.87504717301;6320.16682310364;6348.22871408804" - ; "2007-05-15,13136.1;13627.6;13633.1,6316.9815675959;6553.33759719931;6555.9824764726" - ; "2007-06-15,13266.7;13408.6;13676.3,6367.44547688527;6435.55137459684;6564.03586238673" - ; "2007-07-15,13212.0;13212.0;14000.4,6342.8052943125;6342.8052943125;6721.29967018565" - ; "2007-08-15,12845.8;13357.7;13657.9,6178.3307762232;6424.53479032499;6568.91932838585" - ; "2007-09-15,13113.4;13895.6;13912.9,6289.70214398772;6664.87601323804;6673.17377332246" - ; "2007-10-15,13522.0;13930.0;14164.5,6471.83826626335;6667.11337443045;6779.34870008041" - ; "2007-11-15,12743.4;13371.7;13660.9,6063.17532365578;6362.11383738468;6499.71214738054" - ; "2007-12-15,13167.2;13264.8;13727.0,6269.02054885829;6315.48877335314;6535.54628730313" - ; "2008-01-15,11971.2;12650.4;13056.7,5671.40420693576;5993.17794201251;6185.66420314573" - ; "2008-02-15,12182.1;12266.4;12743.2,5754.60690717218;5794.42872461536;6019.66054616827" - ; "2008-03-15,11740.2;12262.9;12548.6,5498.20164100259;5742.99389307257;5876.79367577086" - ; "2008-04-15,12302.1;12820.1;12891.9,5726.62145114815;5967.75019434604;6001.17305875069" - ; "2008-05-15,12479.6;12638.3;13058.2,5760.73710255179;5833.99497765796;6027.82599062004" - ; "2008-06-15,11346.5;11350.0;12604.5,5185.430614903;5187.03013961566;5760.34549733793" - ; "2008-07-15,10962.5;11378.0;11632.4,4983.77007146624;5172.66461784656;5288.31990689385" - ; "2008-08-15,11284.2;11543.6;11782.4,5150.58013748026;5268.98113069753;5377.97942360534" - ; "2008-09-15,10365.5;10850.7;11532.9,4737.79955481002;4959.57181316647;5271.387630666" - ; "2008-10-15,8175.77;9325.01;10831.1,3775.06429702687;4305.71216171914;5001.13125828243" - ; "2008-11-15,7552.29;8829.04;9625.28,3555.27362598564;4156.30928563022;4531.14275626692" - ; "2008-12-15,8149.09;8776.39;8934.18,3876.31048195293;4174.70080103507;4249.75740624465" - ; "2009-01-15,7949.09;8000.86;9034.69,3764.78973965512;3789.30866758548;4278.94365430064" - ; "2009-02-15,7062.93;7062.93;8280.59,3328.54052678458;3328.54052678458;3902.38603535461" - ; "2009-03-15,6547.05;7608.92;7924.56,3077.93746385908;3577.15000305582;3725.54052719913" - ; "2009-04-15,7761.60;8168.12;8185.73,3639.84243106359;3830.48208591259;3838.74038641906" - ; "2009-05-15,8212.41;8500.33;8574.65,3840.15879844381;3974.79144845129;4009.54380517732" - ; "2009-06-15,8299.86;8447.00;8799.26,3847.99692155054;3916.21424895569;4079.52970193748" - ; "2009-07-15,8146.52;9171.61;9171.61,3782.90326025883;4258.91219451036;4258.91219451036" - ; "2009-08-15,9135.34;9496.28;9580.63,4232.5768877934;4399.8072592826;4438.88821965029" - ; "2009-09-15,9280.67;9712.28;9829.87,4297.2232125907;4497.07133894216;4551.51896800004" - ; "2009-10-15,9487.67;9712.73;10092.2,4388.84340147194;4492.9525342659;4668.48924723722" - ] -;; diff --git a/examples/dygraph/stock_data.mli b/examples/dygraph/stock_data.mli deleted file mode 100644 index 103e8d04..00000000 --- a/examples/dygraph/stock_data.mli +++ /dev/null @@ -1,3 +0,0 @@ -open Core - -val data : (Date.t * float * float) list diff --git a/examples/effect_poller/dune b/examples/effect_poller/dune deleted file mode 100644 index 3ccf9db2..00000000 --- a/examples/effect_poller/dune +++ /dev/null @@ -1,6 +0,0 @@ -(executables - (modes byte exe) - (names main) - (libraries bonsai_web) - (preprocess - (pps ppx_bonsai ppx_jane))) diff --git a/examples/effect_poller/index.html b/examples/effect_poller/index.html deleted file mode 100644 index 990495e5..00000000 --- a/examples/effect_poller/index.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - - Hello, Effect Poller! - - - -
- - diff --git a/examples/effect_poller/main.ml b/examples/effect_poller/main.ml deleted file mode 100644 index 06fbde8f..00000000 --- a/examples/effect_poller/main.ml +++ /dev/null @@ -1,46 +0,0 @@ -open! Core -open! Async_kernel -open! Bonsai_web.Cont -open Bonsai.Let_syntax - -let fake_slow_capitalize_string_rpc = - Effect.of_deferred_fun (fun text -> - let rand_delay = Random.float_range 0.0 1.0 in - let%map.Deferred () = Async_kernel.after (Time_ns.Span.of_sec rand_delay) in - String.uppercase text) -;; - -let textbox graph = - let state = - Tuple2.uncurry Bonsai.both - @@ Bonsai.state "" ~sexp_of_model:[%sexp_of: String.t] ~equal:[%equal: String.t] graph - in - let%arr text, set_text = state in - let view = - Vdom.Node.input - ~attrs:[ Vdom.Attr.(value_prop text @ on_input (fun _ -> set_text)) ] - () - in - text, view -;; - -let component graph = - let%sub text, view = textbox graph in - let capitalized = - Bonsai.Edge.Poll.( - effect_on_change - ~sexp_of_input:[%sexp_of: String.t] - ~sexp_of_result:[%sexp_of: String.t] - ~equal_input:[%equal: String.t] - ~equal_result:[%equal: String.t] - (Starting.initial "") - text - ~effect:(Bonsai.return fake_slow_capitalize_string_rpc) - graph) - in - let%arr view = view - and capitalized = capitalized in - Vdom.Node.div [ view; Vdom.Node.text capitalized ] -;; - -let () = Bonsai_web.Start.start component diff --git a/examples/effect_poller/main.mli b/examples/effect_poller/main.mli deleted file mode 100644 index 74bb7298..00000000 --- a/examples/effect_poller/main.mli +++ /dev/null @@ -1 +0,0 @@ -(*_ This signature is deliberately empty. *) diff --git a/examples/element_size_util/dune b/examples/element_size_util/dune deleted file mode 100644 index 6dd51c65..00000000 --- a/examples/element_size_util/dune +++ /dev/null @@ -1,6 +0,0 @@ -(executables - (modes byte exe) - (names main) - (libraries bonsai_web bonsai_web_ui_element_size_hooks) - (preprocess - (pps ppx_jane ppx_bonsai ppx_css))) diff --git a/examples/element_size_util/index.html b/examples/element_size_util/index.html deleted file mode 100644 index 0201ee5e..00000000 --- a/examples/element_size_util/index.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - - Element_size_util example - - - -
- - diff --git a/examples/element_size_util/main.ml b/examples/element_size_util/main.ml deleted file mode 100644 index cb35d8ef..00000000 --- a/examples/element_size_util/main.ml +++ /dev/null @@ -1,273 +0,0 @@ -open! Core -open! Bonsai_web.Cont -open Bonsai.Let_syntax -module Size_hooks = Bonsai_web_ui_element_size_hooks - -module Page = struct - type t = - | Bulk_size - | Size - | Visibility - | Resizer - | Fit - | Position - | Scroll - [@@deriving enumerate, sexp, compare, equal] -end - -module Size = struct - type t = - { width : float - ; height : float - } - [@@deriving sexp, equal] -end - -let bulk_size_component graph = - let state = Size_hooks.Bulk_size_tracker.component (module Int) Prune_stale graph in - let%arr sizes, size_attr = state in - let mk i = - let key = sprintf "resizable-using-css-%d" i in - let attr = Vdom.Attr.many [ Style.resizable_using_css; size_attr i ] in - Vdom.Node.div ~key ~attrs:[ attr ] [] - in - Vdom.Node.div - [ Vdom.Node.h3 [ Vdom.Node.text "Resize me!" ] - ; sizes - |> [%sexp_of: Size_hooks.Bulk_size_tracker.Dimensions.t Map.M(Int).t] - |> Sexp.to_string_hum - |> Vdom.Node.text - |> List.return - |> Vdom.Node.pre ~attrs:[ Style.pre_for_display ] - ; mk 0 - ; mk 1 - ; mk 2 - ] -;; - -let position graph = - let%sub { positions; get_attr; update } = - Size_hooks.Position_tracker.component (module Int) graph - in - let module Model = struct - type t = Size_hooks.Position_tracker.Position.t Int.Map.t [@@deriving sexp, equal] - end - in - let () = - Bonsai.Edge.on_change - ~sexp_of_model:[%sexp_of: Model.t] - ~equal:[%equal: Model.t] - positions - ~callback: - (Fn.const ((Effect.of_sync_fun print_endline) "position changed!") - |> Bonsai.return) - graph - in - let () = - Bonsai.Clock.every - ~when_to_start_next_effect:`Every_multiple_of_period_blocking - (Time_ns.Span.of_sec 2.0) - update - graph - in - let%arr positions = positions - and get_attr = get_attr in - let mk i = - let attr = Vdom.Attr.many [ Style.resizable_using_css; get_attr i ] in - Vdom.Node.div ~attrs:[ attr ] [] - in - Vdom.Node.div - [ Vdom.Node.h3 [ Vdom.Node.text "Resize me!" ] - ; positions - |> [%sexp_of: Size_hooks.Position_tracker.Position.t Map.M(Int).t] - |> Sexp.to_string_hum - |> Vdom.Node.text - |> List.return - |> Vdom.Node.pre ~attrs:[ Style.pre_for_display ] - ; mk 0 - ; mk 1 - ; mk 2 - ; mk 2 - ] -;; - -let size_component graph = - let state = - Tuple2.uncurry Bonsai.both - @@ Bonsai.state_opt graph ~sexp_of_model:[%sexp_of: Size.t] ~equal:[%equal: Size.t] - in - let%arr size, inject_size = state in - Vdom.Node.div - [ Vdom.Node.h3 [ Vdom.Node.text "Resize me!" ] - ; Vdom.Node.div - ~key:"resizable-using-css" - ~attrs: - [ Style.resizable_using_css - ; Size_hooks.Size_tracker.on_change (fun ~width ~height -> - inject_size (Some Size.{ width; height })) - ] - [ Vdom.Node.textf !"%{sexp:Size.t option}" size ] - ] -;; - -let fit _graph = - let open Vdom in - let make s behavior = - Node.div - [ Node.h2 [ Node.text s ] - ; Node.div - ~key:"resizable-using-css" - ~attrs: - [ Style.resizable_using_css - ; Bonsai_web_ui_element_size_hooks.Resize_to_fit.attr_for_parent__recommended - ] - [ Node.span - ~attrs: - [ Bonsai_web_ui_element_size_hooks.Resize_to_fit.attr ~behavior () - ; Vdom.Attr.create "contenteditable" "" - ; Vdom.Attr.style (Css_gen.outline ~style:`None ()) - ] - [ Node.text "hello world" ] - ] - ] - in - Bonsai.return - (Node.div - [ make "shrink to avoid overflow" Shrink_to_avoid_overflow - ; make "grow to fill" Grow_to_fill - ; make "grow or shrink to match parent size" Grow_or_shrink_to_match_parent_size - ]) -;; - -let visibility_component graph = - let open Size_hooks.Visibility_tracker in - let pos_x = Tuple2.uncurry Bonsai.both @@ Bonsai.state 0.0 graph in - let pos_y = Tuple2.uncurry Bonsai.both @@ Bonsai.state 0.0 graph in - let client_rect, set_client_rect = Bonsai.state_opt graph in - let visible_rect, set_visible_rect = Bonsai.state_opt graph in - let%arr pos_x, inject_pos_x = pos_x - and pos_y, inject_pos_y = pos_y - and client_rect = client_rect - and set_visible_rect = set_visible_rect - and set_client_rect = set_client_rect - and visible_rect = visible_rect in - let pos_to_color pos = pos /. 2000. *. 360. |> Float.iround_down_exn in - let attributes = - [ Style.visibility_child - ; Vdom.Attr.style - (let h = pos_to_color pos_y in - let s = Percent.of_mult (1.0 -. (pos_x /. 2000.)) in - Css_gen.background_color - (`HSLA (Css_gen.Color.HSLA.create ~h ~s ~l:(Percent.of_mult 0.5) ()))) - ; Size_hooks.Visibility_tracker.detect - () - ~visible_rect_changed:(fun bounds -> - Effect.Many - [ (match bounds with - | Some bounds -> - Effect.Many [ inject_pos_x bounds.min_x; inject_pos_y bounds.min_y ] - | None -> Effect.Ignore) - ; set_visible_rect bounds - ]) - ~client_rect_changed:(Fn.compose set_client_rect Option.some) - ] - in - Vdom.Node.div - [ Vdom.Node.h3 [ Vdom.Node.text "Scroll me!" ] - ; Vdom.Node.sexp_for_debugging - [%message (client_rect : Bbox.t option) (visible_rect : Bbox.t option)] - ; Vdom.Node.div - ~attrs:[ Style.outer_visibility_parent; Style.resizable_using_css ] - [ Vdom.Node.div - ~attrs:[ Style.inner_visibility_parent; Style.resizable_using_css ] - [ Vdom.Node.div ~attrs:attributes [] ] - ; Vdom.Node.div - ~attrs:[ Style.inner_visibility_parent ] - [ Vdom.Node.text "padding..." ] - ] - ] -;; - -let buttons current inject = - let make_button_for_tab page = - let click_handler = Vdom.Attr.on_click (fun _ -> inject page) in - let attr = - if Page.equal page current - then Vdom.Attr.(Style.primary @ click_handler) - else click_handler - in - Vdom.Node.button - ~attrs:[ attr ] - [ page |> Page.sexp_of_t |> Sexp.to_string |> Vdom.Node.text ] - in - Page.all |> List.map ~f:make_button_for_tab |> Vdom.Node.div -;; - -let resizer_component _graph = - Bonsai.return - (Vdom.Node.div - [ Vdom.Node.h3 [ Vdom.Node.text "Resize me!" ] - ; Vdom.Node.div - ~attrs:[ Style.resizable_using_resizer ] - [ Vdom.Node.text (String.concat (List.init 20 ~f:(Fn.const "Hello world. "))) - ; Vdom.Node.div - ~attrs:[ Style.resizer; Size_hooks.Expert.Resizer.attr ~side:Right ] - [] - ] - ]) -;; - -let scroll_tracker_component graph = - let state = - Tuple2.uncurry Bonsai.both - @@ Bonsai.state_opt - graph - ~sexp_of_model:[%sexp_of: Size_hooks.Scroll_tracker.Scrollable.t] - ~equal:[%equal: Size_hooks.Scroll_tracker.Scrollable.t] - in - let%arr scrollable, set_scrollable = state in - let status = - match scrollable with - | None -> "" - | Some scrollable -> - scrollable |> Size_hooks.Scroll_tracker.Scrollable.sexp_of_t |> Sexp.to_string - in - Vdom.Node.div - [ Vdom.Node.h3 [ Vdom.Node.text "Resize me!" ] - ; Vdom.Node.div - ~key:"resizable-using-css" - ~attrs:[ Style.resizable_using_css ] - [ Vdom.Node.div - ~attrs: - [ Size_hooks.Scroll_tracker.on_change (fun scrollable -> - set_scrollable (Some scrollable)) - ; Style.resizable_using_css - ; Style.child_with_size - ] - [ Vdom.Node.textf "You have these scrollbars: %s" status ] - ] - ] -;; - -let component graph = - let page, inject_page = - Bonsai.state Bulk_size ~sexp_of_model:[%sexp_of: Page.t] ~equal:[%equal: Page.t] graph - in - let page_component = - match%sub page with - | Bulk_size -> bulk_size_component graph - | Size -> size_component graph - | Visibility -> visibility_component graph - | Resizer -> resizer_component graph - | Fit -> fit graph - | Position -> position graph - | Scroll -> scroll_tracker_component graph - in - let%arr page_component = page_component - and page = page - and inject_page = inject_page in - let buttons = buttons page inject_page in - Vdom.Node.div [ buttons; page_component ] -;; - -let () = Bonsai_web.Start.start component diff --git a/examples/element_size_util/main.mli b/examples/element_size_util/main.mli deleted file mode 100644 index 74bb7298..00000000 --- a/examples/element_size_util/main.mli +++ /dev/null @@ -1 +0,0 @@ -(*_ This signature is deliberately empty. *) diff --git a/examples/element_size_util/style.ml b/examples/element_size_util/style.ml deleted file mode 100644 index 631fb8dd..00000000 --- a/examples/element_size_util/style.ml +++ /dev/null @@ -1,78 +0,0 @@ -open! Core - -include - [%css - stylesheet - {| - * { - box-sizing: border-box; - font-family: sans-serif; - } - - .pre_for_display { - height: 50px; - } - - .primary { - background-color: rgb(30,30,30); - color: rgb(240, 240, 240); - } - - .resizable_using_css { - background-color: rgb(30,30,30); - color: rgb(240, 240, 240); - resize: both; - overflow: auto; - width: 200px; - height: 100px; - border: 2px solid var(--js-form-unfocused-color); - } - - .visibility_child { - height: 2000px; - width: 2000px; - } - - .inner_visibility_parent { - height: 200px; - width: 200px; - overflow: scroll; - } - - .outer_visibility_parent { - height: 230px; - width: 230px; - overflow: scroll; - } - - .resizer { - height: 100%; - width: 5px; - display: inline-block; - margin-left: 2px; - background-color: rgb(30, 30, 30); - right: 0px; - top: 0px; - position: absolute; - cursor: col-resize; - transition: all 150ms; - } - - .resizer:active { - background-color: rgb(100, 100, 100); - } - - .resizable_using_resizer { - width: 300px; - border: 2px solid rgb(30, 30, 30); - position: relative; - overflow: hidden; - } - - .child_with_size { - width: 200px; - height: 200px; - border: 2px solid red; - } -|} - ~rewrite:[ "--js-form-unfocused-color", "--js-form-unfocused-color" ]] diff --git a/examples/element_size_util/style.mli b/examples/element_size_util/style.mli deleted file mode 100644 index 62998a77..00000000 --- a/examples/element_size_util/style.mli +++ /dev/null @@ -1,12 +0,0 @@ -open! Core -open Bonsai_web.Cont - -val primary : Vdom.Attr.t -val resizable_using_css : Vdom.Attr.t -val resizable_using_resizer : Vdom.Attr.t -val pre_for_display : Vdom.Attr.t -val visibility_child : Vdom.Attr.t -val inner_visibility_parent : Vdom.Attr.t -val outer_visibility_parent : Vdom.Attr.t -val resizer : Vdom.Attr.t -val child_with_size : Vdom.Attr.t diff --git a/examples/extensible_list/dune b/examples/extensible_list/dune deleted file mode 100644 index d2723d0d..00000000 --- a/examples/extensible_list/dune +++ /dev/null @@ -1,6 +0,0 @@ -(executables - (modes byte exe) - (names main) - (libraries bonsai_web bonsai_web_ui_extendy bonsai_web_counters_example) - (preprocess - (pps ppx_bonsai ppx_jane ppx_pattern_bind))) diff --git a/examples/extensible_list/index.html b/examples/extensible_list/index.html deleted file mode 100644 index c63c0f85..00000000 --- a/examples/extensible_list/index.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - - Extensible List! - - - -
- - diff --git a/examples/extensible_list/main.ml b/examples/extensible_list/main.ml deleted file mode 100644 index 4f4a21e4..00000000 --- a/examples/extensible_list/main.ml +++ /dev/null @@ -1,28 +0,0 @@ -open! Core -open! Bonsai_web.Cont -open Bonsai.Let_syntax -module Extendy = Bonsai_web_ui_extendy - -let component graph = - let wrap_remove view remove_event = - Vdom.Node.div - [ Vdom.Node.button - ~attrs:[ Vdom.Attr.on_click (fun _ -> remove_event) ] - [ Vdom.Node.text "X" ] - ; view - ] - in - let%sub { contents; append; _ } = - Extendy.component' Bonsai_web_counters_example.single_counter ~wrap_remove graph - in - let%arr contents = contents - and append = append in - let views = Map.data contents in - Vdom.Node.div - (Vdom.Node.button - ~attrs:[ Vdom.Attr.on_click (fun _ -> append) ] - [ Vdom.Node.text "Add" ] - :: views) -;; - -let () = Bonsai_web.Start.start component diff --git a/examples/extensible_list/main.mli b/examples/extensible_list/main.mli deleted file mode 100644 index e69de29b..00000000 diff --git a/examples/favicon_svg/dune b/examples/favicon_svg/dune deleted file mode 100644 index 88ae5440..00000000 --- a/examples/favicon_svg/dune +++ /dev/null @@ -1,7 +0,0 @@ -(executables - (modes byte exe) - (names main) - (libraries async_kernel async_js bonsai_web core favicon_svg - virtual_dom.input_widgets) - (preprocess - (pps ppx_bonsai ppx_jane js_of_ocaml-ppx ppx_css ppx_pattern_bind))) diff --git a/examples/favicon_svg/index.html b/examples/favicon_svg/index.html deleted file mode 100644 index 940ae1f2..00000000 --- a/examples/favicon_svg/index.html +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - Favicon svg example - - - - -
- - - diff --git a/examples/favicon_svg/main.ml b/examples/favicon_svg/main.ml deleted file mode 100644 index 30689e1d..00000000 --- a/examples/favicon_svg/main.ml +++ /dev/null @@ -1,232 +0,0 @@ -open! Core -open Bonsai_web.Cont - -module Style = -[%css -stylesheet - {| -.svg-preview { - width: 200px; - height: 200px; - border-style:solid; -} - -.container { - display:flex; - flex-direction:column; - align-items: center; -} - -.section-header { - color: #999999; - font-style: italic; -} -|}] - -let widget uri : Vdom.Node.t = - (* [init] is called whenever [uri] changes, updating the favicon. The DOM element - produced by the widget is unused. *) - Vdom.Node.widget - ~id:(Type_equal.Id.create ~name:"favicon" (const [%sexp "favicon"])) - ~init:(fun () -> - let open Js_of_ocaml in - let icon_node = Dom_html.document##querySelector (Js.string {|link[rel="icon"]|}) in - let href_value = uri |> Uri.to_string in - (match Js.Opt.to_option icon_node with - | Some icon_node -> - icon_node##setAttribute (Js.string "href") (Js.string href_value) - | None -> - let head = Dom_html.document##querySelector (Js.string "head") in - let link = - let open Vdom in - Node.create - "link" - ~attrs: - [ Attr.type_ "image/svg+xml" - ; Attr.create "rel" "icon" - ; Attr.href href_value - ] - [] - |> Node.to_dom - in - let link = (link :> Dom.node Js.t) in - Js.Opt.iter head (fun head -> ignore (head##appendChild link : Dom.node Js.t))); - (), Vdom.Node.to_dom Vdom.Node.none) - () -;; - -let slider ~min ~max ~value ~inject = - let open Vdom in - Node.input - ~attrs: - [ Attr.type_ "range" - ; Attr.min min - ; Attr.max max - ; Attr.value (value |> string_of_int) - ; Attr.on_input (fun _ev value -> inject (int_of_string value)) - ] - () -;; - -let component graph = - let open Bonsai.Let_syntax in - let text = - Tuple2.uncurry Bonsai.both - @@ Bonsai.state_opt - graph - ~sexp_of_model:[%sexp_of: String.t] - ~equal:[%equal: String.t] - ~default_model:"🤯" - in - let size = - Tuple2.uncurry Bonsai.both - @@ Bonsai.state 80 ~sexp_of_model:[%sexp_of: Int.t] ~equal:[%equal: Int.t] graph - in - let pos_x = - Tuple2.uncurry Bonsai.both - @@ Bonsai.state 50 ~sexp_of_model:[%sexp_of: Int.t] ~equal:[%equal: Int.t] graph - in - let pos_y = - Tuple2.uncurry Bonsai.both - @@ Bonsai.state 50 ~sexp_of_model:[%sexp_of: Int.t] ~equal:[%equal: Int.t] graph - in - let fg_color = - Tuple2.uncurry Bonsai.both - @@ Bonsai.state - "#000000" - ~sexp_of_model:[%sexp_of: String.t] - ~equal:[%equal: String.t] - graph - in - let bg_color = - Tuple2.uncurry Bonsai.both - @@ Bonsai.state - "#ffffff" - ~sexp_of_model:[%sexp_of: String.t] - ~equal:[%equal: String.t] - graph - in - let%arr text, inject_text = text - and size, inject_size = size - and pos_x, inject_pos_x = pos_x - and pos_y, inject_pos_y = pos_y - and fg_color, inject_fg_color = fg_color - and bg_color, inject_bg_color = bg_color in - let open Vdom in - let text_box = - Vdom_input_widgets.Entry.text - ~merge_behavior:Legacy_dont_merge - ~value:text - ~on_input:inject_text - ~allow_updates_when_focused:`Never - () - in - let size_slider = slider ~min:1. ~max:200. ~value:size ~inject:inject_size in - let x_slider = slider ~min:0. ~max:100. ~value:pos_x ~inject:inject_pos_x in - let y_slider = slider ~min:0. ~max:100. ~value:pos_y ~inject:inject_pos_y in - let background_color = - if String.equal "#ffffff" bg_color then None else Some (`Hex bg_color) - in - let favicon = - Favicon_svg.of_unicode - ~font_size:(Percent.of_percentage (float_of_int size)) - ~pos_x:(Percent.of_percentage (float_of_int pos_x)) - ~pos_y:(Percent.of_percentage (float_of_int pos_y)) - ?background_color - ~font_color:(`Hex fg_color) - (Option.value text ~default:"") - in - let uri = Favicon_svg.to_embedded_url favicon in - let image = - Node.create "img" ~attrs:[ Attr.src (Uri.to_string uri); Style.svg_preview ] [] - in - let code_section = - match text with - | None -> [] - | Some text -> - let attr fmt cond value = Option.some_if (cond value) (fmt value ^ "\n ") in - let non eq x y = not (eq x y) in - let attrs = - [ attr - (sprintf "~font_size:(Percent.of_percentage %.1f)") - (Fn.const true) - (float_of_int size) - ; attr - (sprintf "~pos_x:(Percent.of_percentage %.1f)") - (non Float.equal 50.) - (float_of_int pos_x) - ; attr - (sprintf "~pos_y:(Percent.of_percentage %.1f)") - (non Float.equal 50.) - (float_of_int pos_y) - ; attr (sprintf {|~font_color:(`Hex "%s")|}) (non String.equal "#000000") fg_color - ; attr - (sprintf {|~background_color:(`Hex "%s")|}) - (non String.equal "#ffffff") - bg_color - ] - in - let attrs = List.filter_opt attrs |> String.concat in - [ Node.div ~attrs:[ Style.section_header ] [ Node.text "Code:" ] - ; Node.pre - [ [%string {| - Favicon_svg.of_unicode - %{attrs}"%{text}" |}] - |> String.strip - |> Node.text - ] - ] - in - let spacer x = Node.div ~attrs:[ Attr.style (Css_gen.height (`Px x)) ] [] in - Node.div - ~attrs:[ Style.container ] - ([ widget uri - ; Node.h3 - [ Node.text "What would you like " - ; Node.create "i" [ Node.text "your" ] - ; Node.text " favicon to say?" - ] - ; spacer 10 - ; text_box - ; spacer 20 - ; Node.div ~attrs:[ Style.section_header ] [ Node.text "Preview:" ] - ; spacer 10 - ; image - ; spacer 20 - ; Node.div ~attrs:[ Style.section_header ] [ Node.text "Fine tuning:" ] - ; spacer 5 - ; Node.div [ Node.text "Size: "; size_slider ] - ; Node.div [ Node.text "Pos x: "; x_slider ] - ; Node.div [ Node.text "Pos y: "; y_slider ] - ; Node.div - [ Node.text "Text color: " - ; Node.input - ~attrs: - [ Attr.type_ "color" - ; Attr.value fg_color - ; Attr.on_input (fun _ev value -> inject_fg_color value) - ] - () - ] - ; spacer 5 - ; Node.div - [ Node.text "Background color: " - ; Node.input - ~attrs: - [ Attr.type_ "color" - ; Attr.value bg_color - ; Attr.on_input (fun _ev value -> inject_bg_color value) - ] - () - ] - ; spacer 20 - ] - @ code_section) -;; - -let run () = - Async_js.init (); - Bonsai_web.Start.start component -;; - -let () = run () diff --git a/examples/favicon_svg/main.mli b/examples/favicon_svg/main.mli deleted file mode 100644 index f8ed7260..00000000 --- a/examples/favicon_svg/main.mli +++ /dev/null @@ -1 +0,0 @@ -(*_ js executable *) diff --git a/examples/feather_icons/controls.ml b/examples/feather_icons/controls.ml deleted file mode 100644 index 7fdf4b12..00000000 --- a/examples/feather_icons/controls.ml +++ /dev/null @@ -1,215 +0,0 @@ -open! Core -open! Import - -type t = - { size : int - ; stroke_width : float - ; stroke : [ `Hex of string ] - ; fill : [ `Hex of string ] option - } -[@@deriving sexp, fields ~iterators:to_list] - -let default = { size = 24; stroke_width = 2.; stroke = `Hex "#000000"; fill = None } - -module Range = -[%css -stylesheet - {| -.class_ { - height: 4px; - cursor: pointer; - appearance: none; - background-color: #d1d5da; - width: 100%; -} - |}] - -let size_slider = - Form.Elements.Range.int - ~extra_attrs:(Bonsai.return [ Range.class_ ]) - ~min:12 - ~max:100 - ~default:default.size - ~step:8 - ~allow_updates_when_focused:`Never - () -;; - -let stroke_width_slider = - Form.Elements.Range.float - ~extra_attrs:(Bonsai.return [ Range.class_ ]) - ~min:0.5 - ~max:3. - ~default:default.stroke_width - ~step:0.25 - ~allow_updates_when_focused:`Never - () -;; - -module Color_input = -[%css -stylesheet {| -.class_ { - cursor: pointer; - height: 3em; - width: 100%; -} - |}] - -let display_none = Vdom.Attr.style (Css_gen.display `None) - -let color_input ?(display = Bonsai.return true) () graph = - let classes_ = Vdom.Attr.many [ Card_like.class_; Color_input.class_ ] in - let extra_attr = - let%arr display = display in - if display then classes_ else Vdom.Attr.(classes_ @ display_none) - in - Form.Elements.Color_picker.hex ~extra_attr () graph -;; - -module Style = -[%css -stylesheet - {| -.header { - display: flex; - justify-content: space-between; - align-items: center; -} - -.reset { - cursor: pointer; -} - -.label { - font-size: 14px; -} - -.control_container { - display: flex; - flex-direction: column; - gap: 8px; -} - -.controls { - display: flex; - flex-direction: column; - align-items: stretch; - flex: 0 0 200px; - gap: 16px; -} -|}] - -module Fill = struct - type t = - { value : [ `Hex of string ] option - ; reset : unit Effect.t - ; view : Vdom.Node.t - } - - module Fill = - [%css - stylesheet {| -.class_ { - display: flex; - justify-content: space-between; -} -|}] - - let component : Bonsai.graph -> t Bonsai.t = - (* Equal to --js-primary-color *) - fun graph -> - let default_fill_color = `Hex "#2085ef" in - let fill_toggle = Form.Elements.Toggle.bool ~default:false () graph in - let fill_on = - let%arr fill_toggle = fill_toggle in - Form.value_or_default fill_toggle ~default:false - in - let fill_input = - let form = color_input ~display:fill_on () graph in - Form.Dynamic.with_default (Bonsai.return default_fill_color) form graph - in - let%arr fill_toggle = fill_toggle - and fill_on = fill_on - and fill_input = fill_input in - let value = - match fill_on with - | false -> None - | true -> - (match Form.value fill_input with - | Error _ -> default.fill - | Ok color -> Some color) - in - let view = - Vdom.Node.div - ~attrs:[ Style.control_container ] - (Vdom.Node.div - ~attrs:[ Fill.class_ ] - [ Vdom.Node.label ~attrs:[ Style.label ] [ Vdom.Node.text "Fill" ] - ; Form.view_as_vdom fill_toggle - ] - :: (Form.view fill_input |> Form.View.to_vdom_plain)) - in - let reset = - Effect.Many [ Form.set fill_toggle false; Form.set fill_input default_fill_color ] - in - { value; reset; view } - ;; -end - -let component graph = - let size_slider = size_slider graph in - let stroke_width_slider = stroke_width_slider graph in - let stroke_input = color_input () graph in - let fill = Fill.component graph in - let%arr size_slider = size_slider - and stroke_width_slider = stroke_width_slider - and stroke_input = stroke_input - and { value = fill; reset = reset_fill; view = fill_view } = fill in - (* We could have several of these forms using Form.Typed.Record, but - refrained in order to retain control over the UI layout. *) - let t = - let size = Form.value_or_default size_slider ~default:default.size in - let stroke_width = - Form.value_or_default stroke_width_slider ~default:default.stroke_width - in - let stroke = Form.value_or_default stroke_input ~default:default.stroke in - { size; stroke_width; stroke; fill } - in - let reset = - Vdom.Node.button - ~attrs: - [ Vdom.Attr.many [ Card_like.class_; Style.reset ] - ; Vdom.Attr.on_click (fun _ -> - let { size; stroke_width; stroke; fill = _ } = default in - Fields.to_list - ~size:(fun _ -> Form.set size_slider size) - ~stroke_width:(fun _ -> Form.set stroke_width_slider stroke_width) - ~stroke:(fun _ -> Form.set stroke_input stroke) - ~fill:(fun _ -> reset_fill) - |> Effect.Many) - ] - [ Vdom.Node.text "Reset" ] - in - let header = - let title = Vdom.Node.h3 [ Vdom.Node.text "Controls" ] in - Vdom.Node.div ~attrs:[ Style.header ] [ title; reset ] - in - let control ~form ~label = - Vdom.Node.div - ~attrs:[ Style.control_container ] - (Vdom.Node.label ~attrs:[ Style.label ] [ Vdom.Node.text label ] - :: (Form.view form |> Form.View.to_vdom_plain)) - in - let size = control ~form:size_slider ~label:(sprintf "Size: %dpx" t.size) in - let stroke_width = - control ~form:stroke_width_slider ~label:(sprintf "Stroke width: %gpx" t.stroke_width) - in - let stroke = control ~form:stroke_input ~label:"Stroke" in - let view = - Vdom.Node.div - ~attrs:[ Style.controls ] - [ header; size; stroke_width; stroke; fill_view ] - in - t, view -;; diff --git a/examples/feather_icons/controls.mli b/examples/feather_icons/controls.mli deleted file mode 100644 index 04bffa92..00000000 --- a/examples/feather_icons/controls.mli +++ /dev/null @@ -1,12 +0,0 @@ -open! Core -open! Import - -type t = - { size : int - ; stroke_width : float - ; stroke : [ `Hex of string ] - ; fill : [ `Hex of string ] option - } - -(** The part on the right where you can control the size/color/etc. of all the icons. *) -val component : Bonsai.graph -> (t * Vdom.Node.t) Bonsai.t diff --git a/examples/feather_icons/dune b/examples/feather_icons/dune deleted file mode 100644 index 7b84d193..00000000 --- a/examples/feather_icons/dune +++ /dev/null @@ -1,6 +0,0 @@ -(executables - (modes byte exe) - (names main) - (libraries bonsai_web bonsai_web_ui_form feather_icon fuzzy_match.match) - (preprocess - (pps ppx_jane ppx_bonsai ppx_css))) diff --git a/examples/feather_icons/icon_grid.ml b/examples/feather_icons/icon_grid.ml deleted file mode 100644 index fa06a388..00000000 --- a/examples/feather_icons/icon_grid.ml +++ /dev/null @@ -1,67 +0,0 @@ -open! Core -open! Import - -module Style = -[%css -stylesheet - {| -.cards { - display: flex; - flex-wrap: wrap; - gap: 16px; -} - -.card { - display: flex; - flex-direction: column; - align-items: center; - height: 165px; - width: 165px; - padding-bottom: 16px; -} - -.label { - flex: 0 0 auto; - font-size: 14px; - text-align: center; - padding: 0 16px; - word-break: break-word; -} - -.icon { - flex: 1 1 auto; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; -} - |}] - -let icon_div icon ~controls = - let { Controls.size; stroke_width; stroke; fill } = controls in - let icon = - Feather_icon.svg - icon - ~size:(`Px size) - ~stroke_width:(`Px_float stroke_width) - ~stroke - ?fill - in - Vdom.Node.div ~attrs:[ Style.icon ] [ icon ] -;; - -let component ~icons ~controls = - Vdom.Node.div - ~attrs:[ Style.cards ] - (List.map icons ~f:(fun icon -> - let icon_div = icon_div icon ~controls in - let label_span = - Vdom.Node.span - ~attrs:[ Style.label ] - [ Vdom.Node.text (Feather_icon.to_string icon) ] - in - Vdom.Node.div - ~key:(Feather_icon.to_string icon) - ~attrs:[ Card_like.class_; Style.card ] - [ icon_div; label_span ])) -;; diff --git a/examples/feather_icons/icon_grid.mli b/examples/feather_icons/icon_grid.mli deleted file mode 100644 index 9ef657a9..00000000 --- a/examples/feather_icons/icon_grid.mli +++ /dev/null @@ -1,5 +0,0 @@ -open! Core -open! Import - -(** The main grid of all the icons *) -val component : icons:Feather_icon.t list -> controls:Controls.t -> Vdom.Node.t diff --git a/examples/feather_icons/import.ml b/examples/feather_icons/import.ml deleted file mode 100644 index 3233bd3a..00000000 --- a/examples/feather_icons/import.ml +++ /dev/null @@ -1,17 +0,0 @@ -open! Core -include Bonsai_web.Cont -include Bonsai.Let_syntax -module Form = Bonsai_web_ui_form.With_automatic_view - -module Card_like = -[%css -stylesheet - {| -.class_ { - border-radius: 8px; - background-color: white; - box-shadow: rgb(0 0 0 / 10%) 0px 4px 6px -1px, rgb(0 0 0 / 6%) 0px 2px 4px -1px; - border: none; - padding: 8px; -} -|}] diff --git a/examples/feather_icons/index.html b/examples/feather_icons/index.html deleted file mode 100644 index 67edca81..00000000 --- a/examples/feather_icons/index.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - - Feather Icons - - - -
- - diff --git a/examples/feather_icons/main.ml b/examples/feather_icons/main.ml deleted file mode 100644 index f3923425..00000000 --- a/examples/feather_icons/main.ml +++ /dev/null @@ -1,79 +0,0 @@ -open! Core -open! Import - -(* global styles *) -let () = - Inline_css.Private.append - {| -*, *::before, *::after { - box-sizing: border-box; -} - -body { - background-color: #f6f8fa; - min-height: 100vh; - width: 100%; - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; -} - -h3 { - margin: 0; -} -|} -;; - -module Left_section = -[%css -stylesheet - {| -.class_ { - display: flex; - flex: 1 1 auto; - flex-direction: column; - gap: 48px; -} -|}] - -let left_section ~controls graph = - let%sub icons, search_bar = Search_bar.component graph in - let%arr icons = icons - and search_bar = search_bar - and controls = controls in - let icon_grid = Icon_grid.component ~icons ~controls in - Vdom.Node.div ~attrs:[ Left_section.class_ ] [ search_bar; icon_grid ] -;; - -module Main = [%css stylesheet {| -.class_ { - display: flex; - gap: 48px; -} -|}] - -let main graph = - let%sub controls, controls_view = Controls.component graph in - let left_section = left_section ~controls graph in - let%arr left_section = left_section - and controls_view = controls_view in - Vdom.Node.main ~attrs:[ Main.class_ ] [ left_section; controls_view ] -;; - -let header = - Vdom.Node.h2 - ~attrs:[ Vdom.Attr.style (Css_gen.font_weight (`Number 400)) ] - [ Vdom.Node.text "Feather icons" ] -;; - -module App = [%css stylesheet {| -.class_ { - padding: 48px; -} -|}] - -let app graph = - let main = main graph in - let%arr main = main in - Vdom.Node.div ~attrs:[ App.class_ ] [ header; main ] -;; - -let () = Bonsai_web.Start.start app diff --git a/examples/feather_icons/main.mli b/examples/feather_icons/main.mli deleted file mode 100644 index 74bb7298..00000000 --- a/examples/feather_icons/main.mli +++ /dev/null @@ -1 +0,0 @@ -(*_ This signature is deliberately empty. *) diff --git a/examples/feather_icons/search_bar.ml b/examples/feather_icons/search_bar.ml deleted file mode 100644 index 6ea24dfe..00000000 --- a/examples/feather_icons/search_bar.ml +++ /dev/null @@ -1,63 +0,0 @@ -open! Core -open! Import - -module Search_container = [%css stylesheet {| -.class_ { - position: relative; -} -|}] - -module Search_icon = -[%css -stylesheet {| -.class_ { - position: absolute; - left: 16px; - top: 8px; -} -|}] - -module Search_bar = -[%css -stylesheet - {| -.class_ { - width: 100%; - height: 40px; - font-size: 16px; - text-indent: 40px; -} -|}] - -let component graph = - let search_bar = - Form.Elements.Textbox.string - ~extra_attrs: - (Bonsai.return [ Vdom.Attr.many [ Card_like.class_; Search_bar.class_ ] ]) - ~placeholder:(Bonsai.return "Search icons") - ~allow_updates_when_focused:`Never - () - graph - in - let%arr search_bar = search_bar in - let icons = - match Form.value search_bar with - | Error error -> - eprint_s [%message "failed to get value from search bar" (error : Error.t)]; - Feather_icon.all - | Ok search -> - List.filter Feather_icon.all ~f:(fun icon -> - let icon_string = Feather_icon.to_string icon in - Fuzzy_match.is_match icon_string ~char_equal:Char.Caseless.equal ~pattern:search) - in - let search_icon = - let gray = `Hex "#959da5" in - Vdom.Node.div ~attrs:[ Search_icon.class_ ] [ Feather_icon.svg Search ~stroke:gray ] - in - let view = - Vdom.Node.div - ~attrs:[ Search_container.class_ ] - (search_icon :: (Form.view search_bar |> Form.View.to_vdom_plain)) - in - icons, view -;; diff --git a/examples/feather_icons/search_bar.mli b/examples/feather_icons/search_bar.mli deleted file mode 100644 index 4aa91392..00000000 --- a/examples/feather_icons/search_bar.mli +++ /dev/null @@ -1,6 +0,0 @@ -open! Core -open! Import - -(** [component] returns the search bar itself (as a Vdom.Node.t) as well as all matching - icons. *) -val component : Bonsai.graph -> (Feather_icon.t list * Vdom.Node.t) Bonsai.t diff --git a/examples/file_download_button/dune b/examples/file_download_button/dune deleted file mode 100644 index 81771375..00000000 --- a/examples/file_download_button/dune +++ /dev/null @@ -1,6 +0,0 @@ -(executables - (modes byte exe) - (names main) - (libraries bonsai_web incr_dom.vdom_file_download) - (preprocess - (pps ppx_jane))) diff --git a/examples/file_download_button/index.html b/examples/file_download_button/index.html deleted file mode 100644 index deac337e..00000000 --- a/examples/file_download_button/index.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - - Hello, File Download! - - - -
- - diff --git a/examples/file_download_button/main.ml b/examples/file_download_button/main.ml deleted file mode 100644 index f5f3fea6..00000000 --- a/examples/file_download_button/main.ml +++ /dev/null @@ -1,17 +0,0 @@ -open! Core -open! Bonsai_web.Cont - -let component _graph = - Bonsai.return - Vdom_file_download.( - Button.create - ~button_text:"click me!" - ~get_download:(fun () -> - create - ~contents:"hello there!" - ~filename:"top_secret.txt" - ~mimetype:"text/plain") - ()) -;; - -let () = Bonsai_web.Start.start component diff --git a/examples/file_download_button/main.mli b/examples/file_download_button/main.mli deleted file mode 100644 index 74bb7298..00000000 --- a/examples/file_download_button/main.mli +++ /dev/null @@ -1 +0,0 @@ -(*_ This signature is deliberately empty. *) diff --git a/examples/finalizer_test/dune b/examples/finalizer_test/dune deleted file mode 100644 index dc7056e2..00000000 --- a/examples/finalizer_test/dune +++ /dev/null @@ -1,7 +0,0 @@ -(executables - (modes byte exe) - (names main) - (libraries core js_of_ocaml core_kernel.weak_pointer core_kernel.weak_array - core_kernel.weak_hashtbl) - (preprocess - (pps js_of_ocaml-ppx ppx_string ppx_demo))) diff --git a/examples/finalizer_test/index.html b/examples/finalizer_test/index.html deleted file mode 100644 index 0bbcc16a..00000000 --- a/examples/finalizer_test/index.html +++ /dev/null @@ -1,33 +0,0 @@ - - - - - Hello, Finalizers! - -