diff options
| author | Sébastien Dailly <sebastien@dailly.me> | 2022-02-07 16:40:45 +0100 | 
|---|---|---|
| committer | Sébastien Dailly <sebastien@dailly.me> | 2022-02-07 16:43:33 +0100 | 
| commit | 8d23a029c57be92a7aed0f18d9fcf1c931c1038e (patch) | |
| tree | 5bce8907c420b171de9f49679045723aad03e247 /editor/editor.ml | |
| parent | 6f1b152a6927171b0c0bfed207307ed1bac1900d (diff) | |
Reformat
Diffstat (limited to 'editor/editor.ml')
| -rwxr-xr-x | editor/editor.ml | 325 | 
1 files changed, 184 insertions, 141 deletions
| diff --git a/editor/editor.ml b/editor/editor.ml index 575e164..d558a7a 100755 --- a/editor/editor.ml +++ b/editor/editor.ml @@ -1,174 +1,217 @@  open Brr  module PM = Prosemirror  module Js = Js_of_ocaml.Js -  module Actions = Editor_actions +let _ = +  Js.Unsafe.global ##. PM := +    object%js +      val commands = Js.Unsafe.js_expr {|require("prosemirror-commands")|} + +      val dropcursor = Js.Unsafe.js_expr {|require("prosemirror-dropcursor")|} + +      val example_setup_ = +        Js.Unsafe.js_expr {|require("prosemirror-example-setup")|} + +      val gapcursor = Js.Unsafe.js_expr {|require("prosemirror-gapcursor")|} + +      val history = Js.Unsafe.js_expr {|require("prosemirror-history")|} + +      val inputrules = Js.Unsafe.js_expr {|require("prosemirror-inputrules")|} + +      val keymap = Js.Unsafe.js_expr {|require("prosemirror-keymap")|} + +      val menu = Js.Unsafe.js_expr {|require("prosemirror-menu")|} + +      val model = Js.Unsafe.js_expr {|require("prosemirror-model")|} + +      val schema_basic_ = +        Js.Unsafe.js_expr {|require("prosemirror-schema-basic")|} + +      val schema_list_ = +        Js.Unsafe.js_expr {|require("prosemirror-schema-list")|} + +      val state = Js.Unsafe.js_expr {|require("prosemirror-state")|} + +      val transform = Js.Unsafe.js_expr {|require("prosemirror-transform")|} + +      val view = Js.Unsafe.js_expr {|require("prosemirror-view")|} +    end + + +(** Load the js-zip library, with browserify *) +let zip = Js.Unsafe.js_expr {|require("jszip")|} +  (** Create a new editor view      [build_view element state] will create the editor and attach it to [element].  *) -let build_view -  : PM.t -> Jstr.t option -> El.t -> PM.View.editor_view Js.t * float -  = fun pm page_id editor -> - -    (* Remove all the elements if any *) -    El.set_children editor []; - -    (* TODO -       This could be improved, instead of creating a new schema, just fetch -       the node and marks from the plungin *) -    let custom_schema = -      Plugins.Footnotes.footnote_schema -        pm -        (PM.SchemaBasic.schema pm) in - -    (* Recreate the full schema by adding all the nodes and marks from the -       plugings *) -    let specs = PM.Model.schema_spec -        (PM.SchemaList.add_list_nodes -           pm -           (custom_schema##.spec##.nodes) -           (Jstr.v "paragraph block*") -           (Some (Jstr.v "block"))) -        (Some custom_schema##.spec##.marks) -        None in -    let full_schema = PM.Model.schema pm specs in -    let stored_content = State.Storage.load page_id in - -    (* This variable contains the last update time, either because it is -       stored, or because it is the date where we create the first page. *) -    let last_backup = Js.Opt.get -        stored_content##.date -        (fun () -> (new%js Js.date_now)##getTime) in - -    let props = PM.View.direct_editor_props () in -    props##.state := State.state_of_storage pm stored_content full_schema; - -    (* Add the custom nodes *) -    props##.nodeViews := PM.O.init -        [| ( "footnote", (Plugins.Footnotes.footnote_view pm)) -        |]; - -    let view = PM.View.editor_view -        pm -        editor -        props in -    view, last_backup +let build_view : +    PM.t -> Jstr.t option -> El.t -> PM.View.editor_view Js.t * float = + fun pm page_id editor -> +  (* Remove all the elements if any *) +  El.set_children editor []; + +  (* TODO +     This could be improved, instead of creating a new schema, just fetch +     the node and marks from the plungin *) +  let custom_schema = +    Plugins.Footnotes.footnote_schema pm (PM.SchemaBasic.schema pm) +  in + +  (* Recreate the full schema by adding all the nodes and marks from the +     plugings *) +  let specs = +    PM.Model.schema_spec +      (PM.SchemaList.add_list_nodes +         pm +         custom_schema##.spec##.nodes +         (Jstr.v "paragraph block*") +         (Some (Jstr.v "block")) ) +      (Some custom_schema##.spec##.marks) +      None +  in +  let full_schema = PM.Model.schema pm specs in +  let stored_content = State.Storage.load page_id in + +  (* This variable contains the last update time, either because it is +     stored, or because it is the date where we create the page. *) +  let last_backup = +    Js.Opt.get stored_content##.date (fun () -> (new%js Js.date_now)##getTime) +  in + +  let props = PM.View.direct_editor_props () in +  props##.state := State.state_of_storage pm stored_content full_schema; + +  (* Add the custom nodes *) +  props##.nodeViews := +    PM.O.init [| ("footnote", Plugins.Footnotes.footnote_view pm) |]; + +  let view = PM.View.editor_view pm editor props in +  (view, last_backup) +  module Store = struct    type t = El.t -  let process -    : t -> State.t -> State.t -    = fun title_element state -> -      let title = El.prop (El.Prop.value) title_element in +  let process : t -> State.t -> State.t = +   fun title_element state -> +    let title = El.prop El.Prop.value title_element in -      let new_date = (new%js Js.date_now)##getTime in -      let content_obj = object%js +    let new_date = (new%js Js.date_now)##getTime in +    let content_obj = +      object%js          val content = Js.some @@ Jv.Id.to_jv (state.view##.state##toJSON ()) +          val title = Js.some title +          val date = Js.some new_date -      end in -      let save = State.Storage.save -          content_obj -          state.page_id -          (* There three date here : -             - The actual date at the time we save the note -             - The date associated with the note when we loaded it first time -             - The date associated with the note at the time we want to update it - -             The two last may differ if the note has been updated in another one tab. *) -          ~check:(fun ~previous ~update -> -              let _ = update in -              Js.Opt.case previous##.date -                (fun () -> true) -                (fun date -> -                   (* I do not figure how the previous date could be older -                      than the last backup. It could be either : - -                      - equal (if we are the only one to update it) -                      - more recent (if the content has been updated elsewhere) - -                      but older shoud be a bug. *) -                   let is_ok = date <= state.last_backup in -                   if (not is_ok) then ( -                     let open Console in -                     log -                       [ Jstr.v "Last backup date is " -                       ; new%js Js.date_fromTimeValue state.last_backup -                       ; Jstr.v " but date is " -                       ; new%js Js.date_fromTimeValue date] ); -                   is_ok)) in -      begin match save with -        | Ok true -> { state with last_backup = new_date } -        | Ok false -> -          Console.(log [Jstr.v "Didn't save"]); -          state -        | Error other -> -          (* TODO In case of error, notify the user *) -          Console.(log [Jstr.v "Couldn't save" ; other]); -          state        end +    in +    let save = +      State.Storage.save +        content_obj +        state.page_id +        (* There three date here : +           - The actual date at the time we save the note +           - The date associated with the note when we loaded it first time +           - The date associated with the note at the time we want to update it + +           The two last may differ if the note has been updated in another one +           tab. *) +        ~check:(fun ~previous ~update -> +          let _ = update in +          Js.Opt.case +            previous##.date +            (fun () -> true) +            (fun date -> +              (* I do not figure how the previous date could be older +                 than the last backup. It could be either : + +                 - equal (if we are the only one to update it) +                 - more recent (if the content has been updated elsewhere) + +                 but older shoud be a bug. *) +              let is_ok = date <= state.last_backup in +              ( if not is_ok +              then +                let open Console in +                log +                  [ Jstr.v "Last backup date is " +                  ; new%js Js.date_fromTimeValue state.last_backup +                  ; Jstr.v " but date is " +                  ; new%js Js.date_fromTimeValue date +                  ] ); +              is_ok ) ) +    in +    match save with +    | Ok true -> { state with last_backup = new_date } +    | Ok false -> +        Console.(log [ Jstr.v "Didn't save" ]); +        state +    | Error other -> +        (* TODO In case of error, notify the user *) +        Console.(log [ Jstr.v "Couldn't save"; other ]); +        state  end  module App = Editor_app  let app id content = -    let title_element = Document.find_el_by_id G.document (Jstr.v "title") in    (* Check the pre-requisite *) -  match title_element, (Jv.is_none id), (Jv.is_none content), Blog.Sidebar.get () with +  match +    (title_element, Jv.is_none id, Jv.is_none content, Blog.Sidebar.get ()) +  with    | Some title, false, false, Some sidebar -> - -    let () = Blog.Sidebar.clean sidebar in - -    let pm = PM.v () in -    let editor:El.t = Jv.Id.of_jv id in -    (* Load the cache for the given page *) -    let page_id = State.Storage.page_id () in -    let view, last_backup = build_view pm page_id editor in - -    let init_state = State.init pm view last_backup page_id in - -    (* Initialize the buttons actions and get the associated events. -       At this point, the HTML element is not yet created, and cannot be -       inserted in the document. -     *) -    let side_elements = Editor_actions.build pm in -    let btn_events = Editor_actions.get_event side_elements in - -    (* Create the main event loop with all the collected events *) -    let app_state = App.run -        ~eq:State.eq -        init_state -        (Note.E.select -           [ Brr_note.Evr.on_els Ev.focusout -               (fun _ _ -> App.dispatch (module Store) title) -               [ editor ; title ] -           ; btn_events -           ]) in - -    (* Get the html element associated with the buttons, and add it in the -       page. - -       The state event is already created, and can be given in the html -       creation in order to update the elements when the state change. -    *) -    let childs = Editor_actions.complete side_elements app_state in -    let () = El.append_children sidebar childs in -    let _ = Note.(Logr.hold (S.log app_state (fun _ -> ()))) in -    () - +      let () = Blog.Sidebar.clean sidebar in + +      let pm = PM.v () in +      let editor : El.t = Jv.Id.of_jv id in +      (* Load the cache for the given page *) +      let page_id = State.Storage.page_id () in +      let view, last_backup = build_view pm page_id editor in + +      let init_state = State.init pm view last_backup page_id in + +      (* Initialize the buttons actions and get the associated events. +         At this point, the HTML element is not yet created, and cannot be +         inserted in the document. +      *) +      let side_elements = Editor_actions.build pm in +      let btn_events = Editor_actions.get_event side_elements in + +      (* Create the main event loop with all the collected events *) +      let app_state = +        App.run +          ~eq:State.eq +          init_state +          (Note.E.select +             [ Brr_note.Evr.on_els +                 Ev.focusout +                 (fun _ _ -> App.dispatch (module Store) title) +                 [ editor; title ] +             ; btn_events +             ] ) +      in + +      (* Get the html element associated with the buttons, and add it in the +         page. + +         The state event is already created, and can be given in the html +         creation in order to update the elements when the state change. +      *) +      let childs = Editor_actions.complete side_elements app_state in +      let () = El.append_children sidebar childs in +      let _ = Note.(Logr.hold (S.log app_state (fun _ -> ()))) in +      ()    | _ -> -    Console.(error [str "No element with id '%s' '%s' found"; id ; content]) +      Console.(error [ str "No element with id '%s' '%s' found"; id; content ]) -let () = +let () =    let open Jv in -  let editor = obj -      [| "attach_prosemirror", (repr app) -      |] in +  let editor = obj [| ("attach_prosemirror", repr app) |] in    set global "editor" editor | 
