package ojs_ed
Using file editor in ojs_base applications, common part
Install
Dune Dependency
Authors
Maintainers
Sources
ojs-base-0.8.0.tar.bz2
md5=e706f1f9ec2f935d29c6b6e4832c8bdf
sha512=2596f6c59bea9c6b89923099c604a0e095a96880e7e91b06357e1de50867ae7e0261c87c35f608b7e426bddd6dd025a9868c07499287116ed458de4a0b9e9f30
doc/src/ojs_ed.js/ojsed_js.ml.html
Source file ojsed_js.ml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347
(*********************************************************************************) (* Ojs-base *) (* *) (* Copyright (C) 2014-2021 INRIA. All rights reserved. *) (* *) (* This program is free software; you can redistribute it and/or modify *) (* it under the terms of the GNU General Public License as *) (* published by the Free Software Foundation, version 3 of the License. *) (* *) (* This program is distributed in the hope that it will be useful, *) (* but WITHOUT ANY WARRANTY; without even the implied warranty of *) (* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *) (* GNU Library General Public License for more details. *) (* *) (* You should have received a copy of the GNU General Public *) (* License along with this program; if not, write to the Free Software *) (* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA *) (* 02111-1307 USA *) (* *) (* As a special exception, you have permission to link this program *) (* with the OCaml compiler and distribute executables, as long as you *) (* follow the requirements of the GNU GPL in regard to all of the *) (* software in the executable aside from the OCaml compiler. *) (* *) (* Contact: Maxence.Guesdon@inria.fr *) (* *) (*********************************************************************************) (** *) open Js_of_ocaml open Ojs_js let (>>=) = Lwt.(>>=) type mime_type = string type session = { sess_file : Ojs_base.Path.t ; sess_mime : mime_type ; sess_ace : Ojs_ace.editSession Js.t ; mutable sess_changed : bool ; } module PMap = Ojs_base.Path.Map let label = let doc = Dom_html.document in let b = doc##createElement(Js.string "button") in let text = doc##createTextNode(Js.string label) in Dom.appendChild b text ; b let is_editable_from_mime = let text = "text/" in let len_text = String.length text in function | "application/octet-stream" -> true | mime -> String.length mime >= len_text && String.sub mime 0 len_text = text module type S = sig module P : Ojs_ed.Types.P class editor : (P.client_msg -> (P.server_msg -> unit Lwt.t) -> unit Lwt.t) -> (P.client_msg -> unit Lwt.t) -> bar_id:string -> msg_id:string -> string -> object val mutable current : session option val mutable sessions : session PMap.t method changed_files : PMap.key list method changed_sessions : session list method display_error : string -> unit method display_filename : session -> unit method display_message : string -> unit method edit_file : ?mime:mime_type -> PMap.key -> unit Lwt.t method get_session : PMap.key -> session option method handle_message : P.server_msg -> bool Js.t method id : string method is_editable_from_mime : mime_type -> bool method load_from_server : session -> unit Lwt.t method msg_id : string method new_session : ?mime:mime_type -> PMap.key -> session method on_changed : session -> unit method reload : unit Lwt.t method reload_file : session -> unit Lwt.t method save : unit Lwt.t method save_changed_files : unit Lwt.t method save_file : session -> unit Lwt.t method simple_call : ?on_ok:(unit -> unit) -> P.client_msg -> unit Lwt.t end class editors : (P.app_client_msg -> (P.app_server_msg -> unit Lwt.t) -> unit Lwt.t) -> (P.app_client_msg -> unit Lwt.t) -> ((P.client_msg -> (P.server_msg -> unit Lwt.t) -> unit Lwt.t) -> (P.client_msg -> unit Lwt.t) -> bar_id:string -> msg_id:string -> string -> editor) -> object val mutable editors : editor Ojs_js.SMap.t method get_editor : Ojs_js.SMap.key -> editor method get_msg_id : Ojs_js.SMap.key -> string method handle_message : P.app_server_msg -> bool Js.t method setup_editor : bar_id:string -> msg_id:string -> Ojs_js.SMap.key -> editor end end module Make(P:Ojs_ed.Types.P) = struct module P = P class editor call (send : P.client_msg -> unit Lwt.t) ~bar_id ~msg_id ed_id = let editor = Ojs_ace.ace##edit (Js.string ed_id) in let _ = editor##setFontSize (Js.string "14px") in let rend = editor##.renderer in let () = rend##setShowGutter (Js.bool true) in let () = rend##.hScrollBarAlwaysVisible := (Js.bool false) in let () = rend##.vScrollBarAlwaysVisible := (Js.bool false) in let _ = editor##setKeyboardHandler (Js.string "ace/keyboard/emacs") in let bar = Ojs_js.node_by_id bar_id in let doc = Dom_html.document in let btn_save = mk_button "Save" in let btn_reload = mk_button "Reload" in let filename_id = ed_id ^ "__filename" in let fname = doc##createElement (Js.string "span") in let _ = fname##setAttribute (Js.string "id") (Js.string filename_id) ; fname##setAttribute (Js.string "class") (Js.string "filename") ; Dom.appendChild bar btn_save ; Dom.appendChild bar btn_reload ; Dom.appendChild bar fname in object(self) val mutable current = (None : session option) val mutable sessions = (PMap.empty : session PMap.t) method id = ed_id method msg_id = msg_id method on_changed sess = match current with | Some s when s.sess_file = sess.sess_file -> self#display_filename s | _ -> () method get_session file = try Some (PMap.find file sessions) with Not_found -> None method display_error msg = Ojs_js.display_text_error msg_id msg method display_message msg = Ojs_js.display_text_message msg_id msg method display_filename s = let node = Ojs_js.node_by_id filename_id in Ojs_js.clear_children node ; let fname = Printf.sprintf "%s%s" (if s.sess_changed then "*" else "") (Ojs_base.Path.to_string s.sess_file) in let t = Dom_html.document##createTextNode (Js.string fname) in Dom.appendChild node t method simple_call : ?on_ok: (unit -> unit) -> 'clt -> unit Lwt.t = fun ?on_ok msg -> call msg (fun msg -> Lwt.return (match msg with | P.SError msg -> self#display_error msg | P.SOk msg -> begin self#display_message msg ; match on_ok with | None -> () | Some f -> f () end | _ -> () ) ) method save_file sess = let on_ok () = let b = sess.sess_changed in if b then begin sess.sess_changed <- false ; self#on_changed sess end in let contents = Js.to_string sess.sess_ace##getValue in self#simple_call ~on_ok (P.Save_file (sess.sess_file, contents)) method save = match current with None -> Lwt.return_unit | Some sess -> self#save_file sess method changed_sessions = PMap.fold (fun _ s acc -> if s.sess_changed then s :: acc else acc) sessions [] method changed_files = PMap.fold (fun path s acc -> if s.sess_changed then path :: acc else acc) sessions [] method save_changed_files = match self#changed_sessions with | [] -> Lwt.return_unit | l -> Lwt_list.iter_p self#save_file l method load_from_server s = let cb = function | P.SFile_contents (file, contents) when s.sess_file = file -> begin s.sess_ace##setValue (Js.string contents); s.sess_changed <- false ; self#on_changed s ; Lwt.return_unit end | _ -> Lwt.return_unit in if self#is_editable_from_mime s.sess_mime then call (P.Get_file_contents s.sess_file) cb else Lwt.return_unit method reload_file sess = let do_it = not sess.sess_changed || ( let msg = Printf.sprintf "%s is modified and not saved.\nDo you really want to reload file from server ?" (Ojs_base.Path.to_string sess.sess_file) in Js.to_bool (Dom_html.window##confirm(Js.string msg)) ) in if do_it then self#load_from_server sess else Lwt.return_unit method reload = match current with | None -> Lwt.return_unit | Some sess -> self#reload_file sess method new_session ?(mime="text/") file = let sess_ace = Ojs_ace.newEditSession "" "" in sess_ace##setUndoManager(Ojs_ace.newUndoManager()); sess_ace##setUseWrapMode(Js.bool true); sess_ace##setUseWorker(Js.bool false); let doc = sess_ace##getDocument in let sess = { sess_ace ; sess_mime = mime ; sess_changed = false ; sess_file = file ; } in let mode = let mode = Ojs_ace.modeList##getModeForPath(Js.string (Ojs_base.Path.to_string file)) in mode##.mode in (*log("mode to set: "^(Js.to_string mode));*) sess_ace##setMode(mode); doc##on (Js.string "change") (fun _ -> if not sess.sess_changed then begin sess.sess_changed <- true; self#on_changed sess end ); sessions <- PMap.add file sess sessions; if not (self#is_editable_from_mime mime) then sess_ace##setReadOnly(Js.bool true); sess method is_editable_from_mime = is_editable_from_mime method edit_file ?mime path = (match self#get_session path with | Some sess -> Lwt.return sess | None -> let s = self#new_session ?mime path in self#load_from_server s >>= fun _ -> Lwt.return s ) >>= fun sess -> ( editor##setSession(sess.sess_ace); current <- Some sess ; Lwt.return (self#on_changed sess) ) method handle_message (msg : 'srv) = try (match msg with | P.SOk msg -> self#display_message msg | P.SError msg -> self#display_error msg | _ -> failwith "Unhandled message received from server" ); Js._false with e -> log (Printexc.to_string e); Js._false initializer Ojs_js.set_onclick btn_save (fun _ -> self#save); Ojs_js.set_onclick btn_reload (fun _ -> self#reload); end class editors (call : P.app_client_msg -> (P.app_server_msg -> unit Lwt.t) -> unit Lwt.t) (send : P.app_client_msg -> unit Lwt.t) (spawn : (P.client_msg -> (P.server_msg -> unit Lwt.t) -> unit Lwt.t) -> (P.client_msg -> unit Lwt.t) -> bar_id: string -> msg_id: string -> string -> editor) = object(self) val mutable editors = (SMap.empty : editor SMap.t) method get_editor id = try SMap.find id editors with Not_found -> failwith (Printf.sprintf "Invalid editor id %S" id) method get_msg_id id = (self#get_editor id)#msg_id method handle_message (msg : P.app_server_msg) = match P.unpack_server_msg msg with | Some (id, msg) -> (self#get_editor id)#handle_message msg | None -> Js._false method setup_editor ~bar_id ~msg_id ed_id = let send msg = send (P.pack_client_msg ed_id msg) in let call msg cb = let cb msg = match P.unpack_server_msg msg with | Some (_, msg) -> cb msg | None -> Lwt.return_unit in call (P.pack_client_msg ed_id msg) cb in let editor = spawn call send ~bar_id ~msg_id ed_id in editors <- SMap.add ed_id editor editors; editor end end
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>