diff --git a/.gitignore b/.gitignore index 405d29eb60..d410766660 100644 --- a/.gitignore +++ b/.gitignore @@ -40,3 +40,6 @@ lib/gwlib.ml # generated by Makefile hd/etc/version.txt +*~ +/_opam +doc/doc \ No newline at end of file diff --git a/Makefile b/Makefile index 944b5a1db7..1c7f1376b0 100644 --- a/Makefile +++ b/Makefile @@ -13,8 +13,10 @@ endif # Variables for packagers. PREFIX=/usr DISTRIB_DIR=distribution - BUILD_DIR=_build/default +ODOC_DIR=$(BUILD_DIR)/_doc/_html + +DOC_DIR=doc ###### [BEGIN] Generated files section @@ -174,8 +176,17 @@ distrib: build doc: | $(GENERATED_FILES_DEP) dune build @doc + if [ ! -d $(DOC_DIR)/doc ] ; then \ + mkdir -p $(DOC_DIR)/doc ; \ + cp -r $(ODOC_DIR)/* $(DOC_DIR)/doc ; \ + fi .PHONY: doc +opendoc : doc + xdg-open $(DOC_DIR)/html/dev-doc/index.html +.PHONY: opendoc + + test: | $(GENERATED_FILES_DEP) dune build @runtest .PHONY: test @@ -202,6 +213,7 @@ clean: @echo -n "Cleaning..." @$(RM) $(GENERATED_FILES_DEP) lib/*_piqi*.ml @$(RM) -r $(DISTRIB_DIR) + @$(RM) -r $(DOC_DIR)/doc @dune clean @echo " Done!" .PHONY: clean diff --git a/bin/ged2gwb/ged2gwb.ml b/bin/ged2gwb/ged2gwb.ml index 0c93aeab4f..4e72f4d7e7 100644 --- a/bin/ged2gwb/ged2gwb.ml +++ b/bin/ged2gwb/ged2gwb.ml @@ -2786,10 +2786,10 @@ let pass3 gen fname = Some ('1'..'9') -> Stream.junk strm__; let (_ : string) = get_to_eoln 0 strm in loop () - | Some _ -> + | Some c -> Stream.junk strm__; print_location !line_cnt; - Printf.fprintf !log_oc "Strange input.\n"; + Printf.fprintf !log_oc "Strange input '%c' (%i).\n" c (Char.code c); flush !log_oc; let (_ : string) = get_to_eoln 0 strm in loop () | _ -> () diff --git a/bin/gwb2ged/gwb2gedLib.mli b/bin/gwb2ged/gwb2gedLib.mli new file mode 100644 index 0000000000..902f929141 --- /dev/null +++ b/bin/gwb2ged/gwb2gedLib.mli @@ -0,0 +1,11 @@ +(** [gwb2ged with_indexes opts sel] + Converts a Geneweb database to a GEDCOM file. + * `with_indexes` specifies if indexes are printed or not; + * `opts` are the export options + * `sel` is a pair of selectors returned by the database export +*) +val gwb2ged : + bool -> + Gwexport.gwexport_opts -> + (Gwdb.iper -> bool) * (Gwdb.ifam -> bool) + -> unit diff --git a/bin/gwc/db1link.ml b/bin/gwc/db1link.ml index cbcd9f9d50..c437fdf6ef 100644 --- a/bin/gwc/db1link.ml +++ b/bin/gwc/db1link.ml @@ -7,18 +7,37 @@ open Def (* From OCaml manual, integer in binary format is 4 bytes long. *) let sizeof_long = 4 +(** Default source field for persons and families without source data *) let default_source = ref "" + +(** Base consistency check *) let do_check = ref true + +(** Compute consanguinity *) let do_consang = ref false + +(** Print base's statistics *) let pr_stats = ref false +(** Extended person's entry in the base *) type person = (int, int, int) Def.gen_person + +(** Person's ascendants entry in the base *) type ascend = int Def.gen_ascend + +(** Person's union entry in the base *) type union = int Def.gen_union + +(** Family's entry in the base *) type family = (int, int, int) Def.gen_family + +(** Family's couple entry in the base *) type couple = int Def.gen_couple + +(** Family's descendants entry in the base *) type descend = int Def.gen_descend +(** Restricted to the minimum [Def.gen_person] data type. *) type ('person, 'string) gen_min_person = { mutable m_first_name : 'string; mutable m_surname : 'string; @@ -29,83 +48,159 @@ type ('person, 'string) gen_min_person = mutable m_sex : sex; mutable m_notes : 'string } +(** Person's entry in the base *) type min_person = (int, int) gen_min_person +(** State of the base collecting all information at link time used to + create further Geneweb database *) type cbase = - { mutable c_persons : min_person array + { + (* Array of persons. Person at position [i] has corresponding to him [ascend] + and [union] at position [i] in [c_ascends] and [c_unions] respectively. *) + mutable c_persons : min_person array + (* Array of ascendants of persons *) ; mutable c_ascends : ascend array + (* Array of unions of persons *) ; mutable c_unions : union array + (* Array of families. Family at position [i] has corresponding to it [couple] + and [descend] at position [i] in [c_couples] and [c_descends] respectively. *) ; mutable c_families : family array + (** Array of couples of families *) ; mutable c_couples : couple array + (* Array of descendants of families *) ; mutable c_descends : descend array + (* Array of unique strings. Stores every string encoded information + (like person's name, bithplace, etc.) for other entries in the base. *) ; mutable c_strings : string array + (* Data base notes and extended page structure *) ; mutable c_bnotes : Def.base_notes } +(** Information about current .gwo file. *) type file_info = - { mutable f_curr_src_file : string + { + (* current .gw filename *) + mutable f_curr_src_file : string + (* current .gwo filename *) ; mutable f_curr_gwo_file : string - ; mutable f_separate : bool + (* all persons from current file should be separated *) + ; mutable f_separate : bool + (* behavior for base notes from current file *) ; mutable f_bnotes : [ `merge | `erase | `first | `drop ] + (* shift all persons from the current file with the given number *) ; mutable f_shift : int + (* Table that associates person's names hash and its occurence number + with the index of person's entry inside the [base]. Contains only + persons from the local file. *) ; mutable f_local_names : (int * int, int) Hashtbl.t } +(** Global linker state *) type gen = - { mutable g_strings : (string, int) Hashtbl.t; + { + (* Table that associates unique string to its position inside + [g_base]'s unique string array *) + mutable g_strings : (string, int) Hashtbl.t; + (* Table that associates person's names hash with the index of + person's entry inside the [g_base].*) mutable g_names : (int, int) Hashtbl.t; + (* Counter of persons inside [g_base] *) mutable g_pcnt : int; + (* Counter of families inside [g_base] *) mutable g_fcnt : int; + (* Counter of unique strings inside [g_base] *) mutable g_scnt : int; + (* Current file info *) g_file_info : file_info; + (* Base of collected information *) g_base : cbase; + (* Wizard notes (wizard id and note's content) *) mutable g_wiznotes : (string * string) list; g_patch_p : (int, person) Hashtbl.t; + (** Array that for every person from [g_base] says if he + was defined before *) mutable g_def : bool array; + (* Table that associates person's first and last name with + the next availiable occurence number for the person with + the same names.*) g_first_av_occ : (string * string, int) Hashtbl.t; + (* Indicates if an error was occured *) mutable g_errored : bool; + (* Temprary output chanel containing [g_pcnt] integers where [i]nth integer + corresponds to the position in [g_per] where [i]nth person is defined. *) g_per_index : out_channel; + (* Temprary output chanel containing person's definition (or non-definition + marker) *) g_per : out_channel; + (* Temprary output chanel containing [g_fcnt] integers where [i]nth integer + corresponds to the position in [g_fam] where [i]nth family is defined. *) g_fam_index : out_channel; + (* Temprary output chanel containing family's definition *) g_fam : out_channel } +(** Set [gen.g_errored] telling that an error was occured *) let check_error gen = gen.g_errored <- true +(** Function that will be called if base's checker will find an error *) let set_error base gen x = Printf.printf "\nError: " ; Check.print_base_error stdout base x ; check_error gen +(** Function that will be called if base's checker will find a warning *) let set_warning base x = Printf.printf "Warning: " ; Check.print_base_warning stdout base x +(** Returns person's entry from [base] at position [i] *) let poi base i = base.c_persons.(i) + +(** Returns ascendant's entry from [base] at position [i] *) let aoi base i = base.c_ascends.(i) + +(** Returns union's entry from [base] at position [i] *) let uoi base i = base.c_unions.(i) + +(** Returns couple's entry from [base] at position [i] *) let coi base i = base.c_couples.(i) + +(** Returns string in [base]'s unque string array at position [i] *) let sou base i = base.c_strings.(i) + +(** Returns first name of a [base]'s person entry [p]. [p.m_first_name] contains + index where first name string representation is stored. *) let p_first_name base p = Mutil.nominative (sou base p.m_first_name) + +(** Returns surname of a [base]'s person entry [p]. [p.m_first_name] contains + index where surname string representation is stored. *) let p_surname base p = Mutil.nominative (sou base p.m_surname) + +(** Returns string designation of person {i firstname.occ surname}. *) let designation base p = let prenom = p_first_name base p in let nom = p_surname base p in prenom ^ "." ^ string_of_int p.m_occ ^ " " ^ nom +(** Same as [Marshal.to_channel oc v [Marshal.No_sharing]] *) let output_item_value oc v = Marshal.to_channel oc v [Marshal.No_sharing] +(** Same as [input_value] *) let input_item_value ic = input_value ic -(**) - +(** Empty string *) let no_string = "" +(** Stores unique string (if not already present) inside the base's string array and + associate this string to its index in mentioned array. Extens array if needed. + Returns associated index. *) let unique_string gen x = try Hashtbl.find gen.g_strings x with Not_found -> + (* string not found *) if gen.g_scnt = Array.length gen.g_base.c_strings then + (* extend arrray of strings and copy previus elements *) begin let arr = gen.g_base.c_strings in let new_size = 2 * Array.length arr + 1 in let new_arr = Array.make new_size no_string in @@ -118,6 +213,7 @@ let unique_string gen x = Hashtbl.add gen.g_strings x u; u +(** Dummy [family] with its empty [couple] and [descendants]. *) let no_family gen = let empty_string = unique_string gen "" in let fam = @@ -138,7 +234,10 @@ let no_family gen = let des = {children = [| |]} in fam, cpl, des -let make_person gen p n occ = +(** Initialises [min_person] with occurence number and index of [p] for first name and + index of [n] for surname in [base]. Other fields are initialised with default value. + Returns also empty [ascend] and [union] attached to the considered person. *) +let make_person gen p n occ : min_person * ascend * union = let empty_string = unique_string gen "" in let p = {m_first_name = unique_string gen p; m_surname = unique_string gen n; @@ -148,8 +247,11 @@ let make_person gen p n occ = and u = {family = [| |]} in p, a, u +(** Dummy [min_person] with its empty [ascend] and [union]. *) let no_person gen = make_person gen "" "" 0 +(** Extends person's acendant's and union's arrays inside [gen.g_base] + if needed. *) let new_iper gen = if gen.g_pcnt = Array.length gen.g_base.c_persons then let per_arr = gen.g_base.c_persons in @@ -170,6 +272,8 @@ let new_iper gen = Array.blit gen.g_def 0 new_def 0 (Array.length gen.g_def); gen.g_def <- new_def +(** Extends family's couple's and decendant's arrays inside [gen.g_base] + if needed. *) let new_ifam gen = if gen.g_fcnt = Array.length gen.g_base.c_families then let fam_arr = gen.g_base.c_families in @@ -187,23 +291,31 @@ let new_ifam gen = Array.blit des_arr 0 new_des_arr 0 (Array.length des_arr); gen.g_base.c_descends <- new_des_arr +(** Convert [string Def.gen_title_name] to [int Def.gen_title_name]. + If title is [Tname] stores title name as a string in the base. *) let title_name_unique_string gen = function Tmain -> Tmain | Tname n -> Tname (unique_string gen n) | Tnone -> Tnone +(** Convert [(string Def.gen_title] to [int Def.gen_title] and insert + all related to title information in the base. *) let title_unique_string gen t = {t_name = title_name_unique_string gen t.t_name; t_ident = unique_string gen t.t_ident; t_place = unique_string gen t.t_place; t_date_start = t.t_date_start; t_date_end = t.t_date_end; t_nth = t.t_nth} +(** Hash of person's first and last names. *) let person_hash first_name surname = let first_name = Mutil.nominative first_name in let surname = Mutil.nominative surname in let s = Name.crush_lower (first_name ^ " " ^ surname) in Hashtbl.hash s +(** Returns index of a person's entry inside the [gen.base] that has the same + first name, surname and occurence number. Raises [Not_found] if person + is not found. *) let find_person_by_global_name gen first_name surname occ = let first_name = Mutil.nominative first_name in let surname = Mutil.nominative surname in @@ -217,6 +329,8 @@ let find_person_by_global_name gen first_name surname occ = [] -> raise Not_found | ip :: ipl -> let p = poi gen.g_base ip in + (* refine search by fullnames comparison (without crushlower) and with + occurence comparison *) if p.m_occ = occ && Name.lower (p_first_name gen.g_base p) = first_name && Name.lower (p_surname gen.g_base p) = surname @@ -226,6 +340,9 @@ let find_person_by_global_name gen first_name surname occ = in loop ipl +(** Returns index of a person's entry inside the [gen.base] that has the same + first name, surname and occurence number. Searches only persons defined + in the current file. Raises [Not_found] if person is not found. *) let find_person_by_local_name gen first_name surname occ = let first_name = Mutil.nominative first_name in let surname = Mutil.nominative surname in @@ -239,6 +356,7 @@ let find_person_by_local_name gen first_name surname occ = [] -> raise Not_found | ip :: ipl -> let p = poi gen.g_base ip in + (* refine search by fullnames comparison (without crushlower) *) if Name.lower (p_first_name gen.g_base p) = first_name && Name.lower (p_surname gen.g_base p) = surname then @@ -247,15 +365,24 @@ let find_person_by_local_name gen first_name surname occ = in loop ipl +(** Returns index of a person's entry inside the [gen.base] that has the same + first name, surname and occurence number. Calls [find_person_by_local_name] + if option [f_separate] is enabled for the current file, otherwise calls + [find_person_by_global_name]. Raises [Not_found] if person is not found. *) let find_person_by_name gen first_name surname occ = if gen.g_file_info.f_separate then find_person_by_local_name gen first_name surname occ else find_person_by_global_name gen first_name surname occ +(** Add entry in the global names table [gen.g_names] for the giving + first and last names associated to the index of their person's entry + in [base]. *) let add_person_by_name gen first_name surname int = let s = Name.crush_lower (Mutil.nominative (first_name ^ " " ^ surname)) in let key = Hashtbl.hash s in Hashtbl.add gen.g_names key int +(** Returns first available occurence number that is >= [occ] for the person + with the giving information. *) let find_first_available_occ gen fn sn occ = let occ = try max occ (Hashtbl.find gen.g_first_av_occ (fn, sn)) with @@ -271,8 +398,31 @@ let find_first_available_occ gen fn sn occ = in loop occ +(** Insert person's reference in the base and modifies all coresponding + fields in [gen] and returns its entry and entry's index in the base. + In details: + + - if considered person doesn't exists in the base (wasn't defined or + referenced before) then function: + + - maps its key (names and occurence number) within the varius + hash tables + - creates entry (of type [min_gen]) for the giving person and his + ascendants and union in the base. + - initialises its entry (with key information) + - stores marker in the [gen.g_per] channel telling that person + wasn't defined. + - stores in [gen.g_per_index] position where marker was stored in + [gen.g_per]. + + - if considered person was referenced or defined before then doesn't do + anything (just returns its entry and entry's index in the base) + + *) let insert_undefined gen key = + (* shift person's occurence *) let occ = key.pk_occ + gen.g_file_info.f_shift in + (* person with its position in the base *) let (x, ip) = try if key.pk_first_name = "?" || key.pk_surname = "?" then raise Not_found @@ -281,7 +431,9 @@ let insert_undefined gen key = find_person_by_name gen key.pk_first_name key.pk_surname occ in poi gen.g_base ip, ip + (* if person not found *) with Not_found -> + (* abailable occurence number *) let new_occ = if gen.g_file_info.f_separate && key.pk_first_name <> "?" && key.pk_surname <> "?" @@ -289,28 +441,37 @@ let insert_undefined gen key = find_first_available_occ gen key.pk_first_name key.pk_surname occ else occ in + (* person's entry index *) let i = gen.g_pcnt in let (x, a, u) = make_person gen key.pk_first_name key.pk_surname new_occ in + (* strore names globally *) if key.pk_first_name <> "?" && key.pk_surname <> "?" then add_person_by_name gen key.pk_first_name key.pk_surname (i) else if !(Gwcomp.create_all_keys) then add_person_by_name gen key.pk_first_name key.pk_surname (i); + (* extend arrays if needed *) new_iper gen; + (* add person to array *) gen.g_base.c_persons.(i) <- x; + (* add associated to person ascendants to array *) gen.g_base.c_ascends.(i) <- a; + (* add associated to person union to array *) gen.g_base.c_unions.(i) <- u; gen.g_pcnt <- gen.g_pcnt + 1; + (* strore names locally *) if key.pk_first_name <> "?" && key.pk_surname <> "?" then begin let h = person_hash key.pk_first_name key.pk_surname in Hashtbl.add gen.g_file_info.f_local_names (h, occ) (i) end; + (* write start position of person in [g_per] *) seek_out gen.g_per_index (sizeof_long * i); output_binary_int gen.g_per_index (pos_out gen.g_per); + (* write marker *) output_char gen.g_per 'U'; x, i in @@ -335,15 +496,49 @@ let insert_undefined gen key = end; x, ip +(** Insert person's definition in the base and modifies all coresponding + fields in [gen] and returns its entry and entry's index in the base. + In details: + + - if considered person doesn't exists in the base (wasn't defined or + referenced before) then function: + + - maps its key (names and occurence number) within the varius + hash tables + - creates entry (of type [min_gen]) for the giving person and his + ascendants and union in the base. + - initialises its entry (with key information) + - marks it as defined + - convert [(_,_,string) gen_person] to [person] (sex, events, titles + and related persons stays uninitialised) and stores it in the + [gen.g_per] channel. + - stores in [gen.g_per_index] position where person was stored in + [gen.g_per]. + + - if considered person was referenced before (but not defined) then + function: + + - get person's entry and its index from the base + - marks it as defined + - convert [(_,_,string) gen_person] to [person] (sex, events and + related persons stays uninitialised) and stores it in the + [gen.g_per] channel. + - updates previus index in [gen.g_per_index] in order to point to + the definition instead of pointing to the reference. +*) let insert_person gen so = + (* shift person's occurence *) let occ = so.occ + gen.g_file_info.f_shift in + (* person with its position in the base *) let (x, ip) = try if so.first_name = "?" || so.surname = "?" then raise Not_found else let ip = find_person_by_name gen so.first_name so.surname occ in poi gen.g_base ip, ip + (* if person not found *) with Not_found -> + (* abailable occurence number *) let new_occ = if gen.g_file_info.f_separate && so.first_name <> "?" && so.surname <> "?" @@ -351,19 +546,26 @@ let insert_person gen so = find_first_available_occ gen so.first_name so.surname occ else occ in + (* person's entry index *) let i = gen.g_pcnt in let (x, a, u) = make_person gen so.first_name so.surname new_occ in + (* strore names globally *) if so.first_name <> "?" && so.surname <> "?" then add_person_by_name gen so.first_name so.surname (i) else if !(Gwcomp.create_all_keys) then add_person_by_name gen so.first_name so.surname (i); + (* extend arrays if needed *) new_iper gen; + (* add person to array *) gen.g_base.c_persons.(i) <- x; + (* add associated to person ascendants to array *) gen.g_base.c_ascends.(i) <- a; + (* add associated to person union to array *) gen.g_base.c_unions.(i) <- u; gen.g_pcnt <- gen.g_pcnt + 1; + (* strore names locally *) if so.first_name <> "?" && so.surname <> "?" then begin let h = person_hash so.first_name so.surname in Hashtbl.add gen.g_file_info.f_local_names (h, occ) @@ -371,8 +573,10 @@ let insert_person gen so = end; x, i in + (* if person wad defined before (not just referenced) *) if gen.g_def.(ip) then begin + (* print error about person beeing already defined *) Printf.printf "\nPerson already defined: \"%s%s %s\"\n" so.first_name (match x.m_occ with 0 -> "" @@ -389,11 +593,13 @@ let insert_person gen so = flush stdout; check_error gen end + (* else set it as defined *) else gen.g_def.(ip) <- true; if not gen.g_errored then if sou gen.g_base x.m_first_name <> so.first_name || sou gen.g_base x.m_surname <> so.surname then + (* print error about person defined with two spellings *) begin Printf.printf "\nPerson defined with two spellings:\n"; Printf.printf " \"%s%s %s\"\n" so.first_name @@ -411,6 +617,7 @@ let insert_person gen so = end; if not gen.g_errored then begin let empty_string = unique_string gen "" in + (* Convert [(_,_,string) gen_person] to [person]. Save all strings in base *) let x = {first_name = empty_string; surname = empty_string; occ = 0; image = unique_string gen so.image; @@ -441,18 +648,24 @@ let insert_person gen so = (if so.psources = "" then !default_source else so.psources); key_index = ip} in + (* write/update start position of person in [g_per] *) seek_out gen.g_per_index (sizeof_long * ip); output_binary_int gen.g_per_index (pos_out gen.g_per); + (* write person *) output_char gen.g_per 'D'; output_item_value gen.g_per (x : person) end; x, ip +(** Insert definition or reference in [gen] and returns its entry and + entry's index in the [gen.g_base]. Calls [insert_person] for definition + and [insert_undefined] for reference. *) let insert_somebody gen = function Undefined key -> insert_undefined gen key | Defined so -> insert_person gen so +(** Checks if childran [ix] doesn't have another parents *) let check_parents_not_already_defined gen ix fath moth = let x = poi gen.g_base ix in match (aoi gen.g_base ix).parents with @@ -478,12 +691,16 @@ let check_parents_not_already_defined gen ix fath moth = check_error gen | _ -> () +(** Assign sex to the person's entry if it's unitialised. + Print message if sexes are different. *) let notice_sex gen p s = if p.m_sex = Neuter then p.m_sex <- s else if p.m_sex <> s && s <> Neuter then Printf.printf "\nInconsistency about the sex of\n %s %s\n" (p_first_name gen.g_base p) (p_surname gen.g_base p) +(** Convert [string Def.gen_fam_event_name] to [int Def.gen_fam_event_name]. + If event is [Efam_Name] stores event name as a string in the base. *) let fevent_name_unique_string gen = function Efam_Marriage | Efam_NoMarriage | Efam_NoMention | Efam_Engage | @@ -493,6 +710,7 @@ let fevent_name_unique_string gen = evt | Efam_Name n -> Efam_Name (unique_string gen n) +(** Update family by looking up information inferred from family events *) let update_family_with_fevents gen fam = let found_marriage = ref false in let found_divorce = ref false in @@ -595,6 +813,7 @@ let update_family_with_fevents gen fam = in loop (List.rev fam.fevents) fam +(** Update family event list by looking up inferred family information. *) let update_fevents_with_family gen fam = let empty_string = 0 in let evt_marr = @@ -654,6 +873,24 @@ let update_fevents_with_family gen fam = in {fam with fevents = fevents} +(** Insert family in the base and modifies all coresponding + fields in [gen] and returns its entry and entry's index in the base. + In details function does: + + - inserts father and mother in the person's base + - insert every witness in the person's base and associate father + as a related person. + - order, convert, adjust and insert events + - insert every childran in the person's base + - creates entry (of type [family]) for the giving family and its + couple and descendants in the base. + - associate father's and mother's union to the current family + - associate every childran's ascendants to the current family + (current couple, since it has the same index) + - stores family in the [gen.g_fam] channel. + - stores in [gen.g_fam_index] position where person was stored in + [gen.g_index]. +*) let insert_family gen co fath_sex moth_sex witl fevtl fo deo = let fath, ifath, moth, imoth = match insert_somebody gen (Adef.father co), insert_somebody gen (Adef.mother co) with @@ -662,23 +899,29 @@ let insert_family gen co fath_sex moth_sex witl fevtl fo deo = (moth, imoth, fath, ifath) | ((fath, ifath), (moth, imoth)) -> (fath, ifath, moth, imoth) in + (* insert all family witnesses *) let witl = List.map (fun (wit, sex) -> let (p, ip) = insert_somebody gen wit in - notice_sex gen p sex; p.m_related <- ifath :: p.m_related; ip) + notice_sex gen p sex; + (* add father to witness' related persons *) + p.m_related <- ifath :: p.m_related; + ip) witl in - (* On tri les évènements pour être sûr. *) + (* Events are sorted by chronological order (if equal then by alphabetical order) *) let fevents = CheckItem.sort_events (fun (name, _, _, _, _, _, _) -> CheckItem.Fsort name) (fun (_, date, _, _, _, _, _) -> date) fevtl in + (* Create [int Def.gen_fam_event_name] list from [fevents]*) let fevents = List.map (fun (name, date, place, reason, src, notes, witl) -> + (* insert all event witnesses *) let witnesses = List.map (fun (wit, sex, wk) -> @@ -696,19 +939,25 @@ let insert_family gen co fath_sex moth_sex witl fevtl fo deo = efam_witnesses = Array.of_list witnesses}) fevents in + (* insert all children *) let children = Array.map (fun key -> let (e, ie) = insert_person gen key in notice_sex gen e key.sex; ie) deo.children in + (* insert family comment *) let comment = unique_string gen fo.comment in + (* insert sources comment *) let fsources = unique_string gen (if fo.fsources = "" then !default_source else fo.fsources) in + (* extend arrays if needed *) new_ifam gen; + (* family's entry index *) let i = gen.g_fcnt in + (* Convert [(_,_,string) gen_family] to [family]. Save all strings in base *) let fam = {marriage = fo.marriage; marriage_place = unique_string gen fo.marriage_place; @@ -718,22 +967,32 @@ let insert_family gen co fath_sex moth_sex witl fevtl fo deo = divorce = fo.divorce; fevents = fevents; comment = comment; origin_file = unique_string gen fo.origin_file; fsources = fsources; fam_index = i} + (* create couple *) and cpl = Adef.couple ifath imoth + (* created descandants *) and des = {children = children} in (* On mets à jour les fevents et events normaux *) let fam = if fevents <> [] then update_family_with_fevents gen fam else update_fevents_with_family gen fam in + (* father's union *) let fath_uni = uoi gen.g_base ifath in + (* mother's union *) let moth_uni = uoi gen.g_base imoth in + (* write start position of family in [g_fam] *) seek_out gen.g_fam_index (sizeof_long * i); output_binary_int gen.g_fam_index (pos_out gen.g_fam); + (* write family *) output_item_value gen.g_fam (fam : family); + (* add family to array *) gen.g_base.c_families.(gen.g_fcnt) <- fam; + (* add couple to array *) gen.g_base.c_couples.(gen.g_fcnt) <- cpl; + (* add descendants to array *) gen.g_base.c_descends.(gen.g_fcnt) <- des; gen.g_fcnt <- gen.g_fcnt + 1; + (* append this family to father's and mother's union *) let fath_uni = {family = Array.append fath_uni.family [| i |]} in @@ -744,14 +1003,18 @@ let insert_family gen co fath_sex moth_sex witl fevtl fo deo = gen.g_base.c_unions.(imoth) <- moth_uni; notice_sex gen fath fath_sex; notice_sex gen moth moth_sex; + (* Append familly to the childran's ascendant *) Array.iter (fun ix -> let a = gen.g_base.c_ascends.(ix) in + (* check if childran has no another parents *) check_parents_not_already_defined gen ix fath moth; let a = {a with parents = Some (i)} in gen.g_base.c_ascends.(ix) <- a) children +(** Convert [string Def.gen_pers_event_name] to [int Def.gen_pers_event_name]. + If event is [Epers_Name] stores event name as a string in the base. *) let pevent_name_unique_string gen = function Epers_Birth | Epers_Baptism | Epers_Death | Epers_Burial | @@ -773,7 +1036,10 @@ let pevent_name_unique_string gen = evt | Epers_Name n -> Epers_Name (unique_string gen n) +(** Insert all related to the event information and add it to the person's entry + in the [gen.g_base] *) let insert_pevents fname gen sb pevtl = + (* insert concered person *) let (p, ip) = insert_somebody gen sb in if p.m_pevents <> [] then begin @@ -785,20 +1051,24 @@ let insert_pevents fname gen sb pevtl = check_error gen end else + (* sort evenets *) let pevents = CheckItem.sort_events (fun (name, _, _, _, _, _, _) -> CheckItem.Psort name) (fun (_, date, _, _, _, _, _) -> date) pevtl in + (* convert evenets. Insert all related to evenet information in the base *) let pevents = List.map (fun (name, date, place, reason, src, notes, witl) -> let witnesses = List.map (fun (wit, sex, wk) -> + (* insert witnesses *) let (wp, wip) = insert_somebody gen wit in notice_sex gen wp sex; + (* add concerned person as witness' relation *) wp.m_related <- ip :: wp.m_related; wip, wk) witl @@ -811,8 +1081,11 @@ let insert_pevents fname gen sb pevtl = epers_witnesses = Array.of_list witnesses}) pevents in + (* add events to the person's entry in the base *) p.m_pevents <- pevents +(** Insert person's notes in the base and associate it to the referenced + with [key] person *) let insert_notes fname gen key str = let occ = key.pk_occ + gen.g_file_info.f_shift in match @@ -836,9 +1109,14 @@ let insert_notes fname gen key str = (if occ = 0 then "" else "." ^ string_of_int occ) key.pk_surname; flush stdout +(** Changes [gen.g_base.c_bnotes] to take into account [nfname] page + and its content [str] that is treated by the way mentioned in + [gen.g_file_info.f_bnotes]. *) let insert_bnotes fname gen nfname str = if gen.g_file_info.f_bnotes <> `drop then begin let old_nread = gen.g_base.c_bnotes.nread in + (* Convert path notation from 'dir1:dir2:file' to 'dir1/dir2/file' + (if a valid path) *) let nfname = if nfname = "" then "" else @@ -868,6 +1146,7 @@ let insert_bnotes fname gen nfname str = gen.g_base.c_bnotes <- bnotes end +(** Add wizard and his note to the [gen] *) let insert_wiznote gen wizid str = gen.g_wiznotes <- (wizid, str) :: gen.g_wiznotes @@ -876,19 +1155,27 @@ let map_option f = Some x -> Some (f x) | None -> None +(** Insert parent in the base and adjust his sex if needed. Concerned + person is added in the list of parent's related persons. *) let insert_relation_parent gen ip s k = let (par, ipar) = insert_somebody gen k in par.m_related <- ip :: par.m_related; if par.m_sex = Neuter then par.m_sex <- s; ipar +(** Convert [(Dune__exe.Gwcomp.somebody, string) Def.gen_relation] to + [(int, int) Def.gen_relation] and insert all related to relation + information in the base. *) let insert_relation gen ip r = {r_type = r.r_type; r_fath = map_option (insert_relation_parent gen ip Male) r.r_fath; r_moth = map_option (insert_relation_parent gen ip Female) r.r_moth; r_sources = unique_string gen r.r_sources} +(** Insert all information related to the person's relations and add those + relations to the person's list of related parents. *) let insert_relations fname gen sb sex rl = + (* insert concerned person *) let (p, ip) = insert_somebody gen sb in if p.m_rparents <> [] then begin @@ -905,6 +1192,7 @@ let insert_relations fname gen sb sex rl = let rl = List.map (insert_relation gen ip) rl in p.m_rparents <- rl end +(** Insert syntax element read from .gwo file. *) let insert_syntax fname gen = function | Family (cpl, fs, ms, witl, fevents, fam, des) -> insert_family gen cpl fs ms witl fevents fam des @@ -914,6 +1202,7 @@ let insert_syntax fname gen = function | Bnotes (nfname, str) -> insert_bnotes fname gen nfname str | Wnotes (wizid, str) -> insert_wiznote gen wizid str +(** Update person by looking up information inferred from person events *) let update_person_with_pevents p = let found_birth = ref false in let found_baptism = ref false in @@ -987,6 +1276,7 @@ let update_person_with_pevents p = in loop p.pevents p +(** Update person's event list by looking up inferred personal information. *) let update_pevents_with_person p = let empty_string = 0 in let evt_birth = @@ -1113,6 +1403,9 @@ let update_pevents_with_person p = in {p with pevents = pevents} +(** Returns list of persons from [min_person list] where some absent information + was extracted from file [per_ic]. Adjusts person with inffered information + from events or inversely. *) let convert_persons per_index_ic per_ic persons = Array.mapi begin fun i mp -> let p = @@ -1124,7 +1417,9 @@ let convert_persons per_index_ic per_ic persons = with End_of_file -> 'U' in match c with + (* if person is defined read person *) | 'D' -> (input_item_value per_ic : person) + (* if read person is undefined then create dummy person *) | 'U' -> let empty_string = 0 in {first_name = empty_string; surname = empty_string; occ = 0; @@ -1155,6 +1450,7 @@ let convert_persons per_index_ic per_ic persons = else update_pevents_with_person p end persons +(** File containing the particles to use *) let particules_file = ref "" let convert_families fam_index_ic fam_ic len = @@ -1165,10 +1461,13 @@ let convert_families fam_index_ic fam_ic len = (input_item_value fam_ic : family) end +(** Returns list of particles from the file. If filename is empty string + then returns default particles list *) let input_particles = function | "" -> Mutil.default_particles | file -> Mutil.input_particles file +(** Empty base *) let empty_base : cbase = { c_persons = [| |] ; c_ascends = [| |] @@ -1181,12 +1480,14 @@ let empty_base : cbase = ; norigin_file = "" ; efiles = fun _ -> [] } } +(** Extract information from the [gen.g_base] and create database *) let make_base bname gen per_index_ic per_ic = let _ = Printf.eprintf "pcnt %d persons %d\n" gen.g_pcnt (Array.length gen.g_base.c_persons); flush stderr in + (* get full persons information *) let persons = let a = Array.sub gen.g_base.c_persons 0 gen.g_pcnt in gen.g_base.c_persons <- [| |]; @@ -1230,9 +1531,11 @@ let make_base bname gen per_index_ic per_ic = ((persons, ascends, unions), (families, couples, descends), strings , gen.g_base.c_bnotes) +(** Write content in the file *) let write_file_contents fname text = let oc = open_out fname in output_string oc text; close_out oc +(** Create and fill a file for every wizard note *) let output_wizard_notes bdir wiznotes = let wizdir = Filename.concat bdir "wiznotes" in if wiznotes <> [] then begin @@ -1244,6 +1547,7 @@ let output_wizard_notes bdir wiznotes = end wiznotes end +(** Create file that contains command used to call this program *) let output_command_line bdir = let oc = open_out (Filename.concat bdir "command.txt") in Printf.fprintf oc "%s" Sys.argv.(0); @@ -1253,6 +1557,7 @@ let output_command_line bdir = Printf.fprintf oc "\n"; close_out oc +(** Link .gwo files and create a database. *) let link next_family_fun bdir = let tmp_dir = Filename.concat "gw_tmp" bdir in Mutil.mkdir_p tmp_dir ; diff --git a/bin/gwc/db1link.mli b/bin/gwc/db1link.mli new file mode 100644 index 0000000000..86988b40ab --- /dev/null +++ b/bin/gwc/db1link.mli @@ -0,0 +1,28 @@ +(** Default source field for persons and families without source data *) +val default_source : string ref + +(** Base consistency check *) +val do_check : bool ref + +(** Compute consanguinity *) +val do_consang : bool ref + +(** Print base's statistics *) +val pr_stats : bool ref + +(** File containing the particles to use *) +val particules_file : string ref + +(** Information about current .gwo file. *) +type file_info = { + mutable f_curr_src_file : string; + mutable f_curr_gwo_file : string; + mutable f_separate : bool; + mutable f_bnotes : [ `drop | `erase | `first | `merge ]; + mutable f_shift : int; + mutable f_local_names : (int * int, int) Hashtbl.t; +} + +(** Link .gwo files and create a database. *) +val link : + (file_info -> unit -> Gwcomp.gw_syntax option) -> string -> bool diff --git a/bin/gwc/gwc.ml b/bin/gwc/gwc.ml index ecfb8ab8c3..c6025c9b98 100644 --- a/bin/gwc/gwc.ml +++ b/bin/gwc/gwc.ml @@ -3,6 +3,7 @@ open Geneweb open Gwcomp +(** Checks a .gwo header and prints fails if header is absent or not compatible. *) let check_magic fname ic = let b = really_input_string ic (String.length magic_gwo) in if b <> magic_gwo then @@ -14,6 +15,17 @@ let check_magic fname ic = ("\"" ^ fname ^ "\" is not a GeneWeb object file, or it is a very old version") +(** [next_family_fun_templ gwo_list fi] creates a function that read + sucessivly a [Gwcomp.gw_syntax] for all .gwo files. In details it does : + + - Switch to the next element in the [gwo_list] if reached the end + of the current file. Each element is [(gwo,separate, bnotes, shift)] + where [gwo] is .gwo filename and [separate], [bnotes], [shift] are + captured options from command line related to the giving file. + - Modify [fi] with mentioned previusly information if needed. + - Start/continue to read current .gwo file content and return + [Gwcomp.gw_syntax]. [None] is returned when reading of the last + .gwo file reaches end of file *) let next_family_fun_templ gwo_list fi = let ngwo = List.length gwo_list in let run = @@ -49,6 +61,7 @@ let next_family_fun_templ gwo_list fi = match r with Some fam -> Some fam | None -> + (* switch to the next .gwo file *) match !gwo_list with (x, separate, bnotes, shift) :: rest -> run (); diff --git a/bin/gwc/gwcomp.ml b/bin/gwc/gwcomp.ml index f99efc8f48..402060736e 100644 --- a/bin/gwc/gwcomp.ml +++ b/bin/gwc/gwcomp.ml @@ -4,38 +4,77 @@ open Geneweb open Def open Gwdb +(** .gwo file header *) let magic_gwo = "GnWo000o" (* Option qui force a créé les clés des individus. De fait, *) (* si la clé est incomplète, on l'enregistre tout de même. *) let create_all_keys = ref false +(** Key to refer a person's definition *) type key = { pk_first_name : string; pk_surname : string; pk_occ : int } +(** Represents a person in .gw file. It could be either reference to a person + (only key elements provided) or definition (all information provided). *) type somebody = - Undefined of key + (** Reference to person *) + | Undefined of key + (** Person's definition *) | Defined of (iper, iper, string) gen_person +(** Blocks that could appear in .gw file. *) type gw_syntax = - Family of - somebody gen_couple * sex * sex * (somebody * sex) list * - (string gen_fam_event_name * cdate * string * string * string * - string * (somebody * sex * witness_kind) list) - list * - ((iper, iper, string) gen_person, ifam, string) gen_family * - (iper, iper, string) gen_person gen_descend + (** Family definition block. Contains: + - Family couple (father's and mother's definition/reference) + - Father's sex + - Mother's sex + - List of witnesses definition/reference with their sex. + - List of information about every family event (name, date, + place, reason, source, notes and witnesses) + - Family definition + - Children (descendants) *) + | Family of + somebody gen_couple + * sex + * sex + * (somebody * sex) list + * (string gen_fam_event_name * cdate * string * string * string * + string * (somebody * sex * witness_kind) list) list + * ((iper, iper, string) gen_person, ifam, string) gen_family + * (iper, iper, string) gen_person gen_descend + (** Block that defines personal notes. First element represents + reference to person. Second is note's content. *) | Notes of key * string + (** Block that defines relations of a person with someone outisde of + family block (like foster parents) (field {i rparents}). Contains: + - Concerned person definition/reference + - Sex of person + - List of his relations. *) | Relations of somebody * sex * (somebody, string) gen_relation list + (** Block that defines events of a person. Specific to gwplus format. Contains: + - Concerned person's definition/reference + - Sex of person + - List of information about every personal event (name, date, + place, reason, source, notes and witnesses)*) | Pevent of - somebody * sex * - (string gen_pers_event_name * cdate * string * string * string * - string * (somebody * sex * witness_kind) list) - list + somebody + * sex + * (string gen_pers_event_name * cdate * string * string * string * + string * (somebody * sex * witness_kind) list) list + (** Block that defines database notes and extended pages. + First string represents name of extended page ("" for + database notes, only one for file). Second is note's + or page's content. *) | Bnotes of string * string + (** Block that defines wizard notes. First string represents + First string represents wizard's id. Second is note's content. *) | Wnotes of string * string +(** {i .gw} file encoding *) type encoding = E_utf_8 | E_iso_8859_1 +(** [copy_decode s i1 i2] decode the word delimited by [i1] and [i2] inside [s] + by remplacing "\\" -> '\' and '_' -> ' ' *) let copy_decode s i1 i2 = let len = let rec loop len i = @@ -61,6 +100,7 @@ let copy_decode s i1 i2 = in loop_copy (Bytes.create len) i1 0 +(** Return list of words inside the [str] *) let fields str = let rec loop beg i = if i < String.length str then @@ -74,6 +114,7 @@ let fields str = in loop 0 0 +(** Removes spaces at the begining an at the end of string. *) let cut_space x = let len = String.length x in if len = 0 then x @@ -83,11 +124,14 @@ let cut_space x = let stop = if x.[len-1] = ' ' then len - 1 else len in if start = 0 && stop = len then x else String.sub x start (stop - start) +(** Returns field if its label [lab] is first element of [l] *) let get_field lab l = match l with lab1 :: x :: l' when lab1 = lab -> cut_space x, l' | _ -> "", l +(** Parses [Def.date] from string that starts at pos [i] + inside [s] *) let date_of_string s i = let champ i = let (neg, i) = @@ -229,10 +273,17 @@ let date_of_string s i = Some (dt, i) -> if i = String.length s then Some dt else error 5 | None -> None +(** Line counter while reading .gw file *) let line_cnt = ref 0 + +(** Do not raise exception if syntax error occured. + Instead print error information on stdout *) let no_fail = ref false + +(** Save path to the images *) let no_picture = ref false +(** Read line from input channel. *) let input_line0 ic = let line = input_line ic in incr line_cnt; @@ -240,16 +291,19 @@ let input_line0 ic = String.sub line 0 (String.length line - 1) else line +(** Read a line and convert it to [encoding]. *) let input_a_line (ic, encoding) = let line = input_line0 ic in match encoding with E_utf_8 -> line | E_iso_8859_1 -> Mutil.utf_8_of_iso_8859_1 line +(** Read a line. If line is empty or only contains a comment, then read next line *) let rec input_real_line ic = let x = input_a_line ic in if x = "" || x.[0] = '#' then input_real_line ic else x +(** Parses person's birthdate if it is present. *) let get_optional_birthdate l = match l with x :: l' -> @@ -263,6 +317,7 @@ let get_optional_birthdate l = end | _ -> None, l +(** Parses person's babtization date if it is present. *) let get_optional_baptdate l = match l with x :: l' -> @@ -276,6 +331,7 @@ let get_optional_baptdate l = else None, l | _ -> None, l +(** Parse death information if present. *) let get_optional_deathdate l = match l with "?" :: l' -> Some DontKnowIfDead, l' @@ -304,6 +360,7 @@ let get_optional_deathdate l = else None, l | _ -> None, l +(** Parse burial information if present. *) let get_burial l = match l with "#buri" :: l -> @@ -332,12 +389,15 @@ let get_burial l = end | _ -> UnknownBurial, l +(** Parse sex of person *) let get_optional_sexe = function "h" :: l -> Male, l | "f" :: l -> Female, l | l -> Neuter, l +(** Parses int from that starts at the position [i] inside [x]. + Raises [Not_found] if integer isn't found. *) let make_int x = let rec loop found n i = if i = String.length x then if found then n else raise Not_found @@ -349,6 +409,8 @@ let make_int x = in loop false 0 +(** Parses person's first name and occurence number. Returns 0 if occurence + number isn't present. *) let get_fst_name str l = match l with x :: l' -> @@ -366,10 +428,11 @@ let get_fst_name str l = | None -> x, 0 in x, occ, l' - | _ -> failwith str + | _ -> failwith str end - | _ -> failwith str + | _ -> failwith str +(** Parses person's first name aliases if they are present *) let rec get_fst_names_aliases str l = match l with x :: l' -> @@ -379,24 +442,31 @@ let rec get_fst_names_aliases str l = else [], l | [] -> [], l +(** Parses person's surname aliases if they are present *) let rec get_surnames_aliases str l = match l with "#salias" :: x :: l' -> let (nl, l) = get_surnames_aliases str l' in cut_space x :: nl, l | _ -> [], l +(** Parses person's qualifiers if they are present *) let rec get_qualifiers str l = match l with "#nick" :: x :: l' -> let (nl, l) = get_qualifiers str l' in cut_space x :: nl, l | _ -> [], l +(** Parses person's aliases if they are present *) let rec get_aliases str l = match l with "#alias" :: x :: l' -> let (nl, l) = get_aliases str l' in cut_space x :: nl, l | _ -> [], l +(** [get_name l] parses a last name. Looks up first element of the list and returns a + [(name,rest)] couple where [name] is a person's last name and [rest] is a tail of + the list. If first element is [#nick], [#alias] start with '{' returns empty string + and list unchanged. *) let get_name l = match l with | "#nick" :: _ | "#alias" :: _ -> "", l @@ -411,6 +481,7 @@ let get_name l = end | _ -> "", l +(** Parses person's public name if present *) let get_pub_name l = match l with | x :: l' -> @@ -419,28 +490,33 @@ let get_pub_name l = else "", l | _ -> "", l +(** Parses person's image path if present *) let get_image l = match l with | ("#image" | "#photo") :: x :: l' -> if !no_picture then "", l' else cut_space x, l' | _ -> "", l +(** Parses person's occupation if present *) let get_occu l = match l with | "#occu" :: x :: l' -> cut_space x, l' | _ -> "", l +(** Parses person's source if present *) let get_sources l = match l with "#src" :: x :: l' -> cut_space x, l' | _ -> "", l +(** Parses person's acces rights *) let get_access l = match l with "#apubl" :: l' -> Public, l' | "#apriv" :: l' -> Private, l' | _ -> IfTitles, l +(** Create [gen_title] from string *) let scan_title t = let next_field i = let rec loop s i = @@ -485,6 +561,7 @@ let scan_title t = t_date_start = Adef.cdate_of_od date_start; t_date_end = Adef.cdate_of_od date_end; t_nth = nth} +(** Parses list of titles ([gen_title]) if they are present. *) let rec get_titles str l = match l with x :: l' -> @@ -496,6 +573,7 @@ let rec get_titles str l = else [], l | _ -> [], l +(** Parses person's event name *) let get_pevent_name str l = match l with "#birt" :: l' -> Epers_Birth, l' @@ -554,6 +632,7 @@ let get_pevent_name str l = else failwith str | _ -> failwith str +(** Parses family event name *) let get_fevent_name str l = match l with | "#marr" :: l' -> Efam_Marriage, l' @@ -573,6 +652,7 @@ let get_fevent_name str l = else failwith str | _ -> failwith str +(** Parses event date if it is present. *) let get_optional_event_date l = match l with x :: l' -> @@ -586,12 +666,21 @@ let get_optional_event_date l = end | _ -> None, l +(** Parse witness kind *) let get_event_witness_kind l = match l with "#godp" :: l' -> Witness_GodParent, l' | "#offi" :: l' -> Witness_Officer, l' | _ -> Witness, l +(** Parses the line containing an information about relationship between parents within family + and returns [((relk, fath_sex, moth_sex), mar, place, note, src, divorce, rest)]. + [relk] i a relation kind between parents ([Def.relation_kind]), [fath_sex] and [moth_sex] + is a sex of each parent, [mar] is a optional mariage date (if married), [place] is a + marriage place if present, [note] is a mariage note if present, [src] is a mariage source + if present, [divorce] is a divorce status [Def.divorce], [rest] is the rest of the line to + parse +*) let get_mar_date str = function x :: l -> @@ -657,10 +746,13 @@ let get_mar_date str = relation, mar, place, note, src, divorce, l | [] -> failwith str +(** Read and return a line with list of words that appears on this line. If + reading raises [Enf_of_file] returns [None] *) let read_line ic = try let str = input_real_line ic in Some (str, fields str) with End_of_file -> None +(** Create a dummy [gen_person]. *) let create_person () = {first_name = ""; surname = ""; occ = 0; image = ""; public_name = ""; qualifiers = []; aliases = []; first_names_aliases = []; @@ -673,8 +765,18 @@ let create_person () = burial_note = ""; burial_src = ""; pevents = []; notes = ""; psources = ""; key_index = Gwdb.dummy_iper} +(** Person is unknown (bogus definition) *) let bogus_def p n = p = "?" || n = "?" +(** Parse the line and create person's [gen_person] definition. + Doesn't modify folowing personal information: + - Key + - Parents + - Related persons + - Events + - Notes + If can't parse person's sources use [comm_psources] instead. + If can't parse bithdate use [comm_birth_place] instead. *) let set_infos fn sn occ sex comm_psources comm_birth_place str u l = let (first_names_aliases, l) = get_fst_names_aliases str l in let (surnames_aliases, l) = get_surnames_aliases str l in @@ -693,6 +795,7 @@ let set_infos fn sn occ sex comm_psources comm_birth_place str u l = let (baptism, l) = get_optional_baptdate l in let (baptism_place, l) = let (pp, l) = get_field "#pp" l in + (* if no baptization place then it's equals to birth place *) if pp = "" then get_field "#bp" l else pp, l in let (bapt_note, l) = get_field "#pn" l in @@ -744,9 +847,16 @@ let set_infos fn sn occ sex comm_psources comm_birth_place str u l = in u, l +(** Parses the line containing a parent and returns [(somebody,np,rest)]. [somebody] is either [Defined p] if + person's definiton was parsed ([p] regroups all personal information) either [Undefined k] if a reference + to a person already defined was parsed ([k] is a key to find corresponding definition). [np] is a person's + surname. [rest] is a rest of line to parse. Could be used to parse familial witnesses. *) let parse_parent str l = + (* last name *) let (np, l) = get_name l in + (* first name and occurence number *) let (pp, op, l) = get_fst_name str l in + (* person is not defined as a child elsewhere (is defined here) *) let defined = if bogus_def pp np then true else @@ -762,6 +872,10 @@ let parse_parent str l = let u = create_person () in let (u, l) = set_infos pp np op u.sex "" "" str u l in Defined u, np, l +(** Parses the line containing a childran and returns a person [gen_person] containing + all extracted information. If a childran definition doesn't provide + surname then father's surname is used. ALso if it doesn't provide a childran's + birth place and source then it uses information provided by family definiton. *) let parse_child str surname sex csrc cbp l = let u = create_person () in let (prenom, occ, l) = get_fst_name str l in @@ -779,6 +893,8 @@ let parse_child str surname sex csrc cbp l = in set_infos prenom nom occ sex csrc cbp str u l +(** Parse relation type [Def.gen_relation] with a person outside of family block + (foster parents, god parent, etc.). *) let get_relation str = function "-" :: x :: l -> @@ -815,6 +931,8 @@ let get_relation str = end | _ -> failwith str +(** Read notes of a person inside [note] block across multiple lines and + concat them. *) let read_notes ic = let notes = try @@ -829,6 +947,8 @@ let read_notes ic = Mutil.strip_all_trailing_spaces notes (* from version 5.00 *) +(** Read database notes across multiple lines and concat them. Stop reading when + encounter [end_text] *) let read_notes_db ic end_txt = let notes = try @@ -848,13 +968,20 @@ let read_notes_db ic end_txt = in Mutil.strip_all_trailing_spaces notes +(** Parsing status of .gw block *) type 'a read_family = - F_some of 'a + (** Read block inside .gw file *) + | F_some of 'a + (** Read block that defines that file use utf-8 encoding *) | F_enc_utf_8 + (** Read block that defines that the file uses gwplus syntax *) | F_gw_plus + (** Read end of the file *) | F_none + (** Exception while reading *) | F_fail of string +(** Read succesive family note lines and concat it. *) let loop_note line ic = let rec loop_note acc str = match fields str with @@ -872,6 +999,9 @@ let loop_note line ic = in loop_note [] line +(** Parse witnesses across the lines and returns list of [(wit,wsex,wk)] + where wit is a witness definition/reference, [wsex] is a sex of witness + and [wk] is a kind of witness relationship to the family. *) let loop_witn line ic = let rec loop_witn acc str = match fields str with @@ -890,20 +1020,29 @@ let loop_witn line ic = in loop_witn [] line +(** Read and parse a gw file block from [ic]. Returns also next line if it's + not the end of the file. *) let read_family ic fname = function - Some (_, ["encoding:"; "utf-8"]) -> F_enc_utf_8 + (* Block that defines that file use utf-8 encoding *) + | Some (_, ["encoding:"; "utf-8"]) -> F_enc_utf_8 + (* Block that defines that the file uses gwplus syntax *) | Some (_, ["gwplus"]) -> F_gw_plus + (* Family block *) | Some (str, "fam" :: l) -> + (* read father *) let (fath_key, surname, l) = parse_parent str l in + (* read relation between parents *) let (relation_ss, marriage, marr_place, marr_note, marr_src, divorce, l) = get_mar_date str l in let (relation, fath_sex, moth_sex) = relation_ss in + (* read mother *) let (moth_key, _, l) = parse_parent str l in if l <> [] then failwith str; let line = read_line ic in + (* read list of witnesses with their sex (if exists) *) let (witn, line) = let rec loop = function @@ -922,25 +1061,30 @@ let read_family ic fname = in loop line in + (* read familial source if present *) let (fsrc, line) = match line with Some (_, ["src"; x]) -> cut_space x, read_line ic | Some (str, "src" :: _) -> failwith str | _ -> "", line in + (* read common children source if present *) let (csrc, line) = match line with Some (_, ["csrc"; x]) -> cut_space x, read_line ic | Some (str, "csrc" :: _) -> failwith str | _ -> "", line in + (* read common children birth place if present *) let (cbp, line) = match line with Some (_, ["cbp"; x]) -> cut_space x, read_line ic | Some (str, "cbp" :: _) -> failwith str | _ -> "", line in + (* create a couple *) let co = Adef.couple fath_key moth_key in + (* read a family comments *) let (comm, line) = match line with Some (str, "comm" :: _) -> @@ -948,6 +1092,7 @@ let read_family ic fname = comm, read_line ic | _ -> "", line in + (* read family events *) let (fevents, line) = match line with Some (_, "fevt" :: _) -> @@ -983,7 +1128,8 @@ let read_family ic fname = | _ -> [], line in begin match line with - Some (_, ["beg"]) -> + (* have children *) + | Some (_, ["beg"]) -> let cles_enfants = let rec loop children = match read_line ic with @@ -997,6 +1143,7 @@ let read_family ic fname = in List.rev (loop []) in + (* create a family definition (without witnesses, events and family index) *) let fo = {marriage = marriage; marriage_place = marr_place; marriage_note = marr_note; marriage_src = marr_src; @@ -1008,6 +1155,7 @@ let read_family ic fname = F_some (Family (co, fath_sex, moth_sex, witn, fevents, fo, deo), read_line ic) + (* no children *) | line -> let fo = {marriage = marriage; marriage_place = marr_place; @@ -1020,9 +1168,11 @@ let read_family ic fname = F_some (Family (co, fath_sex, moth_sex, witn, fevents, fo, deo), line) end + (* Database notes block *) | Some (_, ["notes-db"]) -> let notes = read_notes_db ic "end notes-db" in F_some (Bnotes ("", notes), read_line ic) + (* Extended page block *) | Some (str, ["page-ext"; _]) -> let p = let len = String.length "page-ext" + 1 in @@ -1030,9 +1180,10 @@ let read_family ic fname = in let notes = read_notes_db ic "end page-ext" in F_some (Bnotes (p, notes), read_line ic) + (* Used before version 5.00. Notes block *) | Some (_, ["notes"]) -> - (* used before version 5.00 *) let notes = read_notes ic in F_some (Bnotes ("", notes), read_line ic) + (* Notes block *) | Some (str, "notes" :: l) -> let (surname, l) = get_name l in let (first_name, occ, l) = get_fst_name str l in @@ -1048,6 +1199,7 @@ let read_family ic fname = | Some (str, _) -> failwith str | None -> failwith "end of file" end + (* Wizard note block *) | Some (str, "wizard-note" :: _) -> let wizid = let len = String.length "wizard-note " in @@ -1055,8 +1207,11 @@ let read_family ic fname = in let notes = read_notes_db ic "end wizard-note" in F_some (Wnotes (wizid, notes), read_line ic) + (* Personal relation block *) | Some (str, "rel" :: l) -> + (* get considered person *) let (sb, _, l) = parse_parent str l in + let (sex, l) = match l with "#h" :: l -> Male, l @@ -1066,7 +1221,8 @@ let read_family ic fname = if l <> [] then failwith "str" else begin match read_line ic with - Some (_, ["beg"]) -> + (* Read list of relations *) + | Some (_, ["beg"]) -> let rl = try let rec loop = @@ -1081,7 +1237,9 @@ let read_family ic fname = | Some (str, _) -> failwith str | None -> failwith "end of file" end + (* Person's events block *) | Some (str, "pevt" :: l) -> + (* get considered person *) let (sb, _, l) = parse_parent str l in if l <> [] then failwith str else @@ -1116,20 +1274,26 @@ let read_family ic fname = let pevents = List.rev pevents in F_some (Pevent (sb, Neuter, pevents), read_line ic) | Some (str, _) -> failwith str + (* End of the file *) | None -> F_none +(** Read and return a block of .gw file. If [!no_fail] is disabled raises + [Failure] exception. *) let read_family_1 ic fname line = if !no_fail then try read_family ic fname line with Failure str -> F_fail str else read_family ic fname line +(** Compile .gw file and save result to corresponding .gwo *) let comp_families x = let out_file = Filename.chop_suffix x ".gw" ^ ".gwo" in line_cnt := 0; let oc = open_out_bin out_file in begin try let ic = open_in x in + (* write header *) output_string oc magic_gwo; + (* write source filename *) output_value oc (x : string); let rec loop line encoding = match read_family_1 (ic, encoding) x line with diff --git a/bin/gwc/gwcomp.mli b/bin/gwc/gwcomp.mli index 6dc1eecf10..e94cf30852 100644 --- a/bin/gwc/gwcomp.mli +++ b/bin/gwc/gwcomp.mli @@ -4,35 +4,85 @@ open Geneweb open Def open Gwdb +(** Key to refer a person's definition *) type key = { pk_first_name : string; pk_surname : string; pk_occ : int } + +(** Represents a person in .gw file. It could be either reference to a person + (only key elements provided) or definition (all information provided). *) type somebody = - Undefined of key + (** Reference to person *) + | Undefined of key + (** Person's definition *) | Defined of (iper, iper, string) gen_person +(** Blocks that could appear in .gw file. *) type gw_syntax = - Family of - somebody gen_couple * sex * sex * (somebody * sex) list * - (string gen_fam_event_name * cdate * string * string * string * - string * (somebody * sex * witness_kind) list) - list * - ((iper, iper, string) gen_person, ifam, string) gen_family * - (iper, iper, string) gen_person gen_descend + (** Family definition block. Contains: + - Family couple (father's and mother's definition/reference) + - Father's sex + - Mother's sex + - List of witnesses definition/reference with their sex. + - List of information about every family event (name, date, + place, reason, source, notes and witnesses) + - Family definition + - Children (descendants) *) + | Family of + somebody gen_couple + * sex + * sex + * (somebody * sex) list + * (string gen_fam_event_name * cdate * string * string * string * + string * (somebody * sex * witness_kind) list) list + * ((iper, iper, string) gen_person, ifam, string) gen_family + * (iper, iper, string) gen_person gen_descend + (** Block that defines personal notes. First element represents + reference to person. Second is note's content. *) | Notes of key * string + (** Block that defines relations of a person with someone outisde of + family block. Contains: + - Concerned person definition/reference + - Sex of person + - List of his relations. *) | Relations of somebody * sex * (somebody, string) gen_relation list + (** Block that defines events of a person. Specific to gwplus format. Contains: + - Concerned person definition/reference + - Sex of person + - List of information about every personal event (name, date, + place, reason, source, notes and witnesses)*) | Pevent of - somebody * sex * - (string gen_pers_event_name * cdate * string * string * string * - string * (somebody * sex * witness_kind) list) - list + somebody + * sex + * (string gen_pers_event_name * cdate * string * string * string * + string * (somebody * sex * witness_kind) list) list + (** Block that defines database notes and extended pages. + First string represents name of extended page ("" for + database notes, only one for file). Second is note's + or page's content. *) | Bnotes of string * string + (** Block that defines wizard notes. First string represents + First string represents wizard's id. Second is note's content. *) | Wnotes of string * string +(** .gwo file header *) val magic_gwo : string + +(** Line counter while reading .gw file *) val line_cnt : int ref + +(** Do not raise exception if syntax error occured. + Instead print error information on stdout *) val no_fail : bool ref -val comp_families : string -> unit + +(** Save path to the images *) val no_picture : bool ref + +(** Forces to create all the keys for every persons (even for ? ?). + Enabled for gwplus format. *) val create_all_keys : bool ref +(** Compile .gw file and save result to corresponding .gwo *) +val comp_families : string -> unit + (* Ajout pour l'API *) +(** Parses [Def.date] from string that starts at pos [i] inside [s] *) val date_of_string : string -> int -> date option diff --git a/bin/gwd/gwd.ml b/bin/gwd/gwd.ml index 6f5b92d632..257f354d1b 100644 --- a/bin/gwd/gwd.ml +++ b/bin/gwd/gwd.ml @@ -659,7 +659,7 @@ let allowed_denied_titles key extra_line env base_env () = if fname = "" then [] else let ic = - Secure.open_in (Filename.concat (Secure.bd ()) fname) + Secure.open_in (Filename.concat (Secure.base_dir ()) fname) in let rec loop set = let (line, eof) = @@ -1930,7 +1930,7 @@ let main () = images_url := "file://" ^ slashify abs_dir end; if !(Util.cnt_dir) = Filename.current_dir_name then - Util.cnt_dir := Secure.bd (); + Util.cnt_dir := Secure.base_dir (); Wserver.stop_server := List.fold_left Filename.concat !(Util.cnt_dir) ["cnt"; "STOP_SERVER"]; let (query, cgi) = diff --git a/bin/gwd/gwdLog.mli b/bin/gwd/gwdLog.mli new file mode 100644 index 0000000000..4f47e6af0f --- /dev/null +++ b/bin/gwd/gwdLog.mli @@ -0,0 +1,28 @@ +(** Verbosity level: defines the verbosity level that will + allow the `syslog` function to print anything. *) +val verbosity : int ref + +(** If set to `true`, prints backtrace when printng log. *) +val debug : bool ref + +(** The output channel in which log is written. *) +val oc : out_channel option ref + +(** Prints on `oc` *) +val log : (out_channel -> unit) -> unit + +(** The level of log. *) +type level = [ + | `LOG_EMERG (** Print if `!verbosity >= 0` *) + | `LOG_ALERT (** Print if `!verbosity >= 1` *) + | `LOG_CRIT (** Print if `!verbosity >= 2` *) + | `LOG_ERR (** Print if `!verbosity >= 3` *) + | `LOG_WARNING (** Print if `!verbosity >= 4` *) + | `LOG_NOTICE (** Print if `!verbosity >= 5` *) + | `LOG_INFO (** Print if `!verbosity >= 6` *) + | `LOG_DEBUG (** Print if `!verbosity >= 7` *) + ] + +(** [syslog level msg] + Prints `msg` on `!oc` depending on the verbosity. *) +val syslog : level -> string -> unit diff --git a/bin/gwd/gwdPlugin.mli b/bin/gwd/gwdPlugin.mli new file mode 100644 index 0000000000..5c2d0c6295 --- /dev/null +++ b/bin/gwd/gwdPlugin.mli @@ -0,0 +1,15 @@ +val assets : string ref + +val ht : (string, (string * (Geneweb.Config.config -> Gwdb.base option -> bool))) Hashtbl.t + +val register : + ns:string -> + (string * (string -> Geneweb.Config.config -> Gwdb.base option -> bool)) list -> + unit + +val se : (string * (Geneweb.Config.config -> Gwdb.base option -> unit)) list ref + +val register_se : + ns:string -> + (string -> Geneweb.Config.config -> Gwdb.base option -> unit) -> + unit diff --git a/bin/gwd/gwdPluginDep.mli b/bin/gwd/gwdPluginDep.mli new file mode 100644 index 0000000000..10ecf8bd83 --- /dev/null +++ b/bin/gwd/gwdPluginDep.mli @@ -0,0 +1,10 @@ +type 'a sort_result = Sorted of 'a list | ErrorCycle of 'a list + +(** Given a list of elements (in this case, plugins) and their dependencies, + tries to compute a valid order `l` and return `Sorted l` . + If there is a cycle, returns `ErrorCycle l'` where `l'` is a dependency + cycle. + Uses Kahn's algorithm for cycle detection. + *) +val sort : ('a * 'a list) list -> 'a sort_result + diff --git a/bin/gwd/gwdPluginMD5.mli b/bin/gwd/gwdPluginMD5.mli new file mode 100644 index 0000000000..54fb7b60e4 --- /dev/null +++ b/bin/gwd/gwdPluginMD5.mli @@ -0,0 +1 @@ +val allowed : string -> bool diff --git a/bin/gwd/gwdPluginMETA.mli b/bin/gwd/gwdPluginMETA.mli new file mode 100644 index 0000000000..d38fc780f1 --- /dev/null +++ b/bin/gwd/gwdPluginMETA.mli @@ -0,0 +1,7 @@ +type meta = + { version : string + ; maintainers : string list + ; depends : string list + } + +val parse : string -> meta diff --git a/bin/gwd/request.ml b/bin/gwd/request.ml index 3c2055604d..3b5c63dce6 100644 --- a/bin/gwd/request.ml +++ b/bin/gwd/request.ml @@ -211,6 +211,7 @@ let very_unknown conf _ = Hutil.trailer conf | None -> incorrect_request conf +(* Print Not found page *) let unknown = begin fun conf n -> let title _ = Output.printf conf "%s: \"%s\"" (Utf8.capitalize_fst (transl conf "not found")) diff --git a/bin/gwd/request.mli b/bin/gwd/request.mli index a93f050550..98a0d43b21 100644 --- a/bin/gwd/request.mli +++ b/bin/gwd/request.mli @@ -48,7 +48,6 @@ val w_person (**/**) (* Used internally by [gwd]. Not intended to be used by other programs. *) -val special_vars : string list val treat_request : Config.config -> unit (**/**) diff --git a/bin/gwd/robot.mli b/bin/gwd/robot.mli new file mode 100644 index 0000000000..14b13d72da --- /dev/null +++ b/bin/gwd/robot.mli @@ -0,0 +1,52 @@ +(** A module handling robots requests *) +(* S: This module seems obsolete *) + +val magic_robot : string + +module W : Map.S with type key = string + +type norfriwiz = Normal | Friend of string | Wizard of string + +type who = private { + acc_times : float list; (** The timings of the connexion attempts *) + oldest_time : float; (** The first connection in the specified window + (check option -robot-xcl) of time in which successive + connections are attempted. *) + nb_connect : int; (** The number of connection in the specified window. *) + nbase : string; (** Always be equal to conf.bname *) + utype : norfriwiz; (** The kind of robot *) +} + +(** A collection of robots: the list contains forbidden robots and + the map contains accepted (under conditions) robots. *) +type excl = { + mutable excl : (string * int ref) list; + mutable who : who W.t; + mutable max_conn : int * string; +} + +(** Prints an error "Access refuned" in HTML and raises an `Exit` exception. *) +val robot_error : Geneweb.Config.config -> int -> int -> 'a + +(** Reads the content of the admin file managing robots and returns its content + and the full file name. *) +val robot_excl : unit -> excl * string + +val min_disp_req : int ref + +(** [check tm from max_call sec conf suicide] + Returns a tuple containing: + * the number of robots who attempted to connect twice + * the number of wizard robots who attempted to connect twice + * the number of friend robots who attempted to connect twice + * the wizards list and their last connection attempt. + It also updates the robot file by blocking robots who did too many attempts. +*) +val check : + float -> + string -> + int -> + int -> + Geneweb.Config.config -> + bool -> + int * int * int * (string * float) list diff --git a/bin/gwdiff/gwdiff.ml b/bin/gwdiff/gwdiff.ml index edb6ebe916..0fd4ca6c00 100644 --- a/bin/gwdiff/gwdiff.ml +++ b/bin/gwdiff/gwdiff.ml @@ -15,6 +15,8 @@ let html = ref false let root = ref "" let cr = ref "" +(* Messages are printed when there is a difference between a person + present in the two bases explored. *) type messages = MsgBadChild of iper | MsgBirthDate @@ -34,6 +36,9 @@ type messages = | MsgSpouses of iper | MsgSurname +(* [person_string base iper] + Returns the string associated to the person with id `iper` in the base + `base`. *) let person_string base iper = let p = poi base iper in let fn = sou base (get_first_name p) in @@ -42,12 +47,15 @@ let person_string base iper = fn ^ " " ^ sn ^ " (#" ^ string_of_iper iper ^ ")" else fn ^ "." ^ string_of_int (get_occ p) ^ " " ^ sn +(* Returns the string associated to a person in HTML if the html option is set, + otherwise it has the same effect tja, `person_string`. *) let person_link bname base iper target = if !html then Printf.sprintf "%s" !root bname (string_of_iper iper) target (person_string base iper) else person_string base iper +(* Prints a message *) let print_message base1 msg = Printf.printf " "; begin match msg with @@ -81,6 +89,7 @@ let print_message base1 msg = end; Printf.printf "%s" !cr +(* Prints messages associates to the two families identifiers in argument *) let print_f_messages base1 base2 ifam1 ifam2 res = let f1 = foi base1 ifam1 in let f2 = foi base2 ifam2 in @@ -91,19 +100,30 @@ let print_f_messages base1 base2 ifam1 ifam2 res = (person_link !in_file2 base2 (get_father f2) "base2") !cr; List.iter (print_message base1) res +(* Same, but for persons *) let print_p_messages base1 base2 iper1 iper2 res = Printf.printf "%s / %s%s" (person_link !in_file1 base1 iper1 "base1") (person_link !in_file2 base2 iper2 "base2") !cr; List.iter (print_message base1) res +(* [compatible_names src_name dest_name_list] + Returns true if `src_name` is in `dest_name_list` (case insensitive) *) let compatible_names src_name dest_name_list = let src_name = Name.lower src_name in let dest_name_list = List.map Name.lower dest_name_list in List.mem src_name dest_name_list +(* [compatible_str_field istr1 istr2] + Checks the compatibility of two string identifiers, i.e. + if istr1 is not the empty string identifier, then istr2 + must not be. *) let compatible_str_field istr1 istr2 = is_empty_string istr1 || not (is_empty_string istr2) +(* Returns a list of intervals of SDN (SDN 1 is November 25, 4714 BC Gregorian + calendar) of the date in argument. An interval has the format (b, b'), + where b is an optional lower bound (None => no bound), and b' an optional + upper bound. *) let dmy_to_sdn_range_l dmy = let sdn_of_dmy dmy = let sdn = Calendar.sdn_of_gregorian dmy in @@ -130,6 +150,7 @@ let dmy_to_sdn_range_l dmy = in sdn, sdn2 in + (* S: calls to sdn_of_dmy dmy can be factorized *) match dmy.prec with Sure -> let (sdn1, sdn2) = sdn_of_dmy dmy in [Some sdn1, Some sdn2] | Maybe -> @@ -149,9 +170,13 @@ let dmy_to_sdn_range_l dmy = let (_sdn21, sdn22) = sdn_of_dmy (Date.dmy_of_dmy2 dmy2) in [Some sdn11, Some sdn22] +(* [compatible_sdn i1 i2] + Checks if two intervals `i1` and `i2` (as described for `dmy_to_sdn_range_l`) + are compatible, i.e. if i2 is a sub interval of i1. *) let compatible_sdn (sdn11, sdn12) (sdn21, sdn22) = if (sdn21, sdn22) = (None, None) then true else + (* S: Add unit argument to bool2 to make good use of OCaml laziness *) let bool1 = match sdn11, sdn21 with Some sdn1, Some sdn2 -> sdn1 <= sdn2 @@ -166,15 +191,27 @@ let compatible_sdn (sdn11, sdn12) (sdn21, sdn22) = in bool1 && bool2 +(* [compatible_sdn_l l i] + Checks if there exists an interval in `l` that is compatible with `i` *) let compatible_sdn_l sdn1_l sdn2 = + (* S: replace by List.exists *) List.fold_left (fun r sdn1 -> r || compatible_sdn sdn1 sdn2) false sdn1_l +(* [compatible_sdn_l l1 l2] + Checks if for all intervals `i2` in `l2`, there exists an interval `i1` in + `l1` such that `i1` is compatible with `i2` *) let compatible_sdn_ll sdn1_l sdn2_l = List.fold_left (fun r sdn2 -> r && compatible_sdn_l sdn1_l sdn2) true sdn2_l +(* [compatible_dmys d1 d2] + Checks if `d1` is compatible with `d2`, i.e. if despite a potential lack + of precision in the dates, d2 is more precise than d1. *) let compatible_dmys dmy1 dmy2 = compatible_sdn_ll (dmy_to_sdn_range_l dmy1) (dmy_to_sdn_range_l dmy2) +(* [compatible_dates date1 date2] + Same than before, but also checks the kind of date (Dgreg or Dtext) + and, in the first case, if calendars are compatible. *) let compatible_dates date1 date2 = let compatible_cals cal1 cal2 = match cal1, cal2 with @@ -189,6 +226,7 @@ let compatible_dates date1 date2 = | Dgreg (_, _), Dtext _ -> false | Dtext _, _ -> true +(* Same than before, but for Adef.ctype. *) let compatible_cdates cdate1 cdate2 = let od1 = Adef.od_of_cdate cdate1 in let od2 = Adef.od_of_cdate cdate2 in @@ -197,6 +235,12 @@ let compatible_cdates cdate1 cdate2 = | Some _, None -> false | None, _ -> true +(* Checks if birth between two persons are compatible, i.e. if their birth date + (baptism date if birth date not provided) and place are compatible, and + returns a list of messages. + If birth is not provided, checks bathism date instead. + If birth/bathism date are not compatible, the returned list will have MsgBirthDate + If birth place are not compatible, the returned list will have MsgBirthPlace *) let compatible_birth p1 p2 = let get_birth person = if person.birth = Adef.cdate_None then person.baptism else person.birth @@ -212,6 +256,8 @@ let compatible_birth p1 p2 = in res1 @ res2 +(* Same than before, but for death. Messages returned are + MsgDeathDate and MsgDeathPlace *) let compatible_death p1 p2 = let bool1 = p1.death = p2.death || @@ -223,7 +269,7 @@ let compatible_death p1 p2 = DeadDontKnowWhen, (Death (_, _) | DeadYoung | DeadDontKnowWhen) | DontKnowIfDead, _ -> true - | _ -> false) + | _ -> (* S: avoid non-exhaustive pattern matching *) false) in let res1 = if bool1 then [] else [MsgDeathDate] in let res2 = @@ -232,13 +278,21 @@ let compatible_death p1 p2 = in res1 @ res2 +(* [compatible_sexes p1 p2] + Returns [] if `p1` and `p2` have the same sex, [MsgSex] otherwise. *) let compatible_sexes p1 p2 = if p1.sex = p2.sex then [] else [MsgSex] +(* [compatible_occupations p1 p2] + Returns [] if `p1` and `p2` have compatible occupations, [MsgOccupation] otherwise. *) let compatible_occupations p1 p2 = if compatible_str_field p1.occupation p2.occupation then [] else [MsgOccupation] +(* Checks if two persons' names are compatible wrt. their eventual aliases and returns a + list of messages. + If first names are not compatible, the returned list will have MsgFirstName. + If surnames are not compatible, the returned list will have MsgSurname. *) let compatible_persons_ligth base1 base2 p1 p2 = let fn1 = sou base1 p1.first_name in let fn2 = sou base2 p2.first_name in @@ -250,13 +304,19 @@ let compatible_persons_ligth base1 base2 p1 p2 = let res2 = if compatible_names sn1 asn2 then [] else [MsgSurname] in res1 @ res2 +(* Checks if two persons are compatible and returns all the messages associated + to the compatiblity of their name, sex, birth, death and occupation. *) let compatible_persons base1 base2 p1 p2 = compatible_persons_ligth base1 base2 p1 p2 @ compatible_sexes p1 p2 @ compatible_birth p1 p2 @ compatible_death p1 p2 @ compatible_occupations p1 p2 +(* [find_compatible_persons_ligth base1 base2 iper1 iper2_list] + Returns the sublist of persons of `iper2_list` that are compatible with + `iper1` (only checking names). *) let rec find_compatible_persons_ligth base1 base2 iper1 iper2_list = + (* S: not tail recursive, could be *) match iper2_list with [] -> [] | head :: rest -> @@ -266,6 +326,8 @@ let rec find_compatible_persons_ligth base1 base2 iper1 iper2_list = if compatible_persons_ligth base1 base2 p1 p2 = [] then head :: c_rest else c_rest +(* Same than before, but with full compatibility ( name, sex, birth, death and + occupation) *) let rec find_compatible_persons base1 base2 iper1 iper2_list = match iper2_list with [] -> [] @@ -276,6 +338,8 @@ let rec find_compatible_persons base1 base2 iper1 iper2_list = if compatible_persons base1 base2 p1 p2 = [] then head :: c_rest else c_rest +(* Checks if the spouse of the persons (whose id are in argument) are + compatible (only checking names) and returns the associated messages list. *) let compatible_unions base1 base2 iper1 iper2 ifam1 ifam2 = let get_spouse base iper ifam = let f = foi base ifam in @@ -286,6 +350,9 @@ let compatible_unions base1 base2 iper1 iper2 ifam1 ifam2 = let spouse2 = gen_person_of_person (get_spouse base2 iper2 ifam2) in compatible_persons_ligth base1 base2 spouse1 spouse2 +(* [find_compatible_unions base1 base2 iper1 iper2_list ifam1 ifam2_list] + Returns the sublist of families of `ifam2_list` whose union is compatible + (in the sense of `compatible_unions`). *) let rec find_compatible_unions base1 base2 iper1 iper2 ifam1 ifam2_list = match ifam2_list with [] -> [] @@ -297,12 +364,18 @@ let rec find_compatible_unions base1 base2 iper1 iper2 ifam1 ifam2_list = head :: c_rest else c_rest +(* [compatible_divorces d1 d2] + Returns true if divorces are compatible, i.e. if both divorced, then + checking date compatibility, if d1 is a divorce and d2 is not returns + false, otherwise returns true. *) let compatible_divorces d1 d2 = match d1, d2 with Divorced cdate1, Divorced cdate2 -> compatible_cdates cdate1 cdate2 | Divorced _, _ -> false | _ -> true +(* Checks the compatibility of marriages (mariage date, divorce + and mariage place), then print the list of messages calculated. *) let compatible_marriages base1 base2 ifam1 ifam2 = let f1 = gen_family_of_family (foi base1 ifam1) in let f2 = gen_family_of_family (foi base2 ifam2) in @@ -320,12 +393,16 @@ let compatible_marriages base1 base2 ifam1 ifam2 = let res = res1 @ res2 @ res3 in if res = [] then () else print_f_messages base1 base2 ifam1 ifam2 res +(* Calculates the compatibility of two persons and prints the associated + messages *) let pdiff base1 base2 iper1 iper2 = let p1 = gen_person_of_person (poi base1 iper1) in let p2 = gen_person_of_person (poi base2 iper2) in let res = compatible_persons base1 base2 p1 p2 in if res = [] then () else print_p_messages base1 base2 iper1 iper2 res +(* Calculates the compatibility of two persons' families and prints the + associated messages. *) let compatible_parents base1 base2 iper1 iper2 = let a1 = get_parents (poi base1 iper1) in let a2 = get_parents (poi base2 iper2) in @@ -340,7 +417,11 @@ let compatible_parents base1 base2 iper1 iper2 = | Some _, None -> print_p_messages base1 base2 iper1 iper2 [MsgParentsMissing] +(* Checks che compatibility of two persons and their families, and prints it. + This is performed recursively through their descendants *) let rec ddiff base1 base2 iper1 iper2 d_tab = + (* S: Simplify with statement: + let ddiff iper1 iper2 = ddiff base1 base2 iper1 iper2 d_tab *) let d_check = Gwdb.Marker.get d_tab iper1 in if List.mem iper2 d_check then () else @@ -385,6 +466,7 @@ let rec ddiff base1 base2 iper1 iper2 d_tab = let u2 = Array.to_list (get_family (poi base2 iper2)) in pdiff base1 base2 iper1 iper2; List.iter (fu base1 base2 u2) u1 +(* Returns the eldest persons on the base starting from the persons in argument. *) let rec find_top base1 base2 iper1 iper2 = let p1 = gen_person_of_person (poi base1 iper1) in let p2 = gen_person_of_person (poi base2 iper2) in @@ -411,6 +493,7 @@ let rec find_top base1 base2 iper1 iper2 = [] end +(* Same than ddiff, but starting from the eldest ancestors from the persons in argument *) let addiff base1 base2 iper1 iper2 d_tab = let topdiff (iper1, iper2) = Printf.printf "==> %s / %s%s" (person_link !in_file1 base1 iper1 "base1") diff --git a/bin/gwexport/gwexport.ml b/bin/gwexport/gwexport.ml index d516de149b..de1d0b7d26 100644 --- a/bin/gwexport/gwexport.ml +++ b/bin/gwexport/gwexport.ml @@ -106,7 +106,12 @@ let speclist c = module IPS = Util.IperSet module IFS = Util.IfamSet -(**/**) +(** [is_censored_person max_year person_name] + Returns [true] iff the person has a birth date that is after max_year and + its visibility is not public +*) +(* S: Does it mean private persons whose birth year is before 'max_year' + are uncensored? *) let is_censored_person threshold p = match Adef.od_of_cdate (get_birth p) with | None -> false @@ -115,15 +120,32 @@ let is_censored_person threshold p = | Adef.Dgreg (dmy, _) -> dmy.Adef.year >= threshold && get_access p != Def.Public | _ -> false +(** [is_censored_couple base max_year family] + Returns [true] if either the father or the mother of a given family in the + base is censored +*) let is_censored_couple base threshold cpl = is_censored_person threshold @@ poi base (get_father cpl) || is_censored_person threshold @@ poi base (get_mother cpl) + +(* The following functions are utils set people as "censored" by marking them. + Censoring a person consists in setting a mark defined as: + `Marker.get pmark p lor flag` + + This gets the current mark, being 0 or 1, and `lor`s it with `flag` which is `1`. + TODO: replace integer markers by booleans +*) + +(** Marks a censored person *) let censor_person base pmark flag threshold p no_check = let ps = poi base p in if no_check || is_censored_person threshold ps then Marker.set pmark p (Marker.get pmark p lor flag) +(** Marks all the members of a family that are censored. + If a couple is censored, its parents and all its descendants are marked. +*) let rec censor_family base pmark fmark flag threshold i no_check = let censor_unions p = let uni = poi base p in @@ -148,6 +170,7 @@ let rec censor_family base pmark fmark flag threshold i no_check = let censor_spouse iper = if all_families_censored iper then Marker.set pmark iper (Marker.get pmark iper lor flag) + (* S: Replace this line by `censor_person`? *) in if Marker.get fmark i = 0 then let fam = foi base i in @@ -158,6 +181,7 @@ let rec censor_family base pmark fmark flag threshold i no_check = censor_descendants i end +(** Marks all the families that are censored in the given base. *) let censor_base base pmark fmark flag threshold = Collection.iter begin fun i -> censor_family base pmark fmark flag threshold i false @@ -166,27 +190,34 @@ let censor_base base pmark fmark flag threshold = censor_person base pmark flag threshold i false end (ipers base) +(** Set non visible persons and families as censored *) let restrict_base base per_tab fam_tab flag = + (* Starts by censoring non visible persons of the base *) Collection.iter begin fun i -> if base_visible_get base (fun _ -> false) i - then Marker.set per_tab i (Marker.get per_tab i lor flag) + then Marker.set per_tab i (Marker.get per_tab i lor flag) (* S: replace by `censor_person` ? *) end (ipers base) ; + Collection.iter begin fun i -> let fam = foi base i in - let des_visible = + let des_visible = (* There exists a children of the family that is not censored *) (* FIXME: replace with exists *) Array.fold_left (fun check iper -> check || Marker.get per_tab iper = 0) false (get_children fam) in - let cpl_not_visible = + let cpl_not_visible = (* Father or mother is censored *) Marker.get per_tab (get_father fam) <> 0 || Marker.get per_tab (get_mother fam) <> 0 in + (* If all the children, father and mother are censored, then censor family *) if not des_visible && cpl_not_visible then Marker.set fam_tab i (Marker.get fam_tab i lor flag) end (ifams base) +(** [select_asc conf base max_gen ips] + Returns all the ancestors of persons in the list `ips` up to the `max_gen` + generation. *) let select_asc conf base max_gen ips = let rec loop_asc (gen : int) set ip = if not @@ IPS.mem ip set then begin @@ -203,6 +234,12 @@ let select_asc conf base max_gen ips = in List.fold_left (loop_asc 0) IPS.empty ips +(** [select_surname nase pmark fmark surname] + Sets a `true` marker to families whose mother or father that + match the given surname. Propagates the mark to children + that have this surname. +*) +(* S: only used by `select_surnames` in a List.iter *) (* Should it use search engine functions? *) let select_surname base pmark fmark surname = let surname = Name.strip_lower surname in @@ -226,6 +263,12 @@ let select_surname base pmark fmark surname = end end (ifams base) +(** [select_surnames base surnames] + Calls `select_surname` on every family that have the given surnames. + Returns two functions: + * the first takes a person and returns `true` iff it has been selected + * the second takes a family and returns `false` iff it has been selected +*) let select_surnames base surnames : (iper -> bool) * (ifam -> bool) = let pmark = Gwdb.iper_marker (Gwdb.ipers base) false in let fmark = Gwdb.ifam_marker (Gwdb.ifams base) false in @@ -234,11 +277,14 @@ let select_surnames base surnames : (iper -> bool) * (ifam -> bool) = , (fun i -> Gwdb.Marker.get fmark i) ) (**/**) +(** [select_parentship base ip1 ip2] + Returns the set of common descendants of ip1 and the + ancestors of ip2 and the set of their families. *) let select_parentship base ip1 ip2 = let conf = Config.{ empty with wizard = true ; bname = Gwdb.bname base } in let asc = select_asc conf base max_int [ ip1 ] in let desc = Util.select_desc conf base (-max_int) [ ip2, 0 ] in - let ipers = + let ipers = (* S: The intersection of asc and desc *) if IPS.cardinal asc > Hashtbl.length desc then Hashtbl.fold @@ -249,11 +295,12 @@ let select_parentship base ip1 ip2 = (fun k acc -> if Hashtbl.mem desc k then IPS.add k acc else acc) asc IPS.empty in - let ifams = + let ifams = (* S: families *) IPS.fold begin fun iper acc -> Array.fold_left begin fun acc ifam -> - if IFS.mem ifam acc - || not (IPS.mem (Gutil.spouse iper @@ foi base ifam) ipers) + if IFS.mem ifam acc (* S: useless test? *) + || not (IPS.mem (Gutil.spouse iper @@ foi base ifam) ipers) (* S: is the partner of the + person not in ipers? *) then acc else IFS.add ifam acc end acc (get_family (poi base iper)) @@ -261,7 +308,12 @@ let select_parentship base ip1 ip2 = in ipers, ifams -let select_from_set ipers ifams = +(** [select_from_set ipers ifams] + Returns two functions : + * the first returns true if its input is in ipers + * the second returns true if its input is in ifams +*) +let select_from_set (ipers : IPS.t) (ifams : IFS.t) = let sel_per i = IPS.mem i ipers in let sel_fam i = IFS.mem i ifams in (sel_per, sel_fam) @@ -294,6 +346,8 @@ let select opts ips = in let conf = Config.{ empty with wizard = true } in let sel_per, sel_fam = + (* S: a lot of redundant tests are done here, would be simpler with + pattern matchings and factorization. *) if opts.ascdesc <> None || opts.desc <> None then begin assert (opts.censor = 0) ; let asc = @@ -334,6 +388,7 @@ let select opts ips = let sel_fam i = IFS.mem i ifams in (sel_per, sel_fam) end else match opts.asc with + (* opts.ascdesc = None && opts.desc = None *) | Some asc -> let ipers = select_asc conf base asc ips in let per_sel i = IPS.mem i ipers in diff --git a/bin/gwexport/gwexport.mli b/bin/gwexport/gwexport.mli new file mode 100644 index 0000000000..40c319b850 --- /dev/null +++ b/bin/gwexport/gwexport.mli @@ -0,0 +1,54 @@ +type gwexport_charset = Ansel | Ansi | Ascii | Utf8 + +type gwexport_opts = { + asc : int option; (* Maximum generation of the root's ascendants *) + ascdesc : int option; (* Maximum generation of the root's ascendants descendants *) + base : (string * Gwdb.base) option; (* The base analyzed *) + censor : int; (* Censors the base for 'n' years *) + charset : gwexport_charset; (* The charset of the export *) + desc : int option; (* Maximum generation of the root's descendants *) + img_base_path : string; (* Unused by this module (and not set by options) *) + keys : string list; (* Key reference of additional persons to select *) + mem : bool; (* Unused by this module *) + no_notes : [ `nn | `nnn | `none ]; (* Unused by this module + S: Consider simple ADTs *) + no_picture : bool; (* Unused by this module *) + oc : string * (string -> unit) * (unit -> unit); (* Unused by this module *) + parentship : bool; (* If asc, ascdesc and desc are not set & parenting = true, then + select individuals involved in parentship between pair of keys + (/!\ assumes the input are pairs of keys) *) + picture_path : bool; (* Unused by this module *) + source : string option; (* Unused by this module *) + surnames : string list; (* Used to select persons by their surname *) + verbose : bool; (* Unused by this module *) +} + +(** Default set of options *) +val default_opts : gwexport_opts + +(** Given a set of options, returns default command line arguments for selecting + elements from a base. The output of this function is the first input of + Arg.parse. +*) +val speclist : gwexport_opts ref -> (Arg.key * Arg.spec * Arg.doc) list +(* Used for gwd2ged and gwu. *) + +(** [anonfun opts = fun base_name -> ...] + Given a set of options `opts` where `!opts.base` is uninitialized, + opens the dir `base_name` and initializes !opts.base with the base name. + The output of this function is the second argument of Arg.parse. +*) +val anonfun : gwexport_opts ref -> Arg.anon_fun +(* Arg.anon_fun = string -> unit *) + +(** Default error message. + This is the third argument of Arg.parse. *) +val errmsg : Arg.usage_msg + +(** [select opts ips] + Return filters for [iper] and [ifam] to be used when exporting a (portion of a) base. +*) +val select : + gwexport_opts -> + Gwdb.iper list -> (Gwdb.iper -> bool) * (Gwdb.ifam -> bool) + diff --git a/bin/gwrepl/data.mli b/bin/gwrepl/data.mli new file mode 100644 index 0000000000..4bb9336351 --- /dev/null +++ b/bin/gwrepl/data.mli @@ -0,0 +1,12 @@ +(* the array of etc/lib/XXX where XXX are either dependencies of geneweb or + geneweb/COMPONENT *) +val directories: string array + +(* associations between file names and their (generated) contents: *) +val cmas : ( string * string ) array (* .cma *) +val cmis : ( string * string ) array (* .cmi *) +val shared : ( string * string ) array (* .so *) + +(* An md5 of all the names of the files in [cmis] and [cmas] (not + their contents). *) +val md5 : string diff --git a/bin/gwrepl/mk_data.ml b/bin/gwrepl/mk_data.ml index df29494b67..36c8c5bc8a 100644 --- a/bin/gwrepl/mk_data.ml +++ b/bin/gwrepl/mk_data.ml @@ -1,3 +1,10 @@ +(* This file is used to generate the file 'data.ml', containing all + the files (cmis, cmas, .so) that could be used at runtime by + a geneweb interpreter. + + See 'data.mli' for the signature of the generated file. *) + + let read_lines p = let rec loop () = match input_line p with | exception End_of_file -> close_in p ; [] @@ -30,34 +37,40 @@ let partition_map p l = let (//) = Filename.concat let () = - let ic = open_in ".depend" in - let lines = read_lines ic in - close_in ic ; - let dune_root = List.hd lines in - let out = List.tl lines in - let root = dune_root // "_build" // "default" // "lib" in let opam_swich_prefix = Sys.getenv "OPAM_SWITCH_PREFIX" in let opam_swich_prefix_lib = opam_swich_prefix // "lib" in - let aux fn = - let aux prefix = - if String.length fn > String.length prefix - && String.sub fn 0 (String.length prefix) = prefix - then Some (String.sub fn (String.length prefix) (String.length fn - String.length prefix)) - else None + + let dune_root, root, ( directories0, files0 ) = + + let ic = open_in ".depend" in + let lines = read_lines ic in + close_in ic ; + let dune_root, out = match lines with + | [] -> assert false + | dune_root :: out -> dune_root, out in - match aux opam_swich_prefix_lib with - | Some x -> Some (`opam x) - | None -> match aux root with - | Some x -> Some (`root x) - | None -> None - in - let directories0, files0 = + let root = dune_root // "_build" // "default" // "lib" in + let aux fn = + let aux prefix = + if String.length fn > String.length prefix + && String.sub fn 0 (String.length prefix) = prefix + then Some (String.sub fn (String.length prefix) (String.length fn - String.length prefix)) + else None + in + match aux opam_swich_prefix_lib with + | Some x -> Some (`opam x) + | None -> match aux root with + | Some x -> Some (`root x) + | None -> None + in + dune_root, root, partition_map begin fun s -> try Scanf.sscanf s {|#directory "%[^"]";;|} (fun s -> match aux s with Some s -> Some (Either.Left s) | _ -> None) with _ -> try Scanf.sscanf s {|#load "%[^"]";;|} (fun s -> match aux s with Some s -> Some (Either.Right s) | _ -> None) with _ -> failwith s end out + in let directories = ("etc" // "lib" // "ocaml") @@ -85,7 +98,13 @@ let () = if Filename.check_suffix (Filename.concat dir s) "cmi" then (Filename.concat dir s, "etc" // "lib" // "geneweb" // Filename.concat (Filename.basename dir) s) :: cmis else cmis - end cmis (Sys.readdir dir) + end cmis ( + try + Sys.readdir dir + with exn -> + Printf.eprintf "Error in Sys.readdir(%S)\n%!" dir; + raise exn + ) in (cmas, cmis) end files0 ([], []) @@ -122,12 +141,12 @@ let () = aux "cmas" cmas end ; begin - Printf.fprintf out {|let shared=[||} ; - if Sys.unix then (* FIXME: what is the windows version? *) - List.iter begin fun s -> - Printf.fprintf out {blob|Filename.(concat "etc" (concat "lib" {|%s|})),[%%blob {|%s|}];|blob} s (opam_swich_prefix_lib // s) ; - end [ "ocaml" // "stublibs" // "dllcamlstr.so" ; "ocaml" // "stublibs" // "dllunix.so"] ; - Printf.fprintf out {||];;|} + Printf.fprintf out {|let shared=[||} ; + if Sys.unix then (* FIXME: what is the windows version? *) + List.iter begin fun s -> + Printf.fprintf out {blob|Filename.(concat "etc" (concat "lib" {|%s|})),[%%blob {|%s|}];|blob} s (opam_swich_prefix_lib // s) ; + end [ "ocaml" // "stublibs" // "dllcamlstr.so" ; "ocaml" // "stublibs" // "dllunix.so"] ; + Printf.fprintf out {||];;|} end ; begin let b = Buffer.create 1024 in diff --git a/bin/gwu/gwuLib.ml b/bin/gwu/gwuLib.ml index 7f992ffff7..e3081459d5 100644 --- a/bin/gwu/gwuLib.ml +++ b/bin/gwu/gwuLib.ml @@ -12,6 +12,12 @@ let raw_output = ref false let sep_limit = ref 21 let separate_list = ref [] +(* Returns true if `old_gw` is `true` and there exist an event associated to a + person that: + * is either a birth, baptism, death, burial or a cremation and is associated to + a note or a witness; + * is any other event. + Otherwise, returns false *) let put_events_in_notes base p = (* Si on est en mode old_gw, on mets tous les évènements *) (* dans les notes. *) @@ -33,9 +39,9 @@ let put_events_in_notes base p = loop (get_pevents p) else false -let ht_dup_occ = Hashtbl.create 20001 +let (ht_dup_occ : (Gwdb.iper, int) Hashtbl.t) = Hashtbl.create 20001 -let ht_orig_occ = Hashtbl.create 20001 +let (ht_orig_occ : (string, int list) Hashtbl.t) = Hashtbl.create 20001 let prepare_free_occ ?(select = fun _ -> true) base = (* Parce qu'on est obligé ... *) diff --git a/bin/gwu/gwuLib.mli b/bin/gwu/gwuLib.mli new file mode 100644 index 0000000000..15047572c2 --- /dev/null +++ b/bin/gwu/gwuLib.mli @@ -0,0 +1,21 @@ + +val out_dir : string ref +val old_gw : bool ref +val raw_output : bool ref +val separate_list : string list ref +val only_file : string ref +val sep_limit : int ref + +(** Initializes the internal hashtables. Person whose identifier is + not selected (`select p = false`) are ignored. *) +val prepare_free_occ : ?select:(Gwdb.iper -> bool) -> Gwdb.base -> unit + +(** Prints the `.gw` file. *) +val gwu : + Gwexport.gwexport_opts -> + bool -> + Gwdb.base -> + string -> + string -> + (string, (string -> unit) * bool ref * (unit -> unit)) Hashtbl.t -> + (Gwdb.iper -> bool) * (Gwdb.ifam -> bool) -> unit diff --git a/bin/wserver/wserver.mli b/bin/wserver/wserver.mli index 68c6a0711b..3259abe142 100644 --- a/bin/wserver/wserver.mli +++ b/bin/wserver/wserver.mli @@ -30,43 +30,56 @@ val f -> (Unix.sockaddr * string list -> string -> string -> unit) -> unit +(** Closes the current socket *) val close_connection : unit -> unit +(** Formatter printing in the out channel associated to the connected socket *) val printf : ('a, out_channel, unit) format -> 'a (* To be called to print page contents. *) +(** Prints a string in the out channel associated to the socket *) val print_string : string -> unit (* To be called to print page contents. *) +(** Prints a header; cannot be called if part of content part already has been sent *) val header : string -> unit (* To print an http header line *) +(** Flushes the content of the current socket *) val wflush : unit -> unit (* To flush page contents print. *) + +(** [Output.status conf answer] sends the http header where [answer] + represents the answer status. *) val http : Def.httpStatus -> unit - (* [Output.status conf answer] sends the http header where [answer] - represents the answer status. If empty string, "200 OK" is assumed. *) +(** [Output.status conf_redirect url] sends the http header where [url] + represents the Location where the request needs to be redirected. *) val http_redirect_temporarily : string -> unit - (* [Output.status conf_redirect url] sends the http header where [url] - represents the Location where the request needs to be redirected. *) +(** Returns the request from a stream read from a socket. *) val get_request_and_content : char Stream.t -> string list * string +(** Returns the last used socket *) val wsocket : unit -> Unix.file_descr + +(** Return the out_channel associated to the socket *) val woc : unit -> out_channel +(** Names of the files used in windows implementation to communicate + http requests and html answers. Default "wserver.sin" and + "wserver.sou". Can have relative or absolute paths. *) val sock_in : string ref val sock_out : string ref - (* Names of the files used in windows implementation to communicate - http requests and html answers. Default "wserver.sin" and - "wserver.sou". Can have relative or absolute paths. *) + +(** Name of the file whose presence tells the server to stop (at least + one request is necessary to unfreeze the server to make it check + that this file exits. Default "STOP_SERVER". Can have relative + or absolute path. *) val stop_server : string ref - (* Name of the file whose presence tells the server to stop (at least - one request is necessary to unfreeze the server to make it check - that this file exits. Default "STOP_SERVER". Can have relative - or absolute path. *) + +(** CGI (Common Gateway Interface) mode (default false). *) val cgi : bool ref (* Example: diff --git a/doc/html/.buildinfo b/doc/html/.buildinfo new file mode 100644 index 0000000000..2b98416203 --- /dev/null +++ b/doc/html/.buildinfo @@ -0,0 +1,4 @@ +# Sphinx build info version 1 +# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. +config: 42e050ea871d04cb1c2c51413627c51c +tags: 645f666f9bcd5a90fca523b33c5a78b7 diff --git a/doc/html/_images/diagram.png b/doc/html/_images/diagram.png new file mode 100644 index 0000000000..4f171ed4ce Binary files /dev/null and b/doc/html/_images/diagram.png differ diff --git a/doc/html/_sources/dev-doc/binaries/bench.md.txt b/doc/html/_sources/dev-doc/binaries/bench.md.txt new file mode 100644 index 0000000000..a6654e8e36 --- /dev/null +++ b/doc/html/_sources/dev-doc/binaries/bench.md.txt @@ -0,0 +1,5 @@ +# bench + +Executes multiple tests on the library and output CPU usage +information. +Can be executed with `make bench` or `_build/default/bench/bench.exe`. \ No newline at end of file diff --git a/doc/html/_sources/dev-doc/binaries/cgl.md.txt b/doc/html/_sources/dev-doc/binaries/cgl.md.txt new file mode 100644 index 0000000000..a8e0697ab8 --- /dev/null +++ b/doc/html/_sources/dev-doc/binaries/cgl.md.txt @@ -0,0 +1,4 @@ +# cgl + +Removes the links of an HTML page and replaces them by `span`s. +Registered as a service. diff --git a/doc/html/_sources/dev-doc/binaries/connex.md.txt b/doc/html/_sources/dev-doc/binaries/connex.md.txt new file mode 100644 index 0000000000..c31fa7353d --- /dev/null +++ b/doc/html/_sources/dev-doc/binaries/connex.md.txt @@ -0,0 +1,23 @@ +# connex + +## Documentation + +Calculates the connex components of a base and returns it in HTML. + +``` +usage: geneweb.connex + + -gwd_p : Specify the port number of gwd (default = 2317); > 1024 for normal users. + -server : Name of the server (default is 127.0.0.1). + -a : all connex components + -s : produce statistics + -d : detail for this length + -i : ignore this file + -bf : by origin files + -del : ask for deleting branches whose size <= that value + -cnt : delete cnt branches whose size <= -del value + -exact : delete only branches whose size strictly = -del value + -o : output to this file + -help Display this list of options + --help Display this list of options +``` diff --git a/doc/html/_sources/dev-doc/binaries/consang.md.txt b/doc/html/_sources/dev-doc/binaries/consang.md.txt new file mode 100644 index 0000000000..68eb0500cd --- /dev/null +++ b/doc/html/_sources/dev-doc/binaries/consang.md.txt @@ -0,0 +1,19 @@ +# consang + +## Documentation + +Calculates the consanguinity level of individuals and synchronizes it with +the base. + +``` +usage: geneweb.consang [options] + -q quiet mode + -qq very quiet mode + -fast faster, but use more memory + -scratch : from scratch + -mem : Save memory, but slower when rewritting database + -nolock : do not lock database. +``` + +[Online documentation](https://geneweb.tuxfamily.org/wiki/consang) + diff --git a/doc/html/_sources/dev-doc/binaries/export.md.txt b/doc/html/_sources/dev-doc/binaries/export.md.txt new file mode 100644 index 0000000000..2f998a5b4c --- /dev/null +++ b/doc/html/_sources/dev-doc/binaries/export.md.txt @@ -0,0 +1,4 @@ +# export + +Exports a database in GED or in GW depending on the option. +Registers the "EXPORT" request as an extra request. diff --git a/doc/html/_sources/dev-doc/binaries/fixbase_plugin.md.txt b/doc/html/_sources/dev-doc/binaries/fixbase_plugin.md.txt new file mode 100644 index 0000000000..b189e78c7d --- /dev/null +++ b/doc/html/_sources/dev-doc/binaries/fixbase_plugin.md.txt @@ -0,0 +1,4 @@ +# fixbase + +Applies the different fixbase utils (module `Fixbase`) from a dedicated UI. +Registers two new requests: FIXBASE and FIXBASE_OK. \ No newline at end of file diff --git a/doc/html/_sources/dev-doc/binaries/forum.md.txt b/doc/html/_sources/dev-doc/binaries/forum.md.txt new file mode 100644 index 0000000000..463f5b27b1 --- /dev/null +++ b/doc/html/_sources/dev-doc/binaries/forum.md.txt @@ -0,0 +1,5 @@ +# forum + +Includes a forum platform in the web server. +Registers the requests FORUM, FORUM_ADD, FORUM_ADD_OK, FORUM_DEL, FORUM_P_P, +FORUM_SEARCH, FORUM_VAL, FORUM_VIEW. diff --git a/doc/html/_sources/dev-doc/binaries/ged2gwb.md.txt b/doc/html/_sources/dev-doc/binaries/ged2gwb.md.txt new file mode 100644 index 0000000000..112d7210a4 --- /dev/null +++ b/doc/html/_sources/dev-doc/binaries/ged2gwb.md.txt @@ -0,0 +1,48 @@ +# ged2gwb + +## Documentation + +Imports a GEDCOM 5.5.1 file to a Geneweb base. GEDCOM is a popular format +among genealogy enthusiasts. +[GEDCOM 5.5.1 documentation](http://www.ancestris.org/dl/ancestris/norme_gedcom/Gedcom_norm_551_2019_11_15.pdf) + +``` +Usage: geneweb.ged2gwb [] [options] where options are: + -charset [ANSEL|ASCII|MSDOS] Force given charset decoding, overriding the possible setting in GEDCOM + -dates_dm Interpret months-numbered dates as day/month/year + -dates_md Interpret months-numbered dates as month/day/year + -ds Set the source field for persons and families without source data + -efn When creating a person, if the GEDCOM first name part holds several names, the first of this names becomes the person "first name" and the complete GEDCOM first name part a "first name alias". + -epn When creating a person, if the GEDCOM first name part looks like a public name, i.e. holds: +* a number or a roman number, supposed to be a number of a nobility title, +* one of the words: "der", "den", "die", "el", "le", "la", "the", supposed to be the beginning of a qualifier, then the GEDCOM first name part becomes the person "public name" and its first word his "first name". + -f Remove database if already existing + -fne When creating a person, if the GEDCOM first name part holds a part between 'b' (any character) and 'e' (any character), it is considered to be the usual first name: e.g. -fne '""' or -fne "()". + -lf Convert first names to lowercase letters, with initials in uppercase. + -log Redirect log trace to this file. + -ls Convert surnames to lowercase letters, with initials in uppercase. Try to keep lowercase particles. + -nc No consistency check + -no_efn Cancels the previous option. + -no_epn Cancels the previous option. + -no_nd Don't interpret a year preceded by a minus sign as a negative year + -no_pit Do not consider persons having titles as public + -nopicture Don't extract individual picture. + -o Output database (default: "a"). + -particles Use the given file as list of particles + -rs_no_mention Force relation status to NoMention (default is Married) + -tnd Set negative dates when inconsistency (e.g. birth after death) + -trackid Print gedcom id to gw id matches. + -udi x-y Set the interval for persons whose death part is undefined: + - if before x years, they are considered as alive + - if after y year, they are considered as death + - between x and y year, they are considered as "don't know" + Default x is 80 and y is 120 + -uin Put untreated GEDCOM tags in notes + -us Convert surnames to uppercase letters. +``` + +Depends on the library `gwexport`. + +## Our recommendations + +{doc}`/audit/binaries/ged2gwb` \ No newline at end of file diff --git a/doc/html/_sources/dev-doc/binaries/gwb2ged.md.txt b/doc/html/_sources/dev-doc/binaries/gwb2ged.md.txt new file mode 100644 index 0000000000..0123f647b8 --- /dev/null +++ b/doc/html/_sources/dev-doc/binaries/gwb2ged.md.txt @@ -0,0 +1,29 @@ +# gwb2ged + +## Documentation + +Converts a Geneweb database to a GEDCOM 5.5.1 file. GEDCOM is a popular format +among genealogy enthusiasts. +[GEDCOM 5.5.1 documentation](http://www.ancestris.org/dl/ancestris/norme_gedcom/Gedcom_norm_551_2019_11_15.pdf) + + +``` +Usage: geneweb.gwb2ged [OPT] + -indexes export indexes in gedcom + -a maximum generation of the root's ascendants + -ad maximum generation of the root's ascendants descendants + -key key reference of root person. Used for -a/-d options. Can be used multiple times. Key format is "First Name.occ SURNAME" + -c : when a person is born less than years ago, it is not exported unless it is Public. All the spouses and descendants are also censored. + -charset [ASCII|ANSEL|ANSI|UTF-8] set charset; default is UTF-8 + -d maximum generation of the root's descendants. + -mem save memory space, but slower. + -nn no (database) notes. + -nnn no notes (implies -nn). + -nopicture don't extract individual picture. + -o output file name (default: stdout). + -parentship select individuals involved in parentship computation between pairs of keys. Pairs must be defined with -key option, descendant first: e.g. -key "Descendant.0 SURNAME" -key "Ancestor.0 SURNAME". If multiple pair are provided, union of persons are returned. + -picture-path extract pictures path. + -s select this surname (option usable several times, union of surnames will be used). + -source replace individuals and families sources. Also delete event sources. + -v verbose +``` diff --git a/doc/html/_sources/dev-doc/binaries/gwc.md.txt b/doc/html/_sources/dev-doc/binaries/gwc.md.txt new file mode 100644 index 0000000000..8ede564135 --- /dev/null +++ b/doc/html/_sources/dev-doc/binaries/gwc.md.txt @@ -0,0 +1,30 @@ +# gwc + +## Documentation + +Creates a new database. + +``` +Usage: geneweb.gwc [options] [files] +where [files] are a list of files: + source files end with .gw + object files end with .gwo +and [options] are: + -bnotes [drop|erase|first|merge] Behavior for base notes of the next file. [drop]: dropped. [erase]: erase the current content. [first]: dropped if current content is not empty. [merge]: concatenated to the current content. Default: merge + -c Only compiling + -cg Compute consanguinity + -ds Set the source field for persons and families without source data + -f Remove database if already existing + -mem Save memory, but slower + -nc No consistency check + -nofail No failure in case of error + -nolock Do not lock database + -nopicture Do not create associative pictures + -o Output database (default: a.gwb) + -particles Particles file (default = predefined particles) + -q Quiet + -sep Separate all persons in next file + -sh Shift all persons numbers in next files + -stats Print statistics + -v Verbose +``` diff --git a/doc/html/_sources/dev-doc/binaries/gwd.md.txt b/doc/html/_sources/dev-doc/binaries/gwd.md.txt new file mode 100644 index 0000000000..e7dbcc89a0 --- /dev/null +++ b/doc/html/_sources/dev-doc/binaries/gwd.md.txt @@ -0,0 +1,318 @@ +# gwd + +## Documentation + +The main web-server for geneweb. It depends on `wserver`, a library defined in `bin/`. + +``` +Usage: gwd [options] where options are: + -a
Select a specific address (default = any address of this computer). + -add_lexicon Add file as lexicon. + -allowed_tags HTML tags which are allowed to be displayed. One tag per line in file. + -auth Authorization file to restrict access. The file must hold lines of the form "user:password". + -bd Directory where the databases are installed. + -blang Select the user browser language if any. + -cache_langs Lexicon languages to be cached. + -cgi Force CGI mode. + -conn_tmout Connection timeout (default 120s; 0 means no limit). + -daemon Unix daemon mode. + -debug Enable debug mode + -digest Use Digest authorization scheme (more secure on passwords) + -friend Set a friend password. + -hd Directory where the directory lang is installed. + -images_dir Same than previous but directory name relative to current. + -images_url URL for GeneWeb images (default: gwd send them). + -lang Set a default language (default: fr). + -log Log trace to this file. Use "-" or "" to redirect output to stdout or "" to output log to stderr. + -log_level Send messages with severity <= to syslog (default: 7). + -login_tmout Login timeout for entries with passwords in CGI mode (default 1800s). + -max_clients Max number of clients treated at the same time (default: no limit) (not cgi). + -min_disp_req Minimum number of requests in robot trace (default: 6). + -no_host_address Force no reverse host by address. + -nolock Do not lock files before writing. + -only
Only inet address accepted. + -p Select a port number (default = 2317). + -plugin .cmxs load a safe plugin. Combine with -force to enable for every base. Combine with -unsafe to allow unverified plugins. e.g. "-plugin -unsafe -force". + -plugins load all plugins in . Combine with -force to enable for every base. Combine with -unsafe to allow unverified plugins. e.g. "-plugins -unsafe -force". + -redirect Send a message to say that this service has been redirected to . + -robot_xcl , Exclude connections when more than requests in seconds. + -setup_link Display a link to local gwsetup in bottom of pages. + -trace_failed_passwd Print the failed passwords in log (except if option -digest is set). + -wd Directory for socket communication (Windows) and access count. + -wizard Set a wizard password. + -wjf Wizard just friend (permanently). +``` + +It is build from eight files: `gwdLog.ml`, `gwdPlugin.ml`, `request.ml` (these +three files define `geneweb.gwd_lib`),, `gwdPluginDep.ml`, +`gwdPluginMD5.ml`, `gwdPluginMETA.ml`, `robot.ml` and `gwd.ml`. + +### GwdLog module +Util functions for logging errors & infos. + +### GwdPlugin module +Module used for registering plug-ins to Gwd. + +### Request module +The main interface between the Geneweb core and the rest of the world. + +### GedPluginDep module +Calculates the load order of the different plugins. + +### GwdPluginMD5 module +Checks the plugins .cmxs files and assets did not change since compilation. +This module is written by mk_gwdPluginMD5.ml. After the plug-ins have been compiled, +mk_gwdPluginMD5.ml lists them in a pattern matching with their MD5 hash. + +### GwdPluginMETA module +Handles the metadata of a plug-in: its version, its mainteners' list and +its dependencies. +Metadata is optional, and may contain the dependencies of the plugin (separated +by a comma), its maintainers (separated by a comma) and its version. + +### Robot module +Handles the connection of robots to the server. + +### Gwd module +The executable + +## Requests +Gwd displays different web pages and perform different actions given a mode. +This mode is given as a GET argument with the key `m`. + +Here is a quick documentation of each mode. + +* No mode: displays the default page depending on other arguments. It can be +the index page (no base selected), a welcome page (only a base has been +selected) or a person page (a base and a person has been selected). + +* Mode "A": displays the ascendants of the selected person (selection key is `i`). + +* Mode "ADD_FAM": displays the form for adding families. + +* Mode "ADD_FAM_OK": requests to add a new family. + +* Mode "ADD_IND": displays a form for adding a new person + +* Mode "ADD_IND_OK": requests to add a new person. + +* Mode "ADD_PAR": associate parents to a person (person identifier must be set +with key `ip`) + +* Mode "ADD_PAR_OK" : requests to add new parents. + +* Mode "ANM" : displays the menu of anniversaries modification + +* Mode "AN" : displays anniversaries ; with the `v` key for months, displays +the anniversaries of thay month (v=1 -> January, ...) + +* Mode "AD": displays death anniversaries ; with the `v` key for months, displays +the anniversaries of thay month (v=1 -> January, ...) + +* Mode "AM": displays marriage anniversaries ; with the `v` key for months, displays +the anniversaries of thay month (v=1 -> January, ...) + +* Mode "AS_OK": displays the results of an advanced search + +* Mode "C": displays the cousins menu + +* Mode "CAL": displays the calendars; if no key set, it will use today's date. + +* Mode "CHG_CHN": displays the form for changing children names of a person + +* Mode "CHG_CHN_OK": requests to change the children names a new person. + +* Mode "CHG_EVT_IND_ORD": displays the form for changing the order of events +for a person + +* Mode "CHG_EVT_IND_ORD_OK": requests to change the evenement order of a +person. + +* Mode "CHG_EVT_FAM_ORD": displays the form for changing the order of events +for a family + +* Mode "CHG_EVT_FAM_ORD_OK": requests to change the evenement order of a +family. + +* Mode "CHG_FAM_ORD": displays a menu to change the family order + +* Mode "CHG_FAM_ORD_OK": requests the family order change + +* Mode "CONN_WIZ": displays the connected wizards (the base admins) + +* Mode "D": displays the descendants of the selected person (selection key is `i`). + +* Mode "DAG": displays a relationship graph. + +* Mode "DEL_FAM": displays a page for validating the deletion of the family in +argument (key is `i`). + +* Mode "DEL_FAM_OK": requests to remove a family. + +* Mode "DEL_IND": displays a page for validating the deletion of the person in +argument (key is `i`). + +* Mode "DEL_IND_OK": requests to remove a person. + +* Mode "F": displays the family tree. + +* Mode "H": displays the file `fname.txt` where `fname` is register with the key +`v` and the file being in `hd/etc` (`hd` is the dir specified by option `-hd`). + +* Mode "HIST": displays an history of updates. + +* Mode "HIST_CLEAN": displays the history list associated to the history file +in argument (key is `f`). + +* Mode "HIST_CLEAN_OK": requests to clean the history associated to the history +file in argument + +* Mode "HIST_DIFF": displays the page that allows to select (with variable +`t = "SUM"`) and to view (with variable `t = "DIFF"`) the difference +between all revisions of history file of concerned person in variable `f`. +Intepretate the template file `updhist_diff.txt` + +* Mode "HIST_SEARCH": same as "HIST", but with a default search + +* Mode "IM": displays the image whose name is in argument (key is `s`) + +* Mode "IMH": same than "IM", but returns HTML + +* Mode "INV_FAM": displays a menu for inverting the order of two families (where +a family is given by the `f` key and the individual is given by the `i` key). + +* Mode "INV_FAM_OK": requests to reverse the families. + +* Mode "KILL_ANC": Undocumented feature; kill someone's ancestors. + +* Mode "LB": lists the last births. + +* Mode "LD": lists the last deaths. + +* Mode "LINKED": displays links to pages assocaited to an individual. + +* Mode "LL": lists the persons who lived the longest. + +* Mode "LM": lists the last marriages. + +* Mode "MISC_NOTES": displays a menu to search in notes. + +* Mode "MISC_NOTES_SEARCH": same as "MISC_NOTES", but with a search argument + (key is `s`) + +* Mode "MOD_DATA": displays a menu for updating Geneweb's dictionary of names, +last names, locations, sources and professions. + +* Mode "MOD_DATA_OK": requests a data modification. + +* Mode "MOD_FAM": displays a form for updating a family (key is `i`). + +* Mode "MOD_FAM_OK": requests a family modification. + +* Mode "MOD_IND": displays a form for updating a person (key is `i`). + +* Mode "MOD_IND_OK": requests a person modification. + +* Mode "MOD_NOTES": displays a text form for writing notes. + +* Mode "MOD_NOTES_OK": requests a note update. + +* Mode "MOD_WIZNOTES": displays the HTML page for editing wizard notes. +Fails if wizard authentification is incorrect or if current user cannot +edit. + +* Mode "MOD_WIZNOTES_OK": requests the wizard note modification. + +* Mode "MRG": displays a menu for merging two persons +(key for persons is `i`). + +* Mode "MRG_DUP": displays a menu for merging possible duplications of persons +(key for persons is `ip`). + +* Mode "MRG_DUP_IND_Y_N": either displays the merge dupliate menu if +`answer_y` is not a key of the request, or a form for merging two +person (whose keys are `i` and `i2`). + +* Mod "MRG_DUP_FAM_Y_N": same than "MRG_DUP_IND_Y_N", but for families. + +* Mode "MRG_FAM": displays a menu for merging families (family keys are `i` +and `i2`). Couples must be identical (modulo reversion). + +* Mode "MRG_FAM_OK": requests the family merge + +* Mode "MRG_MOD_FAM_OK": requests the family modification and merge + +* Mode "MRG_IND": displays a form for merging two persons + +* Mode "MRG_IND_OK": requests a merge of two persons + +* Mode "MRG_MOD_IND_OK": requests a merge & modification of two persons + +* Mode "N": lists all the surnames; if a surname is selected (key is `v`), +displays the list of pages where this surname is used. + +* Mode "NG": same than `N`, but expects a name with the key `n` + +* Mode "NOTES": displays the notes + +* Mode "OA": displays the list of the oldest persons that are still alive or, +if unknown, whose death are not probable + +* Mode "OE": same as "OA", but for engaged couples + +* Mode "P": lists all the first names; if a surname is selected (key is `v`), +displays the list of pages where this name is used. + +* Mode "PQP_PYR": displays a population pyramid (reachable from "STAT") + +* Mode "PS": displays all the persons associated to a given place (`ma` key for +marriage, `bi` key for birth, `bp` key for baptism, `de` key for death and `bu` +key for burial). + +* Mode "R": displays the relationship details between two persons + +* Mode "REQUEST": displays the current request + +* Mode "RL": displays the relationship link between two persons + +* Mode "RLM": displays relation ship details between multiple persons + +* Mode "S": displays the results of a search +(from, for example, the main page). + +* Mode "SRC": displays the file in argument (key is `v`) + +* Mode "STAT": displays several links for statistics: latest births, death, +marriages, the oldest couples, persons that are alive and who lives the longest. +There also is a population pyramid. + +* Mode "CHANGE_WIZ_VIS": displays the connected wizards + +* Mode "TT": displays the titles associated to persons + +* Mode "U": displays the page associated to the template `updmenu.txt` + +* Mode "VIEW_WIZNOTES": Prints the HTML page displaying wizard notes + +* Mode "WIZNOTES": Same as VIEW_WIZNOTES, but fails if not authentificated + +* Mode "WIZNOTES_SEARCH": Same as VIEW_WIZNOTES, but highlights HTML with +the specified string searched (key is `s`). + +Any other mode leads to an incorrect request page (Error 400). + +## Plug-ins +Plus-ins are additional features that can be added to `gwd`. They can register +two kind of services: + +* additional modes: with `GwdPlugin.register`, a plug-in can register new +requests + +* additional computations: with `GwdPlugin.register_se`, a plug-in can register +pre/post processors + +## Customization +Each base can be customized with a `.gwf` file. A documented example is +available on the `etc` directory at the root of the project. +Plug-ins are activated for a base with the `plugin=...` directive in the +configuration file. diff --git a/doc/html/_sources/dev-doc/binaries/gwdiff.md.txt b/doc/html/_sources/dev-doc/binaries/gwdiff.md.txt new file mode 100644 index 0000000000..869f1c5a81 --- /dev/null +++ b/doc/html/_sources/dev-doc/binaries/gwdiff.md.txt @@ -0,0 +1,17 @@ +# gwdiff + +## Documentation + +Target differences between two GeneWeb databases (see README for more details) + +``` +Usage: geneweb.gwdiff [options] base1 base2 +Options are: + -1 : (mandatory) defines starting person in base1 + -2 : (mandatory) defines starting person in base2 + -ad : checks descendants of all ascendants + -d : checks descendants (default) + -html : HTML format used for report + -mem : save memory space, but slower +``` + diff --git a/doc/html/_sources/dev-doc/binaries/gwfixbase.md.txt b/doc/html/_sources/dev-doc/binaries/gwfixbase.md.txt new file mode 100644 index 0000000000..3b12587d66 --- /dev/null +++ b/doc/html/_sources/dev-doc/binaries/gwfixbase.md.txt @@ -0,0 +1,29 @@ +# fixbase + +## Documentation + +Checks the consistency of the base, fixes it and applies +the latest patch if there is one. + +``` +Usage: geneweb.gwfixbase [OPTION] base + -dry-run do not commit changes (only print) + -q quiet mode + -qq very quiet mode + -fast fast mode. Needs more memory. + -families-parents missing doc + -families-children missing doc + -persons-NBDS missing doc + -persons-parents missing doc + -persons-families missing doc + -pevents-witnesses missing doc + -fevents-witnesses missing doc + -marriage-divorce missing doc + -person-key missing doc + -index rebuild index. It is automatically enable by any other option. + -invalid-utf8 missing doc +``` + +## Our recommendations + +- {doc}`/audit/binaries/gwfixbase` diff --git a/doc/html/_sources/dev-doc/binaries/gwgc.md.txt b/doc/html/_sources/dev-doc/binaries/gwgc.md.txt new file mode 100644 index 0000000000..867e55c4d4 --- /dev/null +++ b/doc/html/_sources/dev-doc/binaries/gwgc.md.txt @@ -0,0 +1,11 @@ +# gwgc + +## Documentation + +Simplifies a database by removing unused entries of its dictionary. + +``` +Usage: geneweb.gwgc [OPTION] base + --dry-run do not commit changes (only print) + +``` diff --git a/doc/html/_sources/dev-doc/binaries/gwrepl.md.txt b/doc/html/_sources/dev-doc/binaries/gwrepl.md.txt new file mode 100644 index 0000000000..f78793826a --- /dev/null +++ b/doc/html/_sources/dev-doc/binaries/gwrepl.md.txt @@ -0,0 +1,12 @@ +# gwrepl + +## Documentation + +Starts an OCaml interactive top-level for writing scripts manipulating the +database. It loads all the necessary libraries to use the geneweb +libraries. +For script execution, run: + cat | [ GWREPL_PPF=/dev/null ] [ GWREPL_VERBOSE=1 ] [ GWREPL_FORCE_UNPACK=1 ] [ GWREPL_NOPROMPT=1 ] gwrepl.exe [scrip_arg1] ... + +For interactive top-level, run `gwdrepl.exe`. + diff --git a/doc/html/_sources/dev-doc/binaries/gwu.md.txt b/doc/html/_sources/dev-doc/binaries/gwu.md.txt new file mode 100644 index 0000000000..1cf2f2e3c7 --- /dev/null +++ b/doc/html/_sources/dev-doc/binaries/gwu.md.txt @@ -0,0 +1,41 @@ +# gwu + +## Documentation + +Exports the content of a Geneweb base to a .gw file. + +``` +Usage: geneweb.gwu [OPT] + -odir create files from original name in directory (else on -o file) + -isolated export isolated persons (work only if export all database). + -old_gw do not export additional fields (for backward compatibility: < 7.00) + -raw raw output (without possible utf-8 conversion) + -sep <1st_name.num surname> To use together with the option "-odir": separate this person and all his ancestors and descendants sharing the same surname. All the concerned families are displayed on standard output instead of their associated files. This option can be used several times. + -sep_only_file with option "-sep", tells to separate only groups of that file. + -sep_limit When using the option "-sep", groups of families can become isolated in the files. Gwu reconnects them to the separated families (i.e. displays them to standard output) if the size of these groups is less than 21. The present option changes this limit. + -a maximum generation of the root's ascendants + -ad maximum generation of the root's ascendants descendants + -key key reference of root person. Used for -a/-d options. Can be used multiple times. Key format is "First Name.occ SURNAME" + -c : when a person is born less than years ago, it is not exported unless it is Public. All the spouses and descendants are also censored. + -charset [ASCII|ANSEL|ANSI|UTF-8] set charset; default is UTF-8 + -d maximum generation of the root's descendants. + -mem save memory space, but slower. + -nn no (database) notes. + -nnn no notes (implies -nn). + -nopicture don't extract individual picture. + -o output file name (default: stdout). + -parentship select individuals involved in parentship computation between pairs of keys. Pairs must be defined with -key option, descendant first: e.g. -key "Descendant.0 SURNAME" -key "Ancestor.0 SURNAME". If multiple pair are provided, union of persons are returned. + -picture-path extract pictures path. + -s select this surname (option usable several times, union of surnames will be used). + -source replace individuals and families sources. Also delete event sources. + -v verbose + +``` + +See also: +https://geneweb.tuxfamily.org/wiki/gwu +https://geneweb.tuxfamily.org/wiki/save + +## Our recommendations + +{doc}`/audit/binaries/gwu` \ No newline at end of file diff --git a/doc/html/_sources/dev-doc/binaries/gwxjg.md.txt b/doc/html/_sources/dev-doc/binaries/gwxjg.md.txt new file mode 100644 index 0000000000..59baf34347 --- /dev/null +++ b/doc/html/_sources/dev-doc/binaries/gwxjg.md.txt @@ -0,0 +1,5 @@ +# gwxjg + +WARNING: This is not a plugin, it only provides tools for other plugins to use. +This package provides translation tools from Geneweb structure to Jingoo's type system. + diff --git a/doc/html/_sources/dev-doc/binaries/index.rst.txt b/doc/html/_sources/dev-doc/binaries/index.rst.txt new file mode 100644 index 0000000000..426c0fc991 --- /dev/null +++ b/doc/html/_sources/dev-doc/binaries/index.rst.txt @@ -0,0 +1,9 @@ +Project binaries +================ + +.. toctree:: + :maxdepth: 2 + + official_binaries + plugins + tests diff --git a/doc/html/_sources/dev-doc/binaries/jingoo.md.txt b/doc/html/_sources/dev-doc/binaries/jingoo.md.txt new file mode 100644 index 0000000000..dd72219f28 --- /dev/null +++ b/doc/html/_sources/dev-doc/binaries/jingoo.md.txt @@ -0,0 +1,3 @@ +# jingoo + +WARNING: This is not a plugin. This only loads `geneweb.gwd_lib`. \ No newline at end of file diff --git a/doc/html/_sources/dev-doc/binaries/lib_show.md.txt b/doc/html/_sources/dev-doc/binaries/lib_show.md.txt new file mode 100644 index 0000000000..6b0dc8d6f3 --- /dev/null +++ b/doc/html/_sources/dev-doc/binaries/lib_show.md.txt @@ -0,0 +1,3 @@ +# lib_show + +WARNING: This is not a plugin. This only loads `geneweb.def_show`. \ No newline at end of file diff --git a/doc/html/_sources/dev-doc/binaries/no_index.md.txt b/doc/html/_sources/dev-doc/binaries/no_index.md.txt new file mode 100644 index 0000000000..41ea17b895 --- /dev/null +++ b/doc/html/_sources/dev-doc/binaries/no_index.md.txt @@ -0,0 +1 @@ +# no_index \ No newline at end of file diff --git a/doc/html/_sources/dev-doc/binaries/official_binaries.rst.txt b/doc/html/_sources/dev-doc/binaries/official_binaries.rst.txt new file mode 100644 index 0000000000..4f8960bb36 --- /dev/null +++ b/doc/html/_sources/dev-doc/binaries/official_binaries.rst.txt @@ -0,0 +1,24 @@ +Official binaries +================= + +These binaries are from the official geneweb package. They provide +the core utilities for manipulating databases and a web server. + +.. toctree:: + :maxdepth: 2 + :caption: List of binaries: + + connex + consang + ged2gwb + gwb2ged + gwb2ged + gwc + gwd + gwdiff + gwfixbase + gwgc + gwrepl + gwu + setup + update_nldb diff --git a/doc/html/_sources/dev-doc/binaries/plugins.rst.txt b/doc/html/_sources/dev-doc/binaries/plugins.rst.txt new file mode 100644 index 0000000000..ae346c37cd --- /dev/null +++ b/doc/html/_sources/dev-doc/binaries/plugins.rst.txt @@ -0,0 +1,22 @@ +Plug-ins +======== + +Plug-ins are user-defined scripts that were merged into the main repository. +While they are part of the main branch, their purpose it to let user develop +their own OCaml scripts for updating the behaviour or the web werver `gwd`. + +.. toctree:: + :caption: List of plug-ins: + + cgl + export + fixbase_plugin + forum + gwxjg + jingoo + lib_show + no_index + v7 + v7_im + welcome + xhtml diff --git a/doc/html/_sources/dev-doc/binaries/setup.md.txt b/doc/html/_sources/dev-doc/binaries/setup.md.txt new file mode 100644 index 0000000000..09fe5965e7 --- /dev/null +++ b/doc/html/_sources/dev-doc/binaries/setup.md.txt @@ -0,0 +1,15 @@ +# setup + +The database setup portal. It depends on `wserver`, a library defined in `bin/`. + +``` +Usage: setup.exe [options] where options are: + -bd : Directory where the databases are installed. + -gwd_p : Specify the port number of gwd (default = 2317); > 1024 for normal users. + -lang : default lang + -daemon : Unix daemon mode. + -p : Select a port number (default = 2316); > 1024 for normal users. + -only : File containing the only authorized address + -gd : gwsetup directory + -bindir : binary directory (default = value of option -gd) +``` \ No newline at end of file diff --git a/doc/html/_sources/dev-doc/binaries/test.md.txt b/doc/html/_sources/dev-doc/binaries/test.md.txt new file mode 100644 index 0000000000..fa696cd990 --- /dev/null +++ b/doc/html/_sources/dev-doc/binaries/test.md.txt @@ -0,0 +1,38 @@ +# test + +Starts multiple non-regression tests. Tested modules are `MUtil`, `Util` +(`Test_utils`), the choosen `Sosa` implementation (`Test_sosa`), `Place` +(`Test_place`) and `NotesLink` (`Test_wiki`). +Can be executed with `make test` or `_build/default/test/test.exe`. + +``` +usage: default/test/test.exe options* + -conf fn Read configuration file. + -cache-filename str Cache file to store previous results. (default: /home/ovenstent/gits/geneweb-project/geneweb/_build/oUnit-$(suite_name).cache) + -chooser {failfirst|simple} + Select the method to choose tests to run. (default: simple) + -ci {true|false} Display logs for CI, like Travis and AppVeyor, in the console with colors. (default: false) + -display {true|false} Output logs on screen. (default: true) + -health-check-interval f Seconds between checking health of workers. (default: 1.) + -log-encoding str Encoding of the log. (default: utf-8) + -no-cache-filename Reset value of cache_filename. + -no-output-file Reset value of output_file. + -no-output-html-dir Reset value of output_html_dir. + -no-output-junit-file Reset value of output_junit_file. + -no-testdata-dir Reset value of testdata_dir. + -output-file str Output verbose log in the given file. (default: /home/ovenstent/gits/geneweb-project/geneweb/_build/oUnit-$(suite_name)-$(shard_id).log) + -output-html-dir str Output directory of the HTML files. (default: none) + -output-junit-file str Output file for JUnit. (default: none) + -processes-grace-period f Delay to wait for a process to stop. (default: 5.) + -processes-kill-period f Delay to wait for a process to stop after killing it. (default: 5.) + -results-style-1-X {true|false} Use OUnit 1.X results printer (will be deprecated in 2.1.0+). (default: false) + -run-gc-full-major {true|false} Run a Gc.full_major in between tests. (default: true) + -runner {processes|sequential} + Select a the method to run tests. (default: processes) + -shards i Number of shards to use as worker (threads or processes). (default: 4) + -suite-name str The name of the test suite running. (default: Geneweb) + -testdata-dir str Location of the test data directory (absolute path). (default: none) + -verbose {true|false} Run test in verbose mode. (default: false) + -only-test path Run only the selected tests. + -list-test List tests +``` \ No newline at end of file diff --git a/doc/html/_sources/dev-doc/binaries/tests.rst.txt b/doc/html/_sources/dev-doc/binaries/tests.rst.txt new file mode 100644 index 0000000000..18a1504898 --- /dev/null +++ b/doc/html/_sources/dev-doc/binaries/tests.rst.txt @@ -0,0 +1,9 @@ +Tests +===== + +.. toctree:: + :maxdepth: 2 + :caption: List of binaries: + + test + bench diff --git a/doc/html/_sources/dev-doc/binaries/update_nldb.md.txt b/doc/html/_sources/dev-doc/binaries/update_nldb.md.txt new file mode 100644 index 0000000000..81e1d8837a --- /dev/null +++ b/doc/html/_sources/dev-doc/binaries/update_nldb.md.txt @@ -0,0 +1 @@ +# update_nldb \ No newline at end of file diff --git a/doc/html/_sources/dev-doc/binaries/v7.md.txt b/doc/html/_sources/dev-doc/binaries/v7.md.txt new file mode 100644 index 0000000000..4da3171a7a --- /dev/null +++ b/doc/html/_sources/dev-doc/binaries/v7.md.txt @@ -0,0 +1,4 @@ +# v7 + +Defines new reqursts and redefines some old ones with new changes for the V7. +Registers the requests to the index (""), A, C, D, DOC, L, P, PS and TP. diff --git a/doc/html/_sources/dev-doc/binaries/v7_im.md.txt b/doc/html/_sources/dev-doc/binaries/v7_im.md.txt new file mode 100644 index 0000000000..245c2acb40 --- /dev/null +++ b/doc/html/_sources/dev-doc/binaries/v7_im.md.txt @@ -0,0 +1,4 @@ +# v7_im + +Complements plugin v7 with image handling. +Registers requests DEL_IMAGE, DEL_IMAGE_OK, SND_IMAGE and SND_IMAGE_OK. \ No newline at end of file diff --git a/doc/html/_sources/dev-doc/binaries/welcome.md.txt b/doc/html/_sources/dev-doc/binaries/welcome.md.txt new file mode 100644 index 0000000000..3ee9ebe53d --- /dev/null +++ b/doc/html/_sources/dev-doc/binaries/welcome.md.txt @@ -0,0 +1,4 @@ +# welcome + +This plugin facilitates the main page and search page use. +Registers the requests to the index page and S. \ No newline at end of file diff --git a/doc/html/_sources/dev-doc/binaries/xhtml.md.txt b/doc/html/_sources/dev-doc/binaries/xhtml.md.txt new file mode 100644 index 0000000000..3486301b7a --- /dev/null +++ b/doc/html/_sources/dev-doc/binaries/xhtml.md.txt @@ -0,0 +1,3 @@ +# xhtml + +Replaces the `Content-type` field of requests by `xhtml+xml`. \ No newline at end of file diff --git a/doc/html/_sources/dev-doc/geneweb-lib/index.md.txt b/doc/html/_sources/dev-doc/geneweb-lib/index.md.txt new file mode 100644 index 0000000000..30940f2d67 --- /dev/null +++ b/doc/html/_sources/dev-doc/geneweb-lib/index.md.txt @@ -0,0 +1,112 @@ +# Geneweb library overview + +## Def lib + +### Adef + +This module exposes base types and functions used by many other geneweb's libraries. It defines the types and manipulation functions for: +- Consanguinity degrees +- Dates +- Compressed dates +- Polymorphic couple that regroups parents (father and mother). + +### Def +## Util lib + +### Buff + +Module that defines extenisable buffer and auxilarry functions to store charcacters and strings and get buffer's content. The reason to define this module instead of using standard [Buffer](https://docs.ocaml.pro/docs/LIBRARY.stdlib@ocaml-base-compiler.4.10.0/Stdlib/Buffer/index.html) module is that its +storing functions are more simple to use as an argument to the recursive functions over strings that are very common throughout Geneweb library. + +### Name + +Module that defines some useful functions to construct, format, compress and adopt the strings to represent them as being person surnames/first names. + +### Utf8 + +Utf8 is a module that regroups some standard and useful functions dealing with UTF-8 encoded strings, which differs to standard OCaml module [Stdlib.String](https://docs.ocaml.pro/docs/LIBRARY.stdlib@ocaml-base-compiler.4.10.0/Stdlib/String/index.html), that internally considers the string as a table of bytes (Utf8 consider it as a table of UTF-8 encoded character). The major difference of this module comparing to the similar (like [Camomile](http://camomile.sourceforge.net/)) is that sometimes it makes use of [OCaml implementation](https://github.com/geneweb/unidecode) for [Unidecode](https://pypi.org/project/Unidecode/) library, that is used to convert encoded UTF-8 character to its ASCII representation. + +### Secure + +This module grants additional security layer and controls which directories can be read by the webserver, to prevent malicious users from reading/writing on the server disk. Allowed directories are composed of list of **assets** and the **base directory**. + +### Opt + +Module that defines/redefines some useful functions over `option` type. + +### Lock + +Module that define the flag and the functions that allow +to manage simultaneous accesses to the files by puting a lock on them with `Unix.lockf`. + +### ProgrBar + +Module that provides printing functions that helps to iterate over collections with progressive bar that shows current iteration stage (proportion of current index to total number). Here is an exemple how does it used for iteration over collection with 240 elements : + +```bash + +# start () +............................................................ + +# run 0 240 +|........................................................... + +# run 1 240 +/........................................................... + +# run 2 240 +-........................................................... + +# run 3 240 +\........................................................... + +# run 4 240 +o|.......................................................... + +# sucessive runs + +# run 120 240 + +oooooooooooooooooooooooooooooo|............................. + +``` + +### Mutil + +The main module inside *Util* library that regroups many type of useful functions dealing with : +- File system +- Lists +- Arrays +- Names +- Letters +- IO +- System calls +- Parsing different formats (like lexicon.txt or HTTP header) +- Encoding/decoding + + +### Calendar + +This module defines bindings to [calendars](https://github.com/geneweb/calendars) package that allows to manage different calendar's date. Module exposes functions that: +- Convert SDN (serial day number) to and from one of the date for supported calendars (gregorian, julian, french andhebrew). +- Convert any supported calendar's date to and from gregorian's date. +- Give information about moon's phase and about lunar calendar for the given day (SDN). + +### Date + +*Date* provides usefull functions that helps to compare and to count time elapes between two dates. + +### Pqueue + +This module implements priority queues. All operations are purely applicative (no side effects). +The implementation uses [binomial queues from Chris Okasaki](https://lara.epfl.ch/w/_media/fv20/optimalpurelyfunctionalqueues.pdf). Functions `add`, `take` and `union` are in O(log n) in the worst case. + +### Futil + +This module regroups some usefull functions dealing with polymorphic data types (starts with `gen_`). Particulary it exposes functions that map values inside `gen_` data type. Aditionally it provide `gen_person_misc_names` that computes list of various mix between all kind of name of the person. + +## Gwdb-legacy lib + +### Btree + +Module that is equivalent to [Stdlib.Map](https://ocaml.org/api/Map.html) except some added in output signature functions dealing with keys. \ No newline at end of file diff --git a/doc/html/_sources/dev-doc/index.rst.txt b/doc/html/_sources/dev-doc/index.rst.txt new file mode 100644 index 0000000000..b4000c0ddb --- /dev/null +++ b/doc/html/_sources/dev-doc/index.rst.txt @@ -0,0 +1,23 @@ + +Developer documentation +======================== + +This documentation is an overview of the different components of Geneweb. +It describes: + +- The functioning of the build system; + +- An overview of some libraries defined in the project; + +- A documentation of the main binaries; + +- A documentation for libraries generated with **odoc**. + + +.. toctree:: + :maxdepth: 2 + :caption: Contents: + + overview/index + installation/index + binaries/index \ No newline at end of file diff --git a/doc/html/_sources/dev-doc/installation/build-system.md.txt b/doc/html/_sources/dev-doc/installation/build-system.md.txt new file mode 100644 index 0000000000..3711914f8b --- /dev/null +++ b/doc/html/_sources/dev-doc/installation/build-system.md.txt @@ -0,0 +1 @@ +dsd \ No newline at end of file diff --git a/doc/html/_sources/dev-doc/installation/index.md.txt b/doc/html/_sources/dev-doc/installation/index.md.txt new file mode 100644 index 0000000000..4a604eadbe --- /dev/null +++ b/doc/html/_sources/dev-doc/installation/index.md.txt @@ -0,0 +1,824 @@ +# Installation procedure + +## Build documentation +1. Install the dependencies in an opam switch + ``` + $ opam switch create geneweb 4.09.1 + $ opam install . --deps-only + ``` + +2. Run the configuration script + ``` + $ ocaml ./configure.ml + ``` + +3. Build the distibution + ``` + $ make clean distrib + ``` + +## Configure +The `configure.ml` file is an ocaml script whose purpose is to +update `Makefile.config` by setting the environment variables +the `Makefile` needs. + +``` +usage: configure.ml [options] + --gwdb-legacy Use legacy backend + --release Use release profile: no debug informations (defaut: true) + --debug Use dev profile: no optimization, debug informations (default: false) + --sosa-legacy Use legacy Sosa module implementation + --sosa-num Use Sosa module implementation based on `num` library + --sosa-zarith Use Sosa module implementation based on `zarith` library + --syslog Log gwd errors using syslog + -help Display this list of options + --help Display this list of options +``` + +## Build system + +### Makefile + +The root of every action that could be performed on geneweb pass over its **Makefile**. In this section we will describe the variables, targets and its dependencies. + +#### Variables + +Makefile loads variables from **Makefile.config** generated by the configuration script for customised execution of some commands. Variables with suffix `_D` are passed to [cppo](https://github.com/ocaml-community/cppo) command that preprocess files with an equivalent to the C preprocessor. Here is a description of those variables : + +- `OS_TYPE` : kernel name (Linux, Win, Darwin, etc.) +- `STRIP` : command that discards symbols from compiled object files. Depends on kernel (linux command is ```strip```). +- `RM` : command that is used to remove files, directories, etc. Depends on kernel (linux command is ```/bin/rm -f```). +- `EXT` : extension for geneweb executable files. Depends on kernel (on Windows it's *.exe*). +- `GWDB_D` : argument to cppo command that defines a variable that tells which geneweb data base driver implementation will be used (example : *GENEWEB_GWDB_LEGACY*). +- `OS_D` : argument to cppo command that defines a variable for current system (example : *UNIX*). +- `SYSLOG_D` : argument to cppo command that could define a marker variable *SYSLOG*. When defined, activate logging by geneweb server. +- `GWDB_PKG` : dune library that implements geneweb database driver (example : *geneweb.gwdb-legacy*). +- `SOSA_PKG` : dune library that implements sosa (example : *geneweb_sosa_zarith*). +- `SYSLOG_PKG` : if *SYSLOG* is defined, then indicate dune library used for logging functionalities. +- `DUNE_DIRS_EXCLUDE` : specifies directories that will be excluded from *geneweb* library. Used to exclude not chosen implementations for sosa and for geneweb database driver. +- `DUNE_PROFILE` : specifies dune profile (default : release). + +Makefile also defines additional non-configurable variables : + +- `PREFIX` : +- `DISTRIB_DIR` : directory where a local geneweb distribution is stored. +- `BUILD_DISTRIB_DIR` : directory where geneweb executable files for local distribution are stored. +- `BUILD_DIR` : directory where the compiled artefacts are stored. +- `CPPO_D` : regroups all arguments to cppo (`GWDB_D`, `OS_D`, `SYSLOG_D`). If dune's profile is *dev* then also contains the definition of *DEBUG* variable that enables emitting warnings on stderr by geneweb server. +- `BENCH_FILE` : file where benchmark results are stored. + +Nowadays geneweb is entirely compiled by [dune](https://dune.readthedocs.io/en/stable/index.html), due to *dune* files. Non-preprocessed version of *dune* files are stored in *dune.in*. Makefile is responsible for the generation of corresponding *dune* from the *dune.in*. Every *dune* file that should be generated is stored in variable `GENERATED_FILES_DEP` in addition to other files generated by Makefile : + +```Makefile +GENERATED_FILES_DEP = \ + dune-workspace \ + hd/etc/version.txt \ + lib/dune \ + lib/gwdb/dune \ + lib/core/dune \ + lib/gwlib.ml \ + lib/util/dune \ + benchmark/dune \ + bin/connex/dune \ + bin/consang/dune \ + bin/fixbase/dune \ + bin/ged2gwb/dune \ + bin/gwb2ged/dune \ + bin/gwc/dune \ + bin/gwd/dune \ + bin/gwdiff/dune \ + bin/gwgc/dune \ + bin/gwrepl/dune \ + bin/gwrepl/.depend \ + bin/gwu/dune \ + bin/setup/dune \ + bin/update_nldb/dune \ + test/dune \ +``` + +#### Targets + +##### Main targets + +- Makefile.config: configure.ml
+ + Executed when *Makefile.config* isn't up-to-date with *configure.ml*. Prints corresponding error message and terminates execution. This target isn't executed when the goal is `ci` target. + +- lib/gwlib.ml:
+ + Generates the content for *lib/gwlib.ml* that depends on `PREFIX`. + +- bin/gwrepl/.depend:
+ + Generates the content for *bin/gwrepl/.depend*. + +- hd/etc/version.txt:
+ + Generates the content for hd/etc/version.txt that indicates some information about geneweb version like date of compilation and commit used. + +- %/dune: %/dune.in Makefile.config
+ + Generates [dune](https://dune.readthedocs.io/en/stable/dune-files.html#dune) file from corresponding *dune.in* as follows: + - Preprocess with ```cppo -n``` depending on `CPPO_D` argument. + - Remplaces with [sed](https://www.gnu.org/software/sed/manual/sed.html) occurrences of %%%CPPO_D%%%, %%%SOSA_PKG%%%, %%%GWDB_PKG%%%, %%%SYSLOG_PKG%%% and %%%DUNE_DIRS_EXCLUDE%%% by the values of corresponding variables. + +- dune-workspace: dune-workspace.in Makefile.config
+ + Generates [dune-workspace](https://dune.readthedocs.io/en/stable/dune-files.html#dune-workspace) file that is used to define different build contexts and currently selected build profile. Generation is done by replacing the occurrence of %%%DUNE_PROFILE%%% inside *dune-workspace.in* by the value of corresponding variable. + +- build: $(GENERATED_FILES_DEP)
+ + Default goal when ```make``` is called without targets. Executes the target for every file specified in `GENERATED_FILES_DEP` and then build all public artefacts in geneweb package. + +- distrib: build
+ + Principal target. Build entire system and creates a local distibution of geneweb under `DISTRIB_DIR` directory. See for more information. + +- clean:
+ + Removes all generated files that are specified in `GENERATED_FILES_DEP` and `DISTRIB_DIR` + +##### Additional targets + +- generated: $(GENERATED_FILES_DEP)
+ + Executes the target for every file specified in `GENERATED_FILES_DEP`. + +- install: $(GENERATED_FILES_DEP)
+ + Executes the target for every file specified in `GENERATED_FILES_DEP`, compilates all public artefacts and then install geneweb in opam. + +- uninstall: $(GENERATED_FILES_DEP)
+ + Same as **install** but remote geneweb from opam. + +- doc: | $(GENERATED_FILES_DEP)
+ + Generates documentation for all public artefacts in geneweb. Note that prerequisites on the right to `|` are [order-only](https://www.gnu.org/software/make/manual/html_node/Prerequisite-Types.html). + +- test: | $(GENERATED_FILES_DEP)
+ + Executes test suites defined in *test* directory. + +- bench: | $(GENERATED_FILES_DEP)
+ + Executes the benchmark defined in *benchmark* directory. + +- bench-marshal: | $(GENERATED_FILES_DEP)
+ + Executes the benchmark with options ```--marshal --name ${BENCH_NAME} ${BENCH_FILE}``` where `BENCH_NAME` is the name of bench results that will be stored in hash table and marshalled in `BENCH_FILE`. Prerequisite to **ci** target. + +- bench-tabulate: | $(GENERATED_FILES_DEP)
+ + Executes the benchmark with option ```--tabulate ${BENCH_FILE}``` that prints on the screen tables of bench results stored in `BENCH_FILE`. Prerequisite to **ci** target. + +- ci:
+ + - Configure geneweb for every existing implementation of sosa. + - Executes tests and benchmarks (name corresponds to sosa implementation) for each of them (recursive make call with goals **clean**, **test**, **bench-marshal** **clean**). + - Prints all benchmark results (recursive make call with goal **bench-tabulate**). + +### Dune configuration files + +#### dune-project + +This file is used to mark the root of projects as well as define project-wide parameters. In the case of geneweb this file specifies version of the dune configuration files with `lang` stanza and sets the name of project with `name` stanza: + +```lisp +(lang dune 2.8) +(name geneweb) +``` + +#### dune-workspace + +This file is used to define different build contexts and to select a build profile. +Geneweb defines only one `default` context. Build profile (`release` or `dev`) is chosen by configuration script (```--release``` option to use release profile) with subsequent ```make```. + +#### dune + +Dune files used to describe libraries, executables, tests, and everything dune needs to know about. Some of dune files are preprocessed by Makefile in order to remplace all occurences of %%%VARIABLE%%% by their real value (see [Variables](#variables)) In this section we will describe each of those file inside geneweb project. + +```./dune``` + +The root dune file that, in the geneweb case, modifies the environment used with `dev` build profile. Particularly, it passes additional [option](https://ocaml.org/manual/native.html#s:native-options) to the OCaml compiler : + +```bash +-w +a-4-9-35-42-44-48 +``` + +This option tells to compiler to enable all the warnings except : +- Fragile pattern matching. +- Missing fields in a record pattern. +- Unused for-loop index. +- Disambiguated constructor or label name. +- Open statement that shadows an already defined identifier. +- Implicit elimination of optional arguments. + +#### Geneweb library + +```lib/dune``` + +Defines public **geneweb** library . + +- It includes modules under the `lib` directory and every of them is preprocessed with ```cppo```. +- Module *TemplAst* is specified as a module without implementation (has only .mli). +- Module *Templ_parser* is obtained from *templ_parser.mll* with stanza `ocamllex` that makes call for [ocamllex](https://ocaml.org/manual/lexyacc.html#s:ocamllex-overview) lexer. +- Compilation of this library includes compilation of every subdirectory (with `dirs` stanza) except those mentioned in %%%DUNE_DIRS_EXCLUDE%%% variable (not used implementations). +- Depends on geneweb libraries : + - geneweb_core + - geneweb_def + - geneweb_gwdb + - geneweb_sosa_mli + - geneweb_util +- Depends on extrenal libraries : + - [unix](https://docs.ocaml.pro/docs/META.unix@ocamlfind.1.9.1/index.html) + - [str](https://docs.ocaml.pro/docs/META.str@ocamlfind.1.9.1/index.html) + - [stdlib-shims](https://docs.ocaml.pro/docs/META.stdlib-shims@stdlib-shims.0.3.0/index.html) + - [camlp5](https://docs.ocaml.pro/docs/META.camlp5@camlp5.8.00.01/index.html) + - [markup](https://docs.ocaml.pro/docs/META.markup@markup.1.0.0-1/index.html) + +```lib/core/dune``` + +Defines public **geneweb.core** library (locally - **geneweb_core**). + +- It includes modules under the `lib/core` directory and every of them is preprocessed with ```cppo```. +- Depends on geneweb libraries : + - geneweb_def + - geneweb_gwdb + - geneweb_sosa_mli + - geneweb_util +- Depends on extrenal libraries : + - [unix](https://docs.ocaml.pro/docs/META.unix@ocamlfind.1.9.1/index.html) + +```lib/def/dune``` + +Defines public **geneweb.def** library (locally - **geneweb_def**). Includes only two modules : *Adef* and *Def*. + +```lib/gwdb/dune``` + +Defines public **geneweb.gwdb** library (locally - **geneweb_gwdb**). + +- It includes modules under the `lib/gwdb` directory and every of them is preprocessed with ```cppo```. +- Depends on geneweb libraries : + - geneweb_def + - gwdb_driver_mli + - geneweb_util +- Depends on extrenal libraries : + - [unix](https://docs.ocaml.pro/docs/META.unix@ocamlfind.1.9.1/index.html) + +```lib/gwdb_driver.mli/dune``` + +Defines public virtual (that requires an implementation) **geneweb.gwdb_driver** library (locally - **gwdb_driver_mli**). + +- Include only one virtual module *Gwdb_driver*. +- Depends on geneweb libraries : + - geneweb_def +- Depends on extrenal libraries : + - [re](https://docs.ocaml.pro/docs/META.re@re.1.9.0/index.html) + +```lib/gwdb-legacy/dune``` + +Defines public **geneweb.gwdb-legacy** library (locally - **gwdb_legacy**). This library is an implementaion for virtual **gwdb_driver_mli** library. + +- It includes modules under the `lib/gwdb-legacy` directory. +- Module *Dbdisk* is specified as a module without implementation. +- Depends on geneweb libraries : + - geneweb_def + - geneweb_util +- Depends on extrenal libraries : + - [re](https://docs.ocaml.pro/docs/META.re@re.1.9.0/index.html) + - [unix](https://docs.ocaml.pro/docs/META.unix@ocamlfind.1.9.1/index.html) + +```lib/json_export/dune``` + +Defines public **geneweb.export** library (locally - **geneweb_export**). + +- Include only one module *Json_converter*. +- Depends on geneweb libraries : + - geneweb_def + - geneweb_def_show + - geneweb_gwdb +- Depends on extrenal libraries : + - [unix](https://docs.ocaml.pro/docs/META.unix@ocamlfind.1.9.1/index.html) + +```lib/show/dune``` + +Defines public **geneweb.def_show** library (locally - **geneweb_def_show**). + +- Include only one module *Def_show*. This module is preprocessed with [ppx_import](https://github.com/ocaml-ppx/ppx_import) and [ppx_deriving.show](https://github.com/ocaml-ppx/ppx_deriving#plugin-show) PPX rewriters that require feedback from the compilation phase (for that it is used inside `staged_pps` stanza rather than in ordinar `pps`). +- Depends on geneweb libraries : + - geneweb_def + - geneweb_gwdb + +```lib/sosa.mli/dune``` + +Defines public virtual **geneweb.sosa.mli** library (locally - **geneweb_sosa_mli**). Includes only one virtual module *Sosa*. + +```lib/sosa_array/dune``` + +Defines public **geneweb.sosa_array** library (locally - **geneweb_sosa_array**). This library is an one of implementaions for virtual **geneweb_sosa_mli** library. It includes only one module *Sosa*. + +```lib/sosa_num/dune``` + +Defines public **geneweb.sosa_num** library (locally - **geneweb_sosa_num**). This library is an one of implementaions for virtual **geneweb_sosa_mli** library. + +- It includes only one module *Sosa*. +- Depends on extrenal libraries : + - [num](https://docs.ocaml.pro/docs/META.num@num.1.4/index.html) + +```lib/sosa_zarith/dune``` + +Defines public **geneweb.sosa_zarith** library (locally - **geneweb_sosa_zarith**). This library is an one of implementaions for virtual **geneweb_sosa_mli** library. + +- It includes only one module *Sosa*. +- Depends on extrenal libraries : + - [zarith](https://docs.ocaml.pro/docs/META.zarith@zarith.1.11/index.html) + +```lib/util/dune``` + +Defines public **geneweb.util** library (locally - **geneweb_util**). + +- It includes modules under the `lib/util` directory and every of them is preprocessed with ```cppo```. +- Depends on geneweb libraries : + - geneweb_def +- Depends on extrenal libraries : + - [calendars](https://docs.ocaml.pro/docs/META.calendar@calendar.2.04/index.html) + - [stdlib-shims](https://docs.ocaml.pro/docs/META.stdlib-shims@stdlib-shims.0.3.0/index.html) + - [unix](https://docs.ocaml.pro/docs/META.unix@ocamlfind.1.9.1/index.html) + - [unidecode](https://github.com/geneweb/unidecode) + - [re](https://docs.ocaml.pro/docs/META.re@re.1.9.0/index.html) + - [uucp](https://docs.ocaml.pro/docs/META.uucp@uucp.13.0.0/index.html) + - [uunf](https://docs.ocaml.pro/docs/META.uunf@uunf.13.0.0/index.html) + - [uutf](https://docs.ocaml.pro/docs/META.uutf@uutf.1.0.2/index.html) + +##### Geneweb executables + +```bin/connex/dune``` + +Defines public **geneweb.connex** executable (locally - **connex**). + +- It includes only one module *Connex*. +- Depends on geneweb libraries : + - geneweb + - Chosen implementation of gwdb_driver_mli. + - Chosen implementation of geneweb_sosa_mli. +- Depends on extrenal libraries : + - [str](https://docs.ocaml.pro/docs/META.str@ocamlfind.1.9.1/index.html) + - [unix](https://docs.ocaml.pro/docs/META.unix@ocamlfind.1.9.1/index.html) + +```bin/consang/dune``` + +Defines public **geneweb.consang** executable (locally - **consang**). + +- It includes only one module *Consang* that is preprocessed with ```cppo```. +- Depends on geneweb libraries : + - geneweb + - Chosen implementation of gwdb_driver_mli. + - Chosen implementation of geneweb_sosa_mli. +- Depends on extrenal libraries : + - [unix](https://docs.ocaml.pro/docs/META.unix@ocamlfind.1.9.1/index.html) + +```bin/fixbase/dune``` + +Defines public **geneweb.gwfixbase** executable (locally - **gwfixbase**). + +- It includes only one module *Gwfixbase*. +- Depends on geneweb libraries : + - geneweb + - Chosen implementation of gwdb_driver_mli. + - Chosen implementation of geneweb_sosa_mli. +- Depends on extrenal libraries : + - [str](https://docs.ocaml.pro/docs/META.str@ocamlfind.1.9.1/index.html) + - [unix](https://docs.ocaml.pro/docs/META.unix@ocamlfind.1.9.1/index.html) + +```bin/ged2gwb/dune``` + +Defines public **geneweb.ged2gwb** executable (locally - **ged2gwb**). + +- It includes only one module *Ged2gwb* preprocessed by [camlp5o](https://camlp5.github.io/doc/html/) that provides parsing and pretty printing tools. Particulary, it uses following extensions: + - `pr_o.cmo`: pretty print in normal syntax + - `pa_extend.cmo`: syntax extension for grammars + - `q_MLast.cmo`: syntax tree nodes (in revised syntax) +- Depends on geneweb libraries : + - geneweb + - Chosen implementation of gwdb_driver_mli. + - Chosen implementation of geneweb_sosa_mli. +- Depends on extrenal libraries : + - [str](https://docs.ocaml.pro/docs/META.str@ocamlfind.1.9.1/index.html) + - [unix](https://docs.ocaml.pro/docs/META.unix@ocamlfind.1.9.1/index.html) + +```bin/gwb2ged/dune``` + +Defines : +1. Public **geneweb.gwb2ged_lib** library (locally - **gwb2ged_lib**). + - It includes only one module *Gwb2gedLib*. + - Depends on geneweb libraries : + - geneweb + - gwexport_lib +2. Public **geneweb.gwb2ge** executable (locally - **gwb2ged**). + - It includes only one module *Gwb2ged*. + - Depends on geneweb libraries : + - geneweb + - gwb2ged_lib + - gwu_lib + - Chosen implementation of gwdb_driver_mli. + - Chosen implementation of geneweb_sosa_mli. + - Depends on extrenal libraries : + - [str](https://docs.ocaml.pro/docs/META.str@ocamlfind.1.9.1/index.html) + - [unix](https://docs.ocaml.pro/docs/META.unix@ocamlfind.1.9.1/index.html) + +```bin/gwc/dune``` + +Defines public **geneweb.gwc** executable (locally - **gwc**). + +- It includes modules under the `bin/gwc` directory and every of them is preprocessed with ```cppo```. +- Depends on geneweb libraries : + - geneweb + - Chosen implementation of gwdb_driver_mli. + - Chosen implementation of geneweb_sosa_mli. +- Depends on extrenal libraries : + - [unix](https://docs.ocaml.pro/docs/META.unix@ocamlfind.1.9.1/index.html) + +```bin/gwd/dune``` + +Defines: + +1. Public wrapped **geneweb.gwd_lib** library (locally - **gwd_lib**). + - It includes modules *GwdLog*, *GwdPlugin* and *Request* that are preprocessed with ```cppo```. + - Depends on geneweb libraries : + - geneweb + - wserver + - Chosen implementation of gwdb_driver_mli. + - Chosen implementation of geneweb_sosa_mli. +2. Public executable **geneweb.gwd** library (locally - **gwd**). + - It includes modules *Gwd*, *GwdPluginDep*, *GwdPluginMD5*, *GwdPluginMETA* and *Robot* that are preprocessed with ```cppo```. + - Module *GwdPluginMD5* is created by `rule` stanza. Stanza `deps` tells to dune to do firstly: + - Find module *Mk_gwdPluginMD5* + - Construct all recursively defined in ```plugins/``` aliases `@plugin`. + File ```gwdPluginMD5.ml``` is created by running maker script ```mk_gwdPluginMD5.ml``` with path to ```plugins/``` directory specified in argument. + - All the libraies used by executable are compiled with `-linkall` flag. + - Depends on geneweb libraries : + - gwd_lib + - geneweb + - wserver + - Chosen implementation of gwdb_driver_mli. + - Chosen implementation of geneweb_sosa_mli. + - Depends on extrenal libraries : + - [dynlink](https://docs.ocaml.pro/docs/META.dynlink@ocamlfind.1.9.1/index.html) + - [str](https://docs.ocaml.pro/docs/META.str@ocamlfind.1.9.1/index.html) + - [unix](https://docs.ocaml.pro/docs/META.unix@ocamlfind.1.9.1/index.html) + +```bin/gwdiff/dune``` + +Defines public **geneweb.gwdiff** executable (locally - **gwdiff**). + +- It includes only one module *Gwdiff*. +- Depends on geneweb libraries : + - geneweb + - Chosen implementation of gwdb_driver_mli. + - Chosen implementation of geneweb_sosa_mli. +- Depends on extrenal libraries : + - [str](https://docs.ocaml.pro/docs/META.str@ocamlfind.1.9.1/index.html) + - [unix](https://docs.ocaml.pro/docs/META.unix@ocamlfind.1.9.1/index.html) + +```bin/gwexport/dune``` + +Defines public **geneweb.gwexport_lib** library (locally - **gwexport_lib**). + +- It includes only one module *Gwexport*. +- Depends on geneweb libraries : + - geneweb + +```bin/gwgc/dune``` + +Defines public **geneweb.gwgc** executable (locally - **gwgc**). + +- It includes only one module *Gwgc* that is preprocessed with ```cppo```. +- Depends on geneweb libraries : + - geneweb + - Chosen implementation of gwdb_driver_mli. + - Chosen implementation of geneweb_sosa_mli. +- Depends on extrenal libraries : + - [str](https://docs.ocaml.pro/docs/META.str@ocamlfind.1.9.1/index.html) + - [unix](https://docs.ocaml.pro/docs/META.unix@ocamlfind.1.9.1/index.html) + +```bin/gwrepl/dune``` + +Defines: + +1. Private library **gwrepl_deps** that links all dependent libraries. + - Depends on geneweb libraries : + - geneweb_core + - geneweb_def + - geneweb_util + - geneweb_gwdb + - Chosen implementation of gwdb_driver_mli. + - Chosen implementation of geneweb_sosa_mli. + - Depends on external libraries + - [stdlib](https://docs.ocaml.pro/docs/LIBRARY.stdlib@ocaml-base-compiler.4.10.0/index.html) + - [str](https://docs.ocaml.pro/docs/META.str@ocamlfind.1.9.1/index.html) + - [unix](https://docs.ocaml.pro/docs/META.unix@ocamlfind.1.9.1/index.html) +2. Public **gwrepl** executable . + - It includes only two modules *Gwrepl* and *Data*. + - **gwrepl** executable is preprocessed with ```cppo```. + - Module *Data* is created with `rule` stanza from : + - Module *Mk_data* + - File ```.depend``` .
+ File ```data.ml``` is created by running maker script ```mk_data.ml``` that is linked with ```unix.cma``` by OCaml toplevel. + - *Data* is preprocessed with [ppx_blob](https://github.com/johnwhitington/ppx_blob) that is used to extract the content of binary file at compile time. + - It is compiled by bytecode compiler and producing static object files: + ```lisp + (modes byte object) + ``` + - All the libraies used by executable are compiled with `-linkall` flag. + - Linked with ```-custom``` option that is used by bytecode compiler to produce native executable that embeds the ocamlrun virtual machine as well as the byte code. See [dune-executable](https://dune.readthedocs.io/en/stable/dune-files.html#executable). + - Depends on external libraries: + - [compiler-libs.toplevel](https://docs.ocaml.pro/docs/META.compiler-libs.toplevel@ocamlfind.1.9.1/index.html) + - [unix](https://docs.ocaml.pro/docs/META.unix@ocamlfind.1.9.1/index.html) + +```bin/gwu/dune``` + +Defines : +1. Public **geneweb.gwu_lib** library (locally - **gwu_lib**). + - It includes only one module *GwuLib*. + - Depends on geneweb libraries : + - geneweb + - gwexport_lib +2. Public **geneweb.gwu** executable (locally - **gwu**). + - It includes only one module *Gwu*. + - Depends on geneweb libraries : + - geneweb + - gwexport_lib + - gwu_lib + - Chosen implementation of gwdb_driver_mli. + - Chosen implementation of geneweb_sosa_mli. + - Depends on extrenal libraries : + - [str](https://docs.ocaml.pro/docs/META.str@ocamlfind.1.9.1/index.html) + - [unix](https://docs.ocaml.pro/docs/META.unix@ocamlfind.1.9.1/index.html) + +```bin/setup/dune``` + +Defines public **geneweb.setup** executable (locally - **setup**). + +- It includes only one module *Setup* that is preprocessed with ```cppo```. +- Depends on geneweb libraries : + - geneweb + - wserver + - Chosen implementation of gwdb_driver_mli. + - Chosen implementation of geneweb_sosa_mli. +- Depends on extrenal libraries : + - [unix](https://docs.ocaml.pro/docs/META.unix@ocamlfind.1.9.1/index.html) + - [str](https://docs.ocaml.pro/docs/META.str@ocamlfind.1.9.1/index.html) + +```bin/update_nldb/dune``` + +Defines public **geneweb.update_nldb** executable (locally - **update_nldb**). + +- It includes only one module *Update_nldb*. +- Depends on geneweb libraries : + - geneweb + - Chosen implementation of gwdb_driver_mli. + - Chosen implementation of geneweb_sosa_mli. +- Depends on extrenal libraries : + - [str](https://docs.ocaml.pro/docs/META.str@ocamlfind.1.9.1/index.html) + - [unix](https://docs.ocaml.pro/docs/META.unix@ocamlfind.1.9.1/index.html) + + +```bin/wserver/dune``` + +Defines public **geneweb.wserver** library (locally - **wserver**). + +- It includes only one module *Wserver*. +- Depends on geneweb libraries : + - geneweb_util +- Depends on extrenal libraries : + - [unix](https://docs.ocaml.pro/docs/META.unix@ocamlfind.1.9.1/index.html) + +##### Geneweb plugins + +```plugins/cgl/dune``` + +Defines plugin **plugin_cgl**. + +- It includes only one module *Plugin_cgl*. +- Plugin is compiled by native compiler with + ```lisp + (modes (native plugin)) + ``` +- Construction of `@plugin` alias depends on plugin's compilation. +- Depends on geneweb libraries : + - geneweb + - gwd_lib + - wserver + +```plugins/export/dune``` + +Defines plugin **plugin_export**. + +- It includes only one module *Plugin_export*. +- Plugin is compiled by native compiler. +- Construction of `@plugin` alias depends on plugin's compilation. +- Compiled with flags that tells to compiler to disable warnings: + - Innocuous unused variable. + - Constructor or label name used out of scope. +- Linked statically with libraries : + - gwexport_lib + - gwu_lib + - gwb2ged_lib +- Depends on geneweb libraries : + - geneweb + - gwd_lib + - gwb2ged_lib + - gwexport_lib + - gwu_lib + - wserver + +```plugins/fixbase/dune``` + +Defines plugin **plugin_fixbase**. + +- It includes only one module *Plugin_fixbase*. +- Plugin is preprocessed with [ppx_deriving.show](https://github.com/ocaml-ppx/ppx_deriving#plugin-show). +- Plugin is compiled by native compiler with ```-linkall``` option. +- Construction of `@plugin` alias depends on: + - Plugin's compilation + - Every file from *assets* directory. +- Depends on geneweb libraries : + - gwd_lib + - geneweb_def_show + +```plugins/forum/dune``` + +Defines plugin **plugin_forum**. + +- It includes only one module *Plugin_forum* that was obtained by preprocessoing corresponding ```.cppo.ml```. Stanza `rule` in this context is used to remplace lines with directive ```#include``` by one of included file : +```lisp +(rule + (target plugin_forum.ml) + (deps + (:included + %{project_root}/plugins/forum/forum.ml + %{project_root}/plugins/forum/forumDisplay.ml + ) + (:src plugin_forum.cppo.ml) + ) + (action (run %{bin:cppo} %{src} -o %{target})) +``` +- Plugin is compiled by native compiler. +- Construction of `@plugin` alias depends on plugin's compilation. +- Depends on geneweb libraries : + - geneweb + - gwd_lib + - wserver + +```plugins/gwxjg/dune``` + +Defines : + +1. Public **geneweb.plugin_gwxjg_lib** library (locally - **plugin_gwxjg_lib**). + - It includes modules *Gwxjg_ezgw*, *Gwxjg_data*, *Gwxjg_trans* and *Gwxjg_lexicon_parser*. + - Module *Gwxjg_lexicon_parser* is obtained from *gwxjg_lexicon_parser.mll* with stanza `ocamllex` that makes call for [ocamllex](https://ocaml.org/manual/lexyacc.html#s:ocamllex-overview) lexer. + - Compiled with flags that tells to compiler to disable warnings: + - Constructor or label name used out of scope. + - Disambiguated constructor or label name. + - Depends on geneweb libraries : + - geneweb + - Depends on external libraries : + - [jingoo](https://docs.ocaml.pro/docs/META.jingoo@jingoo.1.4.3/index.html) + - [unix](https://docs.ocaml.pro/docs/META.unix@ocamlfind.1.9.1/index.html) +2. Plugin **plugin_gwxjg**. + - It includes only one module *Plugin_gwxjg*. + - Plugin is compiled by native compiler with ```-linkall``` option. + - Construction of `@plugin` alias depends on: + - Plugin's compilation + - *META* file. + - Linked statically with libraries : + - plugin_gwxjg_lib + - Depends on geneweb libraries : + - gwd_lib + - plugin_gwxjg_lib + +```plugins/jingoo/dune``` + +Defines plugin **plugin_jingoo**. + +- It includes only one module *Plugin_jingoo*. +- Plugin is compiled by native compiler with ```-linkall``` option. +- Construction of `@plugin` alias depends on plugin's compilation. +- Linked statically with libraries : + - [jingoo](https://docs.ocaml.pro/docs/META.jingoo@jingoo.1.4.3/index.html) +- Depends on geneweb libraries : + - gwd_lib + +```plugins/lib_show/dune``` + +Defines plugin **plugin_lib_show**. + +- It includes only one module *Plugin_lib_show*. +- Plugin is compiled by native compiler with ```-linkall``` option. +- Construction of `@plugin` alias depends on plugin's compilation. +- Linked statically with libraries : + - geneweb_def_show + +```plugins/no_index/dune``` + +Defines plugin **plugin_no_index**. + +- It includes only one module *Plugin_no_index*. +- Plugin is compiled by native compiler. +- Construction of `@plugin` alias depends on plugin's compilation. +- Depends on geneweb libraries : + - geneweb + - gwd_lib + - wserver + +```plugins/v7/dune``` + +1. Wrapped **plugin_v7_lib** library. + - It includes modules starting with prefix ```V7```. + - Depends on geneweb libraries : + - geneweb + - geneweb_util + - gwd_lib +2. Plugin **plugin_v7**. + - It includes only one module *Plugin_v7*. + - Plugin is compiled by native compiler with ```-linkall``` option. + - Construction of `@plugin` alias depends on: + - Plugin's compilation + - Every file from *assets* directory. + - Linked statically with libraries : + - plugin_v7_lib + - Depends on geneweb libraries : + - gwd_lib + - plugin_v7_lib + +```plugins/v7_im/dune``` + +1. Wrapped **plugin_v7_im_lib** library. + - It includes only one module *V7_im_sendImage*. + - Depends on geneweb libraries : + - geneweb_util + - gwd_lib +2. Plugin **plugin_v7**. + - It includes only one module *Plugin_v7_im*. + - Plugin is compiled by native compiler. + - Construction of `@plugin` alias depends on plugin's compilation. + - Linked statically with libraries : + - plugin_v7_im_lib + - Depends on geneweb libraries : + - gwd_lib + - plugin_v7_im_lib + +```plugins/welcome/dune``` + +Defines plugin **plugin_welcome**. + +- It includes only one module *Plugin_welcome*. +- Plugin is compiled by native compiler with ```-linkall``` option. +- Construction of `@plugin` alias depends on: + - Plugin's compilation. + - Every file from *assets* directory. + - *META* file. +- Depends on geneweb libraries : + - gwd_lib + - wserver + - plugin_v7_lib + +##### Geneweb tests + +```test/dune``` + +Defines: + +1. **dummy_gwdb** library that is an implementation for **gwdb_driver_mli** virtual library. + - It includes only one module *Gwdb_driver*. + - Depends on geneweb libraries: + - geneweb_def + - Depends on external libraries: + - [stdlib-shims](https://docs.ocaml.pro/docs/META.stdlib-shims@stdlib-shims.0.3.0/index.html) +2. **test** executable. + - It includes modules starting with prefix ```Test``` that are preprocessed with [ppx_deriving.show](https://github.com/ocaml-ppx/ppx_deriving#plugin-show) PPX rewriter. + - Construction of `@runtest` alias depends on execution of **test**. + - Depends on geneweb libraries: + - geneweb + - dummy_gwdb + - Chosen implementation of geneweb_sosa_mli. + - Depends on external libraries: + - [stdlib-shims](https://docs.ocaml.pro/docs/META.stdlib-shims@stdlib-shims.0.3.0/index.html) + - [oUnit](https://docs.ocaml.pro/docs/META.oUnit@ounit.2.0.8/index.html) + +##### Geneweb benchmarks + +```benchmark/dune``` + +Defines **bench** executable. + +- It includes only one module *Bench* that is preprocessed with ```cppo```. +- Construction of `@runbench` alias depends on execution of **bench**. +- Depends on geneweb libraries : + - geneweb + - Chosen implementation of gwdb_driver_mli. + - Chosen implementation of geneweb_sosa_mli. +- Depends on extrenal libraries : + - [unix](https://docs.ocaml.pro/docs/META.unix@ocamlfind.1.9.1/index.html) + - [benchmark](https://docs.ocaml.pro/docs/META.benchmark@benchmark.1.6/index.html) diff --git a/doc/html/_sources/dev-doc/overview/database.md.txt b/doc/html/_sources/dev-doc/overview/database.md.txt new file mode 100644 index 0000000000..afa153d85d --- /dev/null +++ b/doc/html/_sources/dev-doc/overview/database.md.txt @@ -0,0 +1,146 @@ +# Database overview + +## Gw files + +Genealogy database could be created by Geneweb from one or from multiple source files with *.gw* extension. Those files describe structurally persons, families all kinds of relationships, different events, etc. You can read more about the file structure [here](https://geneweb.tuxfamily.org/wiki/gw). Binary executable `gwc` reads files *.gw*, extracts all persons and families information and passes it to the **Gwdb** module in order to create the database. + +## Database entries + +Transmitted to **Gwdb** information is composed mainly from: + +- Array of all strings that could be any kind of information encoded as a string, like for example: person's name, birth place, marriage place, etc. Identifier `istr` allows to reference the string in the given array (index of an element inside the array). + +- Array of persons where each element encompasses information about one person. Every string field of a person (like his name, birthplace, etc.) is an identifier where the real string is stored in array mentioned before. Reference to other persons by means of identifier `iper` that reference person in the current array (index of an element inside the persons array). + +- Array of families where each element encompasses information about one family (couple, children, marriage date, etc.). Identifier `ifam` allows to reference the family in the given array (index of element inside the array). + +Each array keeps a data structure defined in the module **Def**. Further, those entries will be the main source for every database request. + +## Storage + +**Gwdb** is responsible for creating the database on the disk from the provided inputs. It creates a directory `dbname.gwb` containing several +files. The main file `base` contains marshalled representation of each array and `base.acc` stores offsets to every entry entry that allows to make constant time access. Additionally, it creates some index files that associate useful for requests information to the entry's identifier in the `base` file. That helps to requests to find instantly entry without iteration over all existing ones in the database. For example `strings.inx` is a string index that allows to find id for a searched string. One file is slightly different: the `patches` file. It stores every modification done inside the base (see [Modifications](#modifications) subsection). The storage manipulation interface is described in `lib/gwdb_driver.mli/gwdb_driver.mli`. This is a virtual module whose +current implementation is available on `gwdb-legacy`. Format and description for every database file is listed below: + +```text +base - the base itself + magic number (magic_gwb) : string of length 8 + number of persons : binary_int + number of families : binary_int + number of strings : binary_int + persons array offset in file : binary_int + ascends array offset in file : binary_int + unions array offset in file : binary_int + families array offset in file : binary_int + couples array offset in file : binary_int + descends array offset in file : binary_int + strings array offset in file : binary_int + notes origin file : value + persons array : value + ascends array : value + unions array : value + families array : value + couples array : value + descends array : value + strings array : value + +base.acc - direct accesses to arrays inside base + persons offsets : array of binary_ints + ascends offsets : array of binary_ints + unions offsets : array of binary_ints + families offsets : array of binary_ints + couples offsets : array of binary_ints + descends offsets : array of binary_ints + strings offsets : array of binary_ints + +names.inx - index for names, strings of first names and surnames + offset to sindex : binary_int + offset to findex : binary_int + 1st index (mixes between names) : value + array, length = 16383, associating: + - a hash value of a "crushed" (module "Name") name + (modulo length) + - to the array of ids of the corresponding persons + 2nd index (surnames sub-strings) : value + array, length = "table_size", associating: + - a hash value of the "crushed" (module "Name") surname + sub-string (modulo length) + - to the array of the corresponding surnnames (string ids) + that contain giving surname sub-string + 3rd index (first name sub-strings) : value + array, length = 16383, associating: + - a hash value of the "crushed" (module "Name") first name + sub-string (modulo length) + - to the array of the corresponding string ids that contains + giving first name sub-string + +names.acc - direct accesses to values inside arrays in names.inx + +strings.inx - index for all strings + length of the strings offset array : binary_int + strings hash table index : 2 arrays of binary_ints + strings offset array (length = prime after 10 * strings + array length) + - associating a hash value of the string modulo length + - to its id in the string array + strings list array (length = string array length) + - associating a string id + - to the id of the next index (previous value) holding the + same hash value + +snames.inx - index for surnames + array ordered by surname + - associating the string id of a surname + - to a pointer (offset) inside snames.dat + +snames.dat - data associated with snames.inx + array of list of persons holding a surname + +fnames.inx - index for first names + array ordered by first name + - associating the string id of a first name + - to a pointer (offset) inside fnames.dat + +fnames.dat - data associated with fnames.inx + array of list of persons holding a first name + +notes - text file containing data base notes. + +notes_d - directory containing .txt for each extended page + +particles.txt - text file with autorised name's particles + +patches - modification inside the database + When updated, none of the previous files are modified. + Only this one is written and rewritten. It holds a record + of type "patches", composed of association lists + "index" - "new value". + +nb_persons - number of real persons (with those added by patches) + +synchro_patches - timestamped history of base's modifications. + +restrict - defines visibility of each person in the base + +``` + +## Modifications + +When a modification is requested, geneweb does not update `base` file itself. It +completes the `patches` file containing all the latest modifications on the +base. Every modification (patch) done is pended until patches are committed with `commit_patches` request. +Commit performs update of the `patches` file. + +Patching signifies only operations that add or modify an entry. Entry suppression is done quite differently. +It is replaced by a *dummy* entry and then removed by Geneweb's garbage collector `gwgc` that performs compaction +of database arrays. Another useful `fixbase` tool, locates and fixes inconsistencies on the base and updates all database files. + +## Example + +Here is an example how Geneweb displays birth dates of persons that have given name (let's say "Pierre") without considering caches: + +- Firstly, it makes dichotomous search inside `fnames.inx` of a string id (`istr`) that references "Pierre" +- Then it reads (with associated to "Pierre" offset from `fnames.inx`) position in the file `fnames.data` where list of ids of persons (`iper`) with first name "Pierre" are stored. +- For every person's id it gets person's entry offset from `base.acc` file +- Then it reads person's entry with giving offset and get field associated to the birth date. +- Displays all extracted birth dates. diff --git a/doc/html/_sources/dev-doc/overview/index.rst.txt b/doc/html/_sources/dev-doc/overview/index.rst.txt new file mode 100644 index 0000000000..7759c4c5fc --- /dev/null +++ b/doc/html/_sources/dev-doc/overview/index.rst.txt @@ -0,0 +1,167 @@ + +Overview +======== + +How to start a Geneweb server +----------------------------- + +Starting the server +~~~~~~~~~~~~~~~~~~~ + +Starting a Geneweb web server requires two tools: + +- :code:`gwd`, the actual web server; +- :code:`setup`, the database setup portal. + +These two tools are part of the main distribution and can be found on :code:`_build`. +If you built the project with :code:`make clean distrib`, two scripts are available on +the directory :code:`distribution/`. Keep in mind they are scripts wrapping the actual +binaries. + +To start the server on the current directory:: + + _build/install/default/bin/geneweb.gwd -hd + +This will start the main web server on port 2317. + +To start the setup server on the current directory:: + + _build/default/bin/setup/setup.exe -gd + +This will start the setup server on port 2316. + +Configuration +~~~~~~~~~~~~~ + +Geneweb can be configured by defining a `.gwb` file. An example is +available in the :code:`etc` directory. + +Architecture of Geneweb +----------------------- + +Geneweb is built from three main components: + +- the storage of the genealogical trees, divided in a patch file and the actual data; +- the libraries, reading the data from the storage and writing the patch file +- the binaries and the web server, reading and writing the storage + +.. image:: diagram.png + +Storage architecture +~~~~~~~~~~~~~~~~~~~~ + +.. toctree:: + :maxdepth: 2 + + database + +Libraries +--------- + + +- `Util`: a library with miscellaneous useful modules for manipulating + base data types. + +- `Def`: the definition of the main type definitions used in Geneweb trees. + +- `Sosa`: describes a Sosa-Stradonitz numbering (known an + Ahnentafel numbering), associating natural identifiers to individuals. This + library is virtual and has three different implementations. One of these + implementations is selected by the configuration script. + + * `Sosa_array` is one of the `sosa` implementations. It represents naturals + as pair of integers stored in an array `a` such that + `sosa = a.(0) + base * a(1)`) where `base` is a hardcoded constant. + + * `Sosa_num` is one of the `sosa` implementation based on the `Big_int` + library. + + * `Sosa_zarith` is one of the `sosa` implementation based on the `Zarith` + library. + +- `Gwdb_driver`: describes the storage implementation. While it is + virtual, it currently has only one implementation, `gwdb-legacy`. It is wrapped + by the `gwdb` library that exports many tools for database updates. + +- `Core`: core of Geneweb for calculating consanguinity between persons. + +- `Geneweb_export` : provides a functor for defining Json convertors for Geneweb's + datatypes. + +- `Def_show`: defines formatters and `string` converters for Geneweb's datatypes. + +- `Geneweb`: the main library. It contains several kinds of files, from + utilitarian modules to HTML generation. + +- `Gwb2gedLib`: defines a function for exporting a base to a GEDCOM file. + +- `Wserver`: a light-weight web server, used by `gwd` and `setup`. + +- `Gwd_lib`: defines additional modules for the `gwd` web server. + +- `GwuLib`: defines useful functions for exporting a database to a `.gw` file. + +The documentation of each module is available on the automatically generated +documentation of geneweb (`$ make doc`) + + +Binaries +~~~~~~~~ + +Official binaries +----------------- + +Here are the binaries maintained by Geneweb: + +- `Connex <../binaries/connex.html>`_: calculates connex components of a base; + +- `Consang <../binaries/consang.html>`_: calculates the consanguinity level of individuals; + +- `Ged2gwb <../binaries/ged2gwb.html>`_: imports a GEDCOM 5.5.1 file to a Geneweb base; + +- `Gwb2ged <../binaries/gwb2ged.html>`_: exports a base to a GEDCOM 5.5.1 base; + +- `Gwc <../binaries/gwc.html>`_: creates a new database; + +- `Gwd <../binaries/gwd.html>`_: starts Geneweb's main web server which allows to interact with bases; + +- `Gwdiff <../binaries/gwdiff.html>`_: targets differences between two databases; + +- `Fixbase <../binaries/gwfixbase.html>`_: checks the consistency of a base and applies patches; + +- `Gwgc <../binaries/gwgc.html>`_: removes unused entries in Geneweb's arrays; + +- `Gwrepl <../binaries/gwrepl.html>`_: an OCaml interactive top level, useful for scripts; + +- `Gwu <../binaries/gwu.html>`_: exports a base to a :code:`.gw` file; + +- `Setup <../binaries/setup.html>`_: a web server for selecting and creating databases. + +.. toctree:: + :maxdepth: 1 + :caption: For mor details about every binary, see: + + ../binaries/official_binaries + +Web-server and plug-ins +----------------------- + +The :code:`gwd` web server is customizable with plug-ins; code replacing +the original behaviour of the web server handling requests. They are +dynamically loaded by :code:`gwd` at its start and each base can be activated +through the :code:`.gwb` file (:code:`plugins=*` for activating all plug-ins, +otherwise :code:`plugins=p1,p2,...`). + +A plug-in is composed of its code, a :code:`dune` file for building and a +:code:`META` file, containing some informations about the plug-in itself. + +Here is an example of :code:`META` file: + +``` +version:1 +maintainers:OCamlPro +depends:plugin1,plugin2 +``` + +The only field taken into account by `gwd` is `depends` as it is used to +check there are no circular dependencies between plug-ins. diff --git a/doc/html/_sources/index.rst.txt b/doc/html/_sources/index.rst.txt new file mode 100644 index 0000000000..828c677972 --- /dev/null +++ b/doc/html/_sources/index.rst.txt @@ -0,0 +1,38 @@ +.. audit documentation master file, created by + sphinx-quickstart on Mon Nov 22 11:15:19 2021. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to Geneweb Audit's Documentation! +========================================= + +This document is one of the results of OCamlPro's first mission over Geneweb. +It consists mainly of two parts: + +- A developer documentation, complementing the `Geneweb wiki `_. + In turn, it provides an overview of the main components of Geneweb, in addition to the documentation for its + code part generated using **odoc**. + +- An audit that gives recommendations starting from comments on the source code and ending with long term improvement + advices. It was based on this `commit `_. + +In addition to the document, a `PR `_ makes part of the mission results that, in turn : + +* Adds interfaces and documentation to the most of existing modules; +* Fixes: + * CI execution with *appveyor.yml*; + * Access to assets files with Secure module. + +.. toctree:: + :maxdepth: 3 + :caption: Contents: + + dev-doc/index + audit/index + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/doc/html/_static/alabaster.css b/doc/html/_static/alabaster.css new file mode 100644 index 0000000000..0eddaeb07d --- /dev/null +++ b/doc/html/_static/alabaster.css @@ -0,0 +1,701 @@ +@import url("basic.css"); + +/* -- page layout ----------------------------------------------------------- */ + +body { + font-family: Georgia, serif; + font-size: 17px; + background-color: #fff; + color: #000; + margin: 0; + padding: 0; +} + + +div.document { + width: 940px; + margin: 30px auto 0 auto; +} + +div.documentwrapper { + float: left; + width: 100%; +} + +div.bodywrapper { + margin: 0 0 0 220px; +} + +div.sphinxsidebar { + width: 220px; + font-size: 14px; + line-height: 1.5; +} + +hr { + border: 1px solid #B1B4B6; +} + +div.body { + background-color: #fff; + color: #3E4349; + padding: 0 30px 0 30px; +} + +div.body > .section { + text-align: left; +} + +div.footer { + width: 940px; + margin: 20px auto 30px auto; + font-size: 14px; + color: #888; + text-align: right; +} + +div.footer a { + color: #888; +} + +p.caption { + font-family: inherit; + font-size: inherit; +} + + +div.relations { + display: none; +} + + +div.sphinxsidebar a { + color: #444; + text-decoration: none; + border-bottom: 1px dotted #999; +} + +div.sphinxsidebar a:hover { + border-bottom: 1px solid #999; +} + +div.sphinxsidebarwrapper { + padding: 18px 10px; +} + +div.sphinxsidebarwrapper p.logo { + padding: 0; + margin: -10px 0 0 0px; + text-align: center; +} + +div.sphinxsidebarwrapper h1.logo { + margin-top: -10px; + text-align: center; + margin-bottom: 5px; + text-align: left; +} + +div.sphinxsidebarwrapper h1.logo-name { + margin-top: 0px; +} + +div.sphinxsidebarwrapper p.blurb { + margin-top: 0; + font-style: normal; +} + +div.sphinxsidebar h3, +div.sphinxsidebar h4 { + font-family: Georgia, serif; + color: #444; + font-size: 24px; + font-weight: normal; + margin: 0 0 5px 0; + padding: 0; +} + +div.sphinxsidebar h4 { + font-size: 20px; +} + +div.sphinxsidebar h3 a { + color: #444; +} + +div.sphinxsidebar p.logo a, +div.sphinxsidebar h3 a, +div.sphinxsidebar p.logo a:hover, +div.sphinxsidebar h3 a:hover { + border: none; +} + +div.sphinxsidebar p { + color: #555; + margin: 10px 0; +} + +div.sphinxsidebar ul { + margin: 10px 0; + padding: 0; + color: #000; +} + +div.sphinxsidebar ul li.toctree-l1 > a { + font-size: 120%; +} + +div.sphinxsidebar ul li.toctree-l2 > a { + font-size: 110%; +} + +div.sphinxsidebar input { + border: 1px solid #CCC; + font-family: Georgia, serif; + font-size: 1em; +} + +div.sphinxsidebar hr { + border: none; + height: 1px; + color: #AAA; + background: #AAA; + + text-align: left; + margin-left: 0; + width: 50%; +} + +div.sphinxsidebar .badge { + border-bottom: none; +} + +div.sphinxsidebar .badge:hover { + border-bottom: none; +} + +/* To address an issue with donation coming after search */ +div.sphinxsidebar h3.donation { + margin-top: 10px; +} + +/* -- body styles ----------------------------------------------------------- */ + +a { + color: #004B6B; + text-decoration: underline; +} + +a:hover { + color: #6D4100; + text-decoration: underline; +} + +div.body h1, +div.body h2, +div.body h3, +div.body h4, +div.body h5, +div.body h6 { + font-family: Georgia, serif; + font-weight: normal; + margin: 30px 0px 10px 0px; + padding: 0; +} + +div.body h1 { margin-top: 0; padding-top: 0; font-size: 240%; } +div.body h2 { font-size: 180%; } +div.body h3 { font-size: 150%; } +div.body h4 { font-size: 130%; } +div.body h5 { font-size: 100%; } +div.body h6 { font-size: 100%; } + +a.headerlink { + color: #DDD; + padding: 0 4px; + text-decoration: none; +} + +a.headerlink:hover { + color: #444; + background: #EAEAEA; +} + +div.body p, div.body dd, div.body li { + line-height: 1.4em; +} + +div.admonition { + margin: 20px 0px; + padding: 10px 30px; + background-color: #EEE; + border: 1px solid #CCC; +} + +div.admonition tt.xref, div.admonition code.xref, div.admonition a tt { + background-color: #FBFBFB; + border-bottom: 1px solid #fafafa; +} + +div.admonition p.admonition-title { + font-family: Georgia, serif; + font-weight: normal; + font-size: 24px; + margin: 0 0 10px 0; + padding: 0; + line-height: 1; +} + +div.admonition p.last { + margin-bottom: 0; +} + +div.highlight { + background-color: #fff; +} + +dt:target, .highlight { + background: #FAF3E8; +} + +div.warning { + background-color: #FCC; + border: 1px solid #FAA; +} + +div.danger { + background-color: #FCC; + border: 1px solid #FAA; + -moz-box-shadow: 2px 2px 4px #D52C2C; + -webkit-box-shadow: 2px 2px 4px #D52C2C; + box-shadow: 2px 2px 4px #D52C2C; +} + +div.error { + background-color: #FCC; + border: 1px solid #FAA; + -moz-box-shadow: 2px 2px 4px #D52C2C; + -webkit-box-shadow: 2px 2px 4px #D52C2C; + box-shadow: 2px 2px 4px #D52C2C; +} + +div.caution { + background-color: #FCC; + border: 1px solid #FAA; +} + +div.attention { + background-color: #FCC; + border: 1px solid #FAA; +} + +div.important { + background-color: #EEE; + border: 1px solid #CCC; +} + +div.note { + background-color: #EEE; + border: 1px solid #CCC; +} + +div.tip { + background-color: #EEE; + border: 1px solid #CCC; +} + +div.hint { + background-color: #EEE; + border: 1px solid #CCC; +} + +div.seealso { + background-color: #EEE; + border: 1px solid #CCC; +} + +div.topic { + background-color: #EEE; +} + +p.admonition-title { + display: inline; +} + +p.admonition-title:after { + content: ":"; +} + +pre, tt, code { + font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; + font-size: 0.9em; +} + +.hll { + background-color: #FFC; + margin: 0 -12px; + padding: 0 12px; + display: block; +} + +img.screenshot { +} + +tt.descname, tt.descclassname, code.descname, code.descclassname { + font-size: 0.95em; +} + +tt.descname, code.descname { + padding-right: 0.08em; +} + +img.screenshot { + -moz-box-shadow: 2px 2px 4px #EEE; + -webkit-box-shadow: 2px 2px 4px #EEE; + box-shadow: 2px 2px 4px #EEE; +} + +table.docutils { + border: 1px solid #888; + -moz-box-shadow: 2px 2px 4px #EEE; + -webkit-box-shadow: 2px 2px 4px #EEE; + box-shadow: 2px 2px 4px #EEE; +} + +table.docutils td, table.docutils th { + border: 1px solid #888; + padding: 0.25em 0.7em; +} + +table.field-list, table.footnote { + border: none; + -moz-box-shadow: none; + -webkit-box-shadow: none; + box-shadow: none; +} + +table.footnote { + margin: 15px 0; + width: 100%; + border: 1px solid #EEE; + background: #FDFDFD; + font-size: 0.9em; +} + +table.footnote + table.footnote { + margin-top: -15px; + border-top: none; +} + +table.field-list th { + padding: 0 0.8em 0 0; +} + +table.field-list td { + padding: 0; +} + +table.field-list p { + margin-bottom: 0.8em; +} + +/* Cloned from + * https://github.com/sphinx-doc/sphinx/commit/ef60dbfce09286b20b7385333d63a60321784e68 + */ +.field-name { + -moz-hyphens: manual; + -ms-hyphens: manual; + -webkit-hyphens: manual; + hyphens: manual; +} + +table.footnote td.label { + width: .1px; + padding: 0.3em 0 0.3em 0.5em; +} + +table.footnote td { + padding: 0.3em 0.5em; +} + +dl { + margin: 0; + padding: 0; +} + +dl dd { + margin-left: 30px; +} + +blockquote { + margin: 0 0 0 30px; + padding: 0; +} + +ul, ol { + /* Matches the 30px from the narrow-screen "li > ul" selector below */ + margin: 10px 0 10px 30px; + padding: 0; +} + +pre { + background: #EEE; + padding: 7px 30px; + margin: 15px 0px; + line-height: 1.3em; +} + +div.viewcode-block:target { + background: #ffd; +} + +dl pre, blockquote pre, li pre { + margin-left: 0; + padding-left: 30px; +} + +tt, code { + background-color: #ecf0f3; + color: #222; + /* padding: 1px 2px; */ +} + +tt.xref, code.xref, a tt { + background-color: #FBFBFB; + border-bottom: 1px solid #fff; +} + +a.reference { + text-decoration: none; + border-bottom: 1px dotted #004B6B; +} + +/* Don't put an underline on images */ +a.image-reference, a.image-reference:hover { + border-bottom: none; +} + +a.reference:hover { + border-bottom: 1px solid #6D4100; +} + +a.footnote-reference { + text-decoration: none; + font-size: 0.7em; + vertical-align: top; + border-bottom: 1px dotted #004B6B; +} + +a.footnote-reference:hover { + border-bottom: 1px solid #6D4100; +} + +a:hover tt, a:hover code { + background: #EEE; +} + + +@media screen and (max-width: 870px) { + + div.sphinxsidebar { + display: none; + } + + div.document { + width: 100%; + + } + + div.documentwrapper { + margin-left: 0; + margin-top: 0; + margin-right: 0; + margin-bottom: 0; + } + + div.bodywrapper { + margin-top: 0; + margin-right: 0; + margin-bottom: 0; + margin-left: 0; + } + + ul { + margin-left: 0; + } + + li > ul { + /* Matches the 30px from the "ul, ol" selector above */ + margin-left: 30px; + } + + .document { + width: auto; + } + + .footer { + width: auto; + } + + .bodywrapper { + margin: 0; + } + + .footer { + width: auto; + } + + .github { + display: none; + } + + + +} + + + +@media screen and (max-width: 875px) { + + body { + margin: 0; + padding: 20px 30px; + } + + div.documentwrapper { + float: none; + background: #fff; + } + + div.sphinxsidebar { + display: block; + float: none; + width: 102.5%; + margin: 50px -30px -20px -30px; + padding: 10px 20px; + background: #333; + color: #FFF; + } + + div.sphinxsidebar h3, div.sphinxsidebar h4, div.sphinxsidebar p, + div.sphinxsidebar h3 a { + color: #fff; + } + + div.sphinxsidebar a { + color: #AAA; + } + + div.sphinxsidebar p.logo { + display: none; + } + + div.document { + width: 100%; + margin: 0; + } + + div.footer { + display: none; + } + + div.bodywrapper { + margin: 0; + } + + div.body { + min-height: 0; + padding: 0; + } + + .rtd_doc_footer { + display: none; + } + + .document { + width: auto; + } + + .footer { + width: auto; + } + + .footer { + width: auto; + } + + .github { + display: none; + } +} + + +/* misc. */ + +.revsys-inline { + display: none!important; +} + +/* Make nested-list/multi-paragraph items look better in Releases changelog + * pages. Without this, docutils' magical list fuckery causes inconsistent + * formatting between different release sub-lists. + */ +div#changelog > div.section > ul > li > p:only-child { + margin-bottom: 0; +} + +/* Hide fugly table cell borders in ..bibliography:: directive output */ +table.docutils.citation, table.docutils.citation td, table.docutils.citation th { + border: none; + /* Below needed in some edge cases; if not applied, bottom shadows appear */ + -moz-box-shadow: none; + -webkit-box-shadow: none; + box-shadow: none; +} + + +/* relbar */ + +.related { + line-height: 30px; + width: 100%; + font-size: 0.9rem; +} + +.related.top { + border-bottom: 1px solid #EEE; + margin-bottom: 20px; +} + +.related.bottom { + border-top: 1px solid #EEE; +} + +.related ul { + padding: 0; + margin: 0; + list-style: none; +} + +.related li { + display: inline; +} + +nav#rellinks { + float: right; +} + +nav#rellinks li+li:before { + content: "|"; +} + +nav#breadcrumbs li+li:before { + content: "\00BB"; +} + +/* Hide certain items when printing */ +@media print { + div.related { + display: none; + } +} \ No newline at end of file diff --git a/doc/html/_static/basic.css b/doc/html/_static/basic.css new file mode 100644 index 0000000000..be19270e4a --- /dev/null +++ b/doc/html/_static/basic.css @@ -0,0 +1,856 @@ +/* + * basic.css + * ~~~~~~~~~ + * + * Sphinx stylesheet -- basic theme. + * + * :copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ + +/* -- main layout ----------------------------------------------------------- */ + +div.clearer { + clear: both; +} + +div.section::after { + display: block; + content: ''; + clear: left; +} + +/* -- relbar ---------------------------------------------------------------- */ + +div.related { + width: 100%; + font-size: 90%; +} + +div.related h3 { + display: none; +} + +div.related ul { + margin: 0; + padding: 0 0 0 10px; + list-style: none; +} + +div.related li { + display: inline; +} + +div.related li.right { + float: right; + margin-right: 5px; +} + +/* -- sidebar --------------------------------------------------------------- */ + +div.sphinxsidebarwrapper { + padding: 10px 5px 0 10px; +} + +div.sphinxsidebar { + float: left; + width: 230px; + margin-left: -100%; + font-size: 90%; + word-wrap: break-word; + overflow-wrap : break-word; +} + +div.sphinxsidebar ul { + list-style: none; +} + +div.sphinxsidebar ul ul, +div.sphinxsidebar ul.want-points { + margin-left: 20px; + list-style: square; +} + +div.sphinxsidebar ul ul { + margin-top: 0; + margin-bottom: 0; +} + +div.sphinxsidebar form { + margin-top: 10px; +} + +div.sphinxsidebar input { + border: 1px solid #98dbcc; + font-family: sans-serif; + font-size: 1em; +} + +div.sphinxsidebar #searchbox form.search { + overflow: hidden; +} + +div.sphinxsidebar #searchbox input[type="text"] { + float: left; + width: 80%; + padding: 0.25em; + box-sizing: border-box; +} + +div.sphinxsidebar #searchbox input[type="submit"] { + float: left; + width: 20%; + border-left: none; + padding: 0.25em; + box-sizing: border-box; +} + + +img { + border: 0; + max-width: 100%; +} + +/* -- search page ----------------------------------------------------------- */ + +ul.search { + margin: 10px 0 0 20px; + padding: 0; +} + +ul.search li { + padding: 5px 0 5px 20px; + background-image: url(file.png); + background-repeat: no-repeat; + background-position: 0 7px; +} + +ul.search li a { + font-weight: bold; +} + +ul.search li div.context { + color: #888; + margin: 2px 0 0 30px; + text-align: left; +} + +ul.keywordmatches li.goodmatch a { + font-weight: bold; +} + +/* -- index page ------------------------------------------------------------ */ + +table.contentstable { + width: 90%; + margin-left: auto; + margin-right: auto; +} + +table.contentstable p.biglink { + line-height: 150%; +} + +a.biglink { + font-size: 1.3em; +} + +span.linkdescr { + font-style: italic; + padding-top: 5px; + font-size: 90%; +} + +/* -- general index --------------------------------------------------------- */ + +table.indextable { + width: 100%; +} + +table.indextable td { + text-align: left; + vertical-align: top; +} + +table.indextable ul { + margin-top: 0; + margin-bottom: 0; + list-style-type: none; +} + +table.indextable > tbody > tr > td > ul { + padding-left: 0em; +} + +table.indextable tr.pcap { + height: 10px; +} + +table.indextable tr.cap { + margin-top: 10px; + background-color: #f2f2f2; +} + +img.toggler { + margin-right: 3px; + margin-top: 3px; + cursor: pointer; +} + +div.modindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +div.genindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +/* -- domain module index --------------------------------------------------- */ + +table.modindextable td { + padding: 2px; + border-collapse: collapse; +} + +/* -- general body styles --------------------------------------------------- */ + +div.body { + min-width: 450px; + max-width: 800px; +} + +div.body p, div.body dd, div.body li, div.body blockquote { + -moz-hyphens: auto; + -ms-hyphens: auto; + -webkit-hyphens: auto; + hyphens: auto; +} + +a.headerlink { + visibility: hidden; +} + +a.brackets:before, +span.brackets > a:before{ + content: "["; +} + +a.brackets:after, +span.brackets > a:after { + content: "]"; +} + +h1:hover > a.headerlink, +h2:hover > a.headerlink, +h3:hover > a.headerlink, +h4:hover > a.headerlink, +h5:hover > a.headerlink, +h6:hover > a.headerlink, +dt:hover > a.headerlink, +caption:hover > a.headerlink, +p.caption:hover > a.headerlink, +div.code-block-caption:hover > a.headerlink { + visibility: visible; +} + +div.body p.caption { + text-align: inherit; +} + +div.body td { + text-align: left; +} + +.first { + margin-top: 0 !important; +} + +p.rubric { + margin-top: 30px; + font-weight: bold; +} + +img.align-left, .figure.align-left, object.align-left { + clear: left; + float: left; + margin-right: 1em; +} + +img.align-right, .figure.align-right, object.align-right { + clear: right; + float: right; + margin-left: 1em; +} + +img.align-center, .figure.align-center, object.align-center { + display: block; + margin-left: auto; + margin-right: auto; +} + +img.align-default, .figure.align-default { + display: block; + margin-left: auto; + margin-right: auto; +} + +.align-left { + text-align: left; +} + +.align-center { + text-align: center; +} + +.align-default { + text-align: center; +} + +.align-right { + text-align: right; +} + +/* -- sidebars -------------------------------------------------------------- */ + +div.sidebar { + margin: 0 0 0.5em 1em; + border: 1px solid #ddb; + padding: 7px; + background-color: #ffe; + width: 40%; + float: right; + clear: right; + overflow-x: auto; +} + +p.sidebar-title { + font-weight: bold; +} + +div.admonition, div.topic, blockquote { + clear: left; +} + +/* -- topics ---------------------------------------------------------------- */ + +div.topic { + border: 1px solid #ccc; + padding: 7px; + margin: 10px 0 10px 0; +} + +p.topic-title { + font-size: 1.1em; + font-weight: bold; + margin-top: 10px; +} + +/* -- admonitions ----------------------------------------------------------- */ + +div.admonition { + margin-top: 10px; + margin-bottom: 10px; + padding: 7px; +} + +div.admonition dt { + font-weight: bold; +} + +p.admonition-title { + margin: 0px 10px 5px 0px; + font-weight: bold; +} + +div.body p.centered { + text-align: center; + margin-top: 25px; +} + +/* -- content of sidebars/topics/admonitions -------------------------------- */ + +div.sidebar > :last-child, +div.topic > :last-child, +div.admonition > :last-child { + margin-bottom: 0; +} + +div.sidebar::after, +div.topic::after, +div.admonition::after, +blockquote::after { + display: block; + content: ''; + clear: both; +} + +/* -- tables ---------------------------------------------------------------- */ + +table.docutils { + margin-top: 10px; + margin-bottom: 10px; + border: 0; + border-collapse: collapse; +} + +table.align-center { + margin-left: auto; + margin-right: auto; +} + +table.align-default { + margin-left: auto; + margin-right: auto; +} + +table caption span.caption-number { + font-style: italic; +} + +table caption span.caption-text { +} + +table.docutils td, table.docutils th { + padding: 1px 8px 1px 5px; + border-top: 0; + border-left: 0; + border-right: 0; + border-bottom: 1px solid #aaa; +} + +table.footnote td, table.footnote th { + border: 0 !important; +} + +th { + text-align: left; + padding-right: 5px; +} + +table.citation { + border-left: solid 1px gray; + margin-left: 1px; +} + +table.citation td { + border-bottom: none; +} + +th > :first-child, +td > :first-child { + margin-top: 0px; +} + +th > :last-child, +td > :last-child { + margin-bottom: 0px; +} + +/* -- figures --------------------------------------------------------------- */ + +div.figure { + margin: 0.5em; + padding: 0.5em; +} + +div.figure p.caption { + padding: 0.3em; +} + +div.figure p.caption span.caption-number { + font-style: italic; +} + +div.figure p.caption span.caption-text { +} + +/* -- field list styles ----------------------------------------------------- */ + +table.field-list td, table.field-list th { + border: 0 !important; +} + +.field-list ul { + margin: 0; + padding-left: 1em; +} + +.field-list p { + margin: 0; +} + +.field-name { + -moz-hyphens: manual; + -ms-hyphens: manual; + -webkit-hyphens: manual; + hyphens: manual; +} + +/* -- hlist styles ---------------------------------------------------------- */ + +table.hlist { + margin: 1em 0; +} + +table.hlist td { + vertical-align: top; +} + + +/* -- other body styles ----------------------------------------------------- */ + +ol.arabic { + list-style: decimal; +} + +ol.loweralpha { + list-style: lower-alpha; +} + +ol.upperalpha { + list-style: upper-alpha; +} + +ol.lowerroman { + list-style: lower-roman; +} + +ol.upperroman { + list-style: upper-roman; +} + +:not(li) > ol > li:first-child > :first-child, +:not(li) > ul > li:first-child > :first-child { + margin-top: 0px; +} + +:not(li) > ol > li:last-child > :last-child, +:not(li) > ul > li:last-child > :last-child { + margin-bottom: 0px; +} + +ol.simple ol p, +ol.simple ul p, +ul.simple ol p, +ul.simple ul p { + margin-top: 0; +} + +ol.simple > li:not(:first-child) > p, +ul.simple > li:not(:first-child) > p { + margin-top: 0; +} + +ol.simple p, +ul.simple p { + margin-bottom: 0; +} + +dl.footnote > dt, +dl.citation > dt { + float: left; + margin-right: 0.5em; +} + +dl.footnote > dd, +dl.citation > dd { + margin-bottom: 0em; +} + +dl.footnote > dd:after, +dl.citation > dd:after { + content: ""; + clear: both; +} + +dl.field-list { + display: grid; + grid-template-columns: fit-content(30%) auto; +} + +dl.field-list > dt { + font-weight: bold; + word-break: break-word; + padding-left: 0.5em; + padding-right: 5px; +} + +dl.field-list > dt:after { + content: ":"; +} + +dl.field-list > dd { + padding-left: 0.5em; + margin-top: 0em; + margin-left: 0em; + margin-bottom: 0em; +} + +dl { + margin-bottom: 15px; +} + +dd > :first-child { + margin-top: 0px; +} + +dd ul, dd table { + margin-bottom: 10px; +} + +dd { + margin-top: 3px; + margin-bottom: 10px; + margin-left: 30px; +} + +dl > dd:last-child, +dl > dd:last-child > :last-child { + margin-bottom: 0; +} + +dt:target, span.highlighted { + background-color: #fbe54e; +} + +rect.highlighted { + fill: #fbe54e; +} + +dl.glossary dt { + font-weight: bold; + font-size: 1.1em; +} + +.optional { + font-size: 1.3em; +} + +.sig-paren { + font-size: larger; +} + +.versionmodified { + font-style: italic; +} + +.system-message { + background-color: #fda; + padding: 5px; + border: 3px solid red; +} + +.footnote:target { + background-color: #ffa; +} + +.line-block { + display: block; + margin-top: 1em; + margin-bottom: 1em; +} + +.line-block .line-block { + margin-top: 0; + margin-bottom: 0; + margin-left: 1.5em; +} + +.guilabel, .menuselection { + font-family: sans-serif; +} + +.accelerator { + text-decoration: underline; +} + +.classifier { + font-style: oblique; +} + +.classifier:before { + font-style: normal; + margin: 0.5em; + content: ":"; +} + +abbr, acronym { + border-bottom: dotted 1px; + cursor: help; +} + +/* -- code displays --------------------------------------------------------- */ + +pre { + overflow: auto; + overflow-y: hidden; /* fixes display issues on Chrome browsers */ +} + +pre, div[class*="highlight-"] { + clear: both; +} + +span.pre { + -moz-hyphens: none; + -ms-hyphens: none; + -webkit-hyphens: none; + hyphens: none; +} + +div[class*="highlight-"] { + margin: 1em 0; +} + +td.linenos pre { + border: 0; + background-color: transparent; + color: #aaa; +} + +table.highlighttable { + display: block; +} + +table.highlighttable tbody { + display: block; +} + +table.highlighttable tr { + display: flex; +} + +table.highlighttable td { + margin: 0; + padding: 0; +} + +table.highlighttable td.linenos { + padding-right: 0.5em; +} + +table.highlighttable td.code { + flex: 1; + overflow: hidden; +} + +.highlight .hll { + display: block; +} + +div.highlight pre, +table.highlighttable pre { + margin: 0; +} + +div.code-block-caption + div { + margin-top: 0; +} + +div.code-block-caption { + margin-top: 1em; + padding: 2px 5px; + font-size: small; +} + +div.code-block-caption code { + background-color: transparent; +} + +table.highlighttable td.linenos, +span.linenos, +div.doctest > div.highlight span.gp { /* gp: Generic.Prompt */ + user-select: none; +} + +div.code-block-caption span.caption-number { + padding: 0.1em 0.3em; + font-style: italic; +} + +div.code-block-caption span.caption-text { +} + +div.literal-block-wrapper { + margin: 1em 0; +} + +code.descname { + background-color: transparent; + font-weight: bold; + font-size: 1.2em; +} + +code.descclassname { + background-color: transparent; +} + +code.xref, a code { + background-color: transparent; + font-weight: bold; +} + +h1 code, h2 code, h3 code, h4 code, h5 code, h6 code { + background-color: transparent; +} + +.viewcode-link { + float: right; +} + +.viewcode-back { + float: right; + font-family: sans-serif; +} + +div.viewcode-block:target { + margin: -1px -10px; + padding: 0 10px; +} + +/* -- math display ---------------------------------------------------------- */ + +img.math { + vertical-align: middle; +} + +div.body div.math p { + text-align: center; +} + +span.eqno { + float: right; +} + +span.eqno a.headerlink { + position: absolute; + z-index: 1; +} + +div.math:hover a.headerlink { + visibility: visible; +} + +/* -- printout stylesheet --------------------------------------------------- */ + +@media print { + div.document, + div.documentwrapper, + div.bodywrapper { + margin: 0 !important; + width: 100%; + } + + div.sphinxsidebar, + div.related, + div.footer, + #top-link { + display: none; + } +} \ No newline at end of file diff --git a/doc/html/_static/custom.css b/doc/html/_static/custom.css new file mode 100644 index 0000000000..2a924f1d6a --- /dev/null +++ b/doc/html/_static/custom.css @@ -0,0 +1 @@ +/* This file intentionally left blank. */ diff --git a/doc/html/_static/doctools.js b/doc/html/_static/doctools.js new file mode 100644 index 0000000000..61ac9d266f --- /dev/null +++ b/doc/html/_static/doctools.js @@ -0,0 +1,321 @@ +/* + * doctools.js + * ~~~~~~~~~~~ + * + * Sphinx JavaScript utilities for all documentation. + * + * :copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ + +/** + * select a different prefix for underscore + */ +$u = _.noConflict(); + +/** + * make the code below compatible with browsers without + * an installed firebug like debugger +if (!window.console || !console.firebug) { + var names = ["log", "debug", "info", "warn", "error", "assert", "dir", + "dirxml", "group", "groupEnd", "time", "timeEnd", "count", "trace", + "profile", "profileEnd"]; + window.console = {}; + for (var i = 0; i < names.length; ++i) + window.console[names[i]] = function() {}; +} + */ + +/** + * small helper function to urldecode strings + * + * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent#Decoding_query_parameters_from_a_URL + */ +jQuery.urldecode = function(x) { + if (!x) { + return x + } + return decodeURIComponent(x.replace(/\+/g, ' ')); +}; + +/** + * small helper function to urlencode strings + */ +jQuery.urlencode = encodeURIComponent; + +/** + * This function returns the parsed url parameters of the + * current request. Multiple values per key are supported, + * it will always return arrays of strings for the value parts. + */ +jQuery.getQueryParameters = function(s) { + if (typeof s === 'undefined') + s = document.location.search; + var parts = s.substr(s.indexOf('?') + 1).split('&'); + var result = {}; + for (var i = 0; i < parts.length; i++) { + var tmp = parts[i].split('=', 2); + var key = jQuery.urldecode(tmp[0]); + var value = jQuery.urldecode(tmp[1]); + if (key in result) + result[key].push(value); + else + result[key] = [value]; + } + return result; +}; + +/** + * highlight a given string on a jquery object by wrapping it in + * span elements with the given class name. + */ +jQuery.fn.highlightText = function(text, className) { + function highlight(node, addItems) { + if (node.nodeType === 3) { + var val = node.nodeValue; + var pos = val.toLowerCase().indexOf(text); + if (pos >= 0 && + !jQuery(node.parentNode).hasClass(className) && + !jQuery(node.parentNode).hasClass("nohighlight")) { + var span; + var isInSVG = jQuery(node).closest("body, svg, foreignObject").is("svg"); + if (isInSVG) { + span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); + } else { + span = document.createElement("span"); + span.className = className; + } + span.appendChild(document.createTextNode(val.substr(pos, text.length))); + node.parentNode.insertBefore(span, node.parentNode.insertBefore( + document.createTextNode(val.substr(pos + text.length)), + node.nextSibling)); + node.nodeValue = val.substr(0, pos); + if (isInSVG) { + var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect"); + var bbox = node.parentElement.getBBox(); + rect.x.baseVal.value = bbox.x; + rect.y.baseVal.value = bbox.y; + rect.width.baseVal.value = bbox.width; + rect.height.baseVal.value = bbox.height; + rect.setAttribute('class', className); + addItems.push({ + "parent": node.parentNode, + "target": rect}); + } + } + } + else if (!jQuery(node).is("button, select, textarea")) { + jQuery.each(node.childNodes, function() { + highlight(this, addItems); + }); + } + } + var addItems = []; + var result = this.each(function() { + highlight(this, addItems); + }); + for (var i = 0; i < addItems.length; ++i) { + jQuery(addItems[i].parent).before(addItems[i].target); + } + return result; +}; + +/* + * backward compatibility for jQuery.browser + * This will be supported until firefox bug is fixed. + */ +if (!jQuery.browser) { + jQuery.uaMatch = function(ua) { + ua = ua.toLowerCase(); + + var match = /(chrome)[ \/]([\w.]+)/.exec(ua) || + /(webkit)[ \/]([\w.]+)/.exec(ua) || + /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) || + /(msie) ([\w.]+)/.exec(ua) || + ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) || + []; + + return { + browser: match[ 1 ] || "", + version: match[ 2 ] || "0" + }; + }; + jQuery.browser = {}; + jQuery.browser[jQuery.uaMatch(navigator.userAgent).browser] = true; +} + +/** + * Small JavaScript module for the documentation. + */ +var Documentation = { + + init : function() { + this.fixFirefoxAnchorBug(); + this.highlightSearchWords(); + this.initIndexTable(); + if (DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) { + this.initOnKeyListeners(); + } + }, + + /** + * i18n support + */ + TRANSLATIONS : {}, + PLURAL_EXPR : function(n) { return n === 1 ? 0 : 1; }, + LOCALE : 'unknown', + + // gettext and ngettext don't access this so that the functions + // can safely bound to a different name (_ = Documentation.gettext) + gettext : function(string) { + var translated = Documentation.TRANSLATIONS[string]; + if (typeof translated === 'undefined') + return string; + return (typeof translated === 'string') ? translated : translated[0]; + }, + + ngettext : function(singular, plural, n) { + var translated = Documentation.TRANSLATIONS[singular]; + if (typeof translated === 'undefined') + return (n == 1) ? singular : plural; + return translated[Documentation.PLURALEXPR(n)]; + }, + + addTranslations : function(catalog) { + for (var key in catalog.messages) + this.TRANSLATIONS[key] = catalog.messages[key]; + this.PLURAL_EXPR = new Function('n', 'return +(' + catalog.plural_expr + ')'); + this.LOCALE = catalog.locale; + }, + + /** + * add context elements like header anchor links + */ + addContextElements : function() { + $('div[id] > :header:first').each(function() { + $('\u00B6'). + attr('href', '#' + this.id). + attr('title', _('Permalink to this headline')). + appendTo(this); + }); + $('dt[id]').each(function() { + $('\u00B6'). + attr('href', '#' + this.id). + attr('title', _('Permalink to this definition')). + appendTo(this); + }); + }, + + /** + * workaround a firefox stupidity + * see: https://bugzilla.mozilla.org/show_bug.cgi?id=645075 + */ + fixFirefoxAnchorBug : function() { + if (document.location.hash && $.browser.mozilla) + window.setTimeout(function() { + document.location.href += ''; + }, 10); + }, + + /** + * highlight the search words provided in the url in the text + */ + highlightSearchWords : function() { + var params = $.getQueryParameters(); + var terms = (params.highlight) ? params.highlight[0].split(/\s+/) : []; + if (terms.length) { + var body = $('div.body'); + if (!body.length) { + body = $('body'); + } + window.setTimeout(function() { + $.each(terms, function() { + body.highlightText(this.toLowerCase(), 'highlighted'); + }); + }, 10); + $('') + .appendTo($('#searchbox')); + } + }, + + /** + * init the domain index toggle buttons + */ + initIndexTable : function() { + var togglers = $('img.toggler').click(function() { + var src = $(this).attr('src'); + var idnum = $(this).attr('id').substr(7); + $('tr.cg-' + idnum).toggle(); + if (src.substr(-9) === 'minus.png') + $(this).attr('src', src.substr(0, src.length-9) + 'plus.png'); + else + $(this).attr('src', src.substr(0, src.length-8) + 'minus.png'); + }).css('display', ''); + if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) { + togglers.click(); + } + }, + + /** + * helper function to hide the search marks again + */ + hideSearchWords : function() { + $('#searchbox .highlight-link').fadeOut(300); + $('span.highlighted').removeClass('highlighted'); + }, + + /** + * make the url absolute + */ + makeURL : function(relativeURL) { + return DOCUMENTATION_OPTIONS.URL_ROOT + '/' + relativeURL; + }, + + /** + * get the current relative url + */ + getCurrentURL : function() { + var path = document.location.pathname; + var parts = path.split(/\//); + $.each(DOCUMENTATION_OPTIONS.URL_ROOT.split(/\//), function() { + if (this === '..') + parts.pop(); + }); + var url = parts.join('/'); + return path.substring(url.lastIndexOf('/') + 1, path.length - 1); + }, + + initOnKeyListeners: function() { + $(document).keydown(function(event) { + var activeElementType = document.activeElement.tagName; + // don't navigate when in search box, textarea, dropdown or button + if (activeElementType !== 'TEXTAREA' && activeElementType !== 'INPUT' && activeElementType !== 'SELECT' + && activeElementType !== 'BUTTON' && !event.altKey && !event.ctrlKey && !event.metaKey + && !event.shiftKey) { + switch (event.keyCode) { + case 37: // left + var prevHref = $('link[rel="prev"]').prop('href'); + if (prevHref) { + window.location.href = prevHref; + return false; + } + case 39: // right + var nextHref = $('link[rel="next"]').prop('href'); + if (nextHref) { + window.location.href = nextHref; + return false; + } + } + } + }); + } +}; + +// quick alias for translations +_ = Documentation.gettext; + +$(document).ready(function() { + Documentation.init(); +}); diff --git a/doc/html/_static/documentation_options.js b/doc/html/_static/documentation_options.js new file mode 100644 index 0000000000..8839ac8c2c --- /dev/null +++ b/doc/html/_static/documentation_options.js @@ -0,0 +1,12 @@ +var DOCUMENTATION_OPTIONS = { + URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'), + VERSION: '0.1', + LANGUAGE: 'None', + COLLAPSE_INDEX: false, + BUILDER: 'html', + FILE_SUFFIX: '.html', + LINK_SUFFIX: '.html', + HAS_SOURCE: true, + SOURCELINK_SUFFIX: '.txt', + NAVIGATION_WITH_KEYS: false +}; \ No newline at end of file diff --git a/doc/html/_static/file.png b/doc/html/_static/file.png new file mode 100644 index 0000000000..a858a410e4 Binary files /dev/null and b/doc/html/_static/file.png differ diff --git a/doc/html/_static/jquery-3.5.1.js b/doc/html/_static/jquery-3.5.1.js new file mode 100644 index 0000000000..50937333b9 --- /dev/null +++ b/doc/html/_static/jquery-3.5.1.js @@ -0,0 +1,10872 @@ +/*! + * jQuery JavaScript Library v3.5.1 + * https://jquery.com/ + * + * Includes Sizzle.js + * https://sizzlejs.com/ + * + * Copyright JS Foundation and other contributors + * Released under the MIT license + * https://jquery.org/license + * + * Date: 2020-05-04T22:49Z + */ +( function( global, factory ) { + + "use strict"; + + if ( typeof module === "object" && typeof module.exports === "object" ) { + + // For CommonJS and CommonJS-like environments where a proper `window` + // is present, execute the factory and get jQuery. + // For environments that do not have a `window` with a `document` + // (such as Node.js), expose a factory as module.exports. + // This accentuates the need for the creation of a real `window`. + // e.g. var jQuery = require("jquery")(window); + // See ticket #14549 for more info. + module.exports = global.document ? + factory( global, true ) : + function( w ) { + if ( !w.document ) { + throw new Error( "jQuery requires a window with a document" ); + } + return factory( w ); + }; + } else { + factory( global ); + } + +// Pass this if window is not defined yet +} )( typeof window !== "undefined" ? window : this, function( window, noGlobal ) { + +// Edge <= 12 - 13+, Firefox <=18 - 45+, IE 10 - 11, Safari 5.1 - 9+, iOS 6 - 9.1 +// throw exceptions when non-strict code (e.g., ASP.NET 4.5) accesses strict mode +// arguments.callee.caller (trac-13335). But as of jQuery 3.0 (2016), strict mode should be common +// enough that all such attempts are guarded in a try block. +"use strict"; + +var arr = []; + +var getProto = Object.getPrototypeOf; + +var slice = arr.slice; + +var flat = arr.flat ? function( array ) { + return arr.flat.call( array ); +} : function( array ) { + return arr.concat.apply( [], array ); +}; + + +var push = arr.push; + +var indexOf = arr.indexOf; + +var class2type = {}; + +var toString = class2type.toString; + +var hasOwn = class2type.hasOwnProperty; + +var fnToString = hasOwn.toString; + +var ObjectFunctionString = fnToString.call( Object ); + +var support = {}; + +var isFunction = function isFunction( obj ) { + + // Support: Chrome <=57, Firefox <=52 + // In some browsers, typeof returns "function" for HTML elements + // (i.e., `typeof document.createElement( "object" ) === "function"`). + // We don't want to classify *any* DOM node as a function. + return typeof obj === "function" && typeof obj.nodeType !== "number"; + }; + + +var isWindow = function isWindow( obj ) { + return obj != null && obj === obj.window; + }; + + +var document = window.document; + + + + var preservedScriptAttributes = { + type: true, + src: true, + nonce: true, + noModule: true + }; + + function DOMEval( code, node, doc ) { + doc = doc || document; + + var i, val, + script = doc.createElement( "script" ); + + script.text = code; + if ( node ) { + for ( i in preservedScriptAttributes ) { + + // Support: Firefox 64+, Edge 18+ + // Some browsers don't support the "nonce" property on scripts. + // On the other hand, just using `getAttribute` is not enough as + // the `nonce` attribute is reset to an empty string whenever it + // becomes browsing-context connected. + // See https://github.com/whatwg/html/issues/2369 + // See https://html.spec.whatwg.org/#nonce-attributes + // The `node.getAttribute` check was added for the sake of + // `jQuery.globalEval` so that it can fake a nonce-containing node + // via an object. + val = node[ i ] || node.getAttribute && node.getAttribute( i ); + if ( val ) { + script.setAttribute( i, val ); + } + } + } + doc.head.appendChild( script ).parentNode.removeChild( script ); + } + + +function toType( obj ) { + if ( obj == null ) { + return obj + ""; + } + + // Support: Android <=2.3 only (functionish RegExp) + return typeof obj === "object" || typeof obj === "function" ? + class2type[ toString.call( obj ) ] || "object" : + typeof obj; +} +/* global Symbol */ +// Defining this global in .eslintrc.json would create a danger of using the global +// unguarded in another place, it seems safer to define global only for this module + + + +var + version = "3.5.1", + + // Define a local copy of jQuery + jQuery = function( selector, context ) { + + // The jQuery object is actually just the init constructor 'enhanced' + // Need init if jQuery is called (just allow error to be thrown if not included) + return new jQuery.fn.init( selector, context ); + }; + +jQuery.fn = jQuery.prototype = { + + // The current version of jQuery being used + jquery: version, + + constructor: jQuery, + + // The default length of a jQuery object is 0 + length: 0, + + toArray: function() { + return slice.call( this ); + }, + + // Get the Nth element in the matched element set OR + // Get the whole matched element set as a clean array + get: function( num ) { + + // Return all the elements in a clean array + if ( num == null ) { + return slice.call( this ); + } + + // Return just the one element from the set + return num < 0 ? this[ num + this.length ] : this[ num ]; + }, + + // Take an array of elements and push it onto the stack + // (returning the new matched element set) + pushStack: function( elems ) { + + // Build a new jQuery matched element set + var ret = jQuery.merge( this.constructor(), elems ); + + // Add the old object onto the stack (as a reference) + ret.prevObject = this; + + // Return the newly-formed element set + return ret; + }, + + // Execute a callback for every element in the matched set. + each: function( callback ) { + return jQuery.each( this, callback ); + }, + + map: function( callback ) { + return this.pushStack( jQuery.map( this, function( elem, i ) { + return callback.call( elem, i, elem ); + } ) ); + }, + + slice: function() { + return this.pushStack( slice.apply( this, arguments ) ); + }, + + first: function() { + return this.eq( 0 ); + }, + + last: function() { + return this.eq( -1 ); + }, + + even: function() { + return this.pushStack( jQuery.grep( this, function( _elem, i ) { + return ( i + 1 ) % 2; + } ) ); + }, + + odd: function() { + return this.pushStack( jQuery.grep( this, function( _elem, i ) { + return i % 2; + } ) ); + }, + + eq: function( i ) { + var len = this.length, + j = +i + ( i < 0 ? len : 0 ); + return this.pushStack( j >= 0 && j < len ? [ this[ j ] ] : [] ); + }, + + end: function() { + return this.prevObject || this.constructor(); + }, + + // For internal use only. + // Behaves like an Array's method, not like a jQuery method. + push: push, + sort: arr.sort, + splice: arr.splice +}; + +jQuery.extend = jQuery.fn.extend = function() { + var options, name, src, copy, copyIsArray, clone, + target = arguments[ 0 ] || {}, + i = 1, + length = arguments.length, + deep = false; + + // Handle a deep copy situation + if ( typeof target === "boolean" ) { + deep = target; + + // Skip the boolean and the target + target = arguments[ i ] || {}; + i++; + } + + // Handle case when target is a string or something (possible in deep copy) + if ( typeof target !== "object" && !isFunction( target ) ) { + target = {}; + } + + // Extend jQuery itself if only one argument is passed + if ( i === length ) { + target = this; + i--; + } + + for ( ; i < length; i++ ) { + + // Only deal with non-null/undefined values + if ( ( options = arguments[ i ] ) != null ) { + + // Extend the base object + for ( name in options ) { + copy = options[ name ]; + + // Prevent Object.prototype pollution + // Prevent never-ending loop + if ( name === "__proto__" || target === copy ) { + continue; + } + + // Recurse if we're merging plain objects or arrays + if ( deep && copy && ( jQuery.isPlainObject( copy ) || + ( copyIsArray = Array.isArray( copy ) ) ) ) { + src = target[ name ]; + + // Ensure proper type for the source value + if ( copyIsArray && !Array.isArray( src ) ) { + clone = []; + } else if ( !copyIsArray && !jQuery.isPlainObject( src ) ) { + clone = {}; + } else { + clone = src; + } + copyIsArray = false; + + // Never move original objects, clone them + target[ name ] = jQuery.extend( deep, clone, copy ); + + // Don't bring in undefined values + } else if ( copy !== undefined ) { + target[ name ] = copy; + } + } + } + } + + // Return the modified object + return target; +}; + +jQuery.extend( { + + // Unique for each copy of jQuery on the page + expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ), + + // Assume jQuery is ready without the ready module + isReady: true, + + error: function( msg ) { + throw new Error( msg ); + }, + + noop: function() {}, + + isPlainObject: function( obj ) { + var proto, Ctor; + + // Detect obvious negatives + // Use toString instead of jQuery.type to catch host objects + if ( !obj || toString.call( obj ) !== "[object Object]" ) { + return false; + } + + proto = getProto( obj ); + + // Objects with no prototype (e.g., `Object.create( null )`) are plain + if ( !proto ) { + return true; + } + + // Objects with prototype are plain iff they were constructed by a global Object function + Ctor = hasOwn.call( proto, "constructor" ) && proto.constructor; + return typeof Ctor === "function" && fnToString.call( Ctor ) === ObjectFunctionString; + }, + + isEmptyObject: function( obj ) { + var name; + + for ( name in obj ) { + return false; + } + return true; + }, + + // Evaluates a script in a provided context; falls back to the global one + // if not specified. + globalEval: function( code, options, doc ) { + DOMEval( code, { nonce: options && options.nonce }, doc ); + }, + + each: function( obj, callback ) { + var length, i = 0; + + if ( isArrayLike( obj ) ) { + length = obj.length; + for ( ; i < length; i++ ) { + if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { + break; + } + } + } else { + for ( i in obj ) { + if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { + break; + } + } + } + + return obj; + }, + + // results is for internal usage only + makeArray: function( arr, results ) { + var ret = results || []; + + if ( arr != null ) { + if ( isArrayLike( Object( arr ) ) ) { + jQuery.merge( ret, + typeof arr === "string" ? + [ arr ] : arr + ); + } else { + push.call( ret, arr ); + } + } + + return ret; + }, + + inArray: function( elem, arr, i ) { + return arr == null ? -1 : indexOf.call( arr, elem, i ); + }, + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + merge: function( first, second ) { + var len = +second.length, + j = 0, + i = first.length; + + for ( ; j < len; j++ ) { + first[ i++ ] = second[ j ]; + } + + first.length = i; + + return first; + }, + + grep: function( elems, callback, invert ) { + var callbackInverse, + matches = [], + i = 0, + length = elems.length, + callbackExpect = !invert; + + // Go through the array, only saving the items + // that pass the validator function + for ( ; i < length; i++ ) { + callbackInverse = !callback( elems[ i ], i ); + if ( callbackInverse !== callbackExpect ) { + matches.push( elems[ i ] ); + } + } + + return matches; + }, + + // arg is for internal usage only + map: function( elems, callback, arg ) { + var length, value, + i = 0, + ret = []; + + // Go through the array, translating each of the items to their new values + if ( isArrayLike( elems ) ) { + length = elems.length; + for ( ; i < length; i++ ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret.push( value ); + } + } + + // Go through every key on the object, + } else { + for ( i in elems ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret.push( value ); + } + } + } + + // Flatten any nested arrays + return flat( ret ); + }, + + // A global GUID counter for objects + guid: 1, + + // jQuery.support is not used in Core but other projects attach their + // properties to it so it needs to exist. + support: support +} ); + +if ( typeof Symbol === "function" ) { + jQuery.fn[ Symbol.iterator ] = arr[ Symbol.iterator ]; +} + +// Populate the class2type map +jQuery.each( "Boolean Number String Function Array Date RegExp Object Error Symbol".split( " " ), +function( _i, name ) { + class2type[ "[object " + name + "]" ] = name.toLowerCase(); +} ); + +function isArrayLike( obj ) { + + // Support: real iOS 8.2 only (not reproducible in simulator) + // `in` check used to prevent JIT error (gh-2145) + // hasOwn isn't used here due to false negatives + // regarding Nodelist length in IE + var length = !!obj && "length" in obj && obj.length, + type = toType( obj ); + + if ( isFunction( obj ) || isWindow( obj ) ) { + return false; + } + + return type === "array" || length === 0 || + typeof length === "number" && length > 0 && ( length - 1 ) in obj; +} +var Sizzle = +/*! + * Sizzle CSS Selector Engine v2.3.5 + * https://sizzlejs.com/ + * + * Copyright JS Foundation and other contributors + * Released under the MIT license + * https://js.foundation/ + * + * Date: 2020-03-14 + */ +( function( window ) { +var i, + support, + Expr, + getText, + isXML, + tokenize, + compile, + select, + outermostContext, + sortInput, + hasDuplicate, + + // Local document vars + setDocument, + document, + docElem, + documentIsHTML, + rbuggyQSA, + rbuggyMatches, + matches, + contains, + + // Instance-specific data + expando = "sizzle" + 1 * new Date(), + preferredDoc = window.document, + dirruns = 0, + done = 0, + classCache = createCache(), + tokenCache = createCache(), + compilerCache = createCache(), + nonnativeSelectorCache = createCache(), + sortOrder = function( a, b ) { + if ( a === b ) { + hasDuplicate = true; + } + return 0; + }, + + // Instance methods + hasOwn = ( {} ).hasOwnProperty, + arr = [], + pop = arr.pop, + pushNative = arr.push, + push = arr.push, + slice = arr.slice, + + // Use a stripped-down indexOf as it's faster than native + // https://jsperf.com/thor-indexof-vs-for/5 + indexOf = function( list, elem ) { + var i = 0, + len = list.length; + for ( ; i < len; i++ ) { + if ( list[ i ] === elem ) { + return i; + } + } + return -1; + }, + + booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|" + + "ismap|loop|multiple|open|readonly|required|scoped", + + // Regular expressions + + // http://www.w3.org/TR/css3-selectors/#whitespace + whitespace = "[\\x20\\t\\r\\n\\f]", + + // https://www.w3.org/TR/css-syntax-3/#ident-token-diagram + identifier = "(?:\\\\[\\da-fA-F]{1,6}" + whitespace + + "?|\\\\[^\\r\\n\\f]|[\\w-]|[^\0-\\x7f])+", + + // Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors + attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace + + + // Operator (capture 2) + "*([*^$|!~]?=)" + whitespace + + + // "Attribute values must be CSS identifiers [capture 5] + // or strings [capture 3 or capture 4]" + "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + + whitespace + "*\\]", + + pseudos = ":(" + identifier + ")(?:\\((" + + + // To reduce the number of selectors needing tokenize in the preFilter, prefer arguments: + // 1. quoted (capture 3; capture 4 or capture 5) + "('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" + + + // 2. simple (capture 6) + "((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" + + + // 3. anything else (capture 2) + ".*" + + ")\\)|)", + + // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter + rwhitespace = new RegExp( whitespace + "+", "g" ), + rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + + whitespace + "+$", "g" ), + + rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), + rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + + "*" ), + rdescend = new RegExp( whitespace + "|>" ), + + rpseudo = new RegExp( pseudos ), + ridentifier = new RegExp( "^" + identifier + "$" ), + + matchExpr = { + "ID": new RegExp( "^#(" + identifier + ")" ), + "CLASS": new RegExp( "^\\.(" + identifier + ")" ), + "TAG": new RegExp( "^(" + identifier + "|[*])" ), + "ATTR": new RegExp( "^" + attributes ), + "PSEUDO": new RegExp( "^" + pseudos ), + "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + + whitespace + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + + whitespace + "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), + "bool": new RegExp( "^(?:" + booleans + ")$", "i" ), + + // For use in libraries implementing .is() + // We use this for POS matching in `select` + "needsContext": new RegExp( "^" + whitespace + + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + whitespace + + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" ) + }, + + rhtml = /HTML$/i, + rinputs = /^(?:input|select|textarea|button)$/i, + rheader = /^h\d$/i, + + rnative = /^[^{]+\{\s*\[native \w/, + + // Easily-parseable/retrievable ID or TAG or CLASS selectors + rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, + + rsibling = /[+~]/, + + // CSS escapes + // http://www.w3.org/TR/CSS21/syndata.html#escaped-characters + runescape = new RegExp( "\\\\[\\da-fA-F]{1,6}" + whitespace + "?|\\\\([^\\r\\n\\f])", "g" ), + funescape = function( escape, nonHex ) { + var high = "0x" + escape.slice( 1 ) - 0x10000; + + return nonHex ? + + // Strip the backslash prefix from a non-hex escape sequence + nonHex : + + // Replace a hexadecimal escape sequence with the encoded Unicode code point + // Support: IE <=11+ + // For values outside the Basic Multilingual Plane (BMP), manually construct a + // surrogate pair + high < 0 ? + String.fromCharCode( high + 0x10000 ) : + String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); + }, + + // CSS string/identifier serialization + // https://drafts.csswg.org/cssom/#common-serializing-idioms + rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g, + fcssescape = function( ch, asCodePoint ) { + if ( asCodePoint ) { + + // U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER + if ( ch === "\0" ) { + return "\uFFFD"; + } + + // Control characters and (dependent upon position) numbers get escaped as code points + return ch.slice( 0, -1 ) + "\\" + + ch.charCodeAt( ch.length - 1 ).toString( 16 ) + " "; + } + + // Other potentially-special ASCII characters get backslash-escaped + return "\\" + ch; + }, + + // Used for iframes + // See setDocument() + // Removing the function wrapper causes a "Permission Denied" + // error in IE + unloadHandler = function() { + setDocument(); + }, + + inDisabledFieldset = addCombinator( + function( elem ) { + return elem.disabled === true && elem.nodeName.toLowerCase() === "fieldset"; + }, + { dir: "parentNode", next: "legend" } + ); + +// Optimize for push.apply( _, NodeList ) +try { + push.apply( + ( arr = slice.call( preferredDoc.childNodes ) ), + preferredDoc.childNodes + ); + + // Support: Android<4.0 + // Detect silently failing push.apply + // eslint-disable-next-line no-unused-expressions + arr[ preferredDoc.childNodes.length ].nodeType; +} catch ( e ) { + push = { apply: arr.length ? + + // Leverage slice if possible + function( target, els ) { + pushNative.apply( target, slice.call( els ) ); + } : + + // Support: IE<9 + // Otherwise append directly + function( target, els ) { + var j = target.length, + i = 0; + + // Can't trust NodeList.length + while ( ( target[ j++ ] = els[ i++ ] ) ) {} + target.length = j - 1; + } + }; +} + +function Sizzle( selector, context, results, seed ) { + var m, i, elem, nid, match, groups, newSelector, + newContext = context && context.ownerDocument, + + // nodeType defaults to 9, since context defaults to document + nodeType = context ? context.nodeType : 9; + + results = results || []; + + // Return early from calls with invalid selector or context + if ( typeof selector !== "string" || !selector || + nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) { + + return results; + } + + // Try to shortcut find operations (as opposed to filters) in HTML documents + if ( !seed ) { + setDocument( context ); + context = context || document; + + if ( documentIsHTML ) { + + // If the selector is sufficiently simple, try using a "get*By*" DOM method + // (excepting DocumentFragment context, where the methods don't exist) + if ( nodeType !== 11 && ( match = rquickExpr.exec( selector ) ) ) { + + // ID selector + if ( ( m = match[ 1 ] ) ) { + + // Document context + if ( nodeType === 9 ) { + if ( ( elem = context.getElementById( m ) ) ) { + + // Support: IE, Opera, Webkit + // TODO: identify versions + // getElementById can match elements by name instead of ID + if ( elem.id === m ) { + results.push( elem ); + return results; + } + } else { + return results; + } + + // Element context + } else { + + // Support: IE, Opera, Webkit + // TODO: identify versions + // getElementById can match elements by name instead of ID + if ( newContext && ( elem = newContext.getElementById( m ) ) && + contains( context, elem ) && + elem.id === m ) { + + results.push( elem ); + return results; + } + } + + // Type selector + } else if ( match[ 2 ] ) { + push.apply( results, context.getElementsByTagName( selector ) ); + return results; + + // Class selector + } else if ( ( m = match[ 3 ] ) && support.getElementsByClassName && + context.getElementsByClassName ) { + + push.apply( results, context.getElementsByClassName( m ) ); + return results; + } + } + + // Take advantage of querySelectorAll + if ( support.qsa && + !nonnativeSelectorCache[ selector + " " ] && + ( !rbuggyQSA || !rbuggyQSA.test( selector ) ) && + + // Support: IE 8 only + // Exclude object elements + ( nodeType !== 1 || context.nodeName.toLowerCase() !== "object" ) ) { + + newSelector = selector; + newContext = context; + + // qSA considers elements outside a scoping root when evaluating child or + // descendant combinators, which is not what we want. + // In such cases, we work around the behavior by prefixing every selector in the + // list with an ID selector referencing the scope context. + // The technique has to be used as well when a leading combinator is used + // as such selectors are not recognized by querySelectorAll. + // Thanks to Andrew Dupont for this technique. + if ( nodeType === 1 && + ( rdescend.test( selector ) || rcombinators.test( selector ) ) ) { + + // Expand context for sibling selectors + newContext = rsibling.test( selector ) && testContext( context.parentNode ) || + context; + + // We can use :scope instead of the ID hack if the browser + // supports it & if we're not changing the context. + if ( newContext !== context || !support.scope ) { + + // Capture the context ID, setting it first if necessary + if ( ( nid = context.getAttribute( "id" ) ) ) { + nid = nid.replace( rcssescape, fcssescape ); + } else { + context.setAttribute( "id", ( nid = expando ) ); + } + } + + // Prefix every selector in the list + groups = tokenize( selector ); + i = groups.length; + while ( i-- ) { + groups[ i ] = ( nid ? "#" + nid : ":scope" ) + " " + + toSelector( groups[ i ] ); + } + newSelector = groups.join( "," ); + } + + try { + push.apply( results, + newContext.querySelectorAll( newSelector ) + ); + return results; + } catch ( qsaError ) { + nonnativeSelectorCache( selector, true ); + } finally { + if ( nid === expando ) { + context.removeAttribute( "id" ); + } + } + } + } + } + + // All others + return select( selector.replace( rtrim, "$1" ), context, results, seed ); +} + +/** + * Create key-value caches of limited size + * @returns {function(string, object)} Returns the Object data after storing it on itself with + * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength) + * deleting the oldest entry + */ +function createCache() { + var keys = []; + + function cache( key, value ) { + + // Use (key + " ") to avoid collision with native prototype properties (see Issue #157) + if ( keys.push( key + " " ) > Expr.cacheLength ) { + + // Only keep the most recent entries + delete cache[ keys.shift() ]; + } + return ( cache[ key + " " ] = value ); + } + return cache; +} + +/** + * Mark a function for special use by Sizzle + * @param {Function} fn The function to mark + */ +function markFunction( fn ) { + fn[ expando ] = true; + return fn; +} + +/** + * Support testing using an element + * @param {Function} fn Passed the created element and returns a boolean result + */ +function assert( fn ) { + var el = document.createElement( "fieldset" ); + + try { + return !!fn( el ); + } catch ( e ) { + return false; + } finally { + + // Remove from its parent by default + if ( el.parentNode ) { + el.parentNode.removeChild( el ); + } + + // release memory in IE + el = null; + } +} + +/** + * Adds the same handler for all of the specified attrs + * @param {String} attrs Pipe-separated list of attributes + * @param {Function} handler The method that will be applied + */ +function addHandle( attrs, handler ) { + var arr = attrs.split( "|" ), + i = arr.length; + + while ( i-- ) { + Expr.attrHandle[ arr[ i ] ] = handler; + } +} + +/** + * Checks document order of two siblings + * @param {Element} a + * @param {Element} b + * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b + */ +function siblingCheck( a, b ) { + var cur = b && a, + diff = cur && a.nodeType === 1 && b.nodeType === 1 && + a.sourceIndex - b.sourceIndex; + + // Use IE sourceIndex if available on both nodes + if ( diff ) { + return diff; + } + + // Check if b follows a + if ( cur ) { + while ( ( cur = cur.nextSibling ) ) { + if ( cur === b ) { + return -1; + } + } + } + + return a ? 1 : -1; +} + +/** + * Returns a function to use in pseudos for input types + * @param {String} type + */ +function createInputPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for buttons + * @param {String} type + */ +function createButtonPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return ( name === "input" || name === "button" ) && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for :enabled/:disabled + * @param {Boolean} disabled true for :disabled; false for :enabled + */ +function createDisabledPseudo( disabled ) { + + // Known :disabled false positives: fieldset[disabled] > legend:nth-of-type(n+2) :can-disable + return function( elem ) { + + // Only certain elements can match :enabled or :disabled + // https://html.spec.whatwg.org/multipage/scripting.html#selector-enabled + // https://html.spec.whatwg.org/multipage/scripting.html#selector-disabled + if ( "form" in elem ) { + + // Check for inherited disabledness on relevant non-disabled elements: + // * listed form-associated elements in a disabled fieldset + // https://html.spec.whatwg.org/multipage/forms.html#category-listed + // https://html.spec.whatwg.org/multipage/forms.html#concept-fe-disabled + // * option elements in a disabled optgroup + // https://html.spec.whatwg.org/multipage/forms.html#concept-option-disabled + // All such elements have a "form" property. + if ( elem.parentNode && elem.disabled === false ) { + + // Option elements defer to a parent optgroup if present + if ( "label" in elem ) { + if ( "label" in elem.parentNode ) { + return elem.parentNode.disabled === disabled; + } else { + return elem.disabled === disabled; + } + } + + // Support: IE 6 - 11 + // Use the isDisabled shortcut property to check for disabled fieldset ancestors + return elem.isDisabled === disabled || + + // Where there is no isDisabled, check manually + /* jshint -W018 */ + elem.isDisabled !== !disabled && + inDisabledFieldset( elem ) === disabled; + } + + return elem.disabled === disabled; + + // Try to winnow out elements that can't be disabled before trusting the disabled property. + // Some victims get caught in our net (label, legend, menu, track), but it shouldn't + // even exist on them, let alone have a boolean value. + } else if ( "label" in elem ) { + return elem.disabled === disabled; + } + + // Remaining elements are neither :enabled nor :disabled + return false; + }; +} + +/** + * Returns a function to use in pseudos for positionals + * @param {Function} fn + */ +function createPositionalPseudo( fn ) { + return markFunction( function( argument ) { + argument = +argument; + return markFunction( function( seed, matches ) { + var j, + matchIndexes = fn( [], seed.length, argument ), + i = matchIndexes.length; + + // Match elements found at the specified indexes + while ( i-- ) { + if ( seed[ ( j = matchIndexes[ i ] ) ] ) { + seed[ j ] = !( matches[ j ] = seed[ j ] ); + } + } + } ); + } ); +} + +/** + * Checks a node for validity as a Sizzle context + * @param {Element|Object=} context + * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value + */ +function testContext( context ) { + return context && typeof context.getElementsByTagName !== "undefined" && context; +} + +// Expose support vars for convenience +support = Sizzle.support = {}; + +/** + * Detects XML nodes + * @param {Element|Object} elem An element or a document + * @returns {Boolean} True iff elem is a non-HTML XML node + */ +isXML = Sizzle.isXML = function( elem ) { + var namespace = elem.namespaceURI, + docElem = ( elem.ownerDocument || elem ).documentElement; + + // Support: IE <=8 + // Assume HTML when documentElement doesn't yet exist, such as inside loading iframes + // https://bugs.jquery.com/ticket/4833 + return !rhtml.test( namespace || docElem && docElem.nodeName || "HTML" ); +}; + +/** + * Sets document-related variables once based on the current document + * @param {Element|Object} [doc] An element or document object to use to set the document + * @returns {Object} Returns the current document + */ +setDocument = Sizzle.setDocument = function( node ) { + var hasCompare, subWindow, + doc = node ? node.ownerDocument || node : preferredDoc; + + // Return early if doc is invalid or already selected + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( doc == document || doc.nodeType !== 9 || !doc.documentElement ) { + return document; + } + + // Update global variables + document = doc; + docElem = document.documentElement; + documentIsHTML = !isXML( document ); + + // Support: IE 9 - 11+, Edge 12 - 18+ + // Accessing iframe documents after unload throws "permission denied" errors (jQuery #13936) + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( preferredDoc != document && + ( subWindow = document.defaultView ) && subWindow.top !== subWindow ) { + + // Support: IE 11, Edge + if ( subWindow.addEventListener ) { + subWindow.addEventListener( "unload", unloadHandler, false ); + + // Support: IE 9 - 10 only + } else if ( subWindow.attachEvent ) { + subWindow.attachEvent( "onunload", unloadHandler ); + } + } + + // Support: IE 8 - 11+, Edge 12 - 18+, Chrome <=16 - 25 only, Firefox <=3.6 - 31 only, + // Safari 4 - 5 only, Opera <=11.6 - 12.x only + // IE/Edge & older browsers don't support the :scope pseudo-class. + // Support: Safari 6.0 only + // Safari 6.0 supports :scope but it's an alias of :root there. + support.scope = assert( function( el ) { + docElem.appendChild( el ).appendChild( document.createElement( "div" ) ); + return typeof el.querySelectorAll !== "undefined" && + !el.querySelectorAll( ":scope fieldset div" ).length; + } ); + + /* Attributes + ---------------------------------------------------------------------- */ + + // Support: IE<8 + // Verify that getAttribute really returns attributes and not properties + // (excepting IE8 booleans) + support.attributes = assert( function( el ) { + el.className = "i"; + return !el.getAttribute( "className" ); + } ); + + /* getElement(s)By* + ---------------------------------------------------------------------- */ + + // Check if getElementsByTagName("*") returns only elements + support.getElementsByTagName = assert( function( el ) { + el.appendChild( document.createComment( "" ) ); + return !el.getElementsByTagName( "*" ).length; + } ); + + // Support: IE<9 + support.getElementsByClassName = rnative.test( document.getElementsByClassName ); + + // Support: IE<10 + // Check if getElementById returns elements by name + // The broken getElementById methods don't pick up programmatically-set names, + // so use a roundabout getElementsByName test + support.getById = assert( function( el ) { + docElem.appendChild( el ).id = expando; + return !document.getElementsByName || !document.getElementsByName( expando ).length; + } ); + + // ID filter and find + if ( support.getById ) { + Expr.filter[ "ID" ] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + return elem.getAttribute( "id" ) === attrId; + }; + }; + Expr.find[ "ID" ] = function( id, context ) { + if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { + var elem = context.getElementById( id ); + return elem ? [ elem ] : []; + } + }; + } else { + Expr.filter[ "ID" ] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + var node = typeof elem.getAttributeNode !== "undefined" && + elem.getAttributeNode( "id" ); + return node && node.value === attrId; + }; + }; + + // Support: IE 6 - 7 only + // getElementById is not reliable as a find shortcut + Expr.find[ "ID" ] = function( id, context ) { + if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { + var node, i, elems, + elem = context.getElementById( id ); + + if ( elem ) { + + // Verify the id attribute + node = elem.getAttributeNode( "id" ); + if ( node && node.value === id ) { + return [ elem ]; + } + + // Fall back on getElementsByName + elems = context.getElementsByName( id ); + i = 0; + while ( ( elem = elems[ i++ ] ) ) { + node = elem.getAttributeNode( "id" ); + if ( node && node.value === id ) { + return [ elem ]; + } + } + } + + return []; + } + }; + } + + // Tag + Expr.find[ "TAG" ] = support.getElementsByTagName ? + function( tag, context ) { + if ( typeof context.getElementsByTagName !== "undefined" ) { + return context.getElementsByTagName( tag ); + + // DocumentFragment nodes don't have gEBTN + } else if ( support.qsa ) { + return context.querySelectorAll( tag ); + } + } : + + function( tag, context ) { + var elem, + tmp = [], + i = 0, + + // By happy coincidence, a (broken) gEBTN appears on DocumentFragment nodes too + results = context.getElementsByTagName( tag ); + + // Filter out possible comments + if ( tag === "*" ) { + while ( ( elem = results[ i++ ] ) ) { + if ( elem.nodeType === 1 ) { + tmp.push( elem ); + } + } + + return tmp; + } + return results; + }; + + // Class + Expr.find[ "CLASS" ] = support.getElementsByClassName && function( className, context ) { + if ( typeof context.getElementsByClassName !== "undefined" && documentIsHTML ) { + return context.getElementsByClassName( className ); + } + }; + + /* QSA/matchesSelector + ---------------------------------------------------------------------- */ + + // QSA and matchesSelector support + + // matchesSelector(:active) reports false when true (IE9/Opera 11.5) + rbuggyMatches = []; + + // qSa(:focus) reports false when true (Chrome 21) + // We allow this because of a bug in IE8/9 that throws an error + // whenever `document.activeElement` is accessed on an iframe + // So, we allow :focus to pass through QSA all the time to avoid the IE error + // See https://bugs.jquery.com/ticket/13378 + rbuggyQSA = []; + + if ( ( support.qsa = rnative.test( document.querySelectorAll ) ) ) { + + // Build QSA regex + // Regex strategy adopted from Diego Perini + assert( function( el ) { + + var input; + + // Select is set to empty string on purpose + // This is to test IE's treatment of not explicitly + // setting a boolean content attribute, + // since its presence should be enough + // https://bugs.jquery.com/ticket/12359 + docElem.appendChild( el ).innerHTML = "" + + ""; + + // Support: IE8, Opera 11-12.16 + // Nothing should be selected when empty strings follow ^= or $= or *= + // The test attribute must be unknown in Opera but "safe" for WinRT + // https://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section + if ( el.querySelectorAll( "[msallowcapture^='']" ).length ) { + rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" ); + } + + // Support: IE8 + // Boolean attributes and "value" are not treated correctly + if ( !el.querySelectorAll( "[selected]" ).length ) { + rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" ); + } + + // Support: Chrome<29, Android<4.4, Safari<7.0+, iOS<7.0+, PhantomJS<1.9.8+ + if ( !el.querySelectorAll( "[id~=" + expando + "-]" ).length ) { + rbuggyQSA.push( "~=" ); + } + + // Support: IE 11+, Edge 15 - 18+ + // IE 11/Edge don't find elements on a `[name='']` query in some cases. + // Adding a temporary attribute to the document before the selection works + // around the issue. + // Interestingly, IE 10 & older don't seem to have the issue. + input = document.createElement( "input" ); + input.setAttribute( "name", "" ); + el.appendChild( input ); + if ( !el.querySelectorAll( "[name='']" ).length ) { + rbuggyQSA.push( "\\[" + whitespace + "*name" + whitespace + "*=" + + whitespace + "*(?:''|\"\")" ); + } + + // Webkit/Opera - :checked should return selected option elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + // IE8 throws error here and will not see later tests + if ( !el.querySelectorAll( ":checked" ).length ) { + rbuggyQSA.push( ":checked" ); + } + + // Support: Safari 8+, iOS 8+ + // https://bugs.webkit.org/show_bug.cgi?id=136851 + // In-page `selector#id sibling-combinator selector` fails + if ( !el.querySelectorAll( "a#" + expando + "+*" ).length ) { + rbuggyQSA.push( ".#.+[+~]" ); + } + + // Support: Firefox <=3.6 - 5 only + // Old Firefox doesn't throw on a badly-escaped identifier. + el.querySelectorAll( "\\\f" ); + rbuggyQSA.push( "[\\r\\n\\f]" ); + } ); + + assert( function( el ) { + el.innerHTML = "" + + ""; + + // Support: Windows 8 Native Apps + // The type and name attributes are restricted during .innerHTML assignment + var input = document.createElement( "input" ); + input.setAttribute( "type", "hidden" ); + el.appendChild( input ).setAttribute( "name", "D" ); + + // Support: IE8 + // Enforce case-sensitivity of name attribute + if ( el.querySelectorAll( "[name=d]" ).length ) { + rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" ); + } + + // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) + // IE8 throws error here and will not see later tests + if ( el.querySelectorAll( ":enabled" ).length !== 2 ) { + rbuggyQSA.push( ":enabled", ":disabled" ); + } + + // Support: IE9-11+ + // IE's :disabled selector does not pick up the children of disabled fieldsets + docElem.appendChild( el ).disabled = true; + if ( el.querySelectorAll( ":disabled" ).length !== 2 ) { + rbuggyQSA.push( ":enabled", ":disabled" ); + } + + // Support: Opera 10 - 11 only + // Opera 10-11 does not throw on post-comma invalid pseudos + el.querySelectorAll( "*,:x" ); + rbuggyQSA.push( ",.*:" ); + } ); + } + + if ( ( support.matchesSelector = rnative.test( ( matches = docElem.matches || + docElem.webkitMatchesSelector || + docElem.mozMatchesSelector || + docElem.oMatchesSelector || + docElem.msMatchesSelector ) ) ) ) { + + assert( function( el ) { + + // Check to see if it's possible to do matchesSelector + // on a disconnected node (IE 9) + support.disconnectedMatch = matches.call( el, "*" ); + + // This should fail with an exception + // Gecko does not error, returns false instead + matches.call( el, "[s!='']:x" ); + rbuggyMatches.push( "!=", pseudos ); + } ); + } + + rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join( "|" ) ); + rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join( "|" ) ); + + /* Contains + ---------------------------------------------------------------------- */ + hasCompare = rnative.test( docElem.compareDocumentPosition ); + + // Element contains another + // Purposefully self-exclusive + // As in, an element does not contain itself + contains = hasCompare || rnative.test( docElem.contains ) ? + function( a, b ) { + var adown = a.nodeType === 9 ? a.documentElement : a, + bup = b && b.parentNode; + return a === bup || !!( bup && bup.nodeType === 1 && ( + adown.contains ? + adown.contains( bup ) : + a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16 + ) ); + } : + function( a, b ) { + if ( b ) { + while ( ( b = b.parentNode ) ) { + if ( b === a ) { + return true; + } + } + } + return false; + }; + + /* Sorting + ---------------------------------------------------------------------- */ + + // Document order sorting + sortOrder = hasCompare ? + function( a, b ) { + + // Flag for duplicate removal + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + // Sort on method existence if only one input has compareDocumentPosition + var compare = !a.compareDocumentPosition - !b.compareDocumentPosition; + if ( compare ) { + return compare; + } + + // Calculate position if both inputs belong to the same document + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + compare = ( a.ownerDocument || a ) == ( b.ownerDocument || b ) ? + a.compareDocumentPosition( b ) : + + // Otherwise we know they are disconnected + 1; + + // Disconnected nodes + if ( compare & 1 || + ( !support.sortDetached && b.compareDocumentPosition( a ) === compare ) ) { + + // Choose the first element that is related to our preferred document + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( a == document || a.ownerDocument == preferredDoc && + contains( preferredDoc, a ) ) { + return -1; + } + + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( b == document || b.ownerDocument == preferredDoc && + contains( preferredDoc, b ) ) { + return 1; + } + + // Maintain original order + return sortInput ? + ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : + 0; + } + + return compare & 4 ? -1 : 1; + } : + function( a, b ) { + + // Exit early if the nodes are identical + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + var cur, + i = 0, + aup = a.parentNode, + bup = b.parentNode, + ap = [ a ], + bp = [ b ]; + + // Parentless nodes are either documents or disconnected + if ( !aup || !bup ) { + + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + /* eslint-disable eqeqeq */ + return a == document ? -1 : + b == document ? 1 : + /* eslint-enable eqeqeq */ + aup ? -1 : + bup ? 1 : + sortInput ? + ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : + 0; + + // If the nodes are siblings, we can do a quick check + } else if ( aup === bup ) { + return siblingCheck( a, b ); + } + + // Otherwise we need full lists of their ancestors for comparison + cur = a; + while ( ( cur = cur.parentNode ) ) { + ap.unshift( cur ); + } + cur = b; + while ( ( cur = cur.parentNode ) ) { + bp.unshift( cur ); + } + + // Walk down the tree looking for a discrepancy + while ( ap[ i ] === bp[ i ] ) { + i++; + } + + return i ? + + // Do a sibling check if the nodes have a common ancestor + siblingCheck( ap[ i ], bp[ i ] ) : + + // Otherwise nodes in our document sort first + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + /* eslint-disable eqeqeq */ + ap[ i ] == preferredDoc ? -1 : + bp[ i ] == preferredDoc ? 1 : + /* eslint-enable eqeqeq */ + 0; + }; + + return document; +}; + +Sizzle.matches = function( expr, elements ) { + return Sizzle( expr, null, null, elements ); +}; + +Sizzle.matchesSelector = function( elem, expr ) { + setDocument( elem ); + + if ( support.matchesSelector && documentIsHTML && + !nonnativeSelectorCache[ expr + " " ] && + ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) && + ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) { + + try { + var ret = matches.call( elem, expr ); + + // IE 9's matchesSelector returns false on disconnected nodes + if ( ret || support.disconnectedMatch || + + // As well, disconnected nodes are said to be in a document + // fragment in IE 9 + elem.document && elem.document.nodeType !== 11 ) { + return ret; + } + } catch ( e ) { + nonnativeSelectorCache( expr, true ); + } + } + + return Sizzle( expr, document, null, [ elem ] ).length > 0; +}; + +Sizzle.contains = function( context, elem ) { + + // Set document vars if needed + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( ( context.ownerDocument || context ) != document ) { + setDocument( context ); + } + return contains( context, elem ); +}; + +Sizzle.attr = function( elem, name ) { + + // Set document vars if needed + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( ( elem.ownerDocument || elem ) != document ) { + setDocument( elem ); + } + + var fn = Expr.attrHandle[ name.toLowerCase() ], + + // Don't get fooled by Object.prototype properties (jQuery #13807) + val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ? + fn( elem, name, !documentIsHTML ) : + undefined; + + return val !== undefined ? + val : + support.attributes || !documentIsHTML ? + elem.getAttribute( name ) : + ( val = elem.getAttributeNode( name ) ) && val.specified ? + val.value : + null; +}; + +Sizzle.escape = function( sel ) { + return ( sel + "" ).replace( rcssescape, fcssescape ); +}; + +Sizzle.error = function( msg ) { + throw new Error( "Syntax error, unrecognized expression: " + msg ); +}; + +/** + * Document sorting and removing duplicates + * @param {ArrayLike} results + */ +Sizzle.uniqueSort = function( results ) { + var elem, + duplicates = [], + j = 0, + i = 0; + + // Unless we *know* we can detect duplicates, assume their presence + hasDuplicate = !support.detectDuplicates; + sortInput = !support.sortStable && results.slice( 0 ); + results.sort( sortOrder ); + + if ( hasDuplicate ) { + while ( ( elem = results[ i++ ] ) ) { + if ( elem === results[ i ] ) { + j = duplicates.push( i ); + } + } + while ( j-- ) { + results.splice( duplicates[ j ], 1 ); + } + } + + // Clear input after sorting to release objects + // See https://github.com/jquery/sizzle/pull/225 + sortInput = null; + + return results; +}; + +/** + * Utility function for retrieving the text value of an array of DOM nodes + * @param {Array|Element} elem + */ +getText = Sizzle.getText = function( elem ) { + var node, + ret = "", + i = 0, + nodeType = elem.nodeType; + + if ( !nodeType ) { + + // If no nodeType, this is expected to be an array + while ( ( node = elem[ i++ ] ) ) { + + // Do not traverse comment nodes + ret += getText( node ); + } + } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { + + // Use textContent for elements + // innerText usage removed for consistency of new lines (jQuery #11153) + if ( typeof elem.textContent === "string" ) { + return elem.textContent; + } else { + + // Traverse its children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + ret += getText( elem ); + } + } + } else if ( nodeType === 3 || nodeType === 4 ) { + return elem.nodeValue; + } + + // Do not include comment or processing instruction nodes + + return ret; +}; + +Expr = Sizzle.selectors = { + + // Can be adjusted by the user + cacheLength: 50, + + createPseudo: markFunction, + + match: matchExpr, + + attrHandle: {}, + + find: {}, + + relative: { + ">": { dir: "parentNode", first: true }, + " ": { dir: "parentNode" }, + "+": { dir: "previousSibling", first: true }, + "~": { dir: "previousSibling" } + }, + + preFilter: { + "ATTR": function( match ) { + match[ 1 ] = match[ 1 ].replace( runescape, funescape ); + + // Move the given value to match[3] whether quoted or unquoted + match[ 3 ] = ( match[ 3 ] || match[ 4 ] || + match[ 5 ] || "" ).replace( runescape, funescape ); + + if ( match[ 2 ] === "~=" ) { + match[ 3 ] = " " + match[ 3 ] + " "; + } + + return match.slice( 0, 4 ); + }, + + "CHILD": function( match ) { + + /* matches from matchExpr["CHILD"] + 1 type (only|nth|...) + 2 what (child|of-type) + 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...) + 4 xn-component of xn+y argument ([+-]?\d*n|) + 5 sign of xn-component + 6 x of xn-component + 7 sign of y-component + 8 y of y-component + */ + match[ 1 ] = match[ 1 ].toLowerCase(); + + if ( match[ 1 ].slice( 0, 3 ) === "nth" ) { + + // nth-* requires argument + if ( !match[ 3 ] ) { + Sizzle.error( match[ 0 ] ); + } + + // numeric x and y parameters for Expr.filter.CHILD + // remember that false/true cast respectively to 0/1 + match[ 4 ] = +( match[ 4 ] ? + match[ 5 ] + ( match[ 6 ] || 1 ) : + 2 * ( match[ 3 ] === "even" || match[ 3 ] === "odd" ) ); + match[ 5 ] = +( ( match[ 7 ] + match[ 8 ] ) || match[ 3 ] === "odd" ); + + // other types prohibit arguments + } else if ( match[ 3 ] ) { + Sizzle.error( match[ 0 ] ); + } + + return match; + }, + + "PSEUDO": function( match ) { + var excess, + unquoted = !match[ 6 ] && match[ 2 ]; + + if ( matchExpr[ "CHILD" ].test( match[ 0 ] ) ) { + return null; + } + + // Accept quoted arguments as-is + if ( match[ 3 ] ) { + match[ 2 ] = match[ 4 ] || match[ 5 ] || ""; + + // Strip excess characters from unquoted arguments + } else if ( unquoted && rpseudo.test( unquoted ) && + + // Get excess from tokenize (recursively) + ( excess = tokenize( unquoted, true ) ) && + + // advance to the next closing parenthesis + ( excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length ) ) { + + // excess is a negative index + match[ 0 ] = match[ 0 ].slice( 0, excess ); + match[ 2 ] = unquoted.slice( 0, excess ); + } + + // Return only captures needed by the pseudo filter method (type and argument) + return match.slice( 0, 3 ); + } + }, + + filter: { + + "TAG": function( nodeNameSelector ) { + var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase(); + return nodeNameSelector === "*" ? + function() { + return true; + } : + function( elem ) { + return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; + }; + }, + + "CLASS": function( className ) { + var pattern = classCache[ className + " " ]; + + return pattern || + ( pattern = new RegExp( "(^|" + whitespace + + ")" + className + "(" + whitespace + "|$)" ) ) && classCache( + className, function( elem ) { + return pattern.test( + typeof elem.className === "string" && elem.className || + typeof elem.getAttribute !== "undefined" && + elem.getAttribute( "class" ) || + "" + ); + } ); + }, + + "ATTR": function( name, operator, check ) { + return function( elem ) { + var result = Sizzle.attr( elem, name ); + + if ( result == null ) { + return operator === "!="; + } + if ( !operator ) { + return true; + } + + result += ""; + + /* eslint-disable max-len */ + + return operator === "=" ? result === check : + operator === "!=" ? result !== check : + operator === "^=" ? check && result.indexOf( check ) === 0 : + operator === "*=" ? check && result.indexOf( check ) > -1 : + operator === "$=" ? check && result.slice( -check.length ) === check : + operator === "~=" ? ( " " + result.replace( rwhitespace, " " ) + " " ).indexOf( check ) > -1 : + operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" : + false; + /* eslint-enable max-len */ + + }; + }, + + "CHILD": function( type, what, _argument, first, last ) { + var simple = type.slice( 0, 3 ) !== "nth", + forward = type.slice( -4 ) !== "last", + ofType = what === "of-type"; + + return first === 1 && last === 0 ? + + // Shortcut for :nth-*(n) + function( elem ) { + return !!elem.parentNode; + } : + + function( elem, _context, xml ) { + var cache, uniqueCache, outerCache, node, nodeIndex, start, + dir = simple !== forward ? "nextSibling" : "previousSibling", + parent = elem.parentNode, + name = ofType && elem.nodeName.toLowerCase(), + useCache = !xml && !ofType, + diff = false; + + if ( parent ) { + + // :(first|last|only)-(child|of-type) + if ( simple ) { + while ( dir ) { + node = elem; + while ( ( node = node[ dir ] ) ) { + if ( ofType ? + node.nodeName.toLowerCase() === name : + node.nodeType === 1 ) { + + return false; + } + } + + // Reverse direction for :only-* (if we haven't yet done so) + start = dir = type === "only" && !start && "nextSibling"; + } + return true; + } + + start = [ forward ? parent.firstChild : parent.lastChild ]; + + // non-xml :nth-child(...) stores cache data on `parent` + if ( forward && useCache ) { + + // Seek `elem` from a previously-cached index + + // ...in a gzip-friendly way + node = parent; + outerCache = node[ expando ] || ( node[ expando ] = {} ); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + ( outerCache[ node.uniqueID ] = {} ); + + cache = uniqueCache[ type ] || []; + nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; + diff = nodeIndex && cache[ 2 ]; + node = nodeIndex && parent.childNodes[ nodeIndex ]; + + while ( ( node = ++nodeIndex && node && node[ dir ] || + + // Fallback to seeking `elem` from the start + ( diff = nodeIndex = 0 ) || start.pop() ) ) { + + // When found, cache indexes on `parent` and break + if ( node.nodeType === 1 && ++diff && node === elem ) { + uniqueCache[ type ] = [ dirruns, nodeIndex, diff ]; + break; + } + } + + } else { + + // Use previously-cached element index if available + if ( useCache ) { + + // ...in a gzip-friendly way + node = elem; + outerCache = node[ expando ] || ( node[ expando ] = {} ); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + ( outerCache[ node.uniqueID ] = {} ); + + cache = uniqueCache[ type ] || []; + nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; + diff = nodeIndex; + } + + // xml :nth-child(...) + // or :nth-last-child(...) or :nth(-last)?-of-type(...) + if ( diff === false ) { + + // Use the same loop as above to seek `elem` from the start + while ( ( node = ++nodeIndex && node && node[ dir ] || + ( diff = nodeIndex = 0 ) || start.pop() ) ) { + + if ( ( ofType ? + node.nodeName.toLowerCase() === name : + node.nodeType === 1 ) && + ++diff ) { + + // Cache the index of each encountered element + if ( useCache ) { + outerCache = node[ expando ] || + ( node[ expando ] = {} ); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + ( outerCache[ node.uniqueID ] = {} ); + + uniqueCache[ type ] = [ dirruns, diff ]; + } + + if ( node === elem ) { + break; + } + } + } + } + } + + // Incorporate the offset, then check against cycle size + diff -= last; + return diff === first || ( diff % first === 0 && diff / first >= 0 ); + } + }; + }, + + "PSEUDO": function( pseudo, argument ) { + + // pseudo-class names are case-insensitive + // http://www.w3.org/TR/selectors/#pseudo-classes + // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters + // Remember that setFilters inherits from pseudos + var args, + fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] || + Sizzle.error( "unsupported pseudo: " + pseudo ); + + // The user may use createPseudo to indicate that + // arguments are needed to create the filter function + // just as Sizzle does + if ( fn[ expando ] ) { + return fn( argument ); + } + + // But maintain support for old signatures + if ( fn.length > 1 ) { + args = [ pseudo, pseudo, "", argument ]; + return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ? + markFunction( function( seed, matches ) { + var idx, + matched = fn( seed, argument ), + i = matched.length; + while ( i-- ) { + idx = indexOf( seed, matched[ i ] ); + seed[ idx ] = !( matches[ idx ] = matched[ i ] ); + } + } ) : + function( elem ) { + return fn( elem, 0, args ); + }; + } + + return fn; + } + }, + + pseudos: { + + // Potentially complex pseudos + "not": markFunction( function( selector ) { + + // Trim the selector passed to compile + // to avoid treating leading and trailing + // spaces as combinators + var input = [], + results = [], + matcher = compile( selector.replace( rtrim, "$1" ) ); + + return matcher[ expando ] ? + markFunction( function( seed, matches, _context, xml ) { + var elem, + unmatched = matcher( seed, null, xml, [] ), + i = seed.length; + + // Match elements unmatched by `matcher` + while ( i-- ) { + if ( ( elem = unmatched[ i ] ) ) { + seed[ i ] = !( matches[ i ] = elem ); + } + } + } ) : + function( elem, _context, xml ) { + input[ 0 ] = elem; + matcher( input, null, xml, results ); + + // Don't keep the element (issue #299) + input[ 0 ] = null; + return !results.pop(); + }; + } ), + + "has": markFunction( function( selector ) { + return function( elem ) { + return Sizzle( selector, elem ).length > 0; + }; + } ), + + "contains": markFunction( function( text ) { + text = text.replace( runescape, funescape ); + return function( elem ) { + return ( elem.textContent || getText( elem ) ).indexOf( text ) > -1; + }; + } ), + + // "Whether an element is represented by a :lang() selector + // is based solely on the element's language value + // being equal to the identifier C, + // or beginning with the identifier C immediately followed by "-". + // The matching of C against the element's language value is performed case-insensitively. + // The identifier C does not have to be a valid language name." + // http://www.w3.org/TR/selectors/#lang-pseudo + "lang": markFunction( function( lang ) { + + // lang value must be a valid identifier + if ( !ridentifier.test( lang || "" ) ) { + Sizzle.error( "unsupported lang: " + lang ); + } + lang = lang.replace( runescape, funescape ).toLowerCase(); + return function( elem ) { + var elemLang; + do { + if ( ( elemLang = documentIsHTML ? + elem.lang : + elem.getAttribute( "xml:lang" ) || elem.getAttribute( "lang" ) ) ) { + + elemLang = elemLang.toLowerCase(); + return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0; + } + } while ( ( elem = elem.parentNode ) && elem.nodeType === 1 ); + return false; + }; + } ), + + // Miscellaneous + "target": function( elem ) { + var hash = window.location && window.location.hash; + return hash && hash.slice( 1 ) === elem.id; + }, + + "root": function( elem ) { + return elem === docElem; + }, + + "focus": function( elem ) { + return elem === document.activeElement && + ( !document.hasFocus || document.hasFocus() ) && + !!( elem.type || elem.href || ~elem.tabIndex ); + }, + + // Boolean properties + "enabled": createDisabledPseudo( false ), + "disabled": createDisabledPseudo( true ), + + "checked": function( elem ) { + + // In CSS3, :checked should return both checked and selected elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + var nodeName = elem.nodeName.toLowerCase(); + return ( nodeName === "input" && !!elem.checked ) || + ( nodeName === "option" && !!elem.selected ); + }, + + "selected": function( elem ) { + + // Accessing this property makes selected-by-default + // options in Safari work properly + if ( elem.parentNode ) { + // eslint-disable-next-line no-unused-expressions + elem.parentNode.selectedIndex; + } + + return elem.selected === true; + }, + + // Contents + "empty": function( elem ) { + + // http://www.w3.org/TR/selectors/#empty-pseudo + // :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5), + // but not by others (comment: 8; processing instruction: 7; etc.) + // nodeType < 6 works because attributes (2) do not appear as children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + if ( elem.nodeType < 6 ) { + return false; + } + } + return true; + }, + + "parent": function( elem ) { + return !Expr.pseudos[ "empty" ]( elem ); + }, + + // Element/input types + "header": function( elem ) { + return rheader.test( elem.nodeName ); + }, + + "input": function( elem ) { + return rinputs.test( elem.nodeName ); + }, + + "button": function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === "button" || name === "button"; + }, + + "text": function( elem ) { + var attr; + return elem.nodeName.toLowerCase() === "input" && + elem.type === "text" && + + // Support: IE<8 + // New HTML5 attribute values (e.g., "search") appear with elem.type === "text" + ( ( attr = elem.getAttribute( "type" ) ) == null || + attr.toLowerCase() === "text" ); + }, + + // Position-in-collection + "first": createPositionalPseudo( function() { + return [ 0 ]; + } ), + + "last": createPositionalPseudo( function( _matchIndexes, length ) { + return [ length - 1 ]; + } ), + + "eq": createPositionalPseudo( function( _matchIndexes, length, argument ) { + return [ argument < 0 ? argument + length : argument ]; + } ), + + "even": createPositionalPseudo( function( matchIndexes, length ) { + var i = 0; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + } ), + + "odd": createPositionalPseudo( function( matchIndexes, length ) { + var i = 1; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + } ), + + "lt": createPositionalPseudo( function( matchIndexes, length, argument ) { + var i = argument < 0 ? + argument + length : + argument > length ? + length : + argument; + for ( ; --i >= 0; ) { + matchIndexes.push( i ); + } + return matchIndexes; + } ), + + "gt": createPositionalPseudo( function( matchIndexes, length, argument ) { + var i = argument < 0 ? argument + length : argument; + for ( ; ++i < length; ) { + matchIndexes.push( i ); + } + return matchIndexes; + } ) + } +}; + +Expr.pseudos[ "nth" ] = Expr.pseudos[ "eq" ]; + +// Add button/input type pseudos +for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) { + Expr.pseudos[ i ] = createInputPseudo( i ); +} +for ( i in { submit: true, reset: true } ) { + Expr.pseudos[ i ] = createButtonPseudo( i ); +} + +// Easy API for creating new setFilters +function setFilters() {} +setFilters.prototype = Expr.filters = Expr.pseudos; +Expr.setFilters = new setFilters(); + +tokenize = Sizzle.tokenize = function( selector, parseOnly ) { + var matched, match, tokens, type, + soFar, groups, preFilters, + cached = tokenCache[ selector + " " ]; + + if ( cached ) { + return parseOnly ? 0 : cached.slice( 0 ); + } + + soFar = selector; + groups = []; + preFilters = Expr.preFilter; + + while ( soFar ) { + + // Comma and first run + if ( !matched || ( match = rcomma.exec( soFar ) ) ) { + if ( match ) { + + // Don't consume trailing commas as valid + soFar = soFar.slice( match[ 0 ].length ) || soFar; + } + groups.push( ( tokens = [] ) ); + } + + matched = false; + + // Combinators + if ( ( match = rcombinators.exec( soFar ) ) ) { + matched = match.shift(); + tokens.push( { + value: matched, + + // Cast descendant combinators to space + type: match[ 0 ].replace( rtrim, " " ) + } ); + soFar = soFar.slice( matched.length ); + } + + // Filters + for ( type in Expr.filter ) { + if ( ( match = matchExpr[ type ].exec( soFar ) ) && ( !preFilters[ type ] || + ( match = preFilters[ type ]( match ) ) ) ) { + matched = match.shift(); + tokens.push( { + value: matched, + type: type, + matches: match + } ); + soFar = soFar.slice( matched.length ); + } + } + + if ( !matched ) { + break; + } + } + + // Return the length of the invalid excess + // if we're just parsing + // Otherwise, throw an error or return tokens + return parseOnly ? + soFar.length : + soFar ? + Sizzle.error( selector ) : + + // Cache the tokens + tokenCache( selector, groups ).slice( 0 ); +}; + +function toSelector( tokens ) { + var i = 0, + len = tokens.length, + selector = ""; + for ( ; i < len; i++ ) { + selector += tokens[ i ].value; + } + return selector; +} + +function addCombinator( matcher, combinator, base ) { + var dir = combinator.dir, + skip = combinator.next, + key = skip || dir, + checkNonElements = base && key === "parentNode", + doneName = done++; + + return combinator.first ? + + // Check against closest ancestor/preceding element + function( elem, context, xml ) { + while ( ( elem = elem[ dir ] ) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + return matcher( elem, context, xml ); + } + } + return false; + } : + + // Check against all ancestor/preceding elements + function( elem, context, xml ) { + var oldCache, uniqueCache, outerCache, + newCache = [ dirruns, doneName ]; + + // We can't set arbitrary data on XML nodes, so they don't benefit from combinator caching + if ( xml ) { + while ( ( elem = elem[ dir ] ) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + if ( matcher( elem, context, xml ) ) { + return true; + } + } + } + } else { + while ( ( elem = elem[ dir ] ) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + outerCache = elem[ expando ] || ( elem[ expando ] = {} ); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ elem.uniqueID ] || + ( outerCache[ elem.uniqueID ] = {} ); + + if ( skip && skip === elem.nodeName.toLowerCase() ) { + elem = elem[ dir ] || elem; + } else if ( ( oldCache = uniqueCache[ key ] ) && + oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) { + + // Assign to newCache so results back-propagate to previous elements + return ( newCache[ 2 ] = oldCache[ 2 ] ); + } else { + + // Reuse newcache so results back-propagate to previous elements + uniqueCache[ key ] = newCache; + + // A match means we're done; a fail means we have to keep checking + if ( ( newCache[ 2 ] = matcher( elem, context, xml ) ) ) { + return true; + } + } + } + } + } + return false; + }; +} + +function elementMatcher( matchers ) { + return matchers.length > 1 ? + function( elem, context, xml ) { + var i = matchers.length; + while ( i-- ) { + if ( !matchers[ i ]( elem, context, xml ) ) { + return false; + } + } + return true; + } : + matchers[ 0 ]; +} + +function multipleContexts( selector, contexts, results ) { + var i = 0, + len = contexts.length; + for ( ; i < len; i++ ) { + Sizzle( selector, contexts[ i ], results ); + } + return results; +} + +function condense( unmatched, map, filter, context, xml ) { + var elem, + newUnmatched = [], + i = 0, + len = unmatched.length, + mapped = map != null; + + for ( ; i < len; i++ ) { + if ( ( elem = unmatched[ i ] ) ) { + if ( !filter || filter( elem, context, xml ) ) { + newUnmatched.push( elem ); + if ( mapped ) { + map.push( i ); + } + } + } + } + + return newUnmatched; +} + +function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) { + if ( postFilter && !postFilter[ expando ] ) { + postFilter = setMatcher( postFilter ); + } + if ( postFinder && !postFinder[ expando ] ) { + postFinder = setMatcher( postFinder, postSelector ); + } + return markFunction( function( seed, results, context, xml ) { + var temp, i, elem, + preMap = [], + postMap = [], + preexisting = results.length, + + // Get initial elements from seed or context + elems = seed || multipleContexts( + selector || "*", + context.nodeType ? [ context ] : context, + [] + ), + + // Prefilter to get matcher input, preserving a map for seed-results synchronization + matcherIn = preFilter && ( seed || !selector ) ? + condense( elems, preMap, preFilter, context, xml ) : + elems, + + matcherOut = matcher ? + + // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results, + postFinder || ( seed ? preFilter : preexisting || postFilter ) ? + + // ...intermediate processing is necessary + [] : + + // ...otherwise use results directly + results : + matcherIn; + + // Find primary matches + if ( matcher ) { + matcher( matcherIn, matcherOut, context, xml ); + } + + // Apply postFilter + if ( postFilter ) { + temp = condense( matcherOut, postMap ); + postFilter( temp, [], context, xml ); + + // Un-match failing elements by moving them back to matcherIn + i = temp.length; + while ( i-- ) { + if ( ( elem = temp[ i ] ) ) { + matcherOut[ postMap[ i ] ] = !( matcherIn[ postMap[ i ] ] = elem ); + } + } + } + + if ( seed ) { + if ( postFinder || preFilter ) { + if ( postFinder ) { + + // Get the final matcherOut by condensing this intermediate into postFinder contexts + temp = []; + i = matcherOut.length; + while ( i-- ) { + if ( ( elem = matcherOut[ i ] ) ) { + + // Restore matcherIn since elem is not yet a final match + temp.push( ( matcherIn[ i ] = elem ) ); + } + } + postFinder( null, ( matcherOut = [] ), temp, xml ); + } + + // Move matched elements from seed to results to keep them synchronized + i = matcherOut.length; + while ( i-- ) { + if ( ( elem = matcherOut[ i ] ) && + ( temp = postFinder ? indexOf( seed, elem ) : preMap[ i ] ) > -1 ) { + + seed[ temp ] = !( results[ temp ] = elem ); + } + } + } + + // Add elements to results, through postFinder if defined + } else { + matcherOut = condense( + matcherOut === results ? + matcherOut.splice( preexisting, matcherOut.length ) : + matcherOut + ); + if ( postFinder ) { + postFinder( null, results, matcherOut, xml ); + } else { + push.apply( results, matcherOut ); + } + } + } ); +} + +function matcherFromTokens( tokens ) { + var checkContext, matcher, j, + len = tokens.length, + leadingRelative = Expr.relative[ tokens[ 0 ].type ], + implicitRelative = leadingRelative || Expr.relative[ " " ], + i = leadingRelative ? 1 : 0, + + // The foundational matcher ensures that elements are reachable from top-level context(s) + matchContext = addCombinator( function( elem ) { + return elem === checkContext; + }, implicitRelative, true ), + matchAnyContext = addCombinator( function( elem ) { + return indexOf( checkContext, elem ) > -1; + }, implicitRelative, true ), + matchers = [ function( elem, context, xml ) { + var ret = ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( + ( checkContext = context ).nodeType ? + matchContext( elem, context, xml ) : + matchAnyContext( elem, context, xml ) ); + + // Avoid hanging onto element (issue #299) + checkContext = null; + return ret; + } ]; + + for ( ; i < len; i++ ) { + if ( ( matcher = Expr.relative[ tokens[ i ].type ] ) ) { + matchers = [ addCombinator( elementMatcher( matchers ), matcher ) ]; + } else { + matcher = Expr.filter[ tokens[ i ].type ].apply( null, tokens[ i ].matches ); + + // Return special upon seeing a positional matcher + if ( matcher[ expando ] ) { + + // Find the next relative operator (if any) for proper handling + j = ++i; + for ( ; j < len; j++ ) { + if ( Expr.relative[ tokens[ j ].type ] ) { + break; + } + } + return setMatcher( + i > 1 && elementMatcher( matchers ), + i > 1 && toSelector( + + // If the preceding token was a descendant combinator, insert an implicit any-element `*` + tokens + .slice( 0, i - 1 ) + .concat( { value: tokens[ i - 2 ].type === " " ? "*" : "" } ) + ).replace( rtrim, "$1" ), + matcher, + i < j && matcherFromTokens( tokens.slice( i, j ) ), + j < len && matcherFromTokens( ( tokens = tokens.slice( j ) ) ), + j < len && toSelector( tokens ) + ); + } + matchers.push( matcher ); + } + } + + return elementMatcher( matchers ); +} + +function matcherFromGroupMatchers( elementMatchers, setMatchers ) { + var bySet = setMatchers.length > 0, + byElement = elementMatchers.length > 0, + superMatcher = function( seed, context, xml, results, outermost ) { + var elem, j, matcher, + matchedCount = 0, + i = "0", + unmatched = seed && [], + setMatched = [], + contextBackup = outermostContext, + + // We must always have either seed elements or outermost context + elems = seed || byElement && Expr.find[ "TAG" ]( "*", outermost ), + + // Use integer dirruns iff this is the outermost matcher + dirrunsUnique = ( dirruns += contextBackup == null ? 1 : Math.random() || 0.1 ), + len = elems.length; + + if ( outermost ) { + + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + outermostContext = context == document || context || outermost; + } + + // Add elements passing elementMatchers directly to results + // Support: IE<9, Safari + // Tolerate NodeList properties (IE: "length"; Safari: ) matching elements by id + for ( ; i !== len && ( elem = elems[ i ] ) != null; i++ ) { + if ( byElement && elem ) { + j = 0; + + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( !context && elem.ownerDocument != document ) { + setDocument( elem ); + xml = !documentIsHTML; + } + while ( ( matcher = elementMatchers[ j++ ] ) ) { + if ( matcher( elem, context || document, xml ) ) { + results.push( elem ); + break; + } + } + if ( outermost ) { + dirruns = dirrunsUnique; + } + } + + // Track unmatched elements for set filters + if ( bySet ) { + + // They will have gone through all possible matchers + if ( ( elem = !matcher && elem ) ) { + matchedCount--; + } + + // Lengthen the array for every element, matched or not + if ( seed ) { + unmatched.push( elem ); + } + } + } + + // `i` is now the count of elements visited above, and adding it to `matchedCount` + // makes the latter nonnegative. + matchedCount += i; + + // Apply set filters to unmatched elements + // NOTE: This can be skipped if there are no unmatched elements (i.e., `matchedCount` + // equals `i`), unless we didn't visit _any_ elements in the above loop because we have + // no element matchers and no seed. + // Incrementing an initially-string "0" `i` allows `i` to remain a string only in that + // case, which will result in a "00" `matchedCount` that differs from `i` but is also + // numerically zero. + if ( bySet && i !== matchedCount ) { + j = 0; + while ( ( matcher = setMatchers[ j++ ] ) ) { + matcher( unmatched, setMatched, context, xml ); + } + + if ( seed ) { + + // Reintegrate element matches to eliminate the need for sorting + if ( matchedCount > 0 ) { + while ( i-- ) { + if ( !( unmatched[ i ] || setMatched[ i ] ) ) { + setMatched[ i ] = pop.call( results ); + } + } + } + + // Discard index placeholder values to get only actual matches + setMatched = condense( setMatched ); + } + + // Add matches to results + push.apply( results, setMatched ); + + // Seedless set matches succeeding multiple successful matchers stipulate sorting + if ( outermost && !seed && setMatched.length > 0 && + ( matchedCount + setMatchers.length ) > 1 ) { + + Sizzle.uniqueSort( results ); + } + } + + // Override manipulation of globals by nested matchers + if ( outermost ) { + dirruns = dirrunsUnique; + outermostContext = contextBackup; + } + + return unmatched; + }; + + return bySet ? + markFunction( superMatcher ) : + superMatcher; +} + +compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) { + var i, + setMatchers = [], + elementMatchers = [], + cached = compilerCache[ selector + " " ]; + + if ( !cached ) { + + // Generate a function of recursive functions that can be used to check each element + if ( !match ) { + match = tokenize( selector ); + } + i = match.length; + while ( i-- ) { + cached = matcherFromTokens( match[ i ] ); + if ( cached[ expando ] ) { + setMatchers.push( cached ); + } else { + elementMatchers.push( cached ); + } + } + + // Cache the compiled function + cached = compilerCache( + selector, + matcherFromGroupMatchers( elementMatchers, setMatchers ) + ); + + // Save selector and tokenization + cached.selector = selector; + } + return cached; +}; + +/** + * A low-level selection function that works with Sizzle's compiled + * selector functions + * @param {String|Function} selector A selector or a pre-compiled + * selector function built with Sizzle.compile + * @param {Element} context + * @param {Array} [results] + * @param {Array} [seed] A set of elements to match against + */ +select = Sizzle.select = function( selector, context, results, seed ) { + var i, tokens, token, type, find, + compiled = typeof selector === "function" && selector, + match = !seed && tokenize( ( selector = compiled.selector || selector ) ); + + results = results || []; + + // Try to minimize operations if there is only one selector in the list and no seed + // (the latter of which guarantees us context) + if ( match.length === 1 ) { + + // Reduce context if the leading compound selector is an ID + tokens = match[ 0 ] = match[ 0 ].slice( 0 ); + if ( tokens.length > 2 && ( token = tokens[ 0 ] ).type === "ID" && + context.nodeType === 9 && documentIsHTML && Expr.relative[ tokens[ 1 ].type ] ) { + + context = ( Expr.find[ "ID" ]( token.matches[ 0 ] + .replace( runescape, funescape ), context ) || [] )[ 0 ]; + if ( !context ) { + return results; + + // Precompiled matchers will still verify ancestry, so step up a level + } else if ( compiled ) { + context = context.parentNode; + } + + selector = selector.slice( tokens.shift().value.length ); + } + + // Fetch a seed set for right-to-left matching + i = matchExpr[ "needsContext" ].test( selector ) ? 0 : tokens.length; + while ( i-- ) { + token = tokens[ i ]; + + // Abort if we hit a combinator + if ( Expr.relative[ ( type = token.type ) ] ) { + break; + } + if ( ( find = Expr.find[ type ] ) ) { + + // Search, expanding context for leading sibling combinators + if ( ( seed = find( + token.matches[ 0 ].replace( runescape, funescape ), + rsibling.test( tokens[ 0 ].type ) && testContext( context.parentNode ) || + context + ) ) ) { + + // If seed is empty or no tokens remain, we can return early + tokens.splice( i, 1 ); + selector = seed.length && toSelector( tokens ); + if ( !selector ) { + push.apply( results, seed ); + return results; + } + + break; + } + } + } + } + + // Compile and execute a filtering function if one is not provided + // Provide `match` to avoid retokenization if we modified the selector above + ( compiled || compile( selector, match ) )( + seed, + context, + !documentIsHTML, + results, + !context || rsibling.test( selector ) && testContext( context.parentNode ) || context + ); + return results; +}; + +// One-time assignments + +// Sort stability +support.sortStable = expando.split( "" ).sort( sortOrder ).join( "" ) === expando; + +// Support: Chrome 14-35+ +// Always assume duplicates if they aren't passed to the comparison function +support.detectDuplicates = !!hasDuplicate; + +// Initialize against the default document +setDocument(); + +// Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27) +// Detached nodes confoundingly follow *each other* +support.sortDetached = assert( function( el ) { + + // Should return 1, but returns 4 (following) + return el.compareDocumentPosition( document.createElement( "fieldset" ) ) & 1; +} ); + +// Support: IE<8 +// Prevent attribute/property "interpolation" +// https://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx +if ( !assert( function( el ) { + el.innerHTML = ""; + return el.firstChild.getAttribute( "href" ) === "#"; +} ) ) { + addHandle( "type|href|height|width", function( elem, name, isXML ) { + if ( !isXML ) { + return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 ); + } + } ); +} + +// Support: IE<9 +// Use defaultValue in place of getAttribute("value") +if ( !support.attributes || !assert( function( el ) { + el.innerHTML = ""; + el.firstChild.setAttribute( "value", "" ); + return el.firstChild.getAttribute( "value" ) === ""; +} ) ) { + addHandle( "value", function( elem, _name, isXML ) { + if ( !isXML && elem.nodeName.toLowerCase() === "input" ) { + return elem.defaultValue; + } + } ); +} + +// Support: IE<9 +// Use getAttributeNode to fetch booleans when getAttribute lies +if ( !assert( function( el ) { + return el.getAttribute( "disabled" ) == null; +} ) ) { + addHandle( booleans, function( elem, name, isXML ) { + var val; + if ( !isXML ) { + return elem[ name ] === true ? name.toLowerCase() : + ( val = elem.getAttributeNode( name ) ) && val.specified ? + val.value : + null; + } + } ); +} + +return Sizzle; + +} )( window ); + + + +jQuery.find = Sizzle; +jQuery.expr = Sizzle.selectors; + +// Deprecated +jQuery.expr[ ":" ] = jQuery.expr.pseudos; +jQuery.uniqueSort = jQuery.unique = Sizzle.uniqueSort; +jQuery.text = Sizzle.getText; +jQuery.isXMLDoc = Sizzle.isXML; +jQuery.contains = Sizzle.contains; +jQuery.escapeSelector = Sizzle.escape; + + + + +var dir = function( elem, dir, until ) { + var matched = [], + truncate = until !== undefined; + + while ( ( elem = elem[ dir ] ) && elem.nodeType !== 9 ) { + if ( elem.nodeType === 1 ) { + if ( truncate && jQuery( elem ).is( until ) ) { + break; + } + matched.push( elem ); + } + } + return matched; +}; + + +var siblings = function( n, elem ) { + var matched = []; + + for ( ; n; n = n.nextSibling ) { + if ( n.nodeType === 1 && n !== elem ) { + matched.push( n ); + } + } + + return matched; +}; + + +var rneedsContext = jQuery.expr.match.needsContext; + + + +function nodeName( elem, name ) { + + return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); + +}; +var rsingleTag = ( /^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i ); + + + +// Implement the identical functionality for filter and not +function winnow( elements, qualifier, not ) { + if ( isFunction( qualifier ) ) { + return jQuery.grep( elements, function( elem, i ) { + return !!qualifier.call( elem, i, elem ) !== not; + } ); + } + + // Single element + if ( qualifier.nodeType ) { + return jQuery.grep( elements, function( elem ) { + return ( elem === qualifier ) !== not; + } ); + } + + // Arraylike of elements (jQuery, arguments, Array) + if ( typeof qualifier !== "string" ) { + return jQuery.grep( elements, function( elem ) { + return ( indexOf.call( qualifier, elem ) > -1 ) !== not; + } ); + } + + // Filtered directly for both simple and complex selectors + return jQuery.filter( qualifier, elements, not ); +} + +jQuery.filter = function( expr, elems, not ) { + var elem = elems[ 0 ]; + + if ( not ) { + expr = ":not(" + expr + ")"; + } + + if ( elems.length === 1 && elem.nodeType === 1 ) { + return jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : []; + } + + return jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) { + return elem.nodeType === 1; + } ) ); +}; + +jQuery.fn.extend( { + find: function( selector ) { + var i, ret, + len = this.length, + self = this; + + if ( typeof selector !== "string" ) { + return this.pushStack( jQuery( selector ).filter( function() { + for ( i = 0; i < len; i++ ) { + if ( jQuery.contains( self[ i ], this ) ) { + return true; + } + } + } ) ); + } + + ret = this.pushStack( [] ); + + for ( i = 0; i < len; i++ ) { + jQuery.find( selector, self[ i ], ret ); + } + + return len > 1 ? jQuery.uniqueSort( ret ) : ret; + }, + filter: function( selector ) { + return this.pushStack( winnow( this, selector || [], false ) ); + }, + not: function( selector ) { + return this.pushStack( winnow( this, selector || [], true ) ); + }, + is: function( selector ) { + return !!winnow( + this, + + // If this is a positional/relative selector, check membership in the returned set + // so $("p:first").is("p:last") won't return true for a doc with two "p". + typeof selector === "string" && rneedsContext.test( selector ) ? + jQuery( selector ) : + selector || [], + false + ).length; + } +} ); + + +// Initialize a jQuery object + + +// A central reference to the root jQuery(document) +var rootjQuery, + + // A simple way to check for HTML strings + // Prioritize #id over to avoid XSS via location.hash (#9521) + // Strict HTML recognition (#11290: must start with <) + // Shortcut simple #id case for speed + rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/, + + init = jQuery.fn.init = function( selector, context, root ) { + var match, elem; + + // HANDLE: $(""), $(null), $(undefined), $(false) + if ( !selector ) { + return this; + } + + // Method init() accepts an alternate rootjQuery + // so migrate can support jQuery.sub (gh-2101) + root = root || rootjQuery; + + // Handle HTML strings + if ( typeof selector === "string" ) { + if ( selector[ 0 ] === "<" && + selector[ selector.length - 1 ] === ">" && + selector.length >= 3 ) { + + // Assume that strings that start and end with <> are HTML and skip the regex check + match = [ null, selector, null ]; + + } else { + match = rquickExpr.exec( selector ); + } + + // Match html or make sure no context is specified for #id + if ( match && ( match[ 1 ] || !context ) ) { + + // HANDLE: $(html) -> $(array) + if ( match[ 1 ] ) { + context = context instanceof jQuery ? context[ 0 ] : context; + + // Option to run scripts is true for back-compat + // Intentionally let the error be thrown if parseHTML is not present + jQuery.merge( this, jQuery.parseHTML( + match[ 1 ], + context && context.nodeType ? context.ownerDocument || context : document, + true + ) ); + + // HANDLE: $(html, props) + if ( rsingleTag.test( match[ 1 ] ) && jQuery.isPlainObject( context ) ) { + for ( match in context ) { + + // Properties of context are called as methods if possible + if ( isFunction( this[ match ] ) ) { + this[ match ]( context[ match ] ); + + // ...and otherwise set as attributes + } else { + this.attr( match, context[ match ] ); + } + } + } + + return this; + + // HANDLE: $(#id) + } else { + elem = document.getElementById( match[ 2 ] ); + + if ( elem ) { + + // Inject the element directly into the jQuery object + this[ 0 ] = elem; + this.length = 1; + } + return this; + } + + // HANDLE: $(expr, $(...)) + } else if ( !context || context.jquery ) { + return ( context || root ).find( selector ); + + // HANDLE: $(expr, context) + // (which is just equivalent to: $(context).find(expr) + } else { + return this.constructor( context ).find( selector ); + } + + // HANDLE: $(DOMElement) + } else if ( selector.nodeType ) { + this[ 0 ] = selector; + this.length = 1; + return this; + + // HANDLE: $(function) + // Shortcut for document ready + } else if ( isFunction( selector ) ) { + return root.ready !== undefined ? + root.ready( selector ) : + + // Execute immediately if ready is not present + selector( jQuery ); + } + + return jQuery.makeArray( selector, this ); + }; + +// Give the init function the jQuery prototype for later instantiation +init.prototype = jQuery.fn; + +// Initialize central reference +rootjQuery = jQuery( document ); + + +var rparentsprev = /^(?:parents|prev(?:Until|All))/, + + // Methods guaranteed to produce a unique set when starting from a unique set + guaranteedUnique = { + children: true, + contents: true, + next: true, + prev: true + }; + +jQuery.fn.extend( { + has: function( target ) { + var targets = jQuery( target, this ), + l = targets.length; + + return this.filter( function() { + var i = 0; + for ( ; i < l; i++ ) { + if ( jQuery.contains( this, targets[ i ] ) ) { + return true; + } + } + } ); + }, + + closest: function( selectors, context ) { + var cur, + i = 0, + l = this.length, + matched = [], + targets = typeof selectors !== "string" && jQuery( selectors ); + + // Positional selectors never match, since there's no _selection_ context + if ( !rneedsContext.test( selectors ) ) { + for ( ; i < l; i++ ) { + for ( cur = this[ i ]; cur && cur !== context; cur = cur.parentNode ) { + + // Always skip document fragments + if ( cur.nodeType < 11 && ( targets ? + targets.index( cur ) > -1 : + + // Don't pass non-elements to Sizzle + cur.nodeType === 1 && + jQuery.find.matchesSelector( cur, selectors ) ) ) { + + matched.push( cur ); + break; + } + } + } + } + + return this.pushStack( matched.length > 1 ? jQuery.uniqueSort( matched ) : matched ); + }, + + // Determine the position of an element within the set + index: function( elem ) { + + // No argument, return index in parent + if ( !elem ) { + return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1; + } + + // Index in selector + if ( typeof elem === "string" ) { + return indexOf.call( jQuery( elem ), this[ 0 ] ); + } + + // Locate the position of the desired element + return indexOf.call( this, + + // If it receives a jQuery object, the first element is used + elem.jquery ? elem[ 0 ] : elem + ); + }, + + add: function( selector, context ) { + return this.pushStack( + jQuery.uniqueSort( + jQuery.merge( this.get(), jQuery( selector, context ) ) + ) + ); + }, + + addBack: function( selector ) { + return this.add( selector == null ? + this.prevObject : this.prevObject.filter( selector ) + ); + } +} ); + +function sibling( cur, dir ) { + while ( ( cur = cur[ dir ] ) && cur.nodeType !== 1 ) {} + return cur; +} + +jQuery.each( { + parent: function( elem ) { + var parent = elem.parentNode; + return parent && parent.nodeType !== 11 ? parent : null; + }, + parents: function( elem ) { + return dir( elem, "parentNode" ); + }, + parentsUntil: function( elem, _i, until ) { + return dir( elem, "parentNode", until ); + }, + next: function( elem ) { + return sibling( elem, "nextSibling" ); + }, + prev: function( elem ) { + return sibling( elem, "previousSibling" ); + }, + nextAll: function( elem ) { + return dir( elem, "nextSibling" ); + }, + prevAll: function( elem ) { + return dir( elem, "previousSibling" ); + }, + nextUntil: function( elem, _i, until ) { + return dir( elem, "nextSibling", until ); + }, + prevUntil: function( elem, _i, until ) { + return dir( elem, "previousSibling", until ); + }, + siblings: function( elem ) { + return siblings( ( elem.parentNode || {} ).firstChild, elem ); + }, + children: function( elem ) { + return siblings( elem.firstChild ); + }, + contents: function( elem ) { + if ( elem.contentDocument != null && + + // Support: IE 11+ + // elements with no `data` attribute has an object + // `contentDocument` with a `null` prototype. + getProto( elem.contentDocument ) ) { + + return elem.contentDocument; + } + + // Support: IE 9 - 11 only, iOS 7 only, Android Browser <=4.3 only + // Treat the template element as a regular one in browsers that + // don't support it. + if ( nodeName( elem, "template" ) ) { + elem = elem.content || elem; + } + + return jQuery.merge( [], elem.childNodes ); + } +}, function( name, fn ) { + jQuery.fn[ name ] = function( until, selector ) { + var matched = jQuery.map( this, fn, until ); + + if ( name.slice( -5 ) !== "Until" ) { + selector = until; + } + + if ( selector && typeof selector === "string" ) { + matched = jQuery.filter( selector, matched ); + } + + if ( this.length > 1 ) { + + // Remove duplicates + if ( !guaranteedUnique[ name ] ) { + jQuery.uniqueSort( matched ); + } + + // Reverse order for parents* and prev-derivatives + if ( rparentsprev.test( name ) ) { + matched.reverse(); + } + } + + return this.pushStack( matched ); + }; +} ); +var rnothtmlwhite = ( /[^\x20\t\r\n\f]+/g ); + + + +// Convert String-formatted options into Object-formatted ones +function createOptions( options ) { + var object = {}; + jQuery.each( options.match( rnothtmlwhite ) || [], function( _, flag ) { + object[ flag ] = true; + } ); + return object; +} + +/* + * Create a callback list using the following parameters: + * + * options: an optional list of space-separated options that will change how + * the callback list behaves or a more traditional option object + * + * By default a callback list will act like an event callback list and can be + * "fired" multiple times. + * + * Possible options: + * + * once: will ensure the callback list can only be fired once (like a Deferred) + * + * memory: will keep track of previous values and will call any callback added + * after the list has been fired right away with the latest "memorized" + * values (like a Deferred) + * + * unique: will ensure a callback can only be added once (no duplicate in the list) + * + * stopOnFalse: interrupt callings when a callback returns false + * + */ +jQuery.Callbacks = function( options ) { + + // Convert options from String-formatted to Object-formatted if needed + // (we check in cache first) + options = typeof options === "string" ? + createOptions( options ) : + jQuery.extend( {}, options ); + + var // Flag to know if list is currently firing + firing, + + // Last fire value for non-forgettable lists + memory, + + // Flag to know if list was already fired + fired, + + // Flag to prevent firing + locked, + + // Actual callback list + list = [], + + // Queue of execution data for repeatable lists + queue = [], + + // Index of currently firing callback (modified by add/remove as needed) + firingIndex = -1, + + // Fire callbacks + fire = function() { + + // Enforce single-firing + locked = locked || options.once; + + // Execute callbacks for all pending executions, + // respecting firingIndex overrides and runtime changes + fired = firing = true; + for ( ; queue.length; firingIndex = -1 ) { + memory = queue.shift(); + while ( ++firingIndex < list.length ) { + + // Run callback and check for early termination + if ( list[ firingIndex ].apply( memory[ 0 ], memory[ 1 ] ) === false && + options.stopOnFalse ) { + + // Jump to end and forget the data so .add doesn't re-fire + firingIndex = list.length; + memory = false; + } + } + } + + // Forget the data if we're done with it + if ( !options.memory ) { + memory = false; + } + + firing = false; + + // Clean up if we're done firing for good + if ( locked ) { + + // Keep an empty list if we have data for future add calls + if ( memory ) { + list = []; + + // Otherwise, this object is spent + } else { + list = ""; + } + } + }, + + // Actual Callbacks object + self = { + + // Add a callback or a collection of callbacks to the list + add: function() { + if ( list ) { + + // If we have memory from a past run, we should fire after adding + if ( memory && !firing ) { + firingIndex = list.length - 1; + queue.push( memory ); + } + + ( function add( args ) { + jQuery.each( args, function( _, arg ) { + if ( isFunction( arg ) ) { + if ( !options.unique || !self.has( arg ) ) { + list.push( arg ); + } + } else if ( arg && arg.length && toType( arg ) !== "string" ) { + + // Inspect recursively + add( arg ); + } + } ); + } )( arguments ); + + if ( memory && !firing ) { + fire(); + } + } + return this; + }, + + // Remove a callback from the list + remove: function() { + jQuery.each( arguments, function( _, arg ) { + var index; + while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { + list.splice( index, 1 ); + + // Handle firing indexes + if ( index <= firingIndex ) { + firingIndex--; + } + } + } ); + return this; + }, + + // Check if a given callback is in the list. + // If no argument is given, return whether or not list has callbacks attached. + has: function( fn ) { + return fn ? + jQuery.inArray( fn, list ) > -1 : + list.length > 0; + }, + + // Remove all callbacks from the list + empty: function() { + if ( list ) { + list = []; + } + return this; + }, + + // Disable .fire and .add + // Abort any current/pending executions + // Clear all callbacks and values + disable: function() { + locked = queue = []; + list = memory = ""; + return this; + }, + disabled: function() { + return !list; + }, + + // Disable .fire + // Also disable .add unless we have memory (since it would have no effect) + // Abort any pending executions + lock: function() { + locked = queue = []; + if ( !memory && !firing ) { + list = memory = ""; + } + return this; + }, + locked: function() { + return !!locked; + }, + + // Call all callbacks with the given context and arguments + fireWith: function( context, args ) { + if ( !locked ) { + args = args || []; + args = [ context, args.slice ? args.slice() : args ]; + queue.push( args ); + if ( !firing ) { + fire(); + } + } + return this; + }, + + // Call all the callbacks with the given arguments + fire: function() { + self.fireWith( this, arguments ); + return this; + }, + + // To know if the callbacks have already been called at least once + fired: function() { + return !!fired; + } + }; + + return self; +}; + + +function Identity( v ) { + return v; +} +function Thrower( ex ) { + throw ex; +} + +function adoptValue( value, resolve, reject, noValue ) { + var method; + + try { + + // Check for promise aspect first to privilege synchronous behavior + if ( value && isFunction( ( method = value.promise ) ) ) { + method.call( value ).done( resolve ).fail( reject ); + + // Other thenables + } else if ( value && isFunction( ( method = value.then ) ) ) { + method.call( value, resolve, reject ); + + // Other non-thenables + } else { + + // Control `resolve` arguments by letting Array#slice cast boolean `noValue` to integer: + // * false: [ value ].slice( 0 ) => resolve( value ) + // * true: [ value ].slice( 1 ) => resolve() + resolve.apply( undefined, [ value ].slice( noValue ) ); + } + + // For Promises/A+, convert exceptions into rejections + // Since jQuery.when doesn't unwrap thenables, we can skip the extra checks appearing in + // Deferred#then to conditionally suppress rejection. + } catch ( value ) { + + // Support: Android 4.0 only + // Strict mode functions invoked without .call/.apply get global-object context + reject.apply( undefined, [ value ] ); + } +} + +jQuery.extend( { + + Deferred: function( func ) { + var tuples = [ + + // action, add listener, callbacks, + // ... .then handlers, argument index, [final state] + [ "notify", "progress", jQuery.Callbacks( "memory" ), + jQuery.Callbacks( "memory" ), 2 ], + [ "resolve", "done", jQuery.Callbacks( "once memory" ), + jQuery.Callbacks( "once memory" ), 0, "resolved" ], + [ "reject", "fail", jQuery.Callbacks( "once memory" ), + jQuery.Callbacks( "once memory" ), 1, "rejected" ] + ], + state = "pending", + promise = { + state: function() { + return state; + }, + always: function() { + deferred.done( arguments ).fail( arguments ); + return this; + }, + "catch": function( fn ) { + return promise.then( null, fn ); + }, + + // Keep pipe for back-compat + pipe: function( /* fnDone, fnFail, fnProgress */ ) { + var fns = arguments; + + return jQuery.Deferred( function( newDefer ) { + jQuery.each( tuples, function( _i, tuple ) { + + // Map tuples (progress, done, fail) to arguments (done, fail, progress) + var fn = isFunction( fns[ tuple[ 4 ] ] ) && fns[ tuple[ 4 ] ]; + + // deferred.progress(function() { bind to newDefer or newDefer.notify }) + // deferred.done(function() { bind to newDefer or newDefer.resolve }) + // deferred.fail(function() { bind to newDefer or newDefer.reject }) + deferred[ tuple[ 1 ] ]( function() { + var returned = fn && fn.apply( this, arguments ); + if ( returned && isFunction( returned.promise ) ) { + returned.promise() + .progress( newDefer.notify ) + .done( newDefer.resolve ) + .fail( newDefer.reject ); + } else { + newDefer[ tuple[ 0 ] + "With" ]( + this, + fn ? [ returned ] : arguments + ); + } + } ); + } ); + fns = null; + } ).promise(); + }, + then: function( onFulfilled, onRejected, onProgress ) { + var maxDepth = 0; + function resolve( depth, deferred, handler, special ) { + return function() { + var that = this, + args = arguments, + mightThrow = function() { + var returned, then; + + // Support: Promises/A+ section 2.3.3.3.3 + // https://promisesaplus.com/#point-59 + // Ignore double-resolution attempts + if ( depth < maxDepth ) { + return; + } + + returned = handler.apply( that, args ); + + // Support: Promises/A+ section 2.3.1 + // https://promisesaplus.com/#point-48 + if ( returned === deferred.promise() ) { + throw new TypeError( "Thenable self-resolution" ); + } + + // Support: Promises/A+ sections 2.3.3.1, 3.5 + // https://promisesaplus.com/#point-54 + // https://promisesaplus.com/#point-75 + // Retrieve `then` only once + then = returned && + + // Support: Promises/A+ section 2.3.4 + // https://promisesaplus.com/#point-64 + // Only check objects and functions for thenability + ( typeof returned === "object" || + typeof returned === "function" ) && + returned.then; + + // Handle a returned thenable + if ( isFunction( then ) ) { + + // Special processors (notify) just wait for resolution + if ( special ) { + then.call( + returned, + resolve( maxDepth, deferred, Identity, special ), + resolve( maxDepth, deferred, Thrower, special ) + ); + + // Normal processors (resolve) also hook into progress + } else { + + // ...and disregard older resolution values + maxDepth++; + + then.call( + returned, + resolve( maxDepth, deferred, Identity, special ), + resolve( maxDepth, deferred, Thrower, special ), + resolve( maxDepth, deferred, Identity, + deferred.notifyWith ) + ); + } + + // Handle all other returned values + } else { + + // Only substitute handlers pass on context + // and multiple values (non-spec behavior) + if ( handler !== Identity ) { + that = undefined; + args = [ returned ]; + } + + // Process the value(s) + // Default process is resolve + ( special || deferred.resolveWith )( that, args ); + } + }, + + // Only normal processors (resolve) catch and reject exceptions + process = special ? + mightThrow : + function() { + try { + mightThrow(); + } catch ( e ) { + + if ( jQuery.Deferred.exceptionHook ) { + jQuery.Deferred.exceptionHook( e, + process.stackTrace ); + } + + // Support: Promises/A+ section 2.3.3.3.4.1 + // https://promisesaplus.com/#point-61 + // Ignore post-resolution exceptions + if ( depth + 1 >= maxDepth ) { + + // Only substitute handlers pass on context + // and multiple values (non-spec behavior) + if ( handler !== Thrower ) { + that = undefined; + args = [ e ]; + } + + deferred.rejectWith( that, args ); + } + } + }; + + // Support: Promises/A+ section 2.3.3.3.1 + // https://promisesaplus.com/#point-57 + // Re-resolve promises immediately to dodge false rejection from + // subsequent errors + if ( depth ) { + process(); + } else { + + // Call an optional hook to record the stack, in case of exception + // since it's otherwise lost when execution goes async + if ( jQuery.Deferred.getStackHook ) { + process.stackTrace = jQuery.Deferred.getStackHook(); + } + window.setTimeout( process ); + } + }; + } + + return jQuery.Deferred( function( newDefer ) { + + // progress_handlers.add( ... ) + tuples[ 0 ][ 3 ].add( + resolve( + 0, + newDefer, + isFunction( onProgress ) ? + onProgress : + Identity, + newDefer.notifyWith + ) + ); + + // fulfilled_handlers.add( ... ) + tuples[ 1 ][ 3 ].add( + resolve( + 0, + newDefer, + isFunction( onFulfilled ) ? + onFulfilled : + Identity + ) + ); + + // rejected_handlers.add( ... ) + tuples[ 2 ][ 3 ].add( + resolve( + 0, + newDefer, + isFunction( onRejected ) ? + onRejected : + Thrower + ) + ); + } ).promise(); + }, + + // Get a promise for this deferred + // If obj is provided, the promise aspect is added to the object + promise: function( obj ) { + return obj != null ? jQuery.extend( obj, promise ) : promise; + } + }, + deferred = {}; + + // Add list-specific methods + jQuery.each( tuples, function( i, tuple ) { + var list = tuple[ 2 ], + stateString = tuple[ 5 ]; + + // promise.progress = list.add + // promise.done = list.add + // promise.fail = list.add + promise[ tuple[ 1 ] ] = list.add; + + // Handle state + if ( stateString ) { + list.add( + function() { + + // state = "resolved" (i.e., fulfilled) + // state = "rejected" + state = stateString; + }, + + // rejected_callbacks.disable + // fulfilled_callbacks.disable + tuples[ 3 - i ][ 2 ].disable, + + // rejected_handlers.disable + // fulfilled_handlers.disable + tuples[ 3 - i ][ 3 ].disable, + + // progress_callbacks.lock + tuples[ 0 ][ 2 ].lock, + + // progress_handlers.lock + tuples[ 0 ][ 3 ].lock + ); + } + + // progress_handlers.fire + // fulfilled_handlers.fire + // rejected_handlers.fire + list.add( tuple[ 3 ].fire ); + + // deferred.notify = function() { deferred.notifyWith(...) } + // deferred.resolve = function() { deferred.resolveWith(...) } + // deferred.reject = function() { deferred.rejectWith(...) } + deferred[ tuple[ 0 ] ] = function() { + deferred[ tuple[ 0 ] + "With" ]( this === deferred ? undefined : this, arguments ); + return this; + }; + + // deferred.notifyWith = list.fireWith + // deferred.resolveWith = list.fireWith + // deferred.rejectWith = list.fireWith + deferred[ tuple[ 0 ] + "With" ] = list.fireWith; + } ); + + // Make the deferred a promise + promise.promise( deferred ); + + // Call given func if any + if ( func ) { + func.call( deferred, deferred ); + } + + // All done! + return deferred; + }, + + // Deferred helper + when: function( singleValue ) { + var + + // count of uncompleted subordinates + remaining = arguments.length, + + // count of unprocessed arguments + i = remaining, + + // subordinate fulfillment data + resolveContexts = Array( i ), + resolveValues = slice.call( arguments ), + + // the master Deferred + master = jQuery.Deferred(), + + // subordinate callback factory + updateFunc = function( i ) { + return function( value ) { + resolveContexts[ i ] = this; + resolveValues[ i ] = arguments.length > 1 ? slice.call( arguments ) : value; + if ( !( --remaining ) ) { + master.resolveWith( resolveContexts, resolveValues ); + } + }; + }; + + // Single- and empty arguments are adopted like Promise.resolve + if ( remaining <= 1 ) { + adoptValue( singleValue, master.done( updateFunc( i ) ).resolve, master.reject, + !remaining ); + + // Use .then() to unwrap secondary thenables (cf. gh-3000) + if ( master.state() === "pending" || + isFunction( resolveValues[ i ] && resolveValues[ i ].then ) ) { + + return master.then(); + } + } + + // Multiple arguments are aggregated like Promise.all array elements + while ( i-- ) { + adoptValue( resolveValues[ i ], updateFunc( i ), master.reject ); + } + + return master.promise(); + } +} ); + + +// These usually indicate a programmer mistake during development, +// warn about them ASAP rather than swallowing them by default. +var rerrorNames = /^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/; + +jQuery.Deferred.exceptionHook = function( error, stack ) { + + // Support: IE 8 - 9 only + // Console exists when dev tools are open, which can happen at any time + if ( window.console && window.console.warn && error && rerrorNames.test( error.name ) ) { + window.console.warn( "jQuery.Deferred exception: " + error.message, error.stack, stack ); + } +}; + + + + +jQuery.readyException = function( error ) { + window.setTimeout( function() { + throw error; + } ); +}; + + + + +// The deferred used on DOM ready +var readyList = jQuery.Deferred(); + +jQuery.fn.ready = function( fn ) { + + readyList + .then( fn ) + + // Wrap jQuery.readyException in a function so that the lookup + // happens at the time of error handling instead of callback + // registration. + .catch( function( error ) { + jQuery.readyException( error ); + } ); + + return this; +}; + +jQuery.extend( { + + // Is the DOM ready to be used? Set to true once it occurs. + isReady: false, + + // A counter to track how many items to wait for before + // the ready event fires. See #6781 + readyWait: 1, + + // Handle when the DOM is ready + ready: function( wait ) { + + // Abort if there are pending holds or we're already ready + if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) { + return; + } + + // Remember that the DOM is ready + jQuery.isReady = true; + + // If a normal DOM Ready event fired, decrement, and wait if need be + if ( wait !== true && --jQuery.readyWait > 0 ) { + return; + } + + // If there are functions bound, to execute + readyList.resolveWith( document, [ jQuery ] ); + } +} ); + +jQuery.ready.then = readyList.then; + +// The ready event handler and self cleanup method +function completed() { + document.removeEventListener( "DOMContentLoaded", completed ); + window.removeEventListener( "load", completed ); + jQuery.ready(); +} + +// Catch cases where $(document).ready() is called +// after the browser event has already occurred. +// Support: IE <=9 - 10 only +// Older IE sometimes signals "interactive" too soon +if ( document.readyState === "complete" || + ( document.readyState !== "loading" && !document.documentElement.doScroll ) ) { + + // Handle it asynchronously to allow scripts the opportunity to delay ready + window.setTimeout( jQuery.ready ); + +} else { + + // Use the handy event callback + document.addEventListener( "DOMContentLoaded", completed ); + + // A fallback to window.onload, that will always work + window.addEventListener( "load", completed ); +} + + + + +// Multifunctional method to get and set values of a collection +// The value/s can optionally be executed if it's a function +var access = function( elems, fn, key, value, chainable, emptyGet, raw ) { + var i = 0, + len = elems.length, + bulk = key == null; + + // Sets many values + if ( toType( key ) === "object" ) { + chainable = true; + for ( i in key ) { + access( elems, fn, i, key[ i ], true, emptyGet, raw ); + } + + // Sets one value + } else if ( value !== undefined ) { + chainable = true; + + if ( !isFunction( value ) ) { + raw = true; + } + + if ( bulk ) { + + // Bulk operations run against the entire set + if ( raw ) { + fn.call( elems, value ); + fn = null; + + // ...except when executing function values + } else { + bulk = fn; + fn = function( elem, _key, value ) { + return bulk.call( jQuery( elem ), value ); + }; + } + } + + if ( fn ) { + for ( ; i < len; i++ ) { + fn( + elems[ i ], key, raw ? + value : + value.call( elems[ i ], i, fn( elems[ i ], key ) ) + ); + } + } + } + + if ( chainable ) { + return elems; + } + + // Gets + if ( bulk ) { + return fn.call( elems ); + } + + return len ? fn( elems[ 0 ], key ) : emptyGet; +}; + + +// Matches dashed string for camelizing +var rmsPrefix = /^-ms-/, + rdashAlpha = /-([a-z])/g; + +// Used by camelCase as callback to replace() +function fcamelCase( _all, letter ) { + return letter.toUpperCase(); +} + +// Convert dashed to camelCase; used by the css and data modules +// Support: IE <=9 - 11, Edge 12 - 15 +// Microsoft forgot to hump their vendor prefix (#9572) +function camelCase( string ) { + return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); +} +var acceptData = function( owner ) { + + // Accepts only: + // - Node + // - Node.ELEMENT_NODE + // - Node.DOCUMENT_NODE + // - Object + // - Any + return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType ); +}; + + + + +function Data() { + this.expando = jQuery.expando + Data.uid++; +} + +Data.uid = 1; + +Data.prototype = { + + cache: function( owner ) { + + // Check if the owner object already has a cache + var value = owner[ this.expando ]; + + // If not, create one + if ( !value ) { + value = {}; + + // We can accept data for non-element nodes in modern browsers, + // but we should not, see #8335. + // Always return an empty object. + if ( acceptData( owner ) ) { + + // If it is a node unlikely to be stringify-ed or looped over + // use plain assignment + if ( owner.nodeType ) { + owner[ this.expando ] = value; + + // Otherwise secure it in a non-enumerable property + // configurable must be true to allow the property to be + // deleted when data is removed + } else { + Object.defineProperty( owner, this.expando, { + value: value, + configurable: true + } ); + } + } + } + + return value; + }, + set: function( owner, data, value ) { + var prop, + cache = this.cache( owner ); + + // Handle: [ owner, key, value ] args + // Always use camelCase key (gh-2257) + if ( typeof data === "string" ) { + cache[ camelCase( data ) ] = value; + + // Handle: [ owner, { properties } ] args + } else { + + // Copy the properties one-by-one to the cache object + for ( prop in data ) { + cache[ camelCase( prop ) ] = data[ prop ]; + } + } + return cache; + }, + get: function( owner, key ) { + return key === undefined ? + this.cache( owner ) : + + // Always use camelCase key (gh-2257) + owner[ this.expando ] && owner[ this.expando ][ camelCase( key ) ]; + }, + access: function( owner, key, value ) { + + // In cases where either: + // + // 1. No key was specified + // 2. A string key was specified, but no value provided + // + // Take the "read" path and allow the get method to determine + // which value to return, respectively either: + // + // 1. The entire cache object + // 2. The data stored at the key + // + if ( key === undefined || + ( ( key && typeof key === "string" ) && value === undefined ) ) { + + return this.get( owner, key ); + } + + // When the key is not a string, or both a key and value + // are specified, set or extend (existing objects) with either: + // + // 1. An object of properties + // 2. A key and value + // + this.set( owner, key, value ); + + // Since the "set" path can have two possible entry points + // return the expected data based on which path was taken[*] + return value !== undefined ? value : key; + }, + remove: function( owner, key ) { + var i, + cache = owner[ this.expando ]; + + if ( cache === undefined ) { + return; + } + + if ( key !== undefined ) { + + // Support array or space separated string of keys + if ( Array.isArray( key ) ) { + + // If key is an array of keys... + // We always set camelCase keys, so remove that. + key = key.map( camelCase ); + } else { + key = camelCase( key ); + + // If a key with the spaces exists, use it. + // Otherwise, create an array by matching non-whitespace + key = key in cache ? + [ key ] : + ( key.match( rnothtmlwhite ) || [] ); + } + + i = key.length; + + while ( i-- ) { + delete cache[ key[ i ] ]; + } + } + + // Remove the expando if there's no more data + if ( key === undefined || jQuery.isEmptyObject( cache ) ) { + + // Support: Chrome <=35 - 45 + // Webkit & Blink performance suffers when deleting properties + // from DOM nodes, so set to undefined instead + // https://bugs.chromium.org/p/chromium/issues/detail?id=378607 (bug restricted) + if ( owner.nodeType ) { + owner[ this.expando ] = undefined; + } else { + delete owner[ this.expando ]; + } + } + }, + hasData: function( owner ) { + var cache = owner[ this.expando ]; + return cache !== undefined && !jQuery.isEmptyObject( cache ); + } +}; +var dataPriv = new Data(); + +var dataUser = new Data(); + + + +// Implementation Summary +// +// 1. Enforce API surface and semantic compatibility with 1.9.x branch +// 2. Improve the module's maintainability by reducing the storage +// paths to a single mechanism. +// 3. Use the same single mechanism to support "private" and "user" data. +// 4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData) +// 5. Avoid exposing implementation details on user objects (eg. expando properties) +// 6. Provide a clear path for implementation upgrade to WeakMap in 2014 + +var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/, + rmultiDash = /[A-Z]/g; + +function getData( data ) { + if ( data === "true" ) { + return true; + } + + if ( data === "false" ) { + return false; + } + + if ( data === "null" ) { + return null; + } + + // Only convert to a number if it doesn't change the string + if ( data === +data + "" ) { + return +data; + } + + if ( rbrace.test( data ) ) { + return JSON.parse( data ); + } + + return data; +} + +function dataAttr( elem, key, data ) { + var name; + + // If nothing was found internally, try to fetch any + // data from the HTML5 data-* attribute + if ( data === undefined && elem.nodeType === 1 ) { + name = "data-" + key.replace( rmultiDash, "-$&" ).toLowerCase(); + data = elem.getAttribute( name ); + + if ( typeof data === "string" ) { + try { + data = getData( data ); + } catch ( e ) {} + + // Make sure we set the data so it isn't changed later + dataUser.set( elem, key, data ); + } else { + data = undefined; + } + } + return data; +} + +jQuery.extend( { + hasData: function( elem ) { + return dataUser.hasData( elem ) || dataPriv.hasData( elem ); + }, + + data: function( elem, name, data ) { + return dataUser.access( elem, name, data ); + }, + + removeData: function( elem, name ) { + dataUser.remove( elem, name ); + }, + + // TODO: Now that all calls to _data and _removeData have been replaced + // with direct calls to dataPriv methods, these can be deprecated. + _data: function( elem, name, data ) { + return dataPriv.access( elem, name, data ); + }, + + _removeData: function( elem, name ) { + dataPriv.remove( elem, name ); + } +} ); + +jQuery.fn.extend( { + data: function( key, value ) { + var i, name, data, + elem = this[ 0 ], + attrs = elem && elem.attributes; + + // Gets all values + if ( key === undefined ) { + if ( this.length ) { + data = dataUser.get( elem ); + + if ( elem.nodeType === 1 && !dataPriv.get( elem, "hasDataAttrs" ) ) { + i = attrs.length; + while ( i-- ) { + + // Support: IE 11 only + // The attrs elements can be null (#14894) + if ( attrs[ i ] ) { + name = attrs[ i ].name; + if ( name.indexOf( "data-" ) === 0 ) { + name = camelCase( name.slice( 5 ) ); + dataAttr( elem, name, data[ name ] ); + } + } + } + dataPriv.set( elem, "hasDataAttrs", true ); + } + } + + return data; + } + + // Sets multiple values + if ( typeof key === "object" ) { + return this.each( function() { + dataUser.set( this, key ); + } ); + } + + return access( this, function( value ) { + var data; + + // The calling jQuery object (element matches) is not empty + // (and therefore has an element appears at this[ 0 ]) and the + // `value` parameter was not undefined. An empty jQuery object + // will result in `undefined` for elem = this[ 0 ] which will + // throw an exception if an attempt to read a data cache is made. + if ( elem && value === undefined ) { + + // Attempt to get data from the cache + // The key will always be camelCased in Data + data = dataUser.get( elem, key ); + if ( data !== undefined ) { + return data; + } + + // Attempt to "discover" the data in + // HTML5 custom data-* attrs + data = dataAttr( elem, key ); + if ( data !== undefined ) { + return data; + } + + // We tried really hard, but the data doesn't exist. + return; + } + + // Set the data... + this.each( function() { + + // We always store the camelCased key + dataUser.set( this, key, value ); + } ); + }, null, value, arguments.length > 1, null, true ); + }, + + removeData: function( key ) { + return this.each( function() { + dataUser.remove( this, key ); + } ); + } +} ); + + +jQuery.extend( { + queue: function( elem, type, data ) { + var queue; + + if ( elem ) { + type = ( type || "fx" ) + "queue"; + queue = dataPriv.get( elem, type ); + + // Speed up dequeue by getting out quickly if this is just a lookup + if ( data ) { + if ( !queue || Array.isArray( data ) ) { + queue = dataPriv.access( elem, type, jQuery.makeArray( data ) ); + } else { + queue.push( data ); + } + } + return queue || []; + } + }, + + dequeue: function( elem, type ) { + type = type || "fx"; + + var queue = jQuery.queue( elem, type ), + startLength = queue.length, + fn = queue.shift(), + hooks = jQuery._queueHooks( elem, type ), + next = function() { + jQuery.dequeue( elem, type ); + }; + + // If the fx queue is dequeued, always remove the progress sentinel + if ( fn === "inprogress" ) { + fn = queue.shift(); + startLength--; + } + + if ( fn ) { + + // Add a progress sentinel to prevent the fx queue from being + // automatically dequeued + if ( type === "fx" ) { + queue.unshift( "inprogress" ); + } + + // Clear up the last queue stop function + delete hooks.stop; + fn.call( elem, next, hooks ); + } + + if ( !startLength && hooks ) { + hooks.empty.fire(); + } + }, + + // Not public - generate a queueHooks object, or return the current one + _queueHooks: function( elem, type ) { + var key = type + "queueHooks"; + return dataPriv.get( elem, key ) || dataPriv.access( elem, key, { + empty: jQuery.Callbacks( "once memory" ).add( function() { + dataPriv.remove( elem, [ type + "queue", key ] ); + } ) + } ); + } +} ); + +jQuery.fn.extend( { + queue: function( type, data ) { + var setter = 2; + + if ( typeof type !== "string" ) { + data = type; + type = "fx"; + setter--; + } + + if ( arguments.length < setter ) { + return jQuery.queue( this[ 0 ], type ); + } + + return data === undefined ? + this : + this.each( function() { + var queue = jQuery.queue( this, type, data ); + + // Ensure a hooks for this queue + jQuery._queueHooks( this, type ); + + if ( type === "fx" && queue[ 0 ] !== "inprogress" ) { + jQuery.dequeue( this, type ); + } + } ); + }, + dequeue: function( type ) { + return this.each( function() { + jQuery.dequeue( this, type ); + } ); + }, + clearQueue: function( type ) { + return this.queue( type || "fx", [] ); + }, + + // Get a promise resolved when queues of a certain type + // are emptied (fx is the type by default) + promise: function( type, obj ) { + var tmp, + count = 1, + defer = jQuery.Deferred(), + elements = this, + i = this.length, + resolve = function() { + if ( !( --count ) ) { + defer.resolveWith( elements, [ elements ] ); + } + }; + + if ( typeof type !== "string" ) { + obj = type; + type = undefined; + } + type = type || "fx"; + + while ( i-- ) { + tmp = dataPriv.get( elements[ i ], type + "queueHooks" ); + if ( tmp && tmp.empty ) { + count++; + tmp.empty.add( resolve ); + } + } + resolve(); + return defer.promise( obj ); + } +} ); +var pnum = ( /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/ ).source; + +var rcssNum = new RegExp( "^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i" ); + + +var cssExpand = [ "Top", "Right", "Bottom", "Left" ]; + +var documentElement = document.documentElement; + + + + var isAttached = function( elem ) { + return jQuery.contains( elem.ownerDocument, elem ); + }, + composed = { composed: true }; + + // Support: IE 9 - 11+, Edge 12 - 18+, iOS 10.0 - 10.2 only + // Check attachment across shadow DOM boundaries when possible (gh-3504) + // Support: iOS 10.0-10.2 only + // Early iOS 10 versions support `attachShadow` but not `getRootNode`, + // leading to errors. We need to check for `getRootNode`. + if ( documentElement.getRootNode ) { + isAttached = function( elem ) { + return jQuery.contains( elem.ownerDocument, elem ) || + elem.getRootNode( composed ) === elem.ownerDocument; + }; + } +var isHiddenWithinTree = function( elem, el ) { + + // isHiddenWithinTree might be called from jQuery#filter function; + // in that case, element will be second argument + elem = el || elem; + + // Inline style trumps all + return elem.style.display === "none" || + elem.style.display === "" && + + // Otherwise, check computed style + // Support: Firefox <=43 - 45 + // Disconnected elements can have computed display: none, so first confirm that elem is + // in the document. + isAttached( elem ) && + + jQuery.css( elem, "display" ) === "none"; + }; + + + +function adjustCSS( elem, prop, valueParts, tween ) { + var adjusted, scale, + maxIterations = 20, + currentValue = tween ? + function() { + return tween.cur(); + } : + function() { + return jQuery.css( elem, prop, "" ); + }, + initial = currentValue(), + unit = valueParts && valueParts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ), + + // Starting value computation is required for potential unit mismatches + initialInUnit = elem.nodeType && + ( jQuery.cssNumber[ prop ] || unit !== "px" && +initial ) && + rcssNum.exec( jQuery.css( elem, prop ) ); + + if ( initialInUnit && initialInUnit[ 3 ] !== unit ) { + + // Support: Firefox <=54 + // Halve the iteration target value to prevent interference from CSS upper bounds (gh-2144) + initial = initial / 2; + + // Trust units reported by jQuery.css + unit = unit || initialInUnit[ 3 ]; + + // Iteratively approximate from a nonzero starting point + initialInUnit = +initial || 1; + + while ( maxIterations-- ) { + + // Evaluate and update our best guess (doubling guesses that zero out). + // Finish if the scale equals or crosses 1 (making the old*new product non-positive). + jQuery.style( elem, prop, initialInUnit + unit ); + if ( ( 1 - scale ) * ( 1 - ( scale = currentValue() / initial || 0.5 ) ) <= 0 ) { + maxIterations = 0; + } + initialInUnit = initialInUnit / scale; + + } + + initialInUnit = initialInUnit * 2; + jQuery.style( elem, prop, initialInUnit + unit ); + + // Make sure we update the tween properties later on + valueParts = valueParts || []; + } + + if ( valueParts ) { + initialInUnit = +initialInUnit || +initial || 0; + + // Apply relative offset (+=/-=) if specified + adjusted = valueParts[ 1 ] ? + initialInUnit + ( valueParts[ 1 ] + 1 ) * valueParts[ 2 ] : + +valueParts[ 2 ]; + if ( tween ) { + tween.unit = unit; + tween.start = initialInUnit; + tween.end = adjusted; + } + } + return adjusted; +} + + +var defaultDisplayMap = {}; + +function getDefaultDisplay( elem ) { + var temp, + doc = elem.ownerDocument, + nodeName = elem.nodeName, + display = defaultDisplayMap[ nodeName ]; + + if ( display ) { + return display; + } + + temp = doc.body.appendChild( doc.createElement( nodeName ) ); + display = jQuery.css( temp, "display" ); + + temp.parentNode.removeChild( temp ); + + if ( display === "none" ) { + display = "block"; + } + defaultDisplayMap[ nodeName ] = display; + + return display; +} + +function showHide( elements, show ) { + var display, elem, + values = [], + index = 0, + length = elements.length; + + // Determine new display value for elements that need to change + for ( ; index < length; index++ ) { + elem = elements[ index ]; + if ( !elem.style ) { + continue; + } + + display = elem.style.display; + if ( show ) { + + // Since we force visibility upon cascade-hidden elements, an immediate (and slow) + // check is required in this first loop unless we have a nonempty display value (either + // inline or about-to-be-restored) + if ( display === "none" ) { + values[ index ] = dataPriv.get( elem, "display" ) || null; + if ( !values[ index ] ) { + elem.style.display = ""; + } + } + if ( elem.style.display === "" && isHiddenWithinTree( elem ) ) { + values[ index ] = getDefaultDisplay( elem ); + } + } else { + if ( display !== "none" ) { + values[ index ] = "none"; + + // Remember what we're overwriting + dataPriv.set( elem, "display", display ); + } + } + } + + // Set the display of the elements in a second loop to avoid constant reflow + for ( index = 0; index < length; index++ ) { + if ( values[ index ] != null ) { + elements[ index ].style.display = values[ index ]; + } + } + + return elements; +} + +jQuery.fn.extend( { + show: function() { + return showHide( this, true ); + }, + hide: function() { + return showHide( this ); + }, + toggle: function( state ) { + if ( typeof state === "boolean" ) { + return state ? this.show() : this.hide(); + } + + return this.each( function() { + if ( isHiddenWithinTree( this ) ) { + jQuery( this ).show(); + } else { + jQuery( this ).hide(); + } + } ); + } +} ); +var rcheckableType = ( /^(?:checkbox|radio)$/i ); + +var rtagName = ( /<([a-z][^\/\0>\x20\t\r\n\f]*)/i ); + +var rscriptType = ( /^$|^module$|\/(?:java|ecma)script/i ); + + + +( function() { + var fragment = document.createDocumentFragment(), + div = fragment.appendChild( document.createElement( "div" ) ), + input = document.createElement( "input" ); + + // Support: Android 4.0 - 4.3 only + // Check state lost if the name is set (#11217) + // Support: Windows Web Apps (WWA) + // `name` and `type` must use .setAttribute for WWA (#14901) + input.setAttribute( "type", "radio" ); + input.setAttribute( "checked", "checked" ); + input.setAttribute( "name", "t" ); + + div.appendChild( input ); + + // Support: Android <=4.1 only + // Older WebKit doesn't clone checked state correctly in fragments + support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked; + + // Support: IE <=11 only + // Make sure textarea (and checkbox) defaultValue is properly cloned + div.innerHTML = ""; + support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue; + + // Support: IE <=9 only + // IE <=9 replaces "; + support.option = !!div.lastChild; +} )(); + + +// We have to close these tags to support XHTML (#13200) +var wrapMap = { + + // XHTML parsers do not magically insert elements in the + // same way that tag soup parsers do. So we cannot shorten + // this by omitting or other required elements. + thead: [ 1, "", "
" ], + col: [ 2, "", "
" ], + tr: [ 2, "", "
" ], + td: [ 3, "", "
" ], + + _default: [ 0, "", "" ] +}; + +wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; +wrapMap.th = wrapMap.td; + +// Support: IE <=9 only +if ( !support.option ) { + wrapMap.optgroup = wrapMap.option = [ 1, "" ]; +} + + +function getAll( context, tag ) { + + // Support: IE <=9 - 11 only + // Use typeof to avoid zero-argument method invocation on host objects (#15151) + var ret; + + if ( typeof context.getElementsByTagName !== "undefined" ) { + ret = context.getElementsByTagName( tag || "*" ); + + } else if ( typeof context.querySelectorAll !== "undefined" ) { + ret = context.querySelectorAll( tag || "*" ); + + } else { + ret = []; + } + + if ( tag === undefined || tag && nodeName( context, tag ) ) { + return jQuery.merge( [ context ], ret ); + } + + return ret; +} + + +// Mark scripts as having already been evaluated +function setGlobalEval( elems, refElements ) { + var i = 0, + l = elems.length; + + for ( ; i < l; i++ ) { + dataPriv.set( + elems[ i ], + "globalEval", + !refElements || dataPriv.get( refElements[ i ], "globalEval" ) + ); + } +} + + +var rhtml = /<|&#?\w+;/; + +function buildFragment( elems, context, scripts, selection, ignored ) { + var elem, tmp, tag, wrap, attached, j, + fragment = context.createDocumentFragment(), + nodes = [], + i = 0, + l = elems.length; + + for ( ; i < l; i++ ) { + elem = elems[ i ]; + + if ( elem || elem === 0 ) { + + // Add nodes directly + if ( toType( elem ) === "object" ) { + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem ); + + // Convert non-html into a text node + } else if ( !rhtml.test( elem ) ) { + nodes.push( context.createTextNode( elem ) ); + + // Convert html into DOM nodes + } else { + tmp = tmp || fragment.appendChild( context.createElement( "div" ) ); + + // Deserialize a standard representation + tag = ( rtagName.exec( elem ) || [ "", "" ] )[ 1 ].toLowerCase(); + wrap = wrapMap[ tag ] || wrapMap._default; + tmp.innerHTML = wrap[ 1 ] + jQuery.htmlPrefilter( elem ) + wrap[ 2 ]; + + // Descend through wrappers to the right content + j = wrap[ 0 ]; + while ( j-- ) { + tmp = tmp.lastChild; + } + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( nodes, tmp.childNodes ); + + // Remember the top-level container + tmp = fragment.firstChild; + + // Ensure the created nodes are orphaned (#12392) + tmp.textContent = ""; + } + } + } + + // Remove wrapper from fragment + fragment.textContent = ""; + + i = 0; + while ( ( elem = nodes[ i++ ] ) ) { + + // Skip elements already in the context collection (trac-4087) + if ( selection && jQuery.inArray( elem, selection ) > -1 ) { + if ( ignored ) { + ignored.push( elem ); + } + continue; + } + + attached = isAttached( elem ); + + // Append to fragment + tmp = getAll( fragment.appendChild( elem ), "script" ); + + // Preserve script evaluation history + if ( attached ) { + setGlobalEval( tmp ); + } + + // Capture executables + if ( scripts ) { + j = 0; + while ( ( elem = tmp[ j++ ] ) ) { + if ( rscriptType.test( elem.type || "" ) ) { + scripts.push( elem ); + } + } + } + } + + return fragment; +} + + +var + rkeyEvent = /^key/, + rmouseEvent = /^(?:mouse|pointer|contextmenu|drag|drop)|click/, + rtypenamespace = /^([^.]*)(?:\.(.+)|)/; + +function returnTrue() { + return true; +} + +function returnFalse() { + return false; +} + +// Support: IE <=9 - 11+ +// focus() and blur() are asynchronous, except when they are no-op. +// So expect focus to be synchronous when the element is already active, +// and blur to be synchronous when the element is not already active. +// (focus and blur are always synchronous in other supported browsers, +// this just defines when we can count on it). +function expectSync( elem, type ) { + return ( elem === safeActiveElement() ) === ( type === "focus" ); +} + +// Support: IE <=9 only +// Accessing document.activeElement can throw unexpectedly +// https://bugs.jquery.com/ticket/13393 +function safeActiveElement() { + try { + return document.activeElement; + } catch ( err ) { } +} + +function on( elem, types, selector, data, fn, one ) { + var origFn, type; + + // Types can be a map of types/handlers + if ( typeof types === "object" ) { + + // ( types-Object, selector, data ) + if ( typeof selector !== "string" ) { + + // ( types-Object, data ) + data = data || selector; + selector = undefined; + } + for ( type in types ) { + on( elem, type, selector, data, types[ type ], one ); + } + return elem; + } + + if ( data == null && fn == null ) { + + // ( types, fn ) + fn = selector; + data = selector = undefined; + } else if ( fn == null ) { + if ( typeof selector === "string" ) { + + // ( types, selector, fn ) + fn = data; + data = undefined; + } else { + + // ( types, data, fn ) + fn = data; + data = selector; + selector = undefined; + } + } + if ( fn === false ) { + fn = returnFalse; + } else if ( !fn ) { + return elem; + } + + if ( one === 1 ) { + origFn = fn; + fn = function( event ) { + + // Can use an empty set, since event contains the info + jQuery().off( event ); + return origFn.apply( this, arguments ); + }; + + // Use same guid so caller can remove using origFn + fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); + } + return elem.each( function() { + jQuery.event.add( this, types, fn, data, selector ); + } ); +} + +/* + * Helper functions for managing events -- not part of the public interface. + * Props to Dean Edwards' addEvent library for many of the ideas. + */ +jQuery.event = { + + global: {}, + + add: function( elem, types, handler, data, selector ) { + + var handleObjIn, eventHandle, tmp, + events, t, handleObj, + special, handlers, type, namespaces, origType, + elemData = dataPriv.get( elem ); + + // Only attach events to objects that accept data + if ( !acceptData( elem ) ) { + return; + } + + // Caller can pass in an object of custom data in lieu of the handler + if ( handler.handler ) { + handleObjIn = handler; + handler = handleObjIn.handler; + selector = handleObjIn.selector; + } + + // Ensure that invalid selectors throw exceptions at attach time + // Evaluate against documentElement in case elem is a non-element node (e.g., document) + if ( selector ) { + jQuery.find.matchesSelector( documentElement, selector ); + } + + // Make sure that the handler has a unique ID, used to find/remove it later + if ( !handler.guid ) { + handler.guid = jQuery.guid++; + } + + // Init the element's event structure and main handler, if this is the first + if ( !( events = elemData.events ) ) { + events = elemData.events = Object.create( null ); + } + if ( !( eventHandle = elemData.handle ) ) { + eventHandle = elemData.handle = function( e ) { + + // Discard the second event of a jQuery.event.trigger() and + // when an event is called after a page has unloaded + return typeof jQuery !== "undefined" && jQuery.event.triggered !== e.type ? + jQuery.event.dispatch.apply( elem, arguments ) : undefined; + }; + } + + // Handle multiple events separated by a space + types = ( types || "" ).match( rnothtmlwhite ) || [ "" ]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[ t ] ) || []; + type = origType = tmp[ 1 ]; + namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); + + // There *must* be a type, no attaching namespace-only handlers + if ( !type ) { + continue; + } + + // If event changes its type, use the special event handlers for the changed type + special = jQuery.event.special[ type ] || {}; + + // If selector defined, determine special event api type, otherwise given type + type = ( selector ? special.delegateType : special.bindType ) || type; + + // Update special based on newly reset type + special = jQuery.event.special[ type ] || {}; + + // handleObj is passed to all event handlers + handleObj = jQuery.extend( { + type: type, + origType: origType, + data: data, + handler: handler, + guid: handler.guid, + selector: selector, + needsContext: selector && jQuery.expr.match.needsContext.test( selector ), + namespace: namespaces.join( "." ) + }, handleObjIn ); + + // Init the event handler queue if we're the first + if ( !( handlers = events[ type ] ) ) { + handlers = events[ type ] = []; + handlers.delegateCount = 0; + + // Only use addEventListener if the special events handler returns false + if ( !special.setup || + special.setup.call( elem, data, namespaces, eventHandle ) === false ) { + + if ( elem.addEventListener ) { + elem.addEventListener( type, eventHandle ); + } + } + } + + if ( special.add ) { + special.add.call( elem, handleObj ); + + if ( !handleObj.handler.guid ) { + handleObj.handler.guid = handler.guid; + } + } + + // Add to the element's handler list, delegates in front + if ( selector ) { + handlers.splice( handlers.delegateCount++, 0, handleObj ); + } else { + handlers.push( handleObj ); + } + + // Keep track of which events have ever been used, for event optimization + jQuery.event.global[ type ] = true; + } + + }, + + // Detach an event or set of events from an element + remove: function( elem, types, handler, selector, mappedTypes ) { + + var j, origCount, tmp, + events, t, handleObj, + special, handlers, type, namespaces, origType, + elemData = dataPriv.hasData( elem ) && dataPriv.get( elem ); + + if ( !elemData || !( events = elemData.events ) ) { + return; + } + + // Once for each type.namespace in types; type may be omitted + types = ( types || "" ).match( rnothtmlwhite ) || [ "" ]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[ t ] ) || []; + type = origType = tmp[ 1 ]; + namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); + + // Unbind all events (on this namespace, if provided) for the element + if ( !type ) { + for ( type in events ) { + jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); + } + continue; + } + + special = jQuery.event.special[ type ] || {}; + type = ( selector ? special.delegateType : special.bindType ) || type; + handlers = events[ type ] || []; + tmp = tmp[ 2 ] && + new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ); + + // Remove matching events + origCount = j = handlers.length; + while ( j-- ) { + handleObj = handlers[ j ]; + + if ( ( mappedTypes || origType === handleObj.origType ) && + ( !handler || handler.guid === handleObj.guid ) && + ( !tmp || tmp.test( handleObj.namespace ) ) && + ( !selector || selector === handleObj.selector || + selector === "**" && handleObj.selector ) ) { + handlers.splice( j, 1 ); + + if ( handleObj.selector ) { + handlers.delegateCount--; + } + if ( special.remove ) { + special.remove.call( elem, handleObj ); + } + } + } + + // Remove generic event handler if we removed something and no more handlers exist + // (avoids potential for endless recursion during removal of special event handlers) + if ( origCount && !handlers.length ) { + if ( !special.teardown || + special.teardown.call( elem, namespaces, elemData.handle ) === false ) { + + jQuery.removeEvent( elem, type, elemData.handle ); + } + + delete events[ type ]; + } + } + + // Remove data and the expando if it's no longer used + if ( jQuery.isEmptyObject( events ) ) { + dataPriv.remove( elem, "handle events" ); + } + }, + + dispatch: function( nativeEvent ) { + + var i, j, ret, matched, handleObj, handlerQueue, + args = new Array( arguments.length ), + + // Make a writable jQuery.Event from the native event object + event = jQuery.event.fix( nativeEvent ), + + handlers = ( + dataPriv.get( this, "events" ) || Object.create( null ) + )[ event.type ] || [], + special = jQuery.event.special[ event.type ] || {}; + + // Use the fix-ed jQuery.Event rather than the (read-only) native event + args[ 0 ] = event; + + for ( i = 1; i < arguments.length; i++ ) { + args[ i ] = arguments[ i ]; + } + + event.delegateTarget = this; + + // Call the preDispatch hook for the mapped type, and let it bail if desired + if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { + return; + } + + // Determine handlers + handlerQueue = jQuery.event.handlers.call( this, event, handlers ); + + // Run delegates first; they may want to stop propagation beneath us + i = 0; + while ( ( matched = handlerQueue[ i++ ] ) && !event.isPropagationStopped() ) { + event.currentTarget = matched.elem; + + j = 0; + while ( ( handleObj = matched.handlers[ j++ ] ) && + !event.isImmediatePropagationStopped() ) { + + // If the event is namespaced, then each handler is only invoked if it is + // specially universal or its namespaces are a superset of the event's. + if ( !event.rnamespace || handleObj.namespace === false || + event.rnamespace.test( handleObj.namespace ) ) { + + event.handleObj = handleObj; + event.data = handleObj.data; + + ret = ( ( jQuery.event.special[ handleObj.origType ] || {} ).handle || + handleObj.handler ).apply( matched.elem, args ); + + if ( ret !== undefined ) { + if ( ( event.result = ret ) === false ) { + event.preventDefault(); + event.stopPropagation(); + } + } + } + } + } + + // Call the postDispatch hook for the mapped type + if ( special.postDispatch ) { + special.postDispatch.call( this, event ); + } + + return event.result; + }, + + handlers: function( event, handlers ) { + var i, handleObj, sel, matchedHandlers, matchedSelectors, + handlerQueue = [], + delegateCount = handlers.delegateCount, + cur = event.target; + + // Find delegate handlers + if ( delegateCount && + + // Support: IE <=9 + // Black-hole SVG instance trees (trac-13180) + cur.nodeType && + + // Support: Firefox <=42 + // Suppress spec-violating clicks indicating a non-primary pointer button (trac-3861) + // https://www.w3.org/TR/DOM-Level-3-Events/#event-type-click + // Support: IE 11 only + // ...but not arrow key "clicks" of radio inputs, which can have `button` -1 (gh-2343) + !( event.type === "click" && event.button >= 1 ) ) { + + for ( ; cur !== this; cur = cur.parentNode || this ) { + + // Don't check non-elements (#13208) + // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764) + if ( cur.nodeType === 1 && !( event.type === "click" && cur.disabled === true ) ) { + matchedHandlers = []; + matchedSelectors = {}; + for ( i = 0; i < delegateCount; i++ ) { + handleObj = handlers[ i ]; + + // Don't conflict with Object.prototype properties (#13203) + sel = handleObj.selector + " "; + + if ( matchedSelectors[ sel ] === undefined ) { + matchedSelectors[ sel ] = handleObj.needsContext ? + jQuery( sel, this ).index( cur ) > -1 : + jQuery.find( sel, this, null, [ cur ] ).length; + } + if ( matchedSelectors[ sel ] ) { + matchedHandlers.push( handleObj ); + } + } + if ( matchedHandlers.length ) { + handlerQueue.push( { elem: cur, handlers: matchedHandlers } ); + } + } + } + } + + // Add the remaining (directly-bound) handlers + cur = this; + if ( delegateCount < handlers.length ) { + handlerQueue.push( { elem: cur, handlers: handlers.slice( delegateCount ) } ); + } + + return handlerQueue; + }, + + addProp: function( name, hook ) { + Object.defineProperty( jQuery.Event.prototype, name, { + enumerable: true, + configurable: true, + + get: isFunction( hook ) ? + function() { + if ( this.originalEvent ) { + return hook( this.originalEvent ); + } + } : + function() { + if ( this.originalEvent ) { + return this.originalEvent[ name ]; + } + }, + + set: function( value ) { + Object.defineProperty( this, name, { + enumerable: true, + configurable: true, + writable: true, + value: value + } ); + } + } ); + }, + + fix: function( originalEvent ) { + return originalEvent[ jQuery.expando ] ? + originalEvent : + new jQuery.Event( originalEvent ); + }, + + special: { + load: { + + // Prevent triggered image.load events from bubbling to window.load + noBubble: true + }, + click: { + + // Utilize native event to ensure correct state for checkable inputs + setup: function( data ) { + + // For mutual compressibility with _default, replace `this` access with a local var. + // `|| data` is dead code meant only to preserve the variable through minification. + var el = this || data; + + // Claim the first handler + if ( rcheckableType.test( el.type ) && + el.click && nodeName( el, "input" ) ) { + + // dataPriv.set( el, "click", ... ) + leverageNative( el, "click", returnTrue ); + } + + // Return false to allow normal processing in the caller + return false; + }, + trigger: function( data ) { + + // For mutual compressibility with _default, replace `this` access with a local var. + // `|| data` is dead code meant only to preserve the variable through minification. + var el = this || data; + + // Force setup before triggering a click + if ( rcheckableType.test( el.type ) && + el.click && nodeName( el, "input" ) ) { + + leverageNative( el, "click" ); + } + + // Return non-false to allow normal event-path propagation + return true; + }, + + // For cross-browser consistency, suppress native .click() on links + // Also prevent it if we're currently inside a leveraged native-event stack + _default: function( event ) { + var target = event.target; + return rcheckableType.test( target.type ) && + target.click && nodeName( target, "input" ) && + dataPriv.get( target, "click" ) || + nodeName( target, "a" ); + } + }, + + beforeunload: { + postDispatch: function( event ) { + + // Support: Firefox 20+ + // Firefox doesn't alert if the returnValue field is not set. + if ( event.result !== undefined && event.originalEvent ) { + event.originalEvent.returnValue = event.result; + } + } + } + } +}; + +// Ensure the presence of an event listener that handles manually-triggered +// synthetic events by interrupting progress until reinvoked in response to +// *native* events that it fires directly, ensuring that state changes have +// already occurred before other listeners are invoked. +function leverageNative( el, type, expectSync ) { + + // Missing expectSync indicates a trigger call, which must force setup through jQuery.event.add + if ( !expectSync ) { + if ( dataPriv.get( el, type ) === undefined ) { + jQuery.event.add( el, type, returnTrue ); + } + return; + } + + // Register the controller as a special universal handler for all event namespaces + dataPriv.set( el, type, false ); + jQuery.event.add( el, type, { + namespace: false, + handler: function( event ) { + var notAsync, result, + saved = dataPriv.get( this, type ); + + if ( ( event.isTrigger & 1 ) && this[ type ] ) { + + // Interrupt processing of the outer synthetic .trigger()ed event + // Saved data should be false in such cases, but might be a leftover capture object + // from an async native handler (gh-4350) + if ( !saved.length ) { + + // Store arguments for use when handling the inner native event + // There will always be at least one argument (an event object), so this array + // will not be confused with a leftover capture object. + saved = slice.call( arguments ); + dataPriv.set( this, type, saved ); + + // Trigger the native event and capture its result + // Support: IE <=9 - 11+ + // focus() and blur() are asynchronous + notAsync = expectSync( this, type ); + this[ type ](); + result = dataPriv.get( this, type ); + if ( saved !== result || notAsync ) { + dataPriv.set( this, type, false ); + } else { + result = {}; + } + if ( saved !== result ) { + + // Cancel the outer synthetic event + event.stopImmediatePropagation(); + event.preventDefault(); + return result.value; + } + + // If this is an inner synthetic event for an event with a bubbling surrogate + // (focus or blur), assume that the surrogate already propagated from triggering the + // native event and prevent that from happening again here. + // This technically gets the ordering wrong w.r.t. to `.trigger()` (in which the + // bubbling surrogate propagates *after* the non-bubbling base), but that seems + // less bad than duplication. + } else if ( ( jQuery.event.special[ type ] || {} ).delegateType ) { + event.stopPropagation(); + } + + // If this is a native event triggered above, everything is now in order + // Fire an inner synthetic event with the original arguments + } else if ( saved.length ) { + + // ...and capture the result + dataPriv.set( this, type, { + value: jQuery.event.trigger( + + // Support: IE <=9 - 11+ + // Extend with the prototype to reset the above stopImmediatePropagation() + jQuery.extend( saved[ 0 ], jQuery.Event.prototype ), + saved.slice( 1 ), + this + ) + } ); + + // Abort handling of the native event + event.stopImmediatePropagation(); + } + } + } ); +} + +jQuery.removeEvent = function( elem, type, handle ) { + + // This "if" is needed for plain objects + if ( elem.removeEventListener ) { + elem.removeEventListener( type, handle ); + } +}; + +jQuery.Event = function( src, props ) { + + // Allow instantiation without the 'new' keyword + if ( !( this instanceof jQuery.Event ) ) { + return new jQuery.Event( src, props ); + } + + // Event object + if ( src && src.type ) { + this.originalEvent = src; + this.type = src.type; + + // Events bubbling up the document may have been marked as prevented + // by a handler lower down the tree; reflect the correct value. + this.isDefaultPrevented = src.defaultPrevented || + src.defaultPrevented === undefined && + + // Support: Android <=2.3 only + src.returnValue === false ? + returnTrue : + returnFalse; + + // Create target properties + // Support: Safari <=6 - 7 only + // Target should not be a text node (#504, #13143) + this.target = ( src.target && src.target.nodeType === 3 ) ? + src.target.parentNode : + src.target; + + this.currentTarget = src.currentTarget; + this.relatedTarget = src.relatedTarget; + + // Event type + } else { + this.type = src; + } + + // Put explicitly provided properties onto the event object + if ( props ) { + jQuery.extend( this, props ); + } + + // Create a timestamp if incoming event doesn't have one + this.timeStamp = src && src.timeStamp || Date.now(); + + // Mark it as fixed + this[ jQuery.expando ] = true; +}; + +// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding +// https://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html +jQuery.Event.prototype = { + constructor: jQuery.Event, + isDefaultPrevented: returnFalse, + isPropagationStopped: returnFalse, + isImmediatePropagationStopped: returnFalse, + isSimulated: false, + + preventDefault: function() { + var e = this.originalEvent; + + this.isDefaultPrevented = returnTrue; + + if ( e && !this.isSimulated ) { + e.preventDefault(); + } + }, + stopPropagation: function() { + var e = this.originalEvent; + + this.isPropagationStopped = returnTrue; + + if ( e && !this.isSimulated ) { + e.stopPropagation(); + } + }, + stopImmediatePropagation: function() { + var e = this.originalEvent; + + this.isImmediatePropagationStopped = returnTrue; + + if ( e && !this.isSimulated ) { + e.stopImmediatePropagation(); + } + + this.stopPropagation(); + } +}; + +// Includes all common event props including KeyEvent and MouseEvent specific props +jQuery.each( { + altKey: true, + bubbles: true, + cancelable: true, + changedTouches: true, + ctrlKey: true, + detail: true, + eventPhase: true, + metaKey: true, + pageX: true, + pageY: true, + shiftKey: true, + view: true, + "char": true, + code: true, + charCode: true, + key: true, + keyCode: true, + button: true, + buttons: true, + clientX: true, + clientY: true, + offsetX: true, + offsetY: true, + pointerId: true, + pointerType: true, + screenX: true, + screenY: true, + targetTouches: true, + toElement: true, + touches: true, + + which: function( event ) { + var button = event.button; + + // Add which for key events + if ( event.which == null && rkeyEvent.test( event.type ) ) { + return event.charCode != null ? event.charCode : event.keyCode; + } + + // Add which for click: 1 === left; 2 === middle; 3 === right + if ( !event.which && button !== undefined && rmouseEvent.test( event.type ) ) { + if ( button & 1 ) { + return 1; + } + + if ( button & 2 ) { + return 3; + } + + if ( button & 4 ) { + return 2; + } + + return 0; + } + + return event.which; + } +}, jQuery.event.addProp ); + +jQuery.each( { focus: "focusin", blur: "focusout" }, function( type, delegateType ) { + jQuery.event.special[ type ] = { + + // Utilize native event if possible so blur/focus sequence is correct + setup: function() { + + // Claim the first handler + // dataPriv.set( this, "focus", ... ) + // dataPriv.set( this, "blur", ... ) + leverageNative( this, type, expectSync ); + + // Return false to allow normal processing in the caller + return false; + }, + trigger: function() { + + // Force setup before trigger + leverageNative( this, type ); + + // Return non-false to allow normal event-path propagation + return true; + }, + + delegateType: delegateType + }; +} ); + +// Create mouseenter/leave events using mouseover/out and event-time checks +// so that event delegation works in jQuery. +// Do the same for pointerenter/pointerleave and pointerover/pointerout +// +// Support: Safari 7 only +// Safari sends mouseenter too often; see: +// https://bugs.chromium.org/p/chromium/issues/detail?id=470258 +// for the description of the bug (it existed in older Chrome versions as well). +jQuery.each( { + mouseenter: "mouseover", + mouseleave: "mouseout", + pointerenter: "pointerover", + pointerleave: "pointerout" +}, function( orig, fix ) { + jQuery.event.special[ orig ] = { + delegateType: fix, + bindType: fix, + + handle: function( event ) { + var ret, + target = this, + related = event.relatedTarget, + handleObj = event.handleObj; + + // For mouseenter/leave call the handler if related is outside the target. + // NB: No relatedTarget if the mouse left/entered the browser window + if ( !related || ( related !== target && !jQuery.contains( target, related ) ) ) { + event.type = handleObj.origType; + ret = handleObj.handler.apply( this, arguments ); + event.type = fix; + } + return ret; + } + }; +} ); + +jQuery.fn.extend( { + + on: function( types, selector, data, fn ) { + return on( this, types, selector, data, fn ); + }, + one: function( types, selector, data, fn ) { + return on( this, types, selector, data, fn, 1 ); + }, + off: function( types, selector, fn ) { + var handleObj, type; + if ( types && types.preventDefault && types.handleObj ) { + + // ( event ) dispatched jQuery.Event + handleObj = types.handleObj; + jQuery( types.delegateTarget ).off( + handleObj.namespace ? + handleObj.origType + "." + handleObj.namespace : + handleObj.origType, + handleObj.selector, + handleObj.handler + ); + return this; + } + if ( typeof types === "object" ) { + + // ( types-object [, selector] ) + for ( type in types ) { + this.off( type, selector, types[ type ] ); + } + return this; + } + if ( selector === false || typeof selector === "function" ) { + + // ( types [, fn] ) + fn = selector; + selector = undefined; + } + if ( fn === false ) { + fn = returnFalse; + } + return this.each( function() { + jQuery.event.remove( this, types, fn, selector ); + } ); + } +} ); + + +var + + // Support: IE <=10 - 11, Edge 12 - 13 only + // In IE/Edge using regex groups here causes severe slowdowns. + // See https://connect.microsoft.com/IE/feedback/details/1736512/ + rnoInnerhtml = /\s*$/g; + +// Prefer a tbody over its parent table for containing new rows +function manipulationTarget( elem, content ) { + if ( nodeName( elem, "table" ) && + nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ) { + + return jQuery( elem ).children( "tbody" )[ 0 ] || elem; + } + + return elem; +} + +// Replace/restore the type attribute of script elements for safe DOM manipulation +function disableScript( elem ) { + elem.type = ( elem.getAttribute( "type" ) !== null ) + "/" + elem.type; + return elem; +} +function restoreScript( elem ) { + if ( ( elem.type || "" ).slice( 0, 5 ) === "true/" ) { + elem.type = elem.type.slice( 5 ); + } else { + elem.removeAttribute( "type" ); + } + + return elem; +} + +function cloneCopyEvent( src, dest ) { + var i, l, type, pdataOld, udataOld, udataCur, events; + + if ( dest.nodeType !== 1 ) { + return; + } + + // 1. Copy private data: events, handlers, etc. + if ( dataPriv.hasData( src ) ) { + pdataOld = dataPriv.get( src ); + events = pdataOld.events; + + if ( events ) { + dataPriv.remove( dest, "handle events" ); + + for ( type in events ) { + for ( i = 0, l = events[ type ].length; i < l; i++ ) { + jQuery.event.add( dest, type, events[ type ][ i ] ); + } + } + } + } + + // 2. Copy user data + if ( dataUser.hasData( src ) ) { + udataOld = dataUser.access( src ); + udataCur = jQuery.extend( {}, udataOld ); + + dataUser.set( dest, udataCur ); + } +} + +// Fix IE bugs, see support tests +function fixInput( src, dest ) { + var nodeName = dest.nodeName.toLowerCase(); + + // Fails to persist the checked state of a cloned checkbox or radio button. + if ( nodeName === "input" && rcheckableType.test( src.type ) ) { + dest.checked = src.checked; + + // Fails to return the selected option to the default selected state when cloning options + } else if ( nodeName === "input" || nodeName === "textarea" ) { + dest.defaultValue = src.defaultValue; + } +} + +function domManip( collection, args, callback, ignored ) { + + // Flatten any nested arrays + args = flat( args ); + + var fragment, first, scripts, hasScripts, node, doc, + i = 0, + l = collection.length, + iNoClone = l - 1, + value = args[ 0 ], + valueIsFunction = isFunction( value ); + + // We can't cloneNode fragments that contain checked, in WebKit + if ( valueIsFunction || + ( l > 1 && typeof value === "string" && + !support.checkClone && rchecked.test( value ) ) ) { + return collection.each( function( index ) { + var self = collection.eq( index ); + if ( valueIsFunction ) { + args[ 0 ] = value.call( this, index, self.html() ); + } + domManip( self, args, callback, ignored ); + } ); + } + + if ( l ) { + fragment = buildFragment( args, collection[ 0 ].ownerDocument, false, collection, ignored ); + first = fragment.firstChild; + + if ( fragment.childNodes.length === 1 ) { + fragment = first; + } + + // Require either new content or an interest in ignored elements to invoke the callback + if ( first || ignored ) { + scripts = jQuery.map( getAll( fragment, "script" ), disableScript ); + hasScripts = scripts.length; + + // Use the original fragment for the last item + // instead of the first because it can end up + // being emptied incorrectly in certain situations (#8070). + for ( ; i < l; i++ ) { + node = fragment; + + if ( i !== iNoClone ) { + node = jQuery.clone( node, true, true ); + + // Keep references to cloned scripts for later restoration + if ( hasScripts ) { + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( scripts, getAll( node, "script" ) ); + } + } + + callback.call( collection[ i ], node, i ); + } + + if ( hasScripts ) { + doc = scripts[ scripts.length - 1 ].ownerDocument; + + // Reenable scripts + jQuery.map( scripts, restoreScript ); + + // Evaluate executable scripts on first document insertion + for ( i = 0; i < hasScripts; i++ ) { + node = scripts[ i ]; + if ( rscriptType.test( node.type || "" ) && + !dataPriv.access( node, "globalEval" ) && + jQuery.contains( doc, node ) ) { + + if ( node.src && ( node.type || "" ).toLowerCase() !== "module" ) { + + // Optional AJAX dependency, but won't run scripts if not present + if ( jQuery._evalUrl && !node.noModule ) { + jQuery._evalUrl( node.src, { + nonce: node.nonce || node.getAttribute( "nonce" ) + }, doc ); + } + } else { + DOMEval( node.textContent.replace( rcleanScript, "" ), node, doc ); + } + } + } + } + } + } + + return collection; +} + +function remove( elem, selector, keepData ) { + var node, + nodes = selector ? jQuery.filter( selector, elem ) : elem, + i = 0; + + for ( ; ( node = nodes[ i ] ) != null; i++ ) { + if ( !keepData && node.nodeType === 1 ) { + jQuery.cleanData( getAll( node ) ); + } + + if ( node.parentNode ) { + if ( keepData && isAttached( node ) ) { + setGlobalEval( getAll( node, "script" ) ); + } + node.parentNode.removeChild( node ); + } + } + + return elem; +} + +jQuery.extend( { + htmlPrefilter: function( html ) { + return html; + }, + + clone: function( elem, dataAndEvents, deepDataAndEvents ) { + var i, l, srcElements, destElements, + clone = elem.cloneNode( true ), + inPage = isAttached( elem ); + + // Fix IE cloning issues + if ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) && + !jQuery.isXMLDoc( elem ) ) { + + // We eschew Sizzle here for performance reasons: https://jsperf.com/getall-vs-sizzle/2 + destElements = getAll( clone ); + srcElements = getAll( elem ); + + for ( i = 0, l = srcElements.length; i < l; i++ ) { + fixInput( srcElements[ i ], destElements[ i ] ); + } + } + + // Copy the events from the original to the clone + if ( dataAndEvents ) { + if ( deepDataAndEvents ) { + srcElements = srcElements || getAll( elem ); + destElements = destElements || getAll( clone ); + + for ( i = 0, l = srcElements.length; i < l; i++ ) { + cloneCopyEvent( srcElements[ i ], destElements[ i ] ); + } + } else { + cloneCopyEvent( elem, clone ); + } + } + + // Preserve script evaluation history + destElements = getAll( clone, "script" ); + if ( destElements.length > 0 ) { + setGlobalEval( destElements, !inPage && getAll( elem, "script" ) ); + } + + // Return the cloned set + return clone; + }, + + cleanData: function( elems ) { + var data, elem, type, + special = jQuery.event.special, + i = 0; + + for ( ; ( elem = elems[ i ] ) !== undefined; i++ ) { + if ( acceptData( elem ) ) { + if ( ( data = elem[ dataPriv.expando ] ) ) { + if ( data.events ) { + for ( type in data.events ) { + if ( special[ type ] ) { + jQuery.event.remove( elem, type ); + + // This is a shortcut to avoid jQuery.event.remove's overhead + } else { + jQuery.removeEvent( elem, type, data.handle ); + } + } + } + + // Support: Chrome <=35 - 45+ + // Assign undefined instead of using delete, see Data#remove + elem[ dataPriv.expando ] = undefined; + } + if ( elem[ dataUser.expando ] ) { + + // Support: Chrome <=35 - 45+ + // Assign undefined instead of using delete, see Data#remove + elem[ dataUser.expando ] = undefined; + } + } + } + } +} ); + +jQuery.fn.extend( { + detach: function( selector ) { + return remove( this, selector, true ); + }, + + remove: function( selector ) { + return remove( this, selector ); + }, + + text: function( value ) { + return access( this, function( value ) { + return value === undefined ? + jQuery.text( this ) : + this.empty().each( function() { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + this.textContent = value; + } + } ); + }, null, value, arguments.length ); + }, + + append: function() { + return domManip( this, arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.appendChild( elem ); + } + } ); + }, + + prepend: function() { + return domManip( this, arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.insertBefore( elem, target.firstChild ); + } + } ); + }, + + before: function() { + return domManip( this, arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this ); + } + } ); + }, + + after: function() { + return domManip( this, arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this.nextSibling ); + } + } ); + }, + + empty: function() { + var elem, + i = 0; + + for ( ; ( elem = this[ i ] ) != null; i++ ) { + if ( elem.nodeType === 1 ) { + + // Prevent memory leaks + jQuery.cleanData( getAll( elem, false ) ); + + // Remove any remaining nodes + elem.textContent = ""; + } + } + + return this; + }, + + clone: function( dataAndEvents, deepDataAndEvents ) { + dataAndEvents = dataAndEvents == null ? false : dataAndEvents; + deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; + + return this.map( function() { + return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); + } ); + }, + + html: function( value ) { + return access( this, function( value ) { + var elem = this[ 0 ] || {}, + i = 0, + l = this.length; + + if ( value === undefined && elem.nodeType === 1 ) { + return elem.innerHTML; + } + + // See if we can take a shortcut and just use innerHTML + if ( typeof value === "string" && !rnoInnerhtml.test( value ) && + !wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) { + + value = jQuery.htmlPrefilter( value ); + + try { + for ( ; i < l; i++ ) { + elem = this[ i ] || {}; + + // Remove element nodes and prevent memory leaks + if ( elem.nodeType === 1 ) { + jQuery.cleanData( getAll( elem, false ) ); + elem.innerHTML = value; + } + } + + elem = 0; + + // If using innerHTML throws an exception, use the fallback method + } catch ( e ) {} + } + + if ( elem ) { + this.empty().append( value ); + } + }, null, value, arguments.length ); + }, + + replaceWith: function() { + var ignored = []; + + // Make the changes, replacing each non-ignored context element with the new content + return domManip( this, arguments, function( elem ) { + var parent = this.parentNode; + + if ( jQuery.inArray( this, ignored ) < 0 ) { + jQuery.cleanData( getAll( this ) ); + if ( parent ) { + parent.replaceChild( elem, this ); + } + } + + // Force callback invocation + }, ignored ); + } +} ); + +jQuery.each( { + appendTo: "append", + prependTo: "prepend", + insertBefore: "before", + insertAfter: "after", + replaceAll: "replaceWith" +}, function( name, original ) { + jQuery.fn[ name ] = function( selector ) { + var elems, + ret = [], + insert = jQuery( selector ), + last = insert.length - 1, + i = 0; + + for ( ; i <= last; i++ ) { + elems = i === last ? this : this.clone( true ); + jQuery( insert[ i ] )[ original ]( elems ); + + // Support: Android <=4.0 only, PhantomJS 1 only + // .get() because push.apply(_, arraylike) throws on ancient WebKit + push.apply( ret, elems.get() ); + } + + return this.pushStack( ret ); + }; +} ); +var rnumnonpx = new RegExp( "^(" + pnum + ")(?!px)[a-z%]+$", "i" ); + +var getStyles = function( elem ) { + + // Support: IE <=11 only, Firefox <=30 (#15098, #14150) + // IE throws on elements created in popups + // FF meanwhile throws on frame elements through "defaultView.getComputedStyle" + var view = elem.ownerDocument.defaultView; + + if ( !view || !view.opener ) { + view = window; + } + + return view.getComputedStyle( elem ); + }; + +var swap = function( elem, options, callback ) { + var ret, name, + old = {}; + + // Remember the old values, and insert the new ones + for ( name in options ) { + old[ name ] = elem.style[ name ]; + elem.style[ name ] = options[ name ]; + } + + ret = callback.call( elem ); + + // Revert the old values + for ( name in options ) { + elem.style[ name ] = old[ name ]; + } + + return ret; +}; + + +var rboxStyle = new RegExp( cssExpand.join( "|" ), "i" ); + + + +( function() { + + // Executing both pixelPosition & boxSizingReliable tests require only one layout + // so they're executed at the same time to save the second computation. + function computeStyleTests() { + + // This is a singleton, we need to execute it only once + if ( !div ) { + return; + } + + container.style.cssText = "position:absolute;left:-11111px;width:60px;" + + "margin-top:1px;padding:0;border:0"; + div.style.cssText = + "position:relative;display:block;box-sizing:border-box;overflow:scroll;" + + "margin:auto;border:1px;padding:1px;" + + "width:60%;top:1%"; + documentElement.appendChild( container ).appendChild( div ); + + var divStyle = window.getComputedStyle( div ); + pixelPositionVal = divStyle.top !== "1%"; + + // Support: Android 4.0 - 4.3 only, Firefox <=3 - 44 + reliableMarginLeftVal = roundPixelMeasures( divStyle.marginLeft ) === 12; + + // Support: Android 4.0 - 4.3 only, Safari <=9.1 - 10.1, iOS <=7.0 - 9.3 + // Some styles come back with percentage values, even though they shouldn't + div.style.right = "60%"; + pixelBoxStylesVal = roundPixelMeasures( divStyle.right ) === 36; + + // Support: IE 9 - 11 only + // Detect misreporting of content dimensions for box-sizing:border-box elements + boxSizingReliableVal = roundPixelMeasures( divStyle.width ) === 36; + + // Support: IE 9 only + // Detect overflow:scroll screwiness (gh-3699) + // Support: Chrome <=64 + // Don't get tricked when zoom affects offsetWidth (gh-4029) + div.style.position = "absolute"; + scrollboxSizeVal = roundPixelMeasures( div.offsetWidth / 3 ) === 12; + + documentElement.removeChild( container ); + + // Nullify the div so it wouldn't be stored in the memory and + // it will also be a sign that checks already performed + div = null; + } + + function roundPixelMeasures( measure ) { + return Math.round( parseFloat( measure ) ); + } + + var pixelPositionVal, boxSizingReliableVal, scrollboxSizeVal, pixelBoxStylesVal, + reliableTrDimensionsVal, reliableMarginLeftVal, + container = document.createElement( "div" ), + div = document.createElement( "div" ); + + // Finish early in limited (non-browser) environments + if ( !div.style ) { + return; + } + + // Support: IE <=9 - 11 only + // Style of cloned element affects source element cloned (#8908) + div.style.backgroundClip = "content-box"; + div.cloneNode( true ).style.backgroundClip = ""; + support.clearCloneStyle = div.style.backgroundClip === "content-box"; + + jQuery.extend( support, { + boxSizingReliable: function() { + computeStyleTests(); + return boxSizingReliableVal; + }, + pixelBoxStyles: function() { + computeStyleTests(); + return pixelBoxStylesVal; + }, + pixelPosition: function() { + computeStyleTests(); + return pixelPositionVal; + }, + reliableMarginLeft: function() { + computeStyleTests(); + return reliableMarginLeftVal; + }, + scrollboxSize: function() { + computeStyleTests(); + return scrollboxSizeVal; + }, + + // Support: IE 9 - 11+, Edge 15 - 18+ + // IE/Edge misreport `getComputedStyle` of table rows with width/height + // set in CSS while `offset*` properties report correct values. + // Behavior in IE 9 is more subtle than in newer versions & it passes + // some versions of this test; make sure not to make it pass there! + reliableTrDimensions: function() { + var table, tr, trChild, trStyle; + if ( reliableTrDimensionsVal == null ) { + table = document.createElement( "table" ); + tr = document.createElement( "tr" ); + trChild = document.createElement( "div" ); + + table.style.cssText = "position:absolute;left:-11111px"; + tr.style.height = "1px"; + trChild.style.height = "9px"; + + documentElement + .appendChild( table ) + .appendChild( tr ) + .appendChild( trChild ); + + trStyle = window.getComputedStyle( tr ); + reliableTrDimensionsVal = parseInt( trStyle.height ) > 3; + + documentElement.removeChild( table ); + } + return reliableTrDimensionsVal; + } + } ); +} )(); + + +function curCSS( elem, name, computed ) { + var width, minWidth, maxWidth, ret, + + // Support: Firefox 51+ + // Retrieving style before computed somehow + // fixes an issue with getting wrong values + // on detached elements + style = elem.style; + + computed = computed || getStyles( elem ); + + // getPropertyValue is needed for: + // .css('filter') (IE 9 only, #12537) + // .css('--customProperty) (#3144) + if ( computed ) { + ret = computed.getPropertyValue( name ) || computed[ name ]; + + if ( ret === "" && !isAttached( elem ) ) { + ret = jQuery.style( elem, name ); + } + + // A tribute to the "awesome hack by Dean Edwards" + // Android Browser returns percentage for some values, + // but width seems to be reliably pixels. + // This is against the CSSOM draft spec: + // https://drafts.csswg.org/cssom/#resolved-values + if ( !support.pixelBoxStyles() && rnumnonpx.test( ret ) && rboxStyle.test( name ) ) { + + // Remember the original values + width = style.width; + minWidth = style.minWidth; + maxWidth = style.maxWidth; + + // Put in the new values to get a computed value out + style.minWidth = style.maxWidth = style.width = ret; + ret = computed.width; + + // Revert the changed values + style.width = width; + style.minWidth = minWidth; + style.maxWidth = maxWidth; + } + } + + return ret !== undefined ? + + // Support: IE <=9 - 11 only + // IE returns zIndex value as an integer. + ret + "" : + ret; +} + + +function addGetHookIf( conditionFn, hookFn ) { + + // Define the hook, we'll check on the first run if it's really needed. + return { + get: function() { + if ( conditionFn() ) { + + // Hook not needed (or it's not possible to use it due + // to missing dependency), remove it. + delete this.get; + return; + } + + // Hook needed; redefine it so that the support test is not executed again. + return ( this.get = hookFn ).apply( this, arguments ); + } + }; +} + + +var cssPrefixes = [ "Webkit", "Moz", "ms" ], + emptyStyle = document.createElement( "div" ).style, + vendorProps = {}; + +// Return a vendor-prefixed property or undefined +function vendorPropName( name ) { + + // Check for vendor prefixed names + var capName = name[ 0 ].toUpperCase() + name.slice( 1 ), + i = cssPrefixes.length; + + while ( i-- ) { + name = cssPrefixes[ i ] + capName; + if ( name in emptyStyle ) { + return name; + } + } +} + +// Return a potentially-mapped jQuery.cssProps or vendor prefixed property +function finalPropName( name ) { + var final = jQuery.cssProps[ name ] || vendorProps[ name ]; + + if ( final ) { + return final; + } + if ( name in emptyStyle ) { + return name; + } + return vendorProps[ name ] = vendorPropName( name ) || name; +} + + +var + + // Swappable if display is none or starts with table + // except "table", "table-cell", or "table-caption" + // See here for display values: https://developer.mozilla.org/en-US/docs/CSS/display + rdisplayswap = /^(none|table(?!-c[ea]).+)/, + rcustomProp = /^--/, + cssShow = { position: "absolute", visibility: "hidden", display: "block" }, + cssNormalTransform = { + letterSpacing: "0", + fontWeight: "400" + }; + +function setPositiveNumber( _elem, value, subtract ) { + + // Any relative (+/-) values have already been + // normalized at this point + var matches = rcssNum.exec( value ); + return matches ? + + // Guard against undefined "subtract", e.g., when used as in cssHooks + Math.max( 0, matches[ 2 ] - ( subtract || 0 ) ) + ( matches[ 3 ] || "px" ) : + value; +} + +function boxModelAdjustment( elem, dimension, box, isBorderBox, styles, computedVal ) { + var i = dimension === "width" ? 1 : 0, + extra = 0, + delta = 0; + + // Adjustment may not be necessary + if ( box === ( isBorderBox ? "border" : "content" ) ) { + return 0; + } + + for ( ; i < 4; i += 2 ) { + + // Both box models exclude margin + if ( box === "margin" ) { + delta += jQuery.css( elem, box + cssExpand[ i ], true, styles ); + } + + // If we get here with a content-box, we're seeking "padding" or "border" or "margin" + if ( !isBorderBox ) { + + // Add padding + delta += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); + + // For "border" or "margin", add border + if ( box !== "padding" ) { + delta += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + + // But still keep track of it otherwise + } else { + extra += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + } + + // If we get here with a border-box (content + padding + border), we're seeking "content" or + // "padding" or "margin" + } else { + + // For "content", subtract padding + if ( box === "content" ) { + delta -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); + } + + // For "content" or "padding", subtract border + if ( box !== "margin" ) { + delta -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + } + } + } + + // Account for positive content-box scroll gutter when requested by providing computedVal + if ( !isBorderBox && computedVal >= 0 ) { + + // offsetWidth/offsetHeight is a rounded sum of content, padding, scroll gutter, and border + // Assuming integer scroll gutter, subtract the rest and round down + delta += Math.max( 0, Math.ceil( + elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] - + computedVal - + delta - + extra - + 0.5 + + // If offsetWidth/offsetHeight is unknown, then we can't determine content-box scroll gutter + // Use an explicit zero to avoid NaN (gh-3964) + ) ) || 0; + } + + return delta; +} + +function getWidthOrHeight( elem, dimension, extra ) { + + // Start with computed style + var styles = getStyles( elem ), + + // To avoid forcing a reflow, only fetch boxSizing if we need it (gh-4322). + // Fake content-box until we know it's needed to know the true value. + boxSizingNeeded = !support.boxSizingReliable() || extra, + isBorderBox = boxSizingNeeded && + jQuery.css( elem, "boxSizing", false, styles ) === "border-box", + valueIsBorderBox = isBorderBox, + + val = curCSS( elem, dimension, styles ), + offsetProp = "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ); + + // Support: Firefox <=54 + // Return a confounding non-pixel value or feign ignorance, as appropriate. + if ( rnumnonpx.test( val ) ) { + if ( !extra ) { + return val; + } + val = "auto"; + } + + + // Support: IE 9 - 11 only + // Use offsetWidth/offsetHeight for when box sizing is unreliable. + // In those cases, the computed value can be trusted to be border-box. + if ( ( !support.boxSizingReliable() && isBorderBox || + + // Support: IE 10 - 11+, Edge 15 - 18+ + // IE/Edge misreport `getComputedStyle` of table rows with width/height + // set in CSS while `offset*` properties report correct values. + // Interestingly, in some cases IE 9 doesn't suffer from this issue. + !support.reliableTrDimensions() && nodeName( elem, "tr" ) || + + // Fall back to offsetWidth/offsetHeight when value is "auto" + // This happens for inline elements with no explicit setting (gh-3571) + val === "auto" || + + // Support: Android <=4.1 - 4.3 only + // Also use offsetWidth/offsetHeight for misreported inline dimensions (gh-3602) + !parseFloat( val ) && jQuery.css( elem, "display", false, styles ) === "inline" ) && + + // Make sure the element is visible & connected + elem.getClientRects().length ) { + + isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box"; + + // Where available, offsetWidth/offsetHeight approximate border box dimensions. + // Where not available (e.g., SVG), assume unreliable box-sizing and interpret the + // retrieved value as a content box dimension. + valueIsBorderBox = offsetProp in elem; + if ( valueIsBorderBox ) { + val = elem[ offsetProp ]; + } + } + + // Normalize "" and auto + val = parseFloat( val ) || 0; + + // Adjust for the element's box model + return ( val + + boxModelAdjustment( + elem, + dimension, + extra || ( isBorderBox ? "border" : "content" ), + valueIsBorderBox, + styles, + + // Provide the current computed size to request scroll gutter calculation (gh-3589) + val + ) + ) + "px"; +} + +jQuery.extend( { + + // Add in style property hooks for overriding the default + // behavior of getting and setting a style property + cssHooks: { + opacity: { + get: function( elem, computed ) { + if ( computed ) { + + // We should always get a number back from opacity + var ret = curCSS( elem, "opacity" ); + return ret === "" ? "1" : ret; + } + } + } + }, + + // Don't automatically add "px" to these possibly-unitless properties + cssNumber: { + "animationIterationCount": true, + "columnCount": true, + "fillOpacity": true, + "flexGrow": true, + "flexShrink": true, + "fontWeight": true, + "gridArea": true, + "gridColumn": true, + "gridColumnEnd": true, + "gridColumnStart": true, + "gridRow": true, + "gridRowEnd": true, + "gridRowStart": true, + "lineHeight": true, + "opacity": true, + "order": true, + "orphans": true, + "widows": true, + "zIndex": true, + "zoom": true + }, + + // Add in properties whose names you wish to fix before + // setting or getting the value + cssProps: {}, + + // Get and set the style property on a DOM Node + style: function( elem, name, value, extra ) { + + // Don't set styles on text and comment nodes + if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) { + return; + } + + // Make sure that we're working with the right name + var ret, type, hooks, + origName = camelCase( name ), + isCustomProp = rcustomProp.test( name ), + style = elem.style; + + // Make sure that we're working with the right name. We don't + // want to query the value if it is a CSS custom property + // since they are user-defined. + if ( !isCustomProp ) { + name = finalPropName( origName ); + } + + // Gets hook for the prefixed version, then unprefixed version + hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; + + // Check if we're setting a value + if ( value !== undefined ) { + type = typeof value; + + // Convert "+=" or "-=" to relative numbers (#7345) + if ( type === "string" && ( ret = rcssNum.exec( value ) ) && ret[ 1 ] ) { + value = adjustCSS( elem, name, ret ); + + // Fixes bug #9237 + type = "number"; + } + + // Make sure that null and NaN values aren't set (#7116) + if ( value == null || value !== value ) { + return; + } + + // If a number was passed in, add the unit (except for certain CSS properties) + // The isCustomProp check can be removed in jQuery 4.0 when we only auto-append + // "px" to a few hardcoded values. + if ( type === "number" && !isCustomProp ) { + value += ret && ret[ 3 ] || ( jQuery.cssNumber[ origName ] ? "" : "px" ); + } + + // background-* props affect original clone's values + if ( !support.clearCloneStyle && value === "" && name.indexOf( "background" ) === 0 ) { + style[ name ] = "inherit"; + } + + // If a hook was provided, use that value, otherwise just set the specified value + if ( !hooks || !( "set" in hooks ) || + ( value = hooks.set( elem, value, extra ) ) !== undefined ) { + + if ( isCustomProp ) { + style.setProperty( name, value ); + } else { + style[ name ] = value; + } + } + + } else { + + // If a hook was provided get the non-computed value from there + if ( hooks && "get" in hooks && + ( ret = hooks.get( elem, false, extra ) ) !== undefined ) { + + return ret; + } + + // Otherwise just get the value from the style object + return style[ name ]; + } + }, + + css: function( elem, name, extra, styles ) { + var val, num, hooks, + origName = camelCase( name ), + isCustomProp = rcustomProp.test( name ); + + // Make sure that we're working with the right name. We don't + // want to modify the value if it is a CSS custom property + // since they are user-defined. + if ( !isCustomProp ) { + name = finalPropName( origName ); + } + + // Try prefixed name followed by the unprefixed name + hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; + + // If a hook was provided get the computed value from there + if ( hooks && "get" in hooks ) { + val = hooks.get( elem, true, extra ); + } + + // Otherwise, if a way to get the computed value exists, use that + if ( val === undefined ) { + val = curCSS( elem, name, styles ); + } + + // Convert "normal" to computed value + if ( val === "normal" && name in cssNormalTransform ) { + val = cssNormalTransform[ name ]; + } + + // Make numeric if forced or a qualifier was provided and val looks numeric + if ( extra === "" || extra ) { + num = parseFloat( val ); + return extra === true || isFinite( num ) ? num || 0 : val; + } + + return val; + } +} ); + +jQuery.each( [ "height", "width" ], function( _i, dimension ) { + jQuery.cssHooks[ dimension ] = { + get: function( elem, computed, extra ) { + if ( computed ) { + + // Certain elements can have dimension info if we invisibly show them + // but it must have a current display style that would benefit + return rdisplayswap.test( jQuery.css( elem, "display" ) ) && + + // Support: Safari 8+ + // Table columns in Safari have non-zero offsetWidth & zero + // getBoundingClientRect().width unless display is changed. + // Support: IE <=11 only + // Running getBoundingClientRect on a disconnected node + // in IE throws an error. + ( !elem.getClientRects().length || !elem.getBoundingClientRect().width ) ? + swap( elem, cssShow, function() { + return getWidthOrHeight( elem, dimension, extra ); + } ) : + getWidthOrHeight( elem, dimension, extra ); + } + }, + + set: function( elem, value, extra ) { + var matches, + styles = getStyles( elem ), + + // Only read styles.position if the test has a chance to fail + // to avoid forcing a reflow. + scrollboxSizeBuggy = !support.scrollboxSize() && + styles.position === "absolute", + + // To avoid forcing a reflow, only fetch boxSizing if we need it (gh-3991) + boxSizingNeeded = scrollboxSizeBuggy || extra, + isBorderBox = boxSizingNeeded && + jQuery.css( elem, "boxSizing", false, styles ) === "border-box", + subtract = extra ? + boxModelAdjustment( + elem, + dimension, + extra, + isBorderBox, + styles + ) : + 0; + + // Account for unreliable border-box dimensions by comparing offset* to computed and + // faking a content-box to get border and padding (gh-3699) + if ( isBorderBox && scrollboxSizeBuggy ) { + subtract -= Math.ceil( + elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] - + parseFloat( styles[ dimension ] ) - + boxModelAdjustment( elem, dimension, "border", false, styles ) - + 0.5 + ); + } + + // Convert to pixels if value adjustment is needed + if ( subtract && ( matches = rcssNum.exec( value ) ) && + ( matches[ 3 ] || "px" ) !== "px" ) { + + elem.style[ dimension ] = value; + value = jQuery.css( elem, dimension ); + } + + return setPositiveNumber( elem, value, subtract ); + } + }; +} ); + +jQuery.cssHooks.marginLeft = addGetHookIf( support.reliableMarginLeft, + function( elem, computed ) { + if ( computed ) { + return ( parseFloat( curCSS( elem, "marginLeft" ) ) || + elem.getBoundingClientRect().left - + swap( elem, { marginLeft: 0 }, function() { + return elem.getBoundingClientRect().left; + } ) + ) + "px"; + } + } +); + +// These hooks are used by animate to expand properties +jQuery.each( { + margin: "", + padding: "", + border: "Width" +}, function( prefix, suffix ) { + jQuery.cssHooks[ prefix + suffix ] = { + expand: function( value ) { + var i = 0, + expanded = {}, + + // Assumes a single number if not a string + parts = typeof value === "string" ? value.split( " " ) : [ value ]; + + for ( ; i < 4; i++ ) { + expanded[ prefix + cssExpand[ i ] + suffix ] = + parts[ i ] || parts[ i - 2 ] || parts[ 0 ]; + } + + return expanded; + } + }; + + if ( prefix !== "margin" ) { + jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber; + } +} ); + +jQuery.fn.extend( { + css: function( name, value ) { + return access( this, function( elem, name, value ) { + var styles, len, + map = {}, + i = 0; + + if ( Array.isArray( name ) ) { + styles = getStyles( elem ); + len = name.length; + + for ( ; i < len; i++ ) { + map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles ); + } + + return map; + } + + return value !== undefined ? + jQuery.style( elem, name, value ) : + jQuery.css( elem, name ); + }, name, value, arguments.length > 1 ); + } +} ); + + +function Tween( elem, options, prop, end, easing ) { + return new Tween.prototype.init( elem, options, prop, end, easing ); +} +jQuery.Tween = Tween; + +Tween.prototype = { + constructor: Tween, + init: function( elem, options, prop, end, easing, unit ) { + this.elem = elem; + this.prop = prop; + this.easing = easing || jQuery.easing._default; + this.options = options; + this.start = this.now = this.cur(); + this.end = end; + this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" ); + }, + cur: function() { + var hooks = Tween.propHooks[ this.prop ]; + + return hooks && hooks.get ? + hooks.get( this ) : + Tween.propHooks._default.get( this ); + }, + run: function( percent ) { + var eased, + hooks = Tween.propHooks[ this.prop ]; + + if ( this.options.duration ) { + this.pos = eased = jQuery.easing[ this.easing ]( + percent, this.options.duration * percent, 0, 1, this.options.duration + ); + } else { + this.pos = eased = percent; + } + this.now = ( this.end - this.start ) * eased + this.start; + + if ( this.options.step ) { + this.options.step.call( this.elem, this.now, this ); + } + + if ( hooks && hooks.set ) { + hooks.set( this ); + } else { + Tween.propHooks._default.set( this ); + } + return this; + } +}; + +Tween.prototype.init.prototype = Tween.prototype; + +Tween.propHooks = { + _default: { + get: function( tween ) { + var result; + + // Use a property on the element directly when it is not a DOM element, + // or when there is no matching style property that exists. + if ( tween.elem.nodeType !== 1 || + tween.elem[ tween.prop ] != null && tween.elem.style[ tween.prop ] == null ) { + return tween.elem[ tween.prop ]; + } + + // Passing an empty string as a 3rd parameter to .css will automatically + // attempt a parseFloat and fallback to a string if the parse fails. + // Simple values such as "10px" are parsed to Float; + // complex values such as "rotate(1rad)" are returned as-is. + result = jQuery.css( tween.elem, tween.prop, "" ); + + // Empty strings, null, undefined and "auto" are converted to 0. + return !result || result === "auto" ? 0 : result; + }, + set: function( tween ) { + + // Use step hook for back compat. + // Use cssHook if its there. + // Use .style if available and use plain properties where available. + if ( jQuery.fx.step[ tween.prop ] ) { + jQuery.fx.step[ tween.prop ]( tween ); + } else if ( tween.elem.nodeType === 1 && ( + jQuery.cssHooks[ tween.prop ] || + tween.elem.style[ finalPropName( tween.prop ) ] != null ) ) { + jQuery.style( tween.elem, tween.prop, tween.now + tween.unit ); + } else { + tween.elem[ tween.prop ] = tween.now; + } + } + } +}; + +// Support: IE <=9 only +// Panic based approach to setting things on disconnected nodes +Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = { + set: function( tween ) { + if ( tween.elem.nodeType && tween.elem.parentNode ) { + tween.elem[ tween.prop ] = tween.now; + } + } +}; + +jQuery.easing = { + linear: function( p ) { + return p; + }, + swing: function( p ) { + return 0.5 - Math.cos( p * Math.PI ) / 2; + }, + _default: "swing" +}; + +jQuery.fx = Tween.prototype.init; + +// Back compat <1.8 extension point +jQuery.fx.step = {}; + + + + +var + fxNow, inProgress, + rfxtypes = /^(?:toggle|show|hide)$/, + rrun = /queueHooks$/; + +function schedule() { + if ( inProgress ) { + if ( document.hidden === false && window.requestAnimationFrame ) { + window.requestAnimationFrame( schedule ); + } else { + window.setTimeout( schedule, jQuery.fx.interval ); + } + + jQuery.fx.tick(); + } +} + +// Animations created synchronously will run synchronously +function createFxNow() { + window.setTimeout( function() { + fxNow = undefined; + } ); + return ( fxNow = Date.now() ); +} + +// Generate parameters to create a standard animation +function genFx( type, includeWidth ) { + var which, + i = 0, + attrs = { height: type }; + + // If we include width, step value is 1 to do all cssExpand values, + // otherwise step value is 2 to skip over Left and Right + includeWidth = includeWidth ? 1 : 0; + for ( ; i < 4; i += 2 - includeWidth ) { + which = cssExpand[ i ]; + attrs[ "margin" + which ] = attrs[ "padding" + which ] = type; + } + + if ( includeWidth ) { + attrs.opacity = attrs.width = type; + } + + return attrs; +} + +function createTween( value, prop, animation ) { + var tween, + collection = ( Animation.tweeners[ prop ] || [] ).concat( Animation.tweeners[ "*" ] ), + index = 0, + length = collection.length; + for ( ; index < length; index++ ) { + if ( ( tween = collection[ index ].call( animation, prop, value ) ) ) { + + // We're done with this property + return tween; + } + } +} + +function defaultPrefilter( elem, props, opts ) { + var prop, value, toggle, hooks, oldfire, propTween, restoreDisplay, display, + isBox = "width" in props || "height" in props, + anim = this, + orig = {}, + style = elem.style, + hidden = elem.nodeType && isHiddenWithinTree( elem ), + dataShow = dataPriv.get( elem, "fxshow" ); + + // Queue-skipping animations hijack the fx hooks + if ( !opts.queue ) { + hooks = jQuery._queueHooks( elem, "fx" ); + if ( hooks.unqueued == null ) { + hooks.unqueued = 0; + oldfire = hooks.empty.fire; + hooks.empty.fire = function() { + if ( !hooks.unqueued ) { + oldfire(); + } + }; + } + hooks.unqueued++; + + anim.always( function() { + + // Ensure the complete handler is called before this completes + anim.always( function() { + hooks.unqueued--; + if ( !jQuery.queue( elem, "fx" ).length ) { + hooks.empty.fire(); + } + } ); + } ); + } + + // Detect show/hide animations + for ( prop in props ) { + value = props[ prop ]; + if ( rfxtypes.test( value ) ) { + delete props[ prop ]; + toggle = toggle || value === "toggle"; + if ( value === ( hidden ? "hide" : "show" ) ) { + + // Pretend to be hidden if this is a "show" and + // there is still data from a stopped show/hide + if ( value === "show" && dataShow && dataShow[ prop ] !== undefined ) { + hidden = true; + + // Ignore all other no-op show/hide data + } else { + continue; + } + } + orig[ prop ] = dataShow && dataShow[ prop ] || jQuery.style( elem, prop ); + } + } + + // Bail out if this is a no-op like .hide().hide() + propTween = !jQuery.isEmptyObject( props ); + if ( !propTween && jQuery.isEmptyObject( orig ) ) { + return; + } + + // Restrict "overflow" and "display" styles during box animations + if ( isBox && elem.nodeType === 1 ) { + + // Support: IE <=9 - 11, Edge 12 - 15 + // Record all 3 overflow attributes because IE does not infer the shorthand + // from identically-valued overflowX and overflowY and Edge just mirrors + // the overflowX value there. + opts.overflow = [ style.overflow, style.overflowX, style.overflowY ]; + + // Identify a display type, preferring old show/hide data over the CSS cascade + restoreDisplay = dataShow && dataShow.display; + if ( restoreDisplay == null ) { + restoreDisplay = dataPriv.get( elem, "display" ); + } + display = jQuery.css( elem, "display" ); + if ( display === "none" ) { + if ( restoreDisplay ) { + display = restoreDisplay; + } else { + + // Get nonempty value(s) by temporarily forcing visibility + showHide( [ elem ], true ); + restoreDisplay = elem.style.display || restoreDisplay; + display = jQuery.css( elem, "display" ); + showHide( [ elem ] ); + } + } + + // Animate inline elements as inline-block + if ( display === "inline" || display === "inline-block" && restoreDisplay != null ) { + if ( jQuery.css( elem, "float" ) === "none" ) { + + // Restore the original display value at the end of pure show/hide animations + if ( !propTween ) { + anim.done( function() { + style.display = restoreDisplay; + } ); + if ( restoreDisplay == null ) { + display = style.display; + restoreDisplay = display === "none" ? "" : display; + } + } + style.display = "inline-block"; + } + } + } + + if ( opts.overflow ) { + style.overflow = "hidden"; + anim.always( function() { + style.overflow = opts.overflow[ 0 ]; + style.overflowX = opts.overflow[ 1 ]; + style.overflowY = opts.overflow[ 2 ]; + } ); + } + + // Implement show/hide animations + propTween = false; + for ( prop in orig ) { + + // General show/hide setup for this element animation + if ( !propTween ) { + if ( dataShow ) { + if ( "hidden" in dataShow ) { + hidden = dataShow.hidden; + } + } else { + dataShow = dataPriv.access( elem, "fxshow", { display: restoreDisplay } ); + } + + // Store hidden/visible for toggle so `.stop().toggle()` "reverses" + if ( toggle ) { + dataShow.hidden = !hidden; + } + + // Show elements before animating them + if ( hidden ) { + showHide( [ elem ], true ); + } + + /* eslint-disable no-loop-func */ + + anim.done( function() { + + /* eslint-enable no-loop-func */ + + // The final step of a "hide" animation is actually hiding the element + if ( !hidden ) { + showHide( [ elem ] ); + } + dataPriv.remove( elem, "fxshow" ); + for ( prop in orig ) { + jQuery.style( elem, prop, orig[ prop ] ); + } + } ); + } + + // Per-property setup + propTween = createTween( hidden ? dataShow[ prop ] : 0, prop, anim ); + if ( !( prop in dataShow ) ) { + dataShow[ prop ] = propTween.start; + if ( hidden ) { + propTween.end = propTween.start; + propTween.start = 0; + } + } + } +} + +function propFilter( props, specialEasing ) { + var index, name, easing, value, hooks; + + // camelCase, specialEasing and expand cssHook pass + for ( index in props ) { + name = camelCase( index ); + easing = specialEasing[ name ]; + value = props[ index ]; + if ( Array.isArray( value ) ) { + easing = value[ 1 ]; + value = props[ index ] = value[ 0 ]; + } + + if ( index !== name ) { + props[ name ] = value; + delete props[ index ]; + } + + hooks = jQuery.cssHooks[ name ]; + if ( hooks && "expand" in hooks ) { + value = hooks.expand( value ); + delete props[ name ]; + + // Not quite $.extend, this won't overwrite existing keys. + // Reusing 'index' because we have the correct "name" + for ( index in value ) { + if ( !( index in props ) ) { + props[ index ] = value[ index ]; + specialEasing[ index ] = easing; + } + } + } else { + specialEasing[ name ] = easing; + } + } +} + +function Animation( elem, properties, options ) { + var result, + stopped, + index = 0, + length = Animation.prefilters.length, + deferred = jQuery.Deferred().always( function() { + + // Don't match elem in the :animated selector + delete tick.elem; + } ), + tick = function() { + if ( stopped ) { + return false; + } + var currentTime = fxNow || createFxNow(), + remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ), + + // Support: Android 2.3 only + // Archaic crash bug won't allow us to use `1 - ( 0.5 || 0 )` (#12497) + temp = remaining / animation.duration || 0, + percent = 1 - temp, + index = 0, + length = animation.tweens.length; + + for ( ; index < length; index++ ) { + animation.tweens[ index ].run( percent ); + } + + deferred.notifyWith( elem, [ animation, percent, remaining ] ); + + // If there's more to do, yield + if ( percent < 1 && length ) { + return remaining; + } + + // If this was an empty animation, synthesize a final progress notification + if ( !length ) { + deferred.notifyWith( elem, [ animation, 1, 0 ] ); + } + + // Resolve the animation and report its conclusion + deferred.resolveWith( elem, [ animation ] ); + return false; + }, + animation = deferred.promise( { + elem: elem, + props: jQuery.extend( {}, properties ), + opts: jQuery.extend( true, { + specialEasing: {}, + easing: jQuery.easing._default + }, options ), + originalProperties: properties, + originalOptions: options, + startTime: fxNow || createFxNow(), + duration: options.duration, + tweens: [], + createTween: function( prop, end ) { + var tween = jQuery.Tween( elem, animation.opts, prop, end, + animation.opts.specialEasing[ prop ] || animation.opts.easing ); + animation.tweens.push( tween ); + return tween; + }, + stop: function( gotoEnd ) { + var index = 0, + + // If we are going to the end, we want to run all the tweens + // otherwise we skip this part + length = gotoEnd ? animation.tweens.length : 0; + if ( stopped ) { + return this; + } + stopped = true; + for ( ; index < length; index++ ) { + animation.tweens[ index ].run( 1 ); + } + + // Resolve when we played the last frame; otherwise, reject + if ( gotoEnd ) { + deferred.notifyWith( elem, [ animation, 1, 0 ] ); + deferred.resolveWith( elem, [ animation, gotoEnd ] ); + } else { + deferred.rejectWith( elem, [ animation, gotoEnd ] ); + } + return this; + } + } ), + props = animation.props; + + propFilter( props, animation.opts.specialEasing ); + + for ( ; index < length; index++ ) { + result = Animation.prefilters[ index ].call( animation, elem, props, animation.opts ); + if ( result ) { + if ( isFunction( result.stop ) ) { + jQuery._queueHooks( animation.elem, animation.opts.queue ).stop = + result.stop.bind( result ); + } + return result; + } + } + + jQuery.map( props, createTween, animation ); + + if ( isFunction( animation.opts.start ) ) { + animation.opts.start.call( elem, animation ); + } + + // Attach callbacks from options + animation + .progress( animation.opts.progress ) + .done( animation.opts.done, animation.opts.complete ) + .fail( animation.opts.fail ) + .always( animation.opts.always ); + + jQuery.fx.timer( + jQuery.extend( tick, { + elem: elem, + anim: animation, + queue: animation.opts.queue + } ) + ); + + return animation; +} + +jQuery.Animation = jQuery.extend( Animation, { + + tweeners: { + "*": [ function( prop, value ) { + var tween = this.createTween( prop, value ); + adjustCSS( tween.elem, prop, rcssNum.exec( value ), tween ); + return tween; + } ] + }, + + tweener: function( props, callback ) { + if ( isFunction( props ) ) { + callback = props; + props = [ "*" ]; + } else { + props = props.match( rnothtmlwhite ); + } + + var prop, + index = 0, + length = props.length; + + for ( ; index < length; index++ ) { + prop = props[ index ]; + Animation.tweeners[ prop ] = Animation.tweeners[ prop ] || []; + Animation.tweeners[ prop ].unshift( callback ); + } + }, + + prefilters: [ defaultPrefilter ], + + prefilter: function( callback, prepend ) { + if ( prepend ) { + Animation.prefilters.unshift( callback ); + } else { + Animation.prefilters.push( callback ); + } + } +} ); + +jQuery.speed = function( speed, easing, fn ) { + var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : { + complete: fn || !fn && easing || + isFunction( speed ) && speed, + duration: speed, + easing: fn && easing || easing && !isFunction( easing ) && easing + }; + + // Go to the end state if fx are off + if ( jQuery.fx.off ) { + opt.duration = 0; + + } else { + if ( typeof opt.duration !== "number" ) { + if ( opt.duration in jQuery.fx.speeds ) { + opt.duration = jQuery.fx.speeds[ opt.duration ]; + + } else { + opt.duration = jQuery.fx.speeds._default; + } + } + } + + // Normalize opt.queue - true/undefined/null -> "fx" + if ( opt.queue == null || opt.queue === true ) { + opt.queue = "fx"; + } + + // Queueing + opt.old = opt.complete; + + opt.complete = function() { + if ( isFunction( opt.old ) ) { + opt.old.call( this ); + } + + if ( opt.queue ) { + jQuery.dequeue( this, opt.queue ); + } + }; + + return opt; +}; + +jQuery.fn.extend( { + fadeTo: function( speed, to, easing, callback ) { + + // Show any hidden elements after setting opacity to 0 + return this.filter( isHiddenWithinTree ).css( "opacity", 0 ).show() + + // Animate to the value specified + .end().animate( { opacity: to }, speed, easing, callback ); + }, + animate: function( prop, speed, easing, callback ) { + var empty = jQuery.isEmptyObject( prop ), + optall = jQuery.speed( speed, easing, callback ), + doAnimation = function() { + + // Operate on a copy of prop so per-property easing won't be lost + var anim = Animation( this, jQuery.extend( {}, prop ), optall ); + + // Empty animations, or finishing resolves immediately + if ( empty || dataPriv.get( this, "finish" ) ) { + anim.stop( true ); + } + }; + doAnimation.finish = doAnimation; + + return empty || optall.queue === false ? + this.each( doAnimation ) : + this.queue( optall.queue, doAnimation ); + }, + stop: function( type, clearQueue, gotoEnd ) { + var stopQueue = function( hooks ) { + var stop = hooks.stop; + delete hooks.stop; + stop( gotoEnd ); + }; + + if ( typeof type !== "string" ) { + gotoEnd = clearQueue; + clearQueue = type; + type = undefined; + } + if ( clearQueue ) { + this.queue( type || "fx", [] ); + } + + return this.each( function() { + var dequeue = true, + index = type != null && type + "queueHooks", + timers = jQuery.timers, + data = dataPriv.get( this ); + + if ( index ) { + if ( data[ index ] && data[ index ].stop ) { + stopQueue( data[ index ] ); + } + } else { + for ( index in data ) { + if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) { + stopQueue( data[ index ] ); + } + } + } + + for ( index = timers.length; index--; ) { + if ( timers[ index ].elem === this && + ( type == null || timers[ index ].queue === type ) ) { + + timers[ index ].anim.stop( gotoEnd ); + dequeue = false; + timers.splice( index, 1 ); + } + } + + // Start the next in the queue if the last step wasn't forced. + // Timers currently will call their complete callbacks, which + // will dequeue but only if they were gotoEnd. + if ( dequeue || !gotoEnd ) { + jQuery.dequeue( this, type ); + } + } ); + }, + finish: function( type ) { + if ( type !== false ) { + type = type || "fx"; + } + return this.each( function() { + var index, + data = dataPriv.get( this ), + queue = data[ type + "queue" ], + hooks = data[ type + "queueHooks" ], + timers = jQuery.timers, + length = queue ? queue.length : 0; + + // Enable finishing flag on private data + data.finish = true; + + // Empty the queue first + jQuery.queue( this, type, [] ); + + if ( hooks && hooks.stop ) { + hooks.stop.call( this, true ); + } + + // Look for any active animations, and finish them + for ( index = timers.length; index--; ) { + if ( timers[ index ].elem === this && timers[ index ].queue === type ) { + timers[ index ].anim.stop( true ); + timers.splice( index, 1 ); + } + } + + // Look for any animations in the old queue and finish them + for ( index = 0; index < length; index++ ) { + if ( queue[ index ] && queue[ index ].finish ) { + queue[ index ].finish.call( this ); + } + } + + // Turn off finishing flag + delete data.finish; + } ); + } +} ); + +jQuery.each( [ "toggle", "show", "hide" ], function( _i, name ) { + var cssFn = jQuery.fn[ name ]; + jQuery.fn[ name ] = function( speed, easing, callback ) { + return speed == null || typeof speed === "boolean" ? + cssFn.apply( this, arguments ) : + this.animate( genFx( name, true ), speed, easing, callback ); + }; +} ); + +// Generate shortcuts for custom animations +jQuery.each( { + slideDown: genFx( "show" ), + slideUp: genFx( "hide" ), + slideToggle: genFx( "toggle" ), + fadeIn: { opacity: "show" }, + fadeOut: { opacity: "hide" }, + fadeToggle: { opacity: "toggle" } +}, function( name, props ) { + jQuery.fn[ name ] = function( speed, easing, callback ) { + return this.animate( props, speed, easing, callback ); + }; +} ); + +jQuery.timers = []; +jQuery.fx.tick = function() { + var timer, + i = 0, + timers = jQuery.timers; + + fxNow = Date.now(); + + for ( ; i < timers.length; i++ ) { + timer = timers[ i ]; + + // Run the timer and safely remove it when done (allowing for external removal) + if ( !timer() && timers[ i ] === timer ) { + timers.splice( i--, 1 ); + } + } + + if ( !timers.length ) { + jQuery.fx.stop(); + } + fxNow = undefined; +}; + +jQuery.fx.timer = function( timer ) { + jQuery.timers.push( timer ); + jQuery.fx.start(); +}; + +jQuery.fx.interval = 13; +jQuery.fx.start = function() { + if ( inProgress ) { + return; + } + + inProgress = true; + schedule(); +}; + +jQuery.fx.stop = function() { + inProgress = null; +}; + +jQuery.fx.speeds = { + slow: 600, + fast: 200, + + // Default speed + _default: 400 +}; + + +// Based off of the plugin by Clint Helfers, with permission. +// https://web.archive.org/web/20100324014747/http://blindsignals.com/index.php/2009/07/jquery-delay/ +jQuery.fn.delay = function( time, type ) { + time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time; + type = type || "fx"; + + return this.queue( type, function( next, hooks ) { + var timeout = window.setTimeout( next, time ); + hooks.stop = function() { + window.clearTimeout( timeout ); + }; + } ); +}; + + +( function() { + var input = document.createElement( "input" ), + select = document.createElement( "select" ), + opt = select.appendChild( document.createElement( "option" ) ); + + input.type = "checkbox"; + + // Support: Android <=4.3 only + // Default value for a checkbox should be "on" + support.checkOn = input.value !== ""; + + // Support: IE <=11 only + // Must access selectedIndex to make default options select + support.optSelected = opt.selected; + + // Support: IE <=11 only + // An input loses its value after becoming a radio + input = document.createElement( "input" ); + input.value = "t"; + input.type = "radio"; + support.radioValue = input.value === "t"; +} )(); + + +var boolHook, + attrHandle = jQuery.expr.attrHandle; + +jQuery.fn.extend( { + attr: function( name, value ) { + return access( this, jQuery.attr, name, value, arguments.length > 1 ); + }, + + removeAttr: function( name ) { + return this.each( function() { + jQuery.removeAttr( this, name ); + } ); + } +} ); + +jQuery.extend( { + attr: function( elem, name, value ) { + var ret, hooks, + nType = elem.nodeType; + + // Don't get/set attributes on text, comment and attribute nodes + if ( nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + // Fallback to prop when attributes are not supported + if ( typeof elem.getAttribute === "undefined" ) { + return jQuery.prop( elem, name, value ); + } + + // Attribute hooks are determined by the lowercase version + // Grab necessary hook if one is defined + if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { + hooks = jQuery.attrHooks[ name.toLowerCase() ] || + ( jQuery.expr.match.bool.test( name ) ? boolHook : undefined ); + } + + if ( value !== undefined ) { + if ( value === null ) { + jQuery.removeAttr( elem, name ); + return; + } + + if ( hooks && "set" in hooks && + ( ret = hooks.set( elem, value, name ) ) !== undefined ) { + return ret; + } + + elem.setAttribute( name, value + "" ); + return value; + } + + if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) { + return ret; + } + + ret = jQuery.find.attr( elem, name ); + + // Non-existent attributes return null, we normalize to undefined + return ret == null ? undefined : ret; + }, + + attrHooks: { + type: { + set: function( elem, value ) { + if ( !support.radioValue && value === "radio" && + nodeName( elem, "input" ) ) { + var val = elem.value; + elem.setAttribute( "type", value ); + if ( val ) { + elem.value = val; + } + return value; + } + } + } + }, + + removeAttr: function( elem, value ) { + var name, + i = 0, + + // Attribute names can contain non-HTML whitespace characters + // https://html.spec.whatwg.org/multipage/syntax.html#attributes-2 + attrNames = value && value.match( rnothtmlwhite ); + + if ( attrNames && elem.nodeType === 1 ) { + while ( ( name = attrNames[ i++ ] ) ) { + elem.removeAttribute( name ); + } + } + } +} ); + +// Hooks for boolean attributes +boolHook = { + set: function( elem, value, name ) { + if ( value === false ) { + + // Remove boolean attributes when set to false + jQuery.removeAttr( elem, name ); + } else { + elem.setAttribute( name, name ); + } + return name; + } +}; + +jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( _i, name ) { + var getter = attrHandle[ name ] || jQuery.find.attr; + + attrHandle[ name ] = function( elem, name, isXML ) { + var ret, handle, + lowercaseName = name.toLowerCase(); + + if ( !isXML ) { + + // Avoid an infinite loop by temporarily removing this function from the getter + handle = attrHandle[ lowercaseName ]; + attrHandle[ lowercaseName ] = ret; + ret = getter( elem, name, isXML ) != null ? + lowercaseName : + null; + attrHandle[ lowercaseName ] = handle; + } + return ret; + }; +} ); + + + + +var rfocusable = /^(?:input|select|textarea|button)$/i, + rclickable = /^(?:a|area)$/i; + +jQuery.fn.extend( { + prop: function( name, value ) { + return access( this, jQuery.prop, name, value, arguments.length > 1 ); + }, + + removeProp: function( name ) { + return this.each( function() { + delete this[ jQuery.propFix[ name ] || name ]; + } ); + } +} ); + +jQuery.extend( { + prop: function( elem, name, value ) { + var ret, hooks, + nType = elem.nodeType; + + // Don't get/set properties on text, comment and attribute nodes + if ( nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { + + // Fix name and attach hooks + name = jQuery.propFix[ name ] || name; + hooks = jQuery.propHooks[ name ]; + } + + if ( value !== undefined ) { + if ( hooks && "set" in hooks && + ( ret = hooks.set( elem, value, name ) ) !== undefined ) { + return ret; + } + + return ( elem[ name ] = value ); + } + + if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) { + return ret; + } + + return elem[ name ]; + }, + + propHooks: { + tabIndex: { + get: function( elem ) { + + // Support: IE <=9 - 11 only + // elem.tabIndex doesn't always return the + // correct value when it hasn't been explicitly set + // https://web.archive.org/web/20141116233347/http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ + // Use proper attribute retrieval(#12072) + var tabindex = jQuery.find.attr( elem, "tabindex" ); + + if ( tabindex ) { + return parseInt( tabindex, 10 ); + } + + if ( + rfocusable.test( elem.nodeName ) || + rclickable.test( elem.nodeName ) && + elem.href + ) { + return 0; + } + + return -1; + } + } + }, + + propFix: { + "for": "htmlFor", + "class": "className" + } +} ); + +// Support: IE <=11 only +// Accessing the selectedIndex property +// forces the browser to respect setting selected +// on the option +// The getter ensures a default option is selected +// when in an optgroup +// eslint rule "no-unused-expressions" is disabled for this code +// since it considers such accessions noop +if ( !support.optSelected ) { + jQuery.propHooks.selected = { + get: function( elem ) { + + /* eslint no-unused-expressions: "off" */ + + var parent = elem.parentNode; + if ( parent && parent.parentNode ) { + parent.parentNode.selectedIndex; + } + return null; + }, + set: function( elem ) { + + /* eslint no-unused-expressions: "off" */ + + var parent = elem.parentNode; + if ( parent ) { + parent.selectedIndex; + + if ( parent.parentNode ) { + parent.parentNode.selectedIndex; + } + } + } + }; +} + +jQuery.each( [ + "tabIndex", + "readOnly", + "maxLength", + "cellSpacing", + "cellPadding", + "rowSpan", + "colSpan", + "useMap", + "frameBorder", + "contentEditable" +], function() { + jQuery.propFix[ this.toLowerCase() ] = this; +} ); + + + + + // Strip and collapse whitespace according to HTML spec + // https://infra.spec.whatwg.org/#strip-and-collapse-ascii-whitespace + function stripAndCollapse( value ) { + var tokens = value.match( rnothtmlwhite ) || []; + return tokens.join( " " ); + } + + +function getClass( elem ) { + return elem.getAttribute && elem.getAttribute( "class" ) || ""; +} + +function classesToArray( value ) { + if ( Array.isArray( value ) ) { + return value; + } + if ( typeof value === "string" ) { + return value.match( rnothtmlwhite ) || []; + } + return []; +} + +jQuery.fn.extend( { + addClass: function( value ) { + var classes, elem, cur, curValue, clazz, j, finalValue, + i = 0; + + if ( isFunction( value ) ) { + return this.each( function( j ) { + jQuery( this ).addClass( value.call( this, j, getClass( this ) ) ); + } ); + } + + classes = classesToArray( value ); + + if ( classes.length ) { + while ( ( elem = this[ i++ ] ) ) { + curValue = getClass( elem ); + cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " ); + + if ( cur ) { + j = 0; + while ( ( clazz = classes[ j++ ] ) ) { + if ( cur.indexOf( " " + clazz + " " ) < 0 ) { + cur += clazz + " "; + } + } + + // Only assign if different to avoid unneeded rendering. + finalValue = stripAndCollapse( cur ); + if ( curValue !== finalValue ) { + elem.setAttribute( "class", finalValue ); + } + } + } + } + + return this; + }, + + removeClass: function( value ) { + var classes, elem, cur, curValue, clazz, j, finalValue, + i = 0; + + if ( isFunction( value ) ) { + return this.each( function( j ) { + jQuery( this ).removeClass( value.call( this, j, getClass( this ) ) ); + } ); + } + + if ( !arguments.length ) { + return this.attr( "class", "" ); + } + + classes = classesToArray( value ); + + if ( classes.length ) { + while ( ( elem = this[ i++ ] ) ) { + curValue = getClass( elem ); + + // This expression is here for better compressibility (see addClass) + cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " ); + + if ( cur ) { + j = 0; + while ( ( clazz = classes[ j++ ] ) ) { + + // Remove *all* instances + while ( cur.indexOf( " " + clazz + " " ) > -1 ) { + cur = cur.replace( " " + clazz + " ", " " ); + } + } + + // Only assign if different to avoid unneeded rendering. + finalValue = stripAndCollapse( cur ); + if ( curValue !== finalValue ) { + elem.setAttribute( "class", finalValue ); + } + } + } + } + + return this; + }, + + toggleClass: function( value, stateVal ) { + var type = typeof value, + isValidValue = type === "string" || Array.isArray( value ); + + if ( typeof stateVal === "boolean" && isValidValue ) { + return stateVal ? this.addClass( value ) : this.removeClass( value ); + } + + if ( isFunction( value ) ) { + return this.each( function( i ) { + jQuery( this ).toggleClass( + value.call( this, i, getClass( this ), stateVal ), + stateVal + ); + } ); + } + + return this.each( function() { + var className, i, self, classNames; + + if ( isValidValue ) { + + // Toggle individual class names + i = 0; + self = jQuery( this ); + classNames = classesToArray( value ); + + while ( ( className = classNames[ i++ ] ) ) { + + // Check each className given, space separated list + if ( self.hasClass( className ) ) { + self.removeClass( className ); + } else { + self.addClass( className ); + } + } + + // Toggle whole class name + } else if ( value === undefined || type === "boolean" ) { + className = getClass( this ); + if ( className ) { + + // Store className if set + dataPriv.set( this, "__className__", className ); + } + + // If the element has a class name or if we're passed `false`, + // then remove the whole classname (if there was one, the above saved it). + // Otherwise bring back whatever was previously saved (if anything), + // falling back to the empty string if nothing was stored. + if ( this.setAttribute ) { + this.setAttribute( "class", + className || value === false ? + "" : + dataPriv.get( this, "__className__" ) || "" + ); + } + } + } ); + }, + + hasClass: function( selector ) { + var className, elem, + i = 0; + + className = " " + selector + " "; + while ( ( elem = this[ i++ ] ) ) { + if ( elem.nodeType === 1 && + ( " " + stripAndCollapse( getClass( elem ) ) + " " ).indexOf( className ) > -1 ) { + return true; + } + } + + return false; + } +} ); + + + + +var rreturn = /\r/g; + +jQuery.fn.extend( { + val: function( value ) { + var hooks, ret, valueIsFunction, + elem = this[ 0 ]; + + if ( !arguments.length ) { + if ( elem ) { + hooks = jQuery.valHooks[ elem.type ] || + jQuery.valHooks[ elem.nodeName.toLowerCase() ]; + + if ( hooks && + "get" in hooks && + ( ret = hooks.get( elem, "value" ) ) !== undefined + ) { + return ret; + } + + ret = elem.value; + + // Handle most common string cases + if ( typeof ret === "string" ) { + return ret.replace( rreturn, "" ); + } + + // Handle cases where value is null/undef or number + return ret == null ? "" : ret; + } + + return; + } + + valueIsFunction = isFunction( value ); + + return this.each( function( i ) { + var val; + + if ( this.nodeType !== 1 ) { + return; + } + + if ( valueIsFunction ) { + val = value.call( this, i, jQuery( this ).val() ); + } else { + val = value; + } + + // Treat null/undefined as ""; convert numbers to string + if ( val == null ) { + val = ""; + + } else if ( typeof val === "number" ) { + val += ""; + + } else if ( Array.isArray( val ) ) { + val = jQuery.map( val, function( value ) { + return value == null ? "" : value + ""; + } ); + } + + hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ]; + + // If set returns undefined, fall back to normal setting + if ( !hooks || !( "set" in hooks ) || hooks.set( this, val, "value" ) === undefined ) { + this.value = val; + } + } ); + } +} ); + +jQuery.extend( { + valHooks: { + option: { + get: function( elem ) { + + var val = jQuery.find.attr( elem, "value" ); + return val != null ? + val : + + // Support: IE <=10 - 11 only + // option.text throws exceptions (#14686, #14858) + // Strip and collapse whitespace + // https://html.spec.whatwg.org/#strip-and-collapse-whitespace + stripAndCollapse( jQuery.text( elem ) ); + } + }, + select: { + get: function( elem ) { + var value, option, i, + options = elem.options, + index = elem.selectedIndex, + one = elem.type === "select-one", + values = one ? null : [], + max = one ? index + 1 : options.length; + + if ( index < 0 ) { + i = max; + + } else { + i = one ? index : 0; + } + + // Loop through all the selected options + for ( ; i < max; i++ ) { + option = options[ i ]; + + // Support: IE <=9 only + // IE8-9 doesn't update selected after form reset (#2551) + if ( ( option.selected || i === index ) && + + // Don't return options that are disabled or in a disabled optgroup + !option.disabled && + ( !option.parentNode.disabled || + !nodeName( option.parentNode, "optgroup" ) ) ) { + + // Get the specific value for the option + value = jQuery( option ).val(); + + // We don't need an array for one selects + if ( one ) { + return value; + } + + // Multi-Selects return an array + values.push( value ); + } + } + + return values; + }, + + set: function( elem, value ) { + var optionSet, option, + options = elem.options, + values = jQuery.makeArray( value ), + i = options.length; + + while ( i-- ) { + option = options[ i ]; + + /* eslint-disable no-cond-assign */ + + if ( option.selected = + jQuery.inArray( jQuery.valHooks.option.get( option ), values ) > -1 + ) { + optionSet = true; + } + + /* eslint-enable no-cond-assign */ + } + + // Force browsers to behave consistently when non-matching value is set + if ( !optionSet ) { + elem.selectedIndex = -1; + } + return values; + } + } + } +} ); + +// Radios and checkboxes getter/setter +jQuery.each( [ "radio", "checkbox" ], function() { + jQuery.valHooks[ this ] = { + set: function( elem, value ) { + if ( Array.isArray( value ) ) { + return ( elem.checked = jQuery.inArray( jQuery( elem ).val(), value ) > -1 ); + } + } + }; + if ( !support.checkOn ) { + jQuery.valHooks[ this ].get = function( elem ) { + return elem.getAttribute( "value" ) === null ? "on" : elem.value; + }; + } +} ); + + + + +// Return jQuery for attributes-only inclusion + + +support.focusin = "onfocusin" in window; + + +var rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, + stopPropagationCallback = function( e ) { + e.stopPropagation(); + }; + +jQuery.extend( jQuery.event, { + + trigger: function( event, data, elem, onlyHandlers ) { + + var i, cur, tmp, bubbleType, ontype, handle, special, lastElement, + eventPath = [ elem || document ], + type = hasOwn.call( event, "type" ) ? event.type : event, + namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split( "." ) : []; + + cur = lastElement = tmp = elem = elem || document; + + // Don't do events on text and comment nodes + if ( elem.nodeType === 3 || elem.nodeType === 8 ) { + return; + } + + // focus/blur morphs to focusin/out; ensure we're not firing them right now + if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { + return; + } + + if ( type.indexOf( "." ) > -1 ) { + + // Namespaced trigger; create a regexp to match event type in handle() + namespaces = type.split( "." ); + type = namespaces.shift(); + namespaces.sort(); + } + ontype = type.indexOf( ":" ) < 0 && "on" + type; + + // Caller can pass in a jQuery.Event object, Object, or just an event type string + event = event[ jQuery.expando ] ? + event : + new jQuery.Event( type, typeof event === "object" && event ); + + // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true) + event.isTrigger = onlyHandlers ? 2 : 3; + event.namespace = namespaces.join( "." ); + event.rnamespace = event.namespace ? + new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ) : + null; + + // Clean up the event in case it is being reused + event.result = undefined; + if ( !event.target ) { + event.target = elem; + } + + // Clone any incoming data and prepend the event, creating the handler arg list + data = data == null ? + [ event ] : + jQuery.makeArray( data, [ event ] ); + + // Allow special events to draw outside the lines + special = jQuery.event.special[ type ] || {}; + if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) { + return; + } + + // Determine event propagation path in advance, per W3C events spec (#9951) + // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) + if ( !onlyHandlers && !special.noBubble && !isWindow( elem ) ) { + + bubbleType = special.delegateType || type; + if ( !rfocusMorph.test( bubbleType + type ) ) { + cur = cur.parentNode; + } + for ( ; cur; cur = cur.parentNode ) { + eventPath.push( cur ); + tmp = cur; + } + + // Only add window if we got to document (e.g., not plain obj or detached DOM) + if ( tmp === ( elem.ownerDocument || document ) ) { + eventPath.push( tmp.defaultView || tmp.parentWindow || window ); + } + } + + // Fire handlers on the event path + i = 0; + while ( ( cur = eventPath[ i++ ] ) && !event.isPropagationStopped() ) { + lastElement = cur; + event.type = i > 1 ? + bubbleType : + special.bindType || type; + + // jQuery handler + handle = ( + dataPriv.get( cur, "events" ) || Object.create( null ) + )[ event.type ] && + dataPriv.get( cur, "handle" ); + if ( handle ) { + handle.apply( cur, data ); + } + + // Native handler + handle = ontype && cur[ ontype ]; + if ( handle && handle.apply && acceptData( cur ) ) { + event.result = handle.apply( cur, data ); + if ( event.result === false ) { + event.preventDefault(); + } + } + } + event.type = type; + + // If nobody prevented the default action, do it now + if ( !onlyHandlers && !event.isDefaultPrevented() ) { + + if ( ( !special._default || + special._default.apply( eventPath.pop(), data ) === false ) && + acceptData( elem ) ) { + + // Call a native DOM method on the target with the same name as the event. + // Don't do default actions on window, that's where global variables be (#6170) + if ( ontype && isFunction( elem[ type ] ) && !isWindow( elem ) ) { + + // Don't re-trigger an onFOO event when we call its FOO() method + tmp = elem[ ontype ]; + + if ( tmp ) { + elem[ ontype ] = null; + } + + // Prevent re-triggering of the same event, since we already bubbled it above + jQuery.event.triggered = type; + + if ( event.isPropagationStopped() ) { + lastElement.addEventListener( type, stopPropagationCallback ); + } + + elem[ type ](); + + if ( event.isPropagationStopped() ) { + lastElement.removeEventListener( type, stopPropagationCallback ); + } + + jQuery.event.triggered = undefined; + + if ( tmp ) { + elem[ ontype ] = tmp; + } + } + } + } + + return event.result; + }, + + // Piggyback on a donor event to simulate a different one + // Used only for `focus(in | out)` events + simulate: function( type, elem, event ) { + var e = jQuery.extend( + new jQuery.Event(), + event, + { + type: type, + isSimulated: true + } + ); + + jQuery.event.trigger( e, null, elem ); + } + +} ); + +jQuery.fn.extend( { + + trigger: function( type, data ) { + return this.each( function() { + jQuery.event.trigger( type, data, this ); + } ); + }, + triggerHandler: function( type, data ) { + var elem = this[ 0 ]; + if ( elem ) { + return jQuery.event.trigger( type, data, elem, true ); + } + } +} ); + + +// Support: Firefox <=44 +// Firefox doesn't have focus(in | out) events +// Related ticket - https://bugzilla.mozilla.org/show_bug.cgi?id=687787 +// +// Support: Chrome <=48 - 49, Safari <=9.0 - 9.1 +// focus(in | out) events fire after focus & blur events, +// which is spec violation - http://www.w3.org/TR/DOM-Level-3-Events/#events-focusevent-event-order +// Related ticket - https://bugs.chromium.org/p/chromium/issues/detail?id=449857 +if ( !support.focusin ) { + jQuery.each( { focus: "focusin", blur: "focusout" }, function( orig, fix ) { + + // Attach a single capturing handler on the document while someone wants focusin/focusout + var handler = function( event ) { + jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ) ); + }; + + jQuery.event.special[ fix ] = { + setup: function() { + + // Handle: regular nodes (via `this.ownerDocument`), window + // (via `this.document`) & document (via `this`). + var doc = this.ownerDocument || this.document || this, + attaches = dataPriv.access( doc, fix ); + + if ( !attaches ) { + doc.addEventListener( orig, handler, true ); + } + dataPriv.access( doc, fix, ( attaches || 0 ) + 1 ); + }, + teardown: function() { + var doc = this.ownerDocument || this.document || this, + attaches = dataPriv.access( doc, fix ) - 1; + + if ( !attaches ) { + doc.removeEventListener( orig, handler, true ); + dataPriv.remove( doc, fix ); + + } else { + dataPriv.access( doc, fix, attaches ); + } + } + }; + } ); +} +var location = window.location; + +var nonce = { guid: Date.now() }; + +var rquery = ( /\?/ ); + + + +// Cross-browser xml parsing +jQuery.parseXML = function( data ) { + var xml; + if ( !data || typeof data !== "string" ) { + return null; + } + + // Support: IE 9 - 11 only + // IE throws on parseFromString with invalid input. + try { + xml = ( new window.DOMParser() ).parseFromString( data, "text/xml" ); + } catch ( e ) { + xml = undefined; + } + + if ( !xml || xml.getElementsByTagName( "parsererror" ).length ) { + jQuery.error( "Invalid XML: " + data ); + } + return xml; +}; + + +var + rbracket = /\[\]$/, + rCRLF = /\r?\n/g, + rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i, + rsubmittable = /^(?:input|select|textarea|keygen)/i; + +function buildParams( prefix, obj, traditional, add ) { + var name; + + if ( Array.isArray( obj ) ) { + + // Serialize array item. + jQuery.each( obj, function( i, v ) { + if ( traditional || rbracket.test( prefix ) ) { + + // Treat each array item as a scalar. + add( prefix, v ); + + } else { + + // Item is non-scalar (array or object), encode its numeric index. + buildParams( + prefix + "[" + ( typeof v === "object" && v != null ? i : "" ) + "]", + v, + traditional, + add + ); + } + } ); + + } else if ( !traditional && toType( obj ) === "object" ) { + + // Serialize object item. + for ( name in obj ) { + buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add ); + } + + } else { + + // Serialize scalar item. + add( prefix, obj ); + } +} + +// Serialize an array of form elements or a set of +// key/values into a query string +jQuery.param = function( a, traditional ) { + var prefix, + s = [], + add = function( key, valueOrFunction ) { + + // If value is a function, invoke it and use its return value + var value = isFunction( valueOrFunction ) ? + valueOrFunction() : + valueOrFunction; + + s[ s.length ] = encodeURIComponent( key ) + "=" + + encodeURIComponent( value == null ? "" : value ); + }; + + if ( a == null ) { + return ""; + } + + // If an array was passed in, assume that it is an array of form elements. + if ( Array.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) { + + // Serialize the form elements + jQuery.each( a, function() { + add( this.name, this.value ); + } ); + + } else { + + // If traditional, encode the "old" way (the way 1.3.2 or older + // did it), otherwise encode params recursively. + for ( prefix in a ) { + buildParams( prefix, a[ prefix ], traditional, add ); + } + } + + // Return the resulting serialization + return s.join( "&" ); +}; + +jQuery.fn.extend( { + serialize: function() { + return jQuery.param( this.serializeArray() ); + }, + serializeArray: function() { + return this.map( function() { + + // Can add propHook for "elements" to filter or add form elements + var elements = jQuery.prop( this, "elements" ); + return elements ? jQuery.makeArray( elements ) : this; + } ) + .filter( function() { + var type = this.type; + + // Use .is( ":disabled" ) so that fieldset[disabled] works + return this.name && !jQuery( this ).is( ":disabled" ) && + rsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) && + ( this.checked || !rcheckableType.test( type ) ); + } ) + .map( function( _i, elem ) { + var val = jQuery( this ).val(); + + if ( val == null ) { + return null; + } + + if ( Array.isArray( val ) ) { + return jQuery.map( val, function( val ) { + return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; + } ); + } + + return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; + } ).get(); + } +} ); + + +var + r20 = /%20/g, + rhash = /#.*$/, + rantiCache = /([?&])_=[^&]*/, + rheaders = /^(.*?):[ \t]*([^\r\n]*)$/mg, + + // #7653, #8125, #8152: local protocol detection + rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/, + rnoContent = /^(?:GET|HEAD)$/, + rprotocol = /^\/\//, + + /* Prefilters + * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example) + * 2) These are called: + * - BEFORE asking for a transport + * - AFTER param serialization (s.data is a string if s.processData is true) + * 3) key is the dataType + * 4) the catchall symbol "*" can be used + * 5) execution will start with transport dataType and THEN continue down to "*" if needed + */ + prefilters = {}, + + /* Transports bindings + * 1) key is the dataType + * 2) the catchall symbol "*" can be used + * 3) selection will start with transport dataType and THEN go to "*" if needed + */ + transports = {}, + + // Avoid comment-prolog char sequence (#10098); must appease lint and evade compression + allTypes = "*/".concat( "*" ), + + // Anchor tag for parsing the document origin + originAnchor = document.createElement( "a" ); + originAnchor.href = location.href; + +// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport +function addToPrefiltersOrTransports( structure ) { + + // dataTypeExpression is optional and defaults to "*" + return function( dataTypeExpression, func ) { + + if ( typeof dataTypeExpression !== "string" ) { + func = dataTypeExpression; + dataTypeExpression = "*"; + } + + var dataType, + i = 0, + dataTypes = dataTypeExpression.toLowerCase().match( rnothtmlwhite ) || []; + + if ( isFunction( func ) ) { + + // For each dataType in the dataTypeExpression + while ( ( dataType = dataTypes[ i++ ] ) ) { + + // Prepend if requested + if ( dataType[ 0 ] === "+" ) { + dataType = dataType.slice( 1 ) || "*"; + ( structure[ dataType ] = structure[ dataType ] || [] ).unshift( func ); + + // Otherwise append + } else { + ( structure[ dataType ] = structure[ dataType ] || [] ).push( func ); + } + } + } + }; +} + +// Base inspection function for prefilters and transports +function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR ) { + + var inspected = {}, + seekingTransport = ( structure === transports ); + + function inspect( dataType ) { + var selected; + inspected[ dataType ] = true; + jQuery.each( structure[ dataType ] || [], function( _, prefilterOrFactory ) { + var dataTypeOrTransport = prefilterOrFactory( options, originalOptions, jqXHR ); + if ( typeof dataTypeOrTransport === "string" && + !seekingTransport && !inspected[ dataTypeOrTransport ] ) { + + options.dataTypes.unshift( dataTypeOrTransport ); + inspect( dataTypeOrTransport ); + return false; + } else if ( seekingTransport ) { + return !( selected = dataTypeOrTransport ); + } + } ); + return selected; + } + + return inspect( options.dataTypes[ 0 ] ) || !inspected[ "*" ] && inspect( "*" ); +} + +// A special extend for ajax options +// that takes "flat" options (not to be deep extended) +// Fixes #9887 +function ajaxExtend( target, src ) { + var key, deep, + flatOptions = jQuery.ajaxSettings.flatOptions || {}; + + for ( key in src ) { + if ( src[ key ] !== undefined ) { + ( flatOptions[ key ] ? target : ( deep || ( deep = {} ) ) )[ key ] = src[ key ]; + } + } + if ( deep ) { + jQuery.extend( true, target, deep ); + } + + return target; +} + +/* Handles responses to an ajax request: + * - finds the right dataType (mediates between content-type and expected dataType) + * - returns the corresponding response + */ +function ajaxHandleResponses( s, jqXHR, responses ) { + + var ct, type, finalDataType, firstDataType, + contents = s.contents, + dataTypes = s.dataTypes; + + // Remove auto dataType and get content-type in the process + while ( dataTypes[ 0 ] === "*" ) { + dataTypes.shift(); + if ( ct === undefined ) { + ct = s.mimeType || jqXHR.getResponseHeader( "Content-Type" ); + } + } + + // Check if we're dealing with a known content-type + if ( ct ) { + for ( type in contents ) { + if ( contents[ type ] && contents[ type ].test( ct ) ) { + dataTypes.unshift( type ); + break; + } + } + } + + // Check to see if we have a response for the expected dataType + if ( dataTypes[ 0 ] in responses ) { + finalDataType = dataTypes[ 0 ]; + } else { + + // Try convertible dataTypes + for ( type in responses ) { + if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[ 0 ] ] ) { + finalDataType = type; + break; + } + if ( !firstDataType ) { + firstDataType = type; + } + } + + // Or just use first one + finalDataType = finalDataType || firstDataType; + } + + // If we found a dataType + // We add the dataType to the list if needed + // and return the corresponding response + if ( finalDataType ) { + if ( finalDataType !== dataTypes[ 0 ] ) { + dataTypes.unshift( finalDataType ); + } + return responses[ finalDataType ]; + } +} + +/* Chain conversions given the request and the original response + * Also sets the responseXXX fields on the jqXHR instance + */ +function ajaxConvert( s, response, jqXHR, isSuccess ) { + var conv2, current, conv, tmp, prev, + converters = {}, + + // Work with a copy of dataTypes in case we need to modify it for conversion + dataTypes = s.dataTypes.slice(); + + // Create converters map with lowercased keys + if ( dataTypes[ 1 ] ) { + for ( conv in s.converters ) { + converters[ conv.toLowerCase() ] = s.converters[ conv ]; + } + } + + current = dataTypes.shift(); + + // Convert to each sequential dataType + while ( current ) { + + if ( s.responseFields[ current ] ) { + jqXHR[ s.responseFields[ current ] ] = response; + } + + // Apply the dataFilter if provided + if ( !prev && isSuccess && s.dataFilter ) { + response = s.dataFilter( response, s.dataType ); + } + + prev = current; + current = dataTypes.shift(); + + if ( current ) { + + // There's only work to do if current dataType is non-auto + if ( current === "*" ) { + + current = prev; + + // Convert response if prev dataType is non-auto and differs from current + } else if ( prev !== "*" && prev !== current ) { + + // Seek a direct converter + conv = converters[ prev + " " + current ] || converters[ "* " + current ]; + + // If none found, seek a pair + if ( !conv ) { + for ( conv2 in converters ) { + + // If conv2 outputs current + tmp = conv2.split( " " ); + if ( tmp[ 1 ] === current ) { + + // If prev can be converted to accepted input + conv = converters[ prev + " " + tmp[ 0 ] ] || + converters[ "* " + tmp[ 0 ] ]; + if ( conv ) { + + // Condense equivalence converters + if ( conv === true ) { + conv = converters[ conv2 ]; + + // Otherwise, insert the intermediate dataType + } else if ( converters[ conv2 ] !== true ) { + current = tmp[ 0 ]; + dataTypes.unshift( tmp[ 1 ] ); + } + break; + } + } + } + } + + // Apply converter (if not an equivalence) + if ( conv !== true ) { + + // Unless errors are allowed to bubble, catch and return them + if ( conv && s.throws ) { + response = conv( response ); + } else { + try { + response = conv( response ); + } catch ( e ) { + return { + state: "parsererror", + error: conv ? e : "No conversion from " + prev + " to " + current + }; + } + } + } + } + } + } + + return { state: "success", data: response }; +} + +jQuery.extend( { + + // Counter for holding the number of active queries + active: 0, + + // Last-Modified header cache for next request + lastModified: {}, + etag: {}, + + ajaxSettings: { + url: location.href, + type: "GET", + isLocal: rlocalProtocol.test( location.protocol ), + global: true, + processData: true, + async: true, + contentType: "application/x-www-form-urlencoded; charset=UTF-8", + + /* + timeout: 0, + data: null, + dataType: null, + username: null, + password: null, + cache: null, + throws: false, + traditional: false, + headers: {}, + */ + + accepts: { + "*": allTypes, + text: "text/plain", + html: "text/html", + xml: "application/xml, text/xml", + json: "application/json, text/javascript" + }, + + contents: { + xml: /\bxml\b/, + html: /\bhtml/, + json: /\bjson\b/ + }, + + responseFields: { + xml: "responseXML", + text: "responseText", + json: "responseJSON" + }, + + // Data converters + // Keys separate source (or catchall "*") and destination types with a single space + converters: { + + // Convert anything to text + "* text": String, + + // Text to html (true = no transformation) + "text html": true, + + // Evaluate text as a json expression + "text json": JSON.parse, + + // Parse text as xml + "text xml": jQuery.parseXML + }, + + // For options that shouldn't be deep extended: + // you can add your own custom options here if + // and when you create one that shouldn't be + // deep extended (see ajaxExtend) + flatOptions: { + url: true, + context: true + } + }, + + // Creates a full fledged settings object into target + // with both ajaxSettings and settings fields. + // If target is omitted, writes into ajaxSettings. + ajaxSetup: function( target, settings ) { + return settings ? + + // Building a settings object + ajaxExtend( ajaxExtend( target, jQuery.ajaxSettings ), settings ) : + + // Extending ajaxSettings + ajaxExtend( jQuery.ajaxSettings, target ); + }, + + ajaxPrefilter: addToPrefiltersOrTransports( prefilters ), + ajaxTransport: addToPrefiltersOrTransports( transports ), + + // Main method + ajax: function( url, options ) { + + // If url is an object, simulate pre-1.5 signature + if ( typeof url === "object" ) { + options = url; + url = undefined; + } + + // Force options to be an object + options = options || {}; + + var transport, + + // URL without anti-cache param + cacheURL, + + // Response headers + responseHeadersString, + responseHeaders, + + // timeout handle + timeoutTimer, + + // Url cleanup var + urlAnchor, + + // Request state (becomes false upon send and true upon completion) + completed, + + // To know if global events are to be dispatched + fireGlobals, + + // Loop variable + i, + + // uncached part of the url + uncached, + + // Create the final options object + s = jQuery.ajaxSetup( {}, options ), + + // Callbacks context + callbackContext = s.context || s, + + // Context for global events is callbackContext if it is a DOM node or jQuery collection + globalEventContext = s.context && + ( callbackContext.nodeType || callbackContext.jquery ) ? + jQuery( callbackContext ) : + jQuery.event, + + // Deferreds + deferred = jQuery.Deferred(), + completeDeferred = jQuery.Callbacks( "once memory" ), + + // Status-dependent callbacks + statusCode = s.statusCode || {}, + + // Headers (they are sent all at once) + requestHeaders = {}, + requestHeadersNames = {}, + + // Default abort message + strAbort = "canceled", + + // Fake xhr + jqXHR = { + readyState: 0, + + // Builds headers hashtable if needed + getResponseHeader: function( key ) { + var match; + if ( completed ) { + if ( !responseHeaders ) { + responseHeaders = {}; + while ( ( match = rheaders.exec( responseHeadersString ) ) ) { + responseHeaders[ match[ 1 ].toLowerCase() + " " ] = + ( responseHeaders[ match[ 1 ].toLowerCase() + " " ] || [] ) + .concat( match[ 2 ] ); + } + } + match = responseHeaders[ key.toLowerCase() + " " ]; + } + return match == null ? null : match.join( ", " ); + }, + + // Raw string + getAllResponseHeaders: function() { + return completed ? responseHeadersString : null; + }, + + // Caches the header + setRequestHeader: function( name, value ) { + if ( completed == null ) { + name = requestHeadersNames[ name.toLowerCase() ] = + requestHeadersNames[ name.toLowerCase() ] || name; + requestHeaders[ name ] = value; + } + return this; + }, + + // Overrides response content-type header + overrideMimeType: function( type ) { + if ( completed == null ) { + s.mimeType = type; + } + return this; + }, + + // Status-dependent callbacks + statusCode: function( map ) { + var code; + if ( map ) { + if ( completed ) { + + // Execute the appropriate callbacks + jqXHR.always( map[ jqXHR.status ] ); + } else { + + // Lazy-add the new callbacks in a way that preserves old ones + for ( code in map ) { + statusCode[ code ] = [ statusCode[ code ], map[ code ] ]; + } + } + } + return this; + }, + + // Cancel the request + abort: function( statusText ) { + var finalText = statusText || strAbort; + if ( transport ) { + transport.abort( finalText ); + } + done( 0, finalText ); + return this; + } + }; + + // Attach deferreds + deferred.promise( jqXHR ); + + // Add protocol if not provided (prefilters might expect it) + // Handle falsy url in the settings object (#10093: consistency with old signature) + // We also use the url parameter if available + s.url = ( ( url || s.url || location.href ) + "" ) + .replace( rprotocol, location.protocol + "//" ); + + // Alias method option to type as per ticket #12004 + s.type = options.method || options.type || s.method || s.type; + + // Extract dataTypes list + s.dataTypes = ( s.dataType || "*" ).toLowerCase().match( rnothtmlwhite ) || [ "" ]; + + // A cross-domain request is in order when the origin doesn't match the current origin. + if ( s.crossDomain == null ) { + urlAnchor = document.createElement( "a" ); + + // Support: IE <=8 - 11, Edge 12 - 15 + // IE throws exception on accessing the href property if url is malformed, + // e.g. http://example.com:80x/ + try { + urlAnchor.href = s.url; + + // Support: IE <=8 - 11 only + // Anchor's host property isn't correctly set when s.url is relative + urlAnchor.href = urlAnchor.href; + s.crossDomain = originAnchor.protocol + "//" + originAnchor.host !== + urlAnchor.protocol + "//" + urlAnchor.host; + } catch ( e ) { + + // If there is an error parsing the URL, assume it is crossDomain, + // it can be rejected by the transport if it is invalid + s.crossDomain = true; + } + } + + // Convert data if not already a string + if ( s.data && s.processData && typeof s.data !== "string" ) { + s.data = jQuery.param( s.data, s.traditional ); + } + + // Apply prefilters + inspectPrefiltersOrTransports( prefilters, s, options, jqXHR ); + + // If request was aborted inside a prefilter, stop there + if ( completed ) { + return jqXHR; + } + + // We can fire global events as of now if asked to + // Don't fire events if jQuery.event is undefined in an AMD-usage scenario (#15118) + fireGlobals = jQuery.event && s.global; + + // Watch for a new set of requests + if ( fireGlobals && jQuery.active++ === 0 ) { + jQuery.event.trigger( "ajaxStart" ); + } + + // Uppercase the type + s.type = s.type.toUpperCase(); + + // Determine if request has content + s.hasContent = !rnoContent.test( s.type ); + + // Save the URL in case we're toying with the If-Modified-Since + // and/or If-None-Match header later on + // Remove hash to simplify url manipulation + cacheURL = s.url.replace( rhash, "" ); + + // More options handling for requests with no content + if ( !s.hasContent ) { + + // Remember the hash so we can put it back + uncached = s.url.slice( cacheURL.length ); + + // If data is available and should be processed, append data to url + if ( s.data && ( s.processData || typeof s.data === "string" ) ) { + cacheURL += ( rquery.test( cacheURL ) ? "&" : "?" ) + s.data; + + // #9682: remove data so that it's not used in an eventual retry + delete s.data; + } + + // Add or update anti-cache param if needed + if ( s.cache === false ) { + cacheURL = cacheURL.replace( rantiCache, "$1" ); + uncached = ( rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + ( nonce.guid++ ) + + uncached; + } + + // Put hash and anti-cache on the URL that will be requested (gh-1732) + s.url = cacheURL + uncached; + + // Change '%20' to '+' if this is encoded form body content (gh-2658) + } else if ( s.data && s.processData && + ( s.contentType || "" ).indexOf( "application/x-www-form-urlencoded" ) === 0 ) { + s.data = s.data.replace( r20, "+" ); + } + + // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. + if ( s.ifModified ) { + if ( jQuery.lastModified[ cacheURL ] ) { + jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ cacheURL ] ); + } + if ( jQuery.etag[ cacheURL ] ) { + jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ cacheURL ] ); + } + } + + // Set the correct header, if data is being sent + if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) { + jqXHR.setRequestHeader( "Content-Type", s.contentType ); + } + + // Set the Accepts header for the server, depending on the dataType + jqXHR.setRequestHeader( + "Accept", + s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[ 0 ] ] ? + s.accepts[ s.dataTypes[ 0 ] ] + + ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) : + s.accepts[ "*" ] + ); + + // Check for headers option + for ( i in s.headers ) { + jqXHR.setRequestHeader( i, s.headers[ i ] ); + } + + // Allow custom headers/mimetypes and early abort + if ( s.beforeSend && + ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || completed ) ) { + + // Abort if not done already and return + return jqXHR.abort(); + } + + // Aborting is no longer a cancellation + strAbort = "abort"; + + // Install callbacks on deferreds + completeDeferred.add( s.complete ); + jqXHR.done( s.success ); + jqXHR.fail( s.error ); + + // Get transport + transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR ); + + // If no transport, we auto-abort + if ( !transport ) { + done( -1, "No Transport" ); + } else { + jqXHR.readyState = 1; + + // Send global event + if ( fireGlobals ) { + globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] ); + } + + // If request was aborted inside ajaxSend, stop there + if ( completed ) { + return jqXHR; + } + + // Timeout + if ( s.async && s.timeout > 0 ) { + timeoutTimer = window.setTimeout( function() { + jqXHR.abort( "timeout" ); + }, s.timeout ); + } + + try { + completed = false; + transport.send( requestHeaders, done ); + } catch ( e ) { + + // Rethrow post-completion exceptions + if ( completed ) { + throw e; + } + + // Propagate others as results + done( -1, e ); + } + } + + // Callback for when everything is done + function done( status, nativeStatusText, responses, headers ) { + var isSuccess, success, error, response, modified, + statusText = nativeStatusText; + + // Ignore repeat invocations + if ( completed ) { + return; + } + + completed = true; + + // Clear timeout if it exists + if ( timeoutTimer ) { + window.clearTimeout( timeoutTimer ); + } + + // Dereference transport for early garbage collection + // (no matter how long the jqXHR object will be used) + transport = undefined; + + // Cache response headers + responseHeadersString = headers || ""; + + // Set readyState + jqXHR.readyState = status > 0 ? 4 : 0; + + // Determine if successful + isSuccess = status >= 200 && status < 300 || status === 304; + + // Get response data + if ( responses ) { + response = ajaxHandleResponses( s, jqXHR, responses ); + } + + // Use a noop converter for missing script + if ( !isSuccess && jQuery.inArray( "script", s.dataTypes ) > -1 ) { + s.converters[ "text script" ] = function() {}; + } + + // Convert no matter what (that way responseXXX fields are always set) + response = ajaxConvert( s, response, jqXHR, isSuccess ); + + // If successful, handle type chaining + if ( isSuccess ) { + + // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. + if ( s.ifModified ) { + modified = jqXHR.getResponseHeader( "Last-Modified" ); + if ( modified ) { + jQuery.lastModified[ cacheURL ] = modified; + } + modified = jqXHR.getResponseHeader( "etag" ); + if ( modified ) { + jQuery.etag[ cacheURL ] = modified; + } + } + + // if no content + if ( status === 204 || s.type === "HEAD" ) { + statusText = "nocontent"; + + // if not modified + } else if ( status === 304 ) { + statusText = "notmodified"; + + // If we have data, let's convert it + } else { + statusText = response.state; + success = response.data; + error = response.error; + isSuccess = !error; + } + } else { + + // Extract error from statusText and normalize for non-aborts + error = statusText; + if ( status || !statusText ) { + statusText = "error"; + if ( status < 0 ) { + status = 0; + } + } + } + + // Set data for the fake xhr object + jqXHR.status = status; + jqXHR.statusText = ( nativeStatusText || statusText ) + ""; + + // Success/Error + if ( isSuccess ) { + deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] ); + } else { + deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] ); + } + + // Status-dependent callbacks + jqXHR.statusCode( statusCode ); + statusCode = undefined; + + if ( fireGlobals ) { + globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError", + [ jqXHR, s, isSuccess ? success : error ] ); + } + + // Complete + completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] ); + + if ( fireGlobals ) { + globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] ); + + // Handle the global AJAX counter + if ( !( --jQuery.active ) ) { + jQuery.event.trigger( "ajaxStop" ); + } + } + } + + return jqXHR; + }, + + getJSON: function( url, data, callback ) { + return jQuery.get( url, data, callback, "json" ); + }, + + getScript: function( url, callback ) { + return jQuery.get( url, undefined, callback, "script" ); + } +} ); + +jQuery.each( [ "get", "post" ], function( _i, method ) { + jQuery[ method ] = function( url, data, callback, type ) { + + // Shift arguments if data argument was omitted + if ( isFunction( data ) ) { + type = type || callback; + callback = data; + data = undefined; + } + + // The url can be an options object (which then must have .url) + return jQuery.ajax( jQuery.extend( { + url: url, + type: method, + dataType: type, + data: data, + success: callback + }, jQuery.isPlainObject( url ) && url ) ); + }; +} ); + +jQuery.ajaxPrefilter( function( s ) { + var i; + for ( i in s.headers ) { + if ( i.toLowerCase() === "content-type" ) { + s.contentType = s.headers[ i ] || ""; + } + } +} ); + + +jQuery._evalUrl = function( url, options, doc ) { + return jQuery.ajax( { + url: url, + + // Make this explicit, since user can override this through ajaxSetup (#11264) + type: "GET", + dataType: "script", + cache: true, + async: false, + global: false, + + // Only evaluate the response if it is successful (gh-4126) + // dataFilter is not invoked for failure responses, so using it instead + // of the default converter is kludgy but it works. + converters: { + "text script": function() {} + }, + dataFilter: function( response ) { + jQuery.globalEval( response, options, doc ); + } + } ); +}; + + +jQuery.fn.extend( { + wrapAll: function( html ) { + var wrap; + + if ( this[ 0 ] ) { + if ( isFunction( html ) ) { + html = html.call( this[ 0 ] ); + } + + // The elements to wrap the target around + wrap = jQuery( html, this[ 0 ].ownerDocument ).eq( 0 ).clone( true ); + + if ( this[ 0 ].parentNode ) { + wrap.insertBefore( this[ 0 ] ); + } + + wrap.map( function() { + var elem = this; + + while ( elem.firstElementChild ) { + elem = elem.firstElementChild; + } + + return elem; + } ).append( this ); + } + + return this; + }, + + wrapInner: function( html ) { + if ( isFunction( html ) ) { + return this.each( function( i ) { + jQuery( this ).wrapInner( html.call( this, i ) ); + } ); + } + + return this.each( function() { + var self = jQuery( this ), + contents = self.contents(); + + if ( contents.length ) { + contents.wrapAll( html ); + + } else { + self.append( html ); + } + } ); + }, + + wrap: function( html ) { + var htmlIsFunction = isFunction( html ); + + return this.each( function( i ) { + jQuery( this ).wrapAll( htmlIsFunction ? html.call( this, i ) : html ); + } ); + }, + + unwrap: function( selector ) { + this.parent( selector ).not( "body" ).each( function() { + jQuery( this ).replaceWith( this.childNodes ); + } ); + return this; + } +} ); + + +jQuery.expr.pseudos.hidden = function( elem ) { + return !jQuery.expr.pseudos.visible( elem ); +}; +jQuery.expr.pseudos.visible = function( elem ) { + return !!( elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length ); +}; + + + + +jQuery.ajaxSettings.xhr = function() { + try { + return new window.XMLHttpRequest(); + } catch ( e ) {} +}; + +var xhrSuccessStatus = { + + // File protocol always yields status code 0, assume 200 + 0: 200, + + // Support: IE <=9 only + // #1450: sometimes IE returns 1223 when it should be 204 + 1223: 204 + }, + xhrSupported = jQuery.ajaxSettings.xhr(); + +support.cors = !!xhrSupported && ( "withCredentials" in xhrSupported ); +support.ajax = xhrSupported = !!xhrSupported; + +jQuery.ajaxTransport( function( options ) { + var callback, errorCallback; + + // Cross domain only allowed if supported through XMLHttpRequest + if ( support.cors || xhrSupported && !options.crossDomain ) { + return { + send: function( headers, complete ) { + var i, + xhr = options.xhr(); + + xhr.open( + options.type, + options.url, + options.async, + options.username, + options.password + ); + + // Apply custom fields if provided + if ( options.xhrFields ) { + for ( i in options.xhrFields ) { + xhr[ i ] = options.xhrFields[ i ]; + } + } + + // Override mime type if needed + if ( options.mimeType && xhr.overrideMimeType ) { + xhr.overrideMimeType( options.mimeType ); + } + + // X-Requested-With header + // For cross-domain requests, seeing as conditions for a preflight are + // akin to a jigsaw puzzle, we simply never set it to be sure. + // (it can always be set on a per-request basis or even using ajaxSetup) + // For same-domain requests, won't change header if already provided. + if ( !options.crossDomain && !headers[ "X-Requested-With" ] ) { + headers[ "X-Requested-With" ] = "XMLHttpRequest"; + } + + // Set headers + for ( i in headers ) { + xhr.setRequestHeader( i, headers[ i ] ); + } + + // Callback + callback = function( type ) { + return function() { + if ( callback ) { + callback = errorCallback = xhr.onload = + xhr.onerror = xhr.onabort = xhr.ontimeout = + xhr.onreadystatechange = null; + + if ( type === "abort" ) { + xhr.abort(); + } else if ( type === "error" ) { + + // Support: IE <=9 only + // On a manual native abort, IE9 throws + // errors on any property access that is not readyState + if ( typeof xhr.status !== "number" ) { + complete( 0, "error" ); + } else { + complete( + + // File: protocol always yields status 0; see #8605, #14207 + xhr.status, + xhr.statusText + ); + } + } else { + complete( + xhrSuccessStatus[ xhr.status ] || xhr.status, + xhr.statusText, + + // Support: IE <=9 only + // IE9 has no XHR2 but throws on binary (trac-11426) + // For XHR2 non-text, let the caller handle it (gh-2498) + ( xhr.responseType || "text" ) !== "text" || + typeof xhr.responseText !== "string" ? + { binary: xhr.response } : + { text: xhr.responseText }, + xhr.getAllResponseHeaders() + ); + } + } + }; + }; + + // Listen to events + xhr.onload = callback(); + errorCallback = xhr.onerror = xhr.ontimeout = callback( "error" ); + + // Support: IE 9 only + // Use onreadystatechange to replace onabort + // to handle uncaught aborts + if ( xhr.onabort !== undefined ) { + xhr.onabort = errorCallback; + } else { + xhr.onreadystatechange = function() { + + // Check readyState before timeout as it changes + if ( xhr.readyState === 4 ) { + + // Allow onerror to be called first, + // but that will not handle a native abort + // Also, save errorCallback to a variable + // as xhr.onerror cannot be accessed + window.setTimeout( function() { + if ( callback ) { + errorCallback(); + } + } ); + } + }; + } + + // Create the abort callback + callback = callback( "abort" ); + + try { + + // Do send the request (this may raise an exception) + xhr.send( options.hasContent && options.data || null ); + } catch ( e ) { + + // #14683: Only rethrow if this hasn't been notified as an error yet + if ( callback ) { + throw e; + } + } + }, + + abort: function() { + if ( callback ) { + callback(); + } + } + }; + } +} ); + + + + +// Prevent auto-execution of scripts when no explicit dataType was provided (See gh-2432) +jQuery.ajaxPrefilter( function( s ) { + if ( s.crossDomain ) { + s.contents.script = false; + } +} ); + +// Install script dataType +jQuery.ajaxSetup( { + accepts: { + script: "text/javascript, application/javascript, " + + "application/ecmascript, application/x-ecmascript" + }, + contents: { + script: /\b(?:java|ecma)script\b/ + }, + converters: { + "text script": function( text ) { + jQuery.globalEval( text ); + return text; + } + } +} ); + +// Handle cache's special case and crossDomain +jQuery.ajaxPrefilter( "script", function( s ) { + if ( s.cache === undefined ) { + s.cache = false; + } + if ( s.crossDomain ) { + s.type = "GET"; + } +} ); + +// Bind script tag hack transport +jQuery.ajaxTransport( "script", function( s ) { + + // This transport only deals with cross domain or forced-by-attrs requests + if ( s.crossDomain || s.scriptAttrs ) { + var script, callback; + return { + send: function( _, complete ) { + script = jQuery( " + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

bench

+

Executes multiple tests on the library and output CPU usage +information. +Can be executed with make bench or _build/default/bench/bench.exe.

+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/doc/html/dev-doc/binaries/cgl.html b/doc/html/dev-doc/binaries/cgl.html new file mode 100644 index 0000000000..b8c66c974b --- /dev/null +++ b/doc/html/dev-doc/binaries/cgl.html @@ -0,0 +1,127 @@ + + + + + + + + cgl — Geneweb 0.1 documentation + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

cgl

+

Removes the links of an HTML page and replaces them by spans. +Registered as a service.

+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/doc/html/dev-doc/binaries/connex.html b/doc/html/dev-doc/binaries/connex.html new file mode 100644 index 0000000000..a37ede2b49 --- /dev/null +++ b/doc/html/dev-doc/binaries/connex.html @@ -0,0 +1,148 @@ + + + + + + + + connex — Geneweb 0.1 documentation + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

connex

+
+

Documentation

+

Calculates the connex components of a base and returns it in HTML.

+
usage: geneweb.connex <base>
+
+  -gwd_p <number>: Specify the port number of gwd (default = 2317); > 1024 for normal users.
+  -server <string>: Name of the server (default is 127.0.0.1).
+  -a : all connex components
+  -s : produce statistics
+  -d <int> : detail for this length
+  -i <file> : ignore this file
+  -bf : by origin files
+  -del <int> : ask for deleting branches whose size <= that value
+  -cnt <int> : delete cnt branches whose size <= -del value
+  -exact : delete only branches whose size strictly = -del value
+  -o <file> : output to this file
+  -help  Display this list of options
+  --help  Display this list of options
+
+
+
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/doc/html/dev-doc/binaries/consang.html b/doc/html/dev-doc/binaries/consang.html new file mode 100644 index 0000000000..624aab88e8 --- /dev/null +++ b/doc/html/dev-doc/binaries/consang.html @@ -0,0 +1,142 @@ + + + + + + + + consang — Geneweb 0.1 documentation + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

consang

+
+

Documentation

+

Calculates the consanguinity level of individuals and synchronizes it with +the base.

+
usage: geneweb.consang [options] <file_name>
+  -q  quiet mode
+  -qq  very quiet mode
+  -fast  faster, but use more memory
+  -scratch : from scratch
+  -mem : Save memory, but slower when rewritting database
+  -nolock : do not lock database.
+
+
+

Online documentation

+
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/doc/html/dev-doc/binaries/export.html b/doc/html/dev-doc/binaries/export.html new file mode 100644 index 0000000000..55ff712d8c --- /dev/null +++ b/doc/html/dev-doc/binaries/export.html @@ -0,0 +1,128 @@ + + + + + + + + export — Geneweb 0.1 documentation + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

export

+

Exports a database in GED or in GW depending on the option. +Registers the “EXPORT” request as an extra request.

+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/doc/html/dev-doc/binaries/fixbase_plugin.html b/doc/html/dev-doc/binaries/fixbase_plugin.html new file mode 100644 index 0000000000..f437442acb --- /dev/null +++ b/doc/html/dev-doc/binaries/fixbase_plugin.html @@ -0,0 +1,124 @@ + + + + + + + + fixbase — Geneweb 0.1 documentation + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

fixbase

+

Applies the different fixbase utils (module Fixbase) from a dedicated UI. +Registers two new requests: FIXBASE and FIXBASE_OK.

+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/doc/html/dev-doc/binaries/forum.html b/doc/html/dev-doc/binaries/forum.html new file mode 100644 index 0000000000..55b0eb1a18 --- /dev/null +++ b/doc/html/dev-doc/binaries/forum.html @@ -0,0 +1,129 @@ + + + + + + + + forum — Geneweb 0.1 documentation + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

forum

+

Includes a forum platform in the web server. +Registers the requests FORUM, FORUM_ADD, FORUM_ADD_OK, FORUM_DEL, FORUM_P_P, +FORUM_SEARCH, FORUM_VAL, FORUM_VIEW.

+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/doc/html/dev-doc/binaries/ged2gwb.html b/doc/html/dev-doc/binaries/ged2gwb.html new file mode 100644 index 0000000000..c2e1ad5957 --- /dev/null +++ b/doc/html/dev-doc/binaries/ged2gwb.html @@ -0,0 +1,163 @@ + + + + + + + + ged2gwb — Geneweb 0.1 documentation + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

ged2gwb

+
+

Documentation

+

Imports a GEDCOM 5.5.1 file to a Geneweb base. GEDCOM is a popular format +among genealogy enthusiasts. +GEDCOM 5.5.1 documentation

+
Usage: geneweb.ged2gwb [<ged>] [options] where options are:
+  -charset [ANSEL|ASCII|MSDOS] Force given charset decoding, overriding the possible setting in GEDCOM
+  -dates_dm Interpret months-numbered dates as day/month/year
+  -dates_md Interpret months-numbered dates as month/day/year
+  -ds Set the source field for persons and families without source data
+  -efn When creating a person, if the GEDCOM first name part holds several names, the first of this names becomes the person "first name" and the complete GEDCOM first name part a "first name alias".
+  -epn When creating a person, if the GEDCOM first name part looks like a public name, i.e. holds:
+* a number or a roman number, supposed to be a number of a nobility title,
+* one of the words: "der", "den", "die", "el", "le", "la", "the", supposed to be the beginning of a qualifier, then the GEDCOM first name part becomes the person "public name" and its first word his "first name".
+  -f Remove database if already existing
+  -fne <be> When creating a person, if the GEDCOM first name part holds a part between 'b' (any character) and 'e' (any character), it is considered to be the usual first name: e.g. -fne '""' or -fne "()".
+  -lf Convert first names to lowercase letters, with initials in uppercase.
+  -log <file> Redirect log trace to this file.
+  -ls Convert surnames to lowercase letters, with initials in uppercase. Try to keep lowercase particles.
+  -nc No consistency check
+  -no_efn Cancels the previous option.
+  -no_epn Cancels the previous option.
+  -no_nd Don't interpret a year preceded by a minus sign as a negative year
+  -no_pit Do not consider persons having titles as public
+  -nopicture Don't extract individual picture.
+  -o <file> Output database (default: "a").
+  -particles <FILE> Use the given file as list of particles
+  -rs_no_mention Force relation status to NoMention (default is Married)
+  -tnd Set negative dates when inconsistency (e.g. birth after death)
+  -trackid Print gedcom id to gw id matches.
+  -udi x-y Set the interval for persons whose death part is undefined:
+       - if before x years, they are considered as alive
+       - if after y year, they are considered as death
+       - between x and y year, they are considered as "don't know"
+       Default x is 80 and y is 120
+  -uin Put untreated GEDCOM tags in notes
+  -us Convert surnames to uppercase letters.
+
+
+

Depends on the library gwexport.

+
+ +
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/doc/html/dev-doc/binaries/gwb2ged.html b/doc/html/dev-doc/binaries/gwb2ged.html new file mode 100644 index 0000000000..0fd71f45bc --- /dev/null +++ b/doc/html/dev-doc/binaries/gwb2ged.html @@ -0,0 +1,154 @@ + + + + + + + + gwb2ged — Geneweb 0.1 documentation + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

gwb2ged

+
+

Documentation

+

Converts a Geneweb database to a GEDCOM 5.5.1 file. GEDCOM is a popular format +among genealogy enthusiasts. +GEDCOM 5.5.1 documentation

+
Usage: geneweb.gwb2ged <BASE> [OPT]
+  -indexes                          export indexes in gedcom
+  -a <N>                            maximum generation of the root's ascendants
+  -ad <N>                           maximum generation of the root's ascendants descendants
+  -key <KEY>                        key reference of root person. Used for -a/-d options. Can be used multiple times. Key format is "First Name.occ SURNAME"
+  -c <NUM>:                         when a person is born less than <num> years ago, it is not exported unless it is Public. All the spouses and descendants are also censored.
+  -charset [ASCII|ANSEL|ANSI|UTF-8] set charset; default is UTF-8
+  -d <N>                            maximum generation of the root's descendants.
+  -mem                              save memory space, but slower.
+  -nn                               no (database) notes.
+  -nnn                              no notes (implies -nn).
+  -nopicture                        don't extract individual picture.
+  -o <GED>                          output file name (default: stdout).
+  -parentship                       select individuals involved in parentship computation between pairs of keys. Pairs must be defined with -key option, descendant first: e.g. -key "Descendant.0 SURNAME" -key "Ancestor.0 SURNAME". If multiple pair are provided, union of persons are returned.
+  -picture-path                     extract pictures path.
+  -s <SN>                           select this surname (option usable several times, union of surnames will be used).
+  -source <SRC>                     replace individuals and families sources. Also delete event sources.
+  -v                                verbose
+
+
+
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/doc/html/dev-doc/binaries/gwc.html b/doc/html/dev-doc/binaries/gwc.html new file mode 100644 index 0000000000..c90d7fc173 --- /dev/null +++ b/doc/html/dev-doc/binaries/gwc.html @@ -0,0 +1,156 @@ + + + + + + + + gwc — Geneweb 0.1 documentation + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

gwc

+
+

Documentation

+

Creates a new database.

+
Usage: geneweb.gwc [options] [files]
+where [files] are a list of files:
+  source files end with .gw
+  object files end with .gwo
+and [options] are:
+  -bnotes [drop|erase|first|merge] Behavior for base notes of the next file. [drop]: dropped. [erase]: erase the current content. [first]: dropped if current content is not empty. [merge]: concatenated to the current content. Default: merge
+  -c                               Only compiling
+  -cg                              Compute consanguinity
+  -ds <str>                        Set the source field for persons and families without source data
+  -f                               Remove database if already existing
+  -mem                             Save memory, but slower
+  -nc                              No consistency check
+  -nofail                          No failure in case of error
+  -nolock                          Do not lock database
+  -nopicture                       Do not create associative pictures
+  -o <file>                        Output database (default: a.gwb)
+  -particles <file>                Particles file (default = predefined particles)
+  -q                               Quiet
+  -sep                             Separate all persons in next file
+  -sh <int>                        Shift all persons numbers in next files
+  -stats                           Print statistics
+  -v                               Verbose
+
+
+
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/doc/html/dev-doc/binaries/gwd.html b/doc/html/dev-doc/binaries/gwd.html new file mode 100644 index 0000000000..1861cd5e5a --- /dev/null +++ b/doc/html/dev-doc/binaries/gwd.html @@ -0,0 +1,363 @@ + + + + + + + + gwd — Geneweb 0.1 documentation + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

gwd

+
+

Documentation

+

The main web-server for geneweb. It depends on wserver, a library defined in bin/.

+
Usage: gwd [options] where options are:
+  -a <ADDRESS>           Select a specific address (default = any address of this computer).
+  -add_lexicon <FILE>    Add file as lexicon.
+  -allowed_tags <FILE>   HTML tags which are allowed to be displayed. One tag per line in file.
+  -auth <FILE>           Authorization file to restrict access. The file must hold lines of the form "user:password".
+  -bd <DIR>              Directory where the databases are installed.
+  -blang                 Select the user browser language if any.
+  -cache_langs           Lexicon languages to be cached.
+  -cgi                   Force CGI mode.
+  -conn_tmout <SEC>      Connection timeout (default 120s; 0 means no limit).
+  -daemon                Unix daemon mode.
+  -debug                 Enable debug mode
+  -digest                Use Digest authorization scheme (more secure on passwords)
+  -friend <PASSWD>       Set a friend password.
+  -hd <DIR>              Directory where the directory lang is installed.
+  -images_dir <DIR>      Same than previous but directory name relative to current.
+  -images_url <URL>      URL for GeneWeb images (default: gwd send them).
+  -lang <LANG>           Set a default language (default: fr).
+  -log <FILE>            Log trace to this file. Use "-" or "<stdout>" to redirect output to stdout or "<stderr>" to output log to stderr.
+  -log_level <N>         Send messages with severity <= <N> to syslog (default: 7).
+  -login_tmout <SEC>     Login timeout for entries with passwords in CGI mode (default 1800s).
+  -max_clients <NUM>     Max number of clients treated at the same time (default: no limit) (not cgi).
+  -min_disp_req          Minimum number of requests in robot trace (default: 6).
+  -no_host_address       Force no reverse host by address.
+  -nolock                Do not lock files before writing.
+  -only <ADDRESS>        Only inet address accepted.
+  -p <NUMBER>            Select a port number (default = 2317).
+  -plugin <PLUGIN>.cmxs  load a safe plugin. Combine with -force to enable for every base. Combine with -unsafe to allow unverified plugins. e.g. "-plugin -unsafe -force".
+  -plugins <DIR>         load all plugins in <DIR>. Combine with -force to enable for every base. Combine with -unsafe to allow unverified plugins. e.g. "-plugins -unsafe -force".
+  -redirect <ADDR>       Send a message to say that this service has been redirected to <ADDR>.
+  -robot_xcl <CNT>,<SEC> Exclude connections when more than <CNT> requests in <SEC> seconds.
+  -setup_link            Display a link to local gwsetup in bottom of pages.
+  -trace_failed_passwd   Print the failed passwords in log (except if option -digest is set).
+  -wd <DIR>              Directory for socket communication (Windows) and access count.
+  -wizard <PASSWD>       Set a wizard password.
+  -wjf                   Wizard just friend (permanently).
+
+
+

It is build from eight files: gwdLog.ml, gwdPlugin.ml, request.ml (these +three files define geneweb.gwd_lib),, gwdPluginDep.ml, +gwdPluginMD5.ml, gwdPluginMETA.ml, robot.ml and gwd.ml.

+
+

GwdLog module

+

Util functions for logging errors & infos.

+
+
+

GwdPlugin module

+

Module used for registering plug-ins to Gwd.

+
+
+

Request module

+

The main interface between the Geneweb core and the rest of the world.

+
+
+

GedPluginDep module

+

Calculates the load order of the different plugins.

+
+
+

GwdPluginMD5 module

+

Checks the plugins .cmxs files and assets did not change since compilation. +This module is written by mk_gwdPluginMD5.ml. After the plug-ins have been compiled, +mk_gwdPluginMD5.ml lists them in a pattern matching with their MD5 hash.

+
+
+

GwdPluginMETA module

+

Handles the metadata of a plug-in: its version, its mainteners’ list and +its dependencies. +Metadata is optional, and may contain the dependencies of the plugin (separated +by a comma), its maintainers (separated by a comma) and its version.

+
+
+

Robot module

+

Handles the connection of robots to the server.

+
+
+

Gwd module

+

The executable

+
+
+
+

Requests

+

Gwd displays different web pages and perform different actions given a mode. +This mode is given as a GET argument with the key m.

+

Here is a quick documentation of each mode.

+
    +
  • No mode: displays the default page depending on other arguments. It can be +the index page (no base selected), a welcome page (only a base has been +selected) or a person page (a base and a person has been selected).

  • +
  • Mode “A”: displays the ascendants of the selected person (selection key is i).

  • +
  • Mode “ADD_FAM”: displays the form for adding families.

  • +
  • Mode “ADD_FAM_OK”: requests to add a new family.

  • +
  • Mode “ADD_IND”: displays a form for adding a new person

  • +
  • Mode “ADD_IND_OK”: requests to add a new person.

  • +
  • Mode “ADD_PAR”: associate parents to a person (person identifier must be set +with key ip)

  • +
  • Mode “ADD_PAR_OK” : requests to add new parents.

  • +
  • Mode “ANM” : displays the menu of anniversaries modification

  • +
  • Mode “AN” : displays anniversaries ; with the v key for months, displays +the anniversaries of thay month (v=1 -> January, …)

  • +
  • Mode “AD”: displays death anniversaries ; with the v key for months, displays +the anniversaries of thay month (v=1 -> January, …)

  • +
  • Mode “AM”: displays marriage anniversaries ; with the v key for months, displays +the anniversaries of thay month (v=1 -> January, …)

  • +
  • Mode “AS_OK”: displays the results of an advanced search

  • +
  • Mode “C”: displays the cousins menu

  • +
  • Mode “CAL”: displays the calendars; if no key set, it will use today’s date.

  • +
  • Mode “CHG_CHN”: displays the form for changing children names of a person

  • +
  • Mode “CHG_CHN_OK”: requests to change the children names a new person.

  • +
  • Mode “CHG_EVT_IND_ORD”: displays the form for changing the order of events +for a person

  • +
  • Mode “CHG_EVT_IND_ORD_OK”: requests to change the evenement order of a +person.

  • +
  • Mode “CHG_EVT_FAM_ORD”: displays the form for changing the order of events +for a family

  • +
  • Mode “CHG_EVT_FAM_ORD_OK”: requests to change the evenement order of a +family.

  • +
  • Mode “CHG_FAM_ORD”: displays a menu to change the family order

  • +
  • Mode “CHG_FAM_ORD_OK”: requests the family order change

  • +
  • Mode “CONN_WIZ”: displays the connected wizards (the base admins)

  • +
  • Mode “D”: displays the descendants of the selected person (selection key is i).

  • +
  • Mode “DAG”: displays a relationship graph.

  • +
  • Mode “DEL_FAM”: displays a page for validating the deletion of the family in +argument (key is i).

  • +
  • Mode “DEL_FAM_OK”: requests to remove a family.

  • +
  • Mode “DEL_IND”: displays a page for validating the deletion of the person in +argument (key is i).

  • +
  • Mode “DEL_IND_OK”: requests to remove a person.

  • +
  • Mode “F”: displays the family tree.

  • +
  • Mode “H”: displays the file fname.txt where fname is register with the key +v and the file being in hd/etc (hd is the dir specified by option -hd).

  • +
  • Mode “HIST”: displays an history of updates.

  • +
  • Mode “HIST_CLEAN”: displays the history list associated to the history file +in argument (key is f).

  • +
  • Mode “HIST_CLEAN_OK”: requests to clean the history associated to the history +file in argument

  • +
  • Mode “HIST_DIFF”: displays the page that allows to select (with variable +t = "SUM") and to view (with variable t = "DIFF") the difference +between all revisions of history file of concerned person in variable f. +Intepretate the template file updhist_diff.txt

  • +
  • Mode “HIST_SEARCH”: same as “HIST”, but with a default search

  • +
  • Mode “IM”: displays the image whose name is in argument (key is s)

  • +
  • Mode “IMH”: same than “IM”, but returns HTML

  • +
  • Mode “INV_FAM”: displays a menu for inverting the order of two families (where +a family is given by the f key and the individual is given by the i key).

  • +
  • Mode “INV_FAM_OK”: requests to reverse the families.

  • +
  • Mode “KILL_ANC”: Undocumented feature; kill someone’s ancestors.

  • +
  • Mode “LB”: lists the last births.

  • +
  • Mode “LD”: lists the last deaths.

  • +
  • Mode “LINKED”: displays links to pages assocaited to an individual.

  • +
  • Mode “LL”: lists the persons who lived the longest.

  • +
  • Mode “LM”: lists the last marriages.

  • +
  • Mode “MISC_NOTES”: displays a menu to search in notes.

  • +
  • Mode “MISC_NOTES_SEARCH”: same as “MISC_NOTES”, but with a search argument +(key is s)

  • +
  • Mode “MOD_DATA”: displays a menu for updating Geneweb’s dictionary of names, +last names, locations, sources and professions.

  • +
  • Mode “MOD_DATA_OK”: requests a data modification.

  • +
  • Mode “MOD_FAM”: displays a form for updating a family (key is i).

  • +
  • Mode “MOD_FAM_OK”: requests a family modification.

  • +
  • Mode “MOD_IND”: displays a form for updating a person (key is i).

  • +
  • Mode “MOD_IND_OK”: requests a person modification.

  • +
  • Mode “MOD_NOTES”: displays a text form for writing notes.

  • +
  • Mode “MOD_NOTES_OK”: requests a note update.

  • +
  • Mode “MOD_WIZNOTES”: displays the HTML page for editing wizard notes. +Fails if wizard authentification is incorrect or if current user cannot +edit.

  • +
  • Mode “MOD_WIZNOTES_OK”: requests the wizard note modification.

  • +
  • Mode “MRG”: displays a menu for merging two persons +(key for persons is i).

  • +
  • Mode “MRG_DUP”: displays a menu for merging possible duplications of persons +(key for persons is ip).

  • +
  • Mode “MRG_DUP_IND_Y_N”: either displays the merge dupliate menu if +answer_y is not a key of the request, or a form for merging two +person (whose keys are i and i2).

  • +
  • Mod “MRG_DUP_FAM_Y_N”: same than “MRG_DUP_IND_Y_N”, but for families.

  • +
  • Mode “MRG_FAM”: displays a menu for merging families (family keys are i +and i2). Couples must be identical (modulo reversion).

  • +
  • Mode “MRG_FAM_OK”: requests the family merge

  • +
  • Mode “MRG_MOD_FAM_OK”: requests the family modification and merge

  • +
  • Mode “MRG_IND”: displays a form for merging two persons

  • +
  • Mode “MRG_IND_OK”: requests a merge of two persons

  • +
  • Mode “MRG_MOD_IND_OK”: requests a merge & modification of two persons

  • +
  • Mode “N”: lists all the surnames; if a surname is selected (key is v), +displays the list of pages where this surname is used.

  • +
  • Mode “NG”: same than N, but expects a name with the key n

  • +
  • Mode “NOTES”: displays the notes

  • +
  • Mode “OA”: displays the list of the oldest persons that are still alive or, +if unknown, whose death are not probable

  • +
  • Mode “OE”: same as “OA”, but for engaged couples

  • +
  • Mode “P”: lists all the first names; if a surname is selected (key is v), +displays the list of pages where this name is used.

  • +
  • Mode “PQP_PYR”: displays a population pyramid (reachable from “STAT”)

  • +
  • Mode “PS”: displays all the persons associated to a given place (ma key for +marriage, bi key for birth, bp key for baptism, de key for death and bu +key for burial).

  • +
  • Mode “R”: displays the relationship details between two persons

  • +
  • Mode “REQUEST”: displays the current request

  • +
  • Mode “RL”: displays the relationship link between two persons

  • +
  • Mode “RLM”: displays relation ship details between multiple persons

  • +
  • Mode “S”: displays the results of a search +(from, for example, the main page).

  • +
  • Mode “SRC”: displays the file in argument (key is v)

  • +
  • Mode “STAT”: displays several links for statistics: latest births, death, +marriages, the oldest couples, persons that are alive and who lives the longest. +There also is a population pyramid.

  • +
  • Mode “CHANGE_WIZ_VIS”: displays the connected wizards

  • +
  • Mode “TT”: displays the titles associated to persons

  • +
  • Mode “U”: displays the page associated to the template updmenu.txt

  • +
  • Mode “VIEW_WIZNOTES”: Prints the HTML page displaying wizard notes

  • +
  • Mode “WIZNOTES”: Same as VIEW_WIZNOTES, but fails if not authentificated

  • +
  • Mode “WIZNOTES_SEARCH”: Same as VIEW_WIZNOTES, but highlights HTML with +the specified string searched (key is s).

  • +
+

Any other mode leads to an incorrect request page (Error 400).

+
+
+

Plug-ins

+

Plus-ins are additional features that can be added to gwd. They can register +two kind of services:

+
    +
  • additional modes: with GwdPlugin.register, a plug-in can register new +requests

  • +
  • additional computations: with GwdPlugin.register_se, a plug-in can register +pre/post processors

  • +
+
+
+

Customization

+

Each base can be customized with a <BASENAME>.gwf file. A documented example is +available on the etc directory at the root of the project. +Plug-ins are activated for a base with the plugin=... directive in the +configuration file.

+
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/doc/html/dev-doc/binaries/gwdiff.html b/doc/html/dev-doc/binaries/gwdiff.html new file mode 100644 index 0000000000..15929e82b6 --- /dev/null +++ b/doc/html/dev-doc/binaries/gwdiff.html @@ -0,0 +1,142 @@ + + + + + + + + gwdiff — Geneweb 0.1 documentation + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

gwdiff

+
+

Documentation

+

Target differences between two GeneWeb databases (see README for more details)

+
Usage: geneweb.gwdiff [options] base1 base2
+Options are: 
+  -1 <fn> <occ> <sn> : (mandatory) defines starting person in base1
+  -2 <fn> <occ> <sn> : (mandatory) defines starting person in base2
+  -ad : checks descendants of all ascendants 
+  -d : checks descendants (default)
+  -html <root>: HTML format used for report
+  -mem : save memory space, but slower
+
+
+
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/doc/html/dev-doc/binaries/gwfixbase.html b/doc/html/dev-doc/binaries/gwfixbase.html new file mode 100644 index 0000000000..d785f3cc37 --- /dev/null +++ b/doc/html/dev-doc/binaries/gwfixbase.html @@ -0,0 +1,145 @@ + + + + + + + + fixbase — Geneweb 0.1 documentation + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

fixbase

+
+

Documentation

+

Checks the consistency of the base, fixes it and applies +the latest patch if there is one.

+
Usage: geneweb.gwfixbase [OPTION] base
+  -dry-run  do not commit changes (only print)
+  -q  quiet mode
+  -qq  very quiet mode
+  -fast  fast mode. Needs more memory.
+  -families-parents  missing doc
+  -families-children  missing doc
+  -persons-NBDS  missing doc
+  -persons-parents  missing doc
+  -persons-families  missing doc
+  -pevents-witnesses  missing doc
+  -fevents-witnesses  missing doc
+  -marriage-divorce  missing doc
+  -person-key  missing doc
+  -index  rebuild index. It is automatically enable by any other option.
+  -invalid-utf8  missing doc
+
+
+
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/doc/html/dev-doc/binaries/gwgc.html b/doc/html/dev-doc/binaries/gwgc.html new file mode 100644 index 0000000000..1f52c9d28a --- /dev/null +++ b/doc/html/dev-doc/binaries/gwgc.html @@ -0,0 +1,137 @@ + + + + + + + + gwgc — Geneweb 0.1 documentation + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

gwgc

+
+

Documentation

+

Simplifies a database by removing unused entries of its dictionary.

+
Usage: geneweb.gwgc [OPTION] base
+  --dry-run  do not commit changes (only print)
+
+
+
+
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/doc/html/dev-doc/binaries/gwrepl.html b/doc/html/dev-doc/binaries/gwrepl.html new file mode 100644 index 0000000000..fc5af3f18f --- /dev/null +++ b/doc/html/dev-doc/binaries/gwrepl.html @@ -0,0 +1,137 @@ + + + + + + + + gwrepl — Geneweb 0.1 documentation + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

gwrepl

+
+

Documentation

+

Starts an OCaml interactive top-level for writing scripts manipulating the +database. It loads all the necessary libraries to use the geneweb +libraries. +For script execution, run: +cat <script.ml> | [ GWREPL_PPF=/dev/null ] [ GWREPL_VERBOSE=1 ] [ GWREPL_FORCE_UNPACK=1 ] [ GWREPL_NOPROMPT=1 ] gwrepl.exe [scrip_arg1] …

+

For interactive top-level, run gwdrepl.exe.

+
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/doc/html/dev-doc/binaries/gwu.html b/doc/html/dev-doc/binaries/gwu.html new file mode 100644 index 0000000000..d64649cea3 --- /dev/null +++ b/doc/html/dev-doc/binaries/gwu.html @@ -0,0 +1,156 @@ + + + + + + + + gwu — Geneweb 0.1 documentation + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

gwu

+
+

Documentation

+

Exports the content of a Geneweb base to a .gw file.

+
Usage: geneweb.gwu <BASE> [OPT]
+  -odir <dir>                       create files from original name in directory (else on -o file)
+  -isolated                         export isolated persons (work only if export all database).
+  -old_gw                           do not export additional fields (for backward compatibility: < 7.00)
+  -raw                              raw output (without possible utf-8 conversion)
+  -sep <1st_name.num surname>       To use together with the option "-odir": separate this person and all his ancestors and descendants sharing the same surname. All the concerned families are displayed on standard output instead of their associated files. This option can be used several times.
+  -sep_only_file <file>             with option "-sep", tells to separate only groups of that file.
+  -sep_limit <num>                  When using the option "-sep", groups of families can become isolated in the files. Gwu reconnects them to the separated families (i.e. displays them to standard output) if the size of these groups is less than 21. The present option changes this limit.
+  -a <N>                            maximum generation of the root's ascendants
+  -ad <N>                           maximum generation of the root's ascendants descendants
+  -key <KEY>                        key reference of root person. Used for -a/-d options. Can be used multiple times. Key format is "First Name.occ SURNAME"
+  -c <NUM>:                         when a person is born less than <num> years ago, it is not exported unless it is Public. All the spouses and descendants are also censored.
+  -charset [ASCII|ANSEL|ANSI|UTF-8] set charset; default is UTF-8
+  -d <N>                            maximum generation of the root's descendants.
+  -mem                              save memory space, but slower.
+  -nn                               no (database) notes.
+  -nnn                              no notes (implies -nn).
+  -nopicture                        don't extract individual picture.
+  -o <GED>                          output file name (default: stdout).
+  -parentship                       select individuals involved in parentship computation between pairs of keys. Pairs must be defined with -key option, descendant first: e.g. -key "Descendant.0 SURNAME" -key "Ancestor.0 SURNAME". If multiple pair are provided, union of persons are returned.
+  -picture-path                     extract pictures path.
+  -s <SN>                           select this surname (option usable several times, union of surnames will be used).
+  -source <SRC>                     replace individuals and families sources. Also delete event sources.
+  -v                                verbose
+
+
+
+

See also: +https://geneweb.tuxfamily.org/wiki/gwu +https://geneweb.tuxfamily.org/wiki/save

+
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/doc/html/dev-doc/binaries/gwxjg.html b/doc/html/dev-doc/binaries/gwxjg.html new file mode 100644 index 0000000000..746b1270a8 --- /dev/null +++ b/doc/html/dev-doc/binaries/gwxjg.html @@ -0,0 +1,128 @@ + + + + + + + + gwxjg — Geneweb 0.1 documentation + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

gwxjg

+

WARNING: This is not a plugin, it only provides tools for other plugins to use. +This package provides translation tools from Geneweb structure to Jingoo’s type system.

+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/doc/html/dev-doc/binaries/index.html b/doc/html/dev-doc/binaries/index.html new file mode 100644 index 0000000000..3e562d4492 --- /dev/null +++ b/doc/html/dev-doc/binaries/index.html @@ -0,0 +1,163 @@ + + + + + + + + Project binaries — Geneweb 0.1 documentation + + + + + + + + + + + + + + + + + + + +
+
+ +
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/doc/html/dev-doc/binaries/jingoo.html b/doc/html/dev-doc/binaries/jingoo.html new file mode 100644 index 0000000000..20d8f916d3 --- /dev/null +++ b/doc/html/dev-doc/binaries/jingoo.html @@ -0,0 +1,123 @@ + + + + + + + + jingoo — Geneweb 0.1 documentation + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

jingoo

+

WARNING: This is not a plugin. This only loads geneweb.gwd_lib.

+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/doc/html/dev-doc/binaries/lib_show.html b/doc/html/dev-doc/binaries/lib_show.html new file mode 100644 index 0000000000..665f4ea3f2 --- /dev/null +++ b/doc/html/dev-doc/binaries/lib_show.html @@ -0,0 +1,123 @@ + + + + + + + + lib_show — Geneweb 0.1 documentation + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

lib_show

+

WARNING: This is not a plugin. This only loads geneweb.def_show.

+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/doc/html/dev-doc/binaries/no_index.html b/doc/html/dev-doc/binaries/no_index.html new file mode 100644 index 0000000000..280ba1a962 --- /dev/null +++ b/doc/html/dev-doc/binaries/no_index.html @@ -0,0 +1,122 @@ + + + + + + + + no_index — Geneweb 0.1 documentation + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

no_index

+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/doc/html/dev-doc/binaries/official_binaries.html b/doc/html/dev-doc/binaries/official_binaries.html new file mode 100644 index 0000000000..157f304794 --- /dev/null +++ b/doc/html/dev-doc/binaries/official_binaries.html @@ -0,0 +1,189 @@ + + + + + + + + Official binaries — Geneweb 0.1 documentation + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

Official binaries

+

These binaries are from the official geneweb package. They provide +the core utilities for manipulating databases and a web server.

+ +
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/doc/html/dev-doc/binaries/plugins.html b/doc/html/dev-doc/binaries/plugins.html new file mode 100644 index 0000000000..3d0353a3e7 --- /dev/null +++ b/doc/html/dev-doc/binaries/plugins.html @@ -0,0 +1,144 @@ + + + + + + + + Plug-ins — Geneweb 0.1 documentation + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

Plug-ins

+

Plug-ins are user-defined scripts that were merged into the main repository. +While they are part of the main branch, their purpose it to let user develop +their own OCaml scripts for updating the behaviour or the web werver gwd.

+
+

List of plug-ins:

+ +
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/doc/html/dev-doc/binaries/setup.html b/doc/html/dev-doc/binaries/setup.html new file mode 100644 index 0000000000..10deb24da3 --- /dev/null +++ b/doc/html/dev-doc/binaries/setup.html @@ -0,0 +1,134 @@ + + + + + + + + setup — Geneweb 0.1 documentation + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

setup

+

The database setup portal. It depends on wserver, a library defined in bin/.

+
Usage: setup.exe [options] where options are:
+  -bd <dir>: Directory where the databases are installed.
+  -gwd_p <number>: Specify the port number of gwd (default = 2317); > 1024 for normal users.
+  -lang <string>: default lang
+  -daemon : Unix daemon mode.
+  -p <number>: Select a port number (default = 2316); > 1024 for normal users.
+  -only <file>: File containing the only authorized address
+  -gd <string>: gwsetup directory
+  -bindir <string>: binary directory (default = value of option -gd)
+
+
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/doc/html/dev-doc/binaries/test.html b/doc/html/dev-doc/binaries/test.html new file mode 100644 index 0000000000..3aa9b9f12a --- /dev/null +++ b/doc/html/dev-doc/binaries/test.html @@ -0,0 +1,156 @@ + + + + + + + + test — Geneweb 0.1 documentation + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

test

+

Starts multiple non-regression tests. Tested modules are MUtil, Util +(Test_utils), the choosen Sosa implementation (Test_sosa), Place +(Test_place) and NotesLink (Test_wiki). +Can be executed with make test or _build/default/test/test.exe.

+
usage: default/test/test.exe options*
+  -conf fn                        Read configuration file.
+  -cache-filename str             Cache file to store previous results. (default: /home/ovenstent/gits/geneweb-project/geneweb/_build/oUnit-$(suite_name).cache)
+  -chooser {failfirst|simple}
+                                  Select the method to choose tests to run. (default: simple)
+  -ci {true|false}                Display logs for CI, like Travis and AppVeyor, in the console with colors. (default: false)
+  -display {true|false}           Output logs on screen. (default: true)
+  -health-check-interval f        Seconds between checking health of workers. (default: 1.)
+  -log-encoding str               Encoding of the log. (default: utf-8)
+  -no-cache-filename              Reset value of cache_filename.
+  -no-output-file                 Reset value of output_file.
+  -no-output-html-dir             Reset value of output_html_dir.
+  -no-output-junit-file           Reset value of output_junit_file.
+  -no-testdata-dir                Reset value of testdata_dir.
+  -output-file str                Output verbose log in the given file. (default: /home/ovenstent/gits/geneweb-project/geneweb/_build/oUnit-$(suite_name)-$(shard_id).log)
+  -output-html-dir str            Output directory of the HTML files. (default: none)
+  -output-junit-file str          Output file for JUnit. (default: none)
+  -processes-grace-period f       Delay to wait for a process to stop. (default: 5.)
+  -processes-kill-period f        Delay to wait for a process to stop after killing it. (default: 5.)
+  -results-style-1-X {true|false} Use OUnit 1.X results printer (will be deprecated in 2.1.0+). (default: false)
+  -run-gc-full-major {true|false} Run a Gc.full_major in between tests. (default: true)
+  -runner {processes|sequential}
+                                  Select a the method to run tests. (default: processes)
+  -shards i                       Number of shards to use as worker (threads or processes). (default: 4)
+  -suite-name str                 The name of the test suite running. (default: Geneweb)
+  -testdata-dir str               Location of the test data directory (absolute path). (default: none)
+  -verbose {true|false}           Run test in verbose mode. (default: false)
+  -only-test path                 Run only the selected tests.
+  -list-test                      List tests
+
+
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/doc/html/dev-doc/binaries/tests.html b/doc/html/dev-doc/binaries/tests.html new file mode 100644 index 0000000000..4d946eaea3 --- /dev/null +++ b/doc/html/dev-doc/binaries/tests.html @@ -0,0 +1,126 @@ + + + + + + + + Tests — Geneweb 0.1 documentation + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

Tests

+
+

List of binaries:

+ +
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/doc/html/dev-doc/binaries/update_nldb.html b/doc/html/dev-doc/binaries/update_nldb.html new file mode 100644 index 0000000000..66a4034c09 --- /dev/null +++ b/doc/html/dev-doc/binaries/update_nldb.html @@ -0,0 +1,122 @@ + + + + + + + + update_nldb — Geneweb 0.1 documentation + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

update_nldb

+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/doc/html/dev-doc/binaries/v7.html b/doc/html/dev-doc/binaries/v7.html new file mode 100644 index 0000000000..32b81016d7 --- /dev/null +++ b/doc/html/dev-doc/binaries/v7.html @@ -0,0 +1,128 @@ + + + + + + + + v7 — Geneweb 0.1 documentation + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

v7

+

Defines new reqursts and redefines some old ones with new changes for the V7. +Registers the requests to the index (“”), A, C, D, DOC, L, P, PS and TP.

+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/doc/html/dev-doc/binaries/v7_im.html b/doc/html/dev-doc/binaries/v7_im.html new file mode 100644 index 0000000000..708d0c5a3c --- /dev/null +++ b/doc/html/dev-doc/binaries/v7_im.html @@ -0,0 +1,128 @@ + + + + + + + + v7_im — Geneweb 0.1 documentation + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

v7_im

+

Complements plugin v7 with image handling. +Registers requests DEL_IMAGE, DEL_IMAGE_OK, SND_IMAGE and SND_IMAGE_OK.

+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/doc/html/dev-doc/binaries/welcome.html b/doc/html/dev-doc/binaries/welcome.html new file mode 100644 index 0000000000..eda436ba41 --- /dev/null +++ b/doc/html/dev-doc/binaries/welcome.html @@ -0,0 +1,128 @@ + + + + + + + + welcome — Geneweb 0.1 documentation + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

welcome

+

This plugin facilitates the main page and search page use. +Registers the requests to the index page and S.

+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/doc/html/dev-doc/binaries/xhtml.html b/doc/html/dev-doc/binaries/xhtml.html new file mode 100644 index 0000000000..e3690ce21d --- /dev/null +++ b/doc/html/dev-doc/binaries/xhtml.html @@ -0,0 +1,122 @@ + + + + + + + + xhtml — Geneweb 0.1 documentation + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

xhtml

+

Replaces the Content-type field of requests by xhtml+xml.

+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/doc/html/dev-doc/index.html b/doc/html/dev-doc/index.html new file mode 100644 index 0000000000..b1c4b2b4d9 --- /dev/null +++ b/doc/html/dev-doc/index.html @@ -0,0 +1,151 @@ + + + + + + + + Developer documentation — Geneweb 0.1 documentation + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

Developer documentation

+

This documentation is an overview of the different components of Geneweb. +It describes:

+
    +
  • The functioning of the build system;

  • +
  • An overview of some libraries defined in the project;

  • +
  • A documentation of the main binaries;

  • +
  • A documentation for libraries generated with odoc.

  • +
+ +
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/doc/html/dev-doc/installation/build-system.html b/doc/html/dev-doc/installation/build-system.html new file mode 100644 index 0000000000..e41e7e3d7e --- /dev/null +++ b/doc/html/dev-doc/installation/build-system.html @@ -0,0 +1,102 @@ + + + + + + + + <no title> — audit 0.1 documentation + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +

dsd

+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/doc/html/dev-doc/installation/index.html b/doc/html/dev-doc/installation/index.html new file mode 100644 index 0000000000..f2e0cf001d --- /dev/null +++ b/doc/html/dev-doc/installation/index.html @@ -0,0 +1,1211 @@ + + + + + + + + Installation procedure — Geneweb 0.1 documentation + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

Installation procedure

+
+

Build documentation

+
    +
  1. Install the dependencies in an opam switch

    +
    $ opam switch create geneweb 4.09.1
    +$ opam install . --deps-only
    +
    +
    +
  2. +
  3. Run the configuration script

    +
    $ ocaml ./configure.ml
    +
    +
    +
  4. +
  5. Build the distibution

    +
    $ make clean distrib
    +
    +
    +
  6. +
+
+
+

Configure

+

The configure.ml file is an ocaml script whose purpose is to +update Makefile.config by setting the environment variables +the Makefile needs.

+
usage: configure.ml [options]
+  --gwdb-legacy Use legacy backend
+  --release Use release profile: no debug informations (defaut: true)
+  --debug Use dev profile: no optimization, debug informations (default: false)
+  --sosa-legacy Use legacy Sosa module implementation
+  --sosa-num Use Sosa module implementation based on `num` library
+  --sosa-zarith Use Sosa module implementation based on `zarith` library
+  --syslog Log gwd errors using syslog
+  -help  Display this list of options
+  --help  Display this list of options
+
+
+
+
+

Build system

+
+

Makefile

+

The root of every action that could be performed on geneweb pass over its Makefile. In this section we will describe the variables, targets and its dependencies.

+
+

Variables

+

Makefile loads variables from Makefile.config generated by the configuration script for customised execution of some commands. Variables with suffix _D are passed to cppo command that preprocess files with an equivalent to the C preprocessor. Here is a description of those variables :

+
    +
  • OS_TYPE : kernel name (Linux, Win, Darwin, etc.)

  • +
  • STRIP : command that discards symbols from compiled object files. Depends on kernel (linux command is strip).

  • +
  • RM : command that is used to remove files, directories, etc. Depends on kernel (linux command is /bin/rm -f).

  • +
  • EXT : extension for geneweb executable files. Depends on kernel (on Windows it’s .exe).

  • +
  • GWDB_D : argument to cppo command that defines a variable that tells which geneweb data base driver implementation will be used (example : GENEWEB_GWDB_LEGACY).

  • +
  • OS_D : argument to cppo command that defines a variable for current system (example : UNIX).

  • +
  • SYSLOG_D : argument to cppo command that could define a marker variable SYSLOG. When defined, activate logging by geneweb server.

  • +
  • GWDB_PKG : dune library that implements geneweb database driver (example : geneweb.gwdb-legacy).

  • +
  • SOSA_PKG : dune library that implements sosa (example : geneweb_sosa_zarith).

  • +
  • SYSLOG_PKG : if SYSLOG is defined, then indicate dune library used for logging functionalities.

  • +
  • DUNE_DIRS_EXCLUDE : specifies directories that will be excluded from geneweb library. Used to exclude not chosen implementations for sosa and for geneweb database driver.

  • +
  • DUNE_PROFILE : specifies dune profile (default : release).

  • +
+

Makefile also defines additional non-configurable variables :

+
    +
  • PREFIX :

  • +
  • DISTRIB_DIR : directory where a local geneweb distribution is stored.

  • +
  • BUILD_DISTRIB_DIR : directory where geneweb executable files for local distribution are stored.

  • +
  • BUILD_DIR : directory where the compiled artefacts are stored.

  • +
  • CPPO_D : regroups all arguments to cppo (GWDB_D, OS_D, SYSLOG_D). If dune’s profile is dev then also contains the definition of DEBUG variable that enables emitting warnings on stderr by geneweb server.

  • +
  • BENCH_FILE : file where benchmark results are stored.

  • +
+

Nowadays geneweb is entirely compiled by dune, due to dune files. Non-preprocessed version of dune files are stored in dune.in. Makefile is responsible for the generation of corresponding dune from the dune.in. Every dune file that should be generated is stored in variable GENERATED_FILES_DEP in addition to other files generated by Makefile :

+
GENERATED_FILES_DEP = \
+	dune-workspace \
+	hd/etc/version.txt \
+	lib/dune \
+	lib/gwdb/dune \
+	lib/core/dune \
+	lib/gwlib.ml \
+	lib/util/dune \
+	benchmark/dune \
+	bin/connex/dune \
+	bin/consang/dune \
+	bin/fixbase/dune \
+	bin/ged2gwb/dune \
+	bin/gwb2ged/dune \
+	bin/gwc/dune \
+	bin/gwd/dune \
+	bin/gwdiff/dune \
+	bin/gwgc/dune \
+	bin/gwrepl/dune \
+	bin/gwrepl/.depend \
+	bin/gwu/dune \
+	bin/setup/dune \
+	bin/update_nldb/dune \
+	test/dune \
+
+
+
+
+

Targets

+
+
Main targets
+
    +
  • Makefile.config: configure.ml

    +

    Executed when Makefile.config isn’t up-to-date with configure.ml. Prints corresponding error message and terminates execution. This target isn’t executed when the goal is ci target.

    +
  • +
  • lib/gwlib.ml:

    +

    Generates the content for lib/gwlib.ml that depends on PREFIX.

    +
  • +
  • bin/gwrepl/.depend:

    +

    Generates the content for bin/gwrepl/.depend.

    +
  • +
  • hd/etc/version.txt:

    +

    Generates the content for hd/etc/version.txt that indicates some information about geneweb version like date of compilation and commit used.

    +
  • +
  • %/dune: %/dune.in Makefile.config

    +

    Generates dune file from corresponding dune.in as follows:

    +
      +
    • Preprocess with cppo -n depending on CPPO_D argument.

    • +
    • Remplaces with sed occurrences of %%%CPPO_D%%%, %%%SOSA_PKG%%%, %%%GWDB_PKG%%%, %%%SYSLOG_PKG%%% and %%%DUNE_DIRS_EXCLUDE%%% by the values of corresponding variables.

    • +
    +
  • +
  • dune-workspace: dune-workspace.in Makefile.config

    +

    Generates dune-workspace file that is used to define different build contexts and currently selected build profile. Generation is done by replacing the occurrence of %%%DUNE_PROFILE%%% inside dune-workspace.in by the value of corresponding variable.

    +
  • +
  • build: $(GENERATED_FILES_DEP)

    +

    Default goal when make is called without targets. Executes the target for every file specified in GENERATED_FILES_DEP and then build all public artefacts in geneweb package.

    +
  • +
  • distrib: build

    +

    Principal target. Build entire system and creates a local distibution of geneweb under DISTRIB_DIR directory. See for more information.

    +
  • +
  • clean:

    +

    Removes all generated files that are specified in GENERATED_FILES_DEP and DISTRIB_DIR

    +
  • +
+
+
+
Additional targets
+
    +
  • generated: $(GENERATED_FILES_DEP)

    +

    Executes the target for every file specified in GENERATED_FILES_DEP.

    +
  • +
  • install: $(GENERATED_FILES_DEP)

    +

    Executes the target for every file specified in GENERATED_FILES_DEP, compilates all public artefacts and then install geneweb in opam.

    +
  • +
  • uninstall: $(GENERATED_FILES_DEP)

    +

    Same as install but remote geneweb from opam.

    +
  • +
  • doc: | $(GENERATED_FILES_DEP)

    +

    Generates documentation for all public artefacts in geneweb. Note that prerequisites on the right to | are order-only.

    +
  • +
  • test: | $(GENERATED_FILES_DEP)

    +

    Executes test suites defined in test directory.

    +
  • +
  • bench: | $(GENERATED_FILES_DEP)

    +

    Executes the benchmark defined in benchmark directory.

    +
  • +
  • bench-marshal: | $(GENERATED_FILES_DEP)

    +

    Executes the benchmark with options --marshal --name ${BENCH_NAME} ${BENCH_FILE} where BENCH_NAME is the name of bench results that will be stored in hash table and marshalled in BENCH_FILE. Prerequisite to ci target.

    +
  • +
  • bench-tabulate: | $(GENERATED_FILES_DEP)

    +

    Executes the benchmark with option --tabulate ${BENCH_FILE} that prints on the screen tables of bench results stored in BENCH_FILE. Prerequisite to ci target.

    +
  • +
  • ci:

    +
      +
    • Configure geneweb for every existing implementation of sosa.

    • +
    • Executes tests and benchmarks (name corresponds to sosa implementation) for each of them (recursive make call with goals clean, test, bench-marshal clean).

    • +
    • Prints all benchmark results (recursive make call with goal bench-tabulate).

    • +
    +
  • +
+
+
+
+
+

Dune configuration files

+
+

dune-project

+

This file is used to mark the root of projects as well as define project-wide parameters. In the case of geneweb this file specifies version of the dune configuration files with lang stanza and sets the name of project with name stanza:

+
(lang dune 2.8)
+(name geneweb)
+
+
+
+
+

dune-workspace

+

This file is used to define different build contexts and to select a build profile. +Geneweb defines only one default context. Build profile (release or dev) is chosen by configuration script (--release option to use release profile) with subsequent make.

+
+
+

dune

+

Dune files used to describe libraries, executables, tests, and everything dune needs to know about. Some of dune files are preprocessed by Makefile in order to remplace all occurences of %%%VARIABLE%%% by their real value (see Variables) In this section we will describe each of those file inside geneweb project.

+

./dune

+

The root dune file that, in the geneweb case, modifies the environment used with dev build profile. Particularly, it passes additional option to the OCaml compiler :

+
-w +a-4-9-35-42-44-48
+
+
+

This option tells to compiler to enable all the warnings except :

+
    +
  • Fragile pattern matching.

  • +
  • Missing fields in a record pattern.

  • +
  • Unused for-loop index.

  • +
  • Disambiguated constructor or label name.

  • +
  • Open statement that shadows an already defined identifier.

  • +
  • Implicit elimination of optional arguments.

  • +
+
+
+

Geneweb library

+

lib/dune

+

Defines public geneweb library .

+
    +
  • It includes modules under the lib directory and every of them is preprocessed with cppo.

  • +
  • Module TemplAst is specified as a module without implementation (has only .mli).

  • +
  • Module Templ_parser is obtained from templ_parser.mll with stanza ocamllex that makes call for ocamllex lexer.

  • +
  • Compilation of this library includes compilation of every subdirectory (with dirs stanza) except those mentioned in %%%DUNE_DIRS_EXCLUDE%%% variable (not used implementations).

  • +
  • Depends on geneweb libraries :

    +
      +
    • geneweb_core

    • +
    • geneweb_def

    • +
    • geneweb_gwdb

    • +
    • geneweb_sosa_mli

    • +
    • geneweb_util

    • +
    +
  • +
  • Depends on extrenal libraries :

    + +
  • +
+

lib/core/dune

+

Defines public geneweb.core library (locally - geneweb_core).

+
    +
  • It includes modules under the lib/core directory and every of them is preprocessed with cppo.

  • +
  • Depends on geneweb libraries :

    +
      +
    • geneweb_def

    • +
    • geneweb_gwdb

    • +
    • geneweb_sosa_mli

    • +
    • geneweb_util

    • +
    +
  • +
  • Depends on extrenal libraries :

    + +
  • +
+

lib/def/dune

+

Defines public geneweb.def library (locally - geneweb_def). Includes only two modules : Adef and Def.

+

lib/gwdb/dune

+

Defines public geneweb.gwdb library (locally - geneweb_gwdb).

+
    +
  • It includes modules under the lib/gwdb directory and every of them is preprocessed with cppo.

  • +
  • Depends on geneweb libraries :

    +
      +
    • geneweb_def

    • +
    • gwdb_driver_mli

    • +
    • geneweb_util

    • +
    +
  • +
  • Depends on extrenal libraries :

    + +
  • +
+

lib/gwdb_driver.mli/dune

+

Defines public virtual (that requires an implementation) geneweb.gwdb_driver library (locally - gwdb_driver_mli).

+
    +
  • Include only one virtual module Gwdb_driver.

  • +
  • Depends on geneweb libraries :

    +
      +
    • geneweb_def

    • +
    +
  • +
  • Depends on extrenal libraries :

    + +
  • +
+

lib/gwdb-legacy/dune

+

Defines public geneweb.gwdb-legacy library (locally - gwdb_legacy). This library is an implementaion for virtual gwdb_driver_mli library.

+
    +
  • It includes modules under the lib/gwdb-legacy directory.

  • +
  • Module Dbdisk is specified as a module without implementation.

  • +
  • Depends on geneweb libraries :

    +
      +
    • geneweb_def

    • +
    • geneweb_util

    • +
    +
  • +
  • Depends on extrenal libraries :

    + +
  • +
+

lib/json_export/dune

+

Defines public geneweb.export library (locally - geneweb_export).

+
    +
  • Include only one module Json_converter.

  • +
  • Depends on geneweb libraries :

    +
      +
    • geneweb_def

    • +
    • geneweb_def_show

    • +
    • geneweb_gwdb

    • +
    +
  • +
  • Depends on extrenal libraries :

    + +
  • +
+

lib/show/dune

+

Defines public geneweb.def_show library (locally - geneweb_def_show).

+
    +
  • Include only one module Def_show. This module is preprocessed with ppx_import and ppx_deriving.show PPX rewriters that require feedback from the compilation phase (for that it is used inside staged_pps stanza rather than in ordinar pps).

  • +
  • Depends on geneweb libraries :

    +
      +
    • geneweb_def

    • +
    • geneweb_gwdb

    • +
    +
  • +
+

lib/sosa.mli/dune

+

Defines public virtual geneweb.sosa.mli library (locally - geneweb_sosa_mli). Includes only one virtual module Sosa.

+

lib/sosa_array/dune

+

Defines public geneweb.sosa_array library (locally - geneweb_sosa_array). This library is an one of implementaions for virtual geneweb_sosa_mli library. It includes only one module Sosa.

+

lib/sosa_num/dune

+

Defines public geneweb.sosa_num library (locally - geneweb_sosa_num). This library is an one of implementaions for virtual geneweb_sosa_mli library.

+
    +
  • It includes only one module Sosa.

  • +
  • Depends on extrenal libraries :

    + +
  • +
+

lib/sosa_zarith/dune

+

Defines public geneweb.sosa_zarith library (locally - geneweb_sosa_zarith). This library is an one of implementaions for virtual geneweb_sosa_mli library.

+
    +
  • It includes only one module Sosa.

  • +
  • Depends on extrenal libraries :

    + +
  • +
+

lib/util/dune

+

Defines public geneweb.util library (locally - geneweb_util).

+
    +
  • It includes modules under the lib/util directory and every of them is preprocessed with cppo.

  • +
  • Depends on geneweb libraries :

    +
      +
    • geneweb_def

    • +
    +
  • +
  • Depends on extrenal libraries :

    + +
  • +
+
+
Geneweb executables
+

bin/connex/dune

+

Defines public geneweb.connex executable (locally - connex).

+
    +
  • It includes only one module Connex.

  • +
  • Depends on geneweb libraries :

    +
      +
    • geneweb

    • +
    • Chosen implementation of gwdb_driver_mli.

    • +
    • Chosen implementation of geneweb_sosa_mli.

    • +
    +
  • +
  • Depends on extrenal libraries :

    + +
  • +
+

bin/consang/dune

+

Defines public geneweb.consang executable (locally - consang).

+
    +
  • It includes only one module Consang that is preprocessed with cppo.

  • +
  • Depends on geneweb libraries :

    +
      +
    • geneweb

    • +
    • Chosen implementation of gwdb_driver_mli.

    • +
    • Chosen implementation of geneweb_sosa_mli.

    • +
    +
  • +
  • Depends on extrenal libraries :

    + +
  • +
+

bin/fixbase/dune

+

Defines public geneweb.gwfixbase executable (locally - gwfixbase).

+
    +
  • It includes only one module Gwfixbase.

  • +
  • Depends on geneweb libraries :

    +
      +
    • geneweb

    • +
    • Chosen implementation of gwdb_driver_mli.

    • +
    • Chosen implementation of geneweb_sosa_mli.

    • +
    +
  • +
  • Depends on extrenal libraries :

    + +
  • +
+

bin/ged2gwb/dune

+

Defines public geneweb.ged2gwb executable (locally - ged2gwb).

+
    +
  • It includes only one module Ged2gwb preprocessed by camlp5o that provides parsing and pretty printing tools. Particulary, it uses following extensions:

    +
      +
    • pr_o.cmo: pretty print in normal syntax

    • +
    • pa_extend.cmo: syntax extension for grammars

    • +
    • q_MLast.cmo: syntax tree nodes (in revised syntax)

    • +
    +
  • +
  • Depends on geneweb libraries :

    +
      +
    • geneweb

    • +
    • Chosen implementation of gwdb_driver_mli.

    • +
    • Chosen implementation of geneweb_sosa_mli.

    • +
    +
  • +
  • Depends on extrenal libraries :

    + +
  • +
+

bin/gwb2ged/dune

+

Defines :

+
    +
  1. Public geneweb.gwb2ged_lib library (locally - gwb2ged_lib).

    +
      +
    • It includes only one module Gwb2gedLib.

    • +
    • Depends on geneweb libraries :

      +
        +
      • geneweb

      • +
      • gwexport_lib

      • +
      +
    • +
    +
  2. +
  3. Public geneweb.gwb2ge executable (locally - gwb2ged).

    +
      +
    • It includes only one module Gwb2ged.

    • +
    • Depends on geneweb libraries :

      +
        +
      • geneweb

      • +
      • gwb2ged_lib

      • +
      • gwu_lib

      • +
      • Chosen implementation of gwdb_driver_mli.

      • +
      • Chosen implementation of geneweb_sosa_mli.

      • +
      +
    • +
    • Depends on extrenal libraries :

      + +
    • +
    +
  4. +
+

bin/gwc/dune

+

Defines public geneweb.gwc executable (locally - gwc).

+
    +
  • It includes modules under the bin/gwc directory and every of them is preprocessed with cppo.

  • +
  • Depends on geneweb libraries :

    +
      +
    • geneweb

    • +
    • Chosen implementation of gwdb_driver_mli.

    • +
    • Chosen implementation of geneweb_sosa_mli.

    • +
    +
  • +
  • Depends on extrenal libraries :

    + +
  • +
+

bin/gwd/dune

+

Defines:

+
    +
  1. Public wrapped geneweb.gwd_lib library (locally - gwd_lib).

    +
      +
    • It includes modules GwdLog, GwdPlugin and Request that are preprocessed with cppo.

    • +
    • Depends on geneweb libraries :

      +
        +
      • geneweb

      • +
      • wserver

      • +
      • Chosen implementation of gwdb_driver_mli.

      • +
      • Chosen implementation of geneweb_sosa_mli.

      • +
      +
    • +
    +
  2. +
  3. Public executable geneweb.gwd library (locally - gwd).

    +
      +
    • It includes modules Gwd, GwdPluginDep, GwdPluginMD5, GwdPluginMETA and Robot that are preprocessed with cppo.

    • +
    • Module GwdPluginMD5 is created by rule stanza. Stanza deps tells to dune to do firstly:

      +
        +
      • Find module Mk_gwdPluginMD5

      • +
      • Construct all recursively defined in plugins/ aliases @plugin. +File gwdPluginMD5.ml is created by running maker script mk_gwdPluginMD5.ml with path to plugins/ directory specified in argument.

      • +
      +
    • +
    • All the libraies used by executable are compiled with -linkall flag.

    • +
    • Depends on geneweb libraries :

      +
        +
      • gwd_lib

      • +
      • geneweb

      • +
      • wserver

      • +
      • Chosen implementation of gwdb_driver_mli.

      • +
      • Chosen implementation of geneweb_sosa_mli.

      • +
      +
    • +
    • Depends on extrenal libraries :

      + +
    • +
    +
  4. +
+

bin/gwdiff/dune

+

Defines public geneweb.gwdiff executable (locally - gwdiff).

+
    +
  • It includes only one module Gwdiff.

  • +
  • Depends on geneweb libraries :

    +
      +
    • geneweb

    • +
    • Chosen implementation of gwdb_driver_mli.

    • +
    • Chosen implementation of geneweb_sosa_mli.

    • +
    +
  • +
  • Depends on extrenal libraries :

    + +
  • +
+

bin/gwexport/dune

+

Defines public geneweb.gwexport_lib library (locally - gwexport_lib).

+
    +
  • It includes only one module Gwexport.

  • +
  • Depends on geneweb libraries :

    +
      +
    • geneweb

    • +
    +
  • +
+

bin/gwgc/dune

+

Defines public geneweb.gwgc executable (locally - gwgc).

+
    +
  • It includes only one module Gwgc that is preprocessed with cppo.

  • +
  • Depends on geneweb libraries :

    +
      +
    • geneweb

    • +
    • Chosen implementation of gwdb_driver_mli.

    • +
    • Chosen implementation of geneweb_sosa_mli.

    • +
    +
  • +
  • Depends on extrenal libraries :

    + +
  • +
+

bin/gwrepl/dune

+

Defines:

+
    +
  1. Private library gwrepl_deps that links all dependent libraries.

    +
      +
    • Depends on geneweb libraries :

      +
        +
      • geneweb_core

      • +
      • geneweb_def

      • +
      • geneweb_util

      • +
      • geneweb_gwdb

      • +
      • Chosen implementation of gwdb_driver_mli.

      • +
      • Chosen implementation of geneweb_sosa_mli.

      • +
      +
    • +
    • Depends on external libraries

      + +
    • +
    +
  2. +
  3. Public gwrepl executable .

    +
      +
    • It includes only two modules Gwrepl and Data.

    • +
    • gwrepl executable is preprocessed with cppo.

    • +
    • Module Data is created with rule stanza from :

      +
        +
      • Module Mk_data

      • +
      • File .depend .
        +File data.ml is created by running maker script mk_data.ml that is linked with unix.cma by OCaml toplevel.

      • +
      +
    • +
    • Data is preprocessed with ppx_blob that is used to extract the content of binary file at compile time.

    • +
    • It is compiled by bytecode compiler and producing static object files:

      +
      (modes byte object)
      +
      +
      +
    • +
    • All the libraies used by executable are compiled with -linkall flag.

    • +
    • Linked with -custom option that is used by bytecode compiler to produce native executable that embeds the ocamlrun virtual machine as well as the byte code. See dune-executable.

    • +
    • Depends on external libraries:

      + +
    • +
    +
  4. +
+

bin/gwu/dune

+

Defines :

+
    +
  1. Public geneweb.gwu_lib library (locally - gwu_lib).

    +
      +
    • It includes only one module GwuLib.

    • +
    • Depends on geneweb libraries :

      +
        +
      • geneweb

      • +
      • gwexport_lib

      • +
      +
    • +
    +
  2. +
  3. Public geneweb.gwu executable (locally - gwu).

    +
      +
    • It includes only one module Gwu.

    • +
    • Depends on geneweb libraries :

      +
        +
      • geneweb

      • +
      • gwexport_lib

      • +
      • gwu_lib

      • +
      • Chosen implementation of gwdb_driver_mli.

      • +
      • Chosen implementation of geneweb_sosa_mli.

      • +
      +
    • +
    • Depends on extrenal libraries :

      + +
    • +
    +
  4. +
+

bin/setup/dune

+

Defines public geneweb.setup executable (locally - setup).

+
    +
  • It includes only one module Setup that is preprocessed with cppo.

  • +
  • Depends on geneweb libraries :

    +
      +
    • geneweb

    • +
    • wserver

    • +
    • Chosen implementation of gwdb_driver_mli.

    • +
    • Chosen implementation of geneweb_sosa_mli.

    • +
    +
  • +
  • Depends on extrenal libraries :

    + +
  • +
+

bin/update_nldb/dune

+

Defines public geneweb.update_nldb executable (locally - update_nldb).

+
    +
  • It includes only one module Update_nldb.

  • +
  • Depends on geneweb libraries :

    +
      +
    • geneweb

    • +
    • Chosen implementation of gwdb_driver_mli.

    • +
    • Chosen implementation of geneweb_sosa_mli.

    • +
    +
  • +
  • Depends on extrenal libraries :

    + +
  • +
+

bin/wserver/dune

+

Defines public geneweb.wserver library (locally - wserver).

+
    +
  • It includes only one module Wserver.

  • +
  • Depends on geneweb libraries :

    +
      +
    • geneweb_util

    • +
    +
  • +
  • Depends on extrenal libraries :

    + +
  • +
+
+
+
Geneweb plugins
+

plugins/cgl/dune

+

Defines plugin plugin_cgl.

+
    +
  • It includes only one module Plugin_cgl.

  • +
  • Plugin is compiled by native compiler with

    +
    (modes (native plugin))
    +
    +
    +
  • +
  • Construction of @plugin alias depends on plugin’s compilation.

  • +
  • Depends on geneweb libraries :

    +
      +
    • geneweb

    • +
    • gwd_lib

    • +
    • wserver

    • +
    +
  • +
+

plugins/export/dune

+

Defines plugin plugin_export.

+
    +
  • It includes only one module Plugin_export.

  • +
  • Plugin is compiled by native compiler.

  • +
  • Construction of @plugin alias depends on plugin’s compilation.

  • +
  • Compiled with flags that tells to compiler to disable warnings:

    +
      +
    • Innocuous unused variable.

    • +
    • Constructor or label name used out of scope.

    • +
    +
  • +
  • Linked statically with libraries :

    +
      +
    • gwexport_lib

    • +
    • gwu_lib

    • +
    • gwb2ged_lib

    • +
    +
  • +
  • Depends on geneweb libraries :

    +
      +
    • geneweb

    • +
    • gwd_lib

    • +
    • gwb2ged_lib

    • +
    • gwexport_lib

    • +
    • gwu_lib

    • +
    • wserver

    • +
    +
  • +
+

plugins/fixbase/dune

+

Defines plugin plugin_fixbase.

+
    +
  • It includes only one module Plugin_fixbase.

  • +
  • Plugin is preprocessed with ppx_deriving.show.

  • +
  • Plugin is compiled by native compiler with -linkall option.

  • +
  • Construction of @plugin alias depends on:

    +
      +
    • Plugin’s compilation

    • +
    • Every file from assets directory.

    • +
    +
  • +
  • Depends on geneweb libraries :

    +
      +
    • gwd_lib

    • +
    • geneweb_def_show

    • +
    +
  • +
+

plugins/forum/dune

+

Defines plugin plugin_forum.

+
    +
  • It includes only one module Plugin_forum that was obtained by preprocessoing corresponding .cppo.ml. Stanza rule in this context is used to remplace lines with directive #include by one of included file :

  • +
+
(rule
+  (target plugin_forum.ml)
+  (deps
+    (:included
+      %{project_root}/plugins/forum/forum.ml
+      %{project_root}/plugins/forum/forumDisplay.ml
+    )
+    (:src plugin_forum.cppo.ml)
+  )
+  (action (run %{bin:cppo} %{src} -o %{target}))
+
+
+
    +
  • Plugin is compiled by native compiler.

  • +
  • Construction of @plugin alias depends on plugin’s compilation.

  • +
  • Depends on geneweb libraries :

    +
      +
    • geneweb

    • +
    • gwd_lib

    • +
    • wserver

    • +
    +
  • +
+

plugins/gwxjg/dune

+

Defines :

+
    +
  1. Public geneweb.plugin_gwxjg_lib library (locally - plugin_gwxjg_lib).

    +
      +
    • It includes modules Gwxjg_ezgw, Gwxjg_data, Gwxjg_trans and Gwxjg_lexicon_parser.

    • +
    • Module Gwxjg_lexicon_parser is obtained from gwxjg_lexicon_parser.mll with stanza ocamllex that makes call for ocamllex lexer.

    • +
    • Compiled with flags that tells to compiler to disable warnings:

      +
        +
      • Constructor or label name used out of scope.

      • +
      • Disambiguated constructor or label name.

      • +
      +
    • +
    • Depends on geneweb libraries :

      +
        +
      • geneweb

      • +
      +
    • +
    • Depends on external libraries :

      + +
    • +
    +
  2. +
  3. Plugin plugin_gwxjg.

    +
      +
    • It includes only one module Plugin_gwxjg.

    • +
    • Plugin is compiled by native compiler with -linkall option.

    • +
    • Construction of @plugin alias depends on:

      +
        +
      • Plugin’s compilation

      • +
      • META file.

      • +
      +
    • +
    • Linked statically with libraries :

      +
        +
      • plugin_gwxjg_lib

      • +
      +
    • +
    • Depends on geneweb libraries :

      +
        +
      • gwd_lib

      • +
      • plugin_gwxjg_lib

      • +
      +
    • +
    +
  4. +
+

plugins/jingoo/dune

+

Defines plugin plugin_jingoo.

+
    +
  • It includes only one module Plugin_jingoo.

  • +
  • Plugin is compiled by native compiler with -linkall option.

  • +
  • Construction of @plugin alias depends on plugin’s compilation.

  • +
  • Linked statically with libraries :

    + +
  • +
  • Depends on geneweb libraries :

    +
      +
    • gwd_lib

    • +
    +
  • +
+

plugins/lib_show/dune

+

Defines plugin plugin_lib_show.

+
    +
  • It includes only one module Plugin_lib_show.

  • +
  • Plugin is compiled by native compiler with -linkall option.

  • +
  • Construction of @plugin alias depends on plugin’s compilation.

  • +
  • Linked statically with libraries :

    +
      +
    • geneweb_def_show

    • +
    +
  • +
+

plugins/no_index/dune

+

Defines plugin plugin_no_index.

+
    +
  • It includes only one module Plugin_no_index.

  • +
  • Plugin is compiled by native compiler.

  • +
  • Construction of @plugin alias depends on plugin’s compilation.

  • +
  • Depends on geneweb libraries :

    +
      +
    • geneweb

    • +
    • gwd_lib

    • +
    • wserver

    • +
    +
  • +
+

plugins/v7/dune

+
    +
  1. Wrapped plugin_v7_lib library.

    +
      +
    • It includes modules starting with prefix V7.

    • +
    • Depends on geneweb libraries :

      +
        +
      • geneweb

      • +
      • geneweb_util

      • +
      • gwd_lib

      • +
      +
    • +
    +
  2. +
  3. Plugin plugin_v7.

    +
      +
    • It includes only one module Plugin_v7.

    • +
    • Plugin is compiled by native compiler with -linkall option.

    • +
    • Construction of @plugin alias depends on:

      +
        +
      • Plugin’s compilation

      • +
      • Every file from assets directory.

      • +
      +
    • +
    • Linked statically with libraries :

      +
        +
      • plugin_v7_lib

      • +
      +
    • +
    • Depends on geneweb libraries :

      +
        +
      • gwd_lib

      • +
      • plugin_v7_lib

      • +
      +
    • +
    +
  4. +
+

plugins/v7_im/dune

+
    +
  1. Wrapped plugin_v7_im_lib library.

    +
      +
    • It includes only one module V7_im_sendImage.

    • +
    • Depends on geneweb libraries :

      +
        +
      • geneweb_util

      • +
      • gwd_lib

      • +
      +
    • +
    +
  2. +
  3. Plugin plugin_v7.

    +
      +
    • It includes only one module Plugin_v7_im.

    • +
    • Plugin is compiled by native compiler.

    • +
    • Construction of @plugin alias depends on plugin’s compilation.

    • +
    • Linked statically with libraries :

      +
        +
      • plugin_v7_im_lib

      • +
      +
    • +
    • Depends on geneweb libraries :

      +
        +
      • gwd_lib

      • +
      • plugin_v7_im_lib

      • +
      +
    • +
    +
  4. +
+

plugins/welcome/dune

+

Defines plugin plugin_welcome.

+
    +
  • It includes only one module Plugin_welcome.

  • +
  • Plugin is compiled by native compiler with -linkall option.

  • +
  • Construction of @plugin alias depends on:

    +
      +
    • Plugin’s compilation.

    • +
    • Every file from assets directory.

    • +
    • META file.

    • +
    +
  • +
  • Depends on geneweb libraries :

    +
      +
    • gwd_lib

    • +
    • wserver

    • +
    • plugin_v7_lib

    • +
    +
  • +
+
+
+
Geneweb tests
+

test/dune

+

Defines:

+
    +
  1. dummy_gwdb library that is an implementation for gwdb_driver_mli virtual library.

    +
      +
    • It includes only one module Gwdb_driver.

    • +
    • Depends on geneweb libraries:

      +
        +
      • geneweb_def

      • +
      +
    • +
    • Depends on external libraries:

      + +
    • +
    +
  2. +
  3. test executable.

    +
      +
    • It includes modules starting with prefix Test that are preprocessed with ppx_deriving.show PPX rewriter.

    • +
    • Construction of @runtest alias depends on execution of test.

    • +
    • Depends on geneweb libraries:

      +
        +
      • geneweb

      • +
      • dummy_gwdb

      • +
      • Chosen implementation of geneweb_sosa_mli.

      • +
      +
    • +
    • Depends on external libraries:

      + +
    • +
    +
  4. +
+
+
+
Geneweb benchmarks
+

benchmark/dune

+

Defines bench executable.

+
    +
  • It includes only one module Bench that is preprocessed with cppo.

  • +
  • Construction of @runbench alias depends on execution of bench.

  • +
  • Depends on geneweb libraries :

    +
      +
    • geneweb

    • +
    • Chosen implementation of gwdb_driver_mli.

    • +
    • Chosen implementation of geneweb_sosa_mli.

    • +
    +
  • +
  • Depends on extrenal libraries :

    + +
  • +
+
+
+
+
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/doc/html/dev-doc/overview/database.html b/doc/html/dev-doc/overview/database.html new file mode 100644 index 0000000000..a5222fa9d2 --- /dev/null +++ b/doc/html/dev-doc/overview/database.html @@ -0,0 +1,267 @@ + + + + + + + + Database overview — Geneweb 0.1 documentation + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

Database overview

+
+

Gw files

+

Genealogy database could be created by Geneweb from one or from multiple source files with .gw extension. Those files describe structurally persons, families all kinds of relationships, different events, etc. You can read more about the file structure here. Binary executable gwc reads files .gw, extracts all persons and families information and passes it to the Gwdb module in order to create the database.

+
+
+

Database entries

+

Transmitted to Gwdb information is composed mainly from:

+
    +
  • Array of all strings that could be any kind of information encoded as a string, like for example: person’s name, birth place, marriage place, etc. Identifier istr allows to reference the string in the given array (index of an element inside the array).

  • +
  • Array of persons where each element encompasses information about one person. Every string field of a person (like his name, birthplace, etc.) is an identifier where the real string is stored in array mentioned before. Reference to other persons by means of identifier iper that reference person in the current array (index of an element inside the persons array).

  • +
  • Array of families where each element encompasses information about one family (couple, children, marriage date, etc.). Identifier ifam allows to reference the family in the given array (index of element inside the array).

  • +
+

Each array keeps a data structure defined in the module Def. Further, those entries will be the main source for every database request.

+
+
+

Storage

+

Gwdb is responsible for creating the database on the disk from the provided inputs. It creates a directory dbname.gwb containing several +files. The main file base contains marshalled representation of each array and base.acc stores offsets to every entry entry that allows to make constant time access. Additionally, it creates some index files that associate useful for requests information to the entry’s identifier in the base file. That helps to requests to find instantly entry without iteration over all existing ones in the database. For example strings.inx is a string index that allows to find id for a searched string. One file is slightly different: the patches file. It stores every modification done inside the base (see Modifications subsection). The storage manipulation interface is described in lib/gwdb_driver.mli/gwdb_driver.mli. This is a virtual module whose +current implementation is available on gwdb-legacy. Format and description for every database file is listed below:

+
base - the base itself
+  magic number (magic_gwb)                 : string of length 8
+  number of persons                        : binary_int
+  number of families                       : binary_int
+  number of strings                        : binary_int
+  persons array offset in file             : binary_int
+  ascends array offset in file             : binary_int
+  unions array offset in file              : binary_int
+  families array offset in file            : binary_int
+  couples array offset in file             : binary_int
+  descends array offset in file            : binary_int
+  strings array offset in file             : binary_int
+  notes origin file                        : value
+  persons array                            : value
+  ascends array                            : value
+  unions array                             : value
+  families array                           : value
+  couples array                            : value
+  descends array                           : value
+  strings array                            : value
+
+base.acc - direct accesses to arrays inside base
+  persons offsets   : array of binary_ints
+  ascends offsets   : array of binary_ints
+  unions offsets    : array of binary_ints
+  families offsets  : array of binary_ints
+  couples offsets   : array of binary_ints
+  descends offsets  : array of binary_ints
+  strings offsets   : array of binary_ints
+
+names.inx - index for names, strings of first names and surnames
+  offset to sindex : binary_int
+  offset to findex : binary_int
+  1st index (mixes between names) : value 
+    array, length = 16383, associating:
+      - a hash value of a "crushed" (module "Name") name 
+        (modulo length)
+      - to the array of ids of the corresponding persons
+  2nd index (surnames sub-strings) : value
+    array, length = "table_size", associating:
+      - a hash value of the "crushed" (module "Name") surname 
+        sub-string (modulo length)
+      - to the array of the corresponding surnnames (string ids) 
+      that contain giving surname sub-string
+  3rd index (first name sub-strings) : value 
+    array, length = 16383, associating:
+      - a hash value of the "crushed" (module "Name") first name 
+      sub-string (modulo length)
+      - to the array of the corresponding string ids that contains 
+      giving first name sub-string
+
+names.acc - direct accesses to values inside arrays in names.inx
+
+strings.inx - index for all strings
+  length of the strings offset array : binary_int
+  strings hash table index           : 2 arrays of binary_ints
+    strings offset array (length = prime after 10 * strings 
+    array length)
+      - associating a hash value of the string modulo length
+      - to its id in the string array
+    strings list array (length = string array length)
+      - associating a string id
+      - to the id of the next index (previous value) holding the 
+      same hash value
+
+snames.inx - index for surnames
+  array ordered by surname  
+    - associating the string id of a surname
+    - to a pointer (offset) inside snames.dat
+
+snames.dat - data associated with snames.inx
+  array of list of persons holding a surname
+
+fnames.inx - index for first names
+  array ordered by first name 
+    - associating the string id of a first name
+    - to a pointer (offset) inside fnames.dat
+
+fnames.dat - data associated with fnames.inx
+  array of list of persons holding a first name
+
+notes - text file containing data base notes.
+
+notes_d - directory containing .txt for each extended page
+
+particles.txt - text file with autorised name's particles
+
+patches - modification inside the database
+  When updated, none of the previous files are modified. 
+  Only this one is written and rewritten. It holds a record 
+  of type "patches", composed of association lists 
+  "index" - "new value".
+
+nb_persons - number of real persons (with those added by patches)
+
+synchro_patches - timestamped history of base's modifications. 
+
+restrict - defines visibility of each person in the base 
+
+
+
+
+

Modifications

+

When a modification is requested, geneweb does not update base file itself. It +completes the patches file containing all the latest modifications on the +base. Every modification (patch) done is pended until patches are committed with commit_patches request. +Commit performs update of the patches file.

+

Patching signifies only operations that add or modify an entry. Entry suppression is done quite differently. +It is replaced by a dummy entry and then removed by Geneweb’s garbage collector gwgc that performs compaction +of database arrays. Another useful fixbase tool, locates and fixes inconsistencies on the base and updates all database files.

+
+
+

Example

+

Here is an example how Geneweb displays birth dates of persons that have given name (let’s say “Pierre”) without considering caches:

+
    +
  • Firstly, it makes dichotomous search inside fnames.inx of a string id (istr) that references “Pierre”

  • +
  • Then it reads (with associated to “Pierre” offset from fnames.inx) position in the file fnames.data where list of ids of persons (iper) with first name “Pierre” are stored.

  • +
  • For every person’s id it gets person’s entry offset from base.acc file

  • +
  • Then it reads person’s entry with giving offset and get field associated to the birth date.

  • +
  • Displays all extracted birth dates.

  • +
+
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/doc/html/dev-doc/overview/index.html b/doc/html/dev-doc/overview/index.html new file mode 100644 index 0000000000..fa71c2bac1 --- /dev/null +++ b/doc/html/dev-doc/overview/index.html @@ -0,0 +1,263 @@ + + + + + + + + Overview — Geneweb 0.1 documentation + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

Overview

+
+

How to start a Geneweb server

+
+

Starting the server

+

Starting a Geneweb web server requires two tools:

+
    +
  • gwd, the actual web server;

  • +
  • setup, the database setup portal.

  • +
+

These two tools are part of the main distribution and can be found on _build. +If you built the project with make clean distrib, two scripts are available on +the directory distribution/. Keep in mind they are scripts wrapping the actual +binaries.

+

To start the server on the current directory:

+
_build/install/default/bin/geneweb.gwd -hd <location_of_hd_dir>
+
+
+

This will start the main web server on port 2317.

+

To start the setup server on the current directory:

+
_build/default/bin/setup/setup.exe -gd <location_of_gwsetup_dir>
+
+
+

This will start the setup server on port 2316.

+
+
+

Configuration

+

Geneweb can be configured by defining a <base name>.gwb file. An example is +available in the etc directory.

+
+
+
+

Architecture of Geneweb

+

Geneweb is built from three main components:

+
    +
  • the storage of the genealogical trees, divided in a patch file and the actual data;

  • +
  • the libraries, reading the data from the storage and writing the patch file

  • +
  • the binaries and the web server, reading and writing the storage

  • +
+../../_images/diagram.png +
+

Storage architecture

+ +
+
+
+

Libraries

+
    +
  • Util: a library with miscellaneous useful modules for manipulating +base data types.

  • +
  • Def: the definition of the main type definitions used in Geneweb trees.

  • +
  • Sosa: describes a Sosa-Stradonitz numbering (known an +Ahnentafel numbering), associating natural identifiers to individuals. This +library is virtual and has three different implementations. One of these +implementations is selected by the configuration script.

    +
    +
      +
    • Sosa_array is one of the sosa implementations. It represents naturals +as pair of integers stored in an array a such that +sosa = a.(0) + base * a(1)) where base is a hardcoded constant.

    • +
    • Sosa_num is one of the sosa implementation based on the Big_int +library.

    • +
    • Sosa_zarith is one of the sosa implementation based on the Zarith +library.

    • +
    +
    +
  • +
  • Gwdb_driver: describes the storage implementation. While it is +virtual, it currently has only one implementation, gwdb-legacy. It is wrapped +by the gwdb library that exports many tools for database updates.

  • +
  • Core: core of Geneweb for calculating consanguinity between persons.

  • +
  • Geneweb_export : provides a functor for defining Json convertors for Geneweb’s +datatypes.

  • +
  • Def_show: defines formatters and string converters for Geneweb’s datatypes.

  • +
  • Geneweb: the main library. It contains several kinds of files, from +utilitarian modules to HTML generation.

  • +
  • Gwb2gedLib: defines a function for exporting a base to a GEDCOM file.

  • +
  • Wserver: a light-weight web server, used by gwd and setup.

  • +
  • Gwd_lib: defines additional modules for the gwd web server.

  • +
  • GwuLib: defines useful functions for exporting a database to a .gw file.

  • +
+

The documentation of each module is available on the automatically generated +documentation of geneweb ($ make doc)

+
+

Binaries

+
+
+
+

Official binaries

+

Here are the binaries maintained by Geneweb:

+
    +
  • Connex: calculates connex components of a base;

  • +
  • Consang: calculates the consanguinity level of individuals;

  • +
  • Ged2gwb: imports a GEDCOM 5.5.1 file to a Geneweb base;

  • +
  • Gwb2ged: exports a base to a GEDCOM 5.5.1 base;

  • +
  • Gwc: creates a new database;

  • +
  • Gwd: starts Geneweb’s main web server which allows to interact with bases;

  • +
  • Gwdiff: targets differences between two databases;

  • +
  • Fixbase: checks the consistency of a base and applies patches;

  • +
  • Gwgc: removes unused entries in Geneweb’s arrays;

  • +
  • Gwrepl: an OCaml interactive top level, useful for scripts;

  • +
  • Gwu: exports a base to a .gw file;

  • +
  • Setup: a web server for selecting and creating databases.

  • +
+
+

For mor details about every binary, see:

+ +
+
+
+

Web-server and plug-ins

+

The gwd web server is customizable with plug-ins; code replacing +the original behaviour of the web server handling requests. They are +dynamically loaded by gwd at its start and each base can be activated +through the .gwb file (plugins=* for activating all plug-ins, +otherwise plugins=p1,p2,...).

+

A plug-in is composed of its code, a dune file for building and a +META file, containing some informations about the plug-in itself.

+

Here is an example of META file:

+

` +version:1 +maintainers:OCamlPro +depends:plugin1,plugin2 +`

+

The only field taken into account by gwd is depends as it is used to +check there are no circular dependencies between plug-ins.

+
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/doc/html/genindex.html b/doc/html/genindex.html new file mode 100644 index 0000000000..415a2b77b8 --- /dev/null +++ b/doc/html/genindex.html @@ -0,0 +1,104 @@ + + + + + + + + Index — Geneweb 0.1 documentation + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ + +

Index

+ +
+ +
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/doc/html/objects.inv b/doc/html/objects.inv new file mode 100644 index 0000000000..1993981048 Binary files /dev/null and b/doc/html/objects.inv differ diff --git a/doc/html/search.html b/doc/html/search.html new file mode 100644 index 0000000000..c6a2f3d9af --- /dev/null +++ b/doc/html/search.html @@ -0,0 +1,114 @@ + + + + + + + + Search — Geneweb 0.1 documentation + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +

Search

+
+ +

+ Please activate JavaScript to enable the search + functionality. +

+
+

+ Searching for multiple words only shows matches that contain + all words. +

+
+ + + +
+ +
+ +
+ +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/doc/html/searchindex.js b/doc/html/searchindex.js new file mode 100644 index 0000000000..551b4ad894 --- /dev/null +++ b/doc/html/searchindex.js @@ -0,0 +1 @@ +Search.setIndex({docnames:["audit/binaries/connex","audit/binaries/consang","audit/binaries/ged2gwb","audit/binaries/gwb2ged","audit/binaries/gwc","audit/binaries/gwd","audit/binaries/gwdiff","audit/binaries/gwfixbase","audit/binaries/gwgc","audit/binaries/gwrepl","audit/binaries/gwu","audit/binaries/index","audit/binaries/plugin_cgl","audit/binaries/plugin_export","audit/binaries/plugin_forum","audit/binaries/plugin_v7","audit/index","audit/introduction/index","audit/introduction/installation","audit/libraries/config","audit/libraries/core","audit/libraries/fixbase","audit/libraries/geneweb","audit/libraries/geneweb_def","audit/libraries/geneweb_gwdb","audit/libraries/geneweb_gwdb-legacy","audit/libraries/geneweb_util","audit/libraries/index","audit/libraries/wserver","dev-doc/binaries/bench","dev-doc/binaries/cgl","dev-doc/binaries/connex","dev-doc/binaries/consang","dev-doc/binaries/export","dev-doc/binaries/fixbase_plugin","dev-doc/binaries/forum","dev-doc/binaries/ged2gwb","dev-doc/binaries/gwb2ged","dev-doc/binaries/gwc","dev-doc/binaries/gwd","dev-doc/binaries/gwdiff","dev-doc/binaries/gwfixbase","dev-doc/binaries/gwgc","dev-doc/binaries/gwrepl","dev-doc/binaries/gwu","dev-doc/binaries/gwxjg","dev-doc/binaries/index","dev-doc/binaries/jingoo","dev-doc/binaries/lib_show","dev-doc/binaries/no_index","dev-doc/binaries/official_binaries","dev-doc/binaries/plugins","dev-doc/binaries/setup","dev-doc/binaries/test","dev-doc/binaries/tests","dev-doc/binaries/update_nldb","dev-doc/binaries/v7","dev-doc/binaries/v7_im","dev-doc/binaries/welcome","dev-doc/binaries/xhtml","dev-doc/geneweb-lib/index","dev-doc/index","dev-doc/installation/index","dev-doc/overview/database","dev-doc/overview/index","index"],envversion:{"sphinx.domains.c":2,"sphinx.domains.changeset":1,"sphinx.domains.citation":1,"sphinx.domains.cpp":3,"sphinx.domains.index":1,"sphinx.domains.javascript":2,"sphinx.domains.math":2,"sphinx.domains.python":2,"sphinx.domains.rst":2,"sphinx.domains.std":2,sphinx:56},filenames:["audit/binaries/connex.md","audit/binaries/consang.md","audit/binaries/ged2gwb.md","audit/binaries/gwb2ged.md","audit/binaries/gwc.md","audit/binaries/gwd.md","audit/binaries/gwdiff.md","audit/binaries/gwfixbase.md","audit/binaries/gwgc.md","audit/binaries/gwrepl.md","audit/binaries/gwu.md","audit/binaries/index.rst","audit/binaries/plugin_cgl.md","audit/binaries/plugin_export.md","audit/binaries/plugin_forum.md","audit/binaries/plugin_v7.md","audit/index.rst","audit/introduction/index.rst","audit/introduction/installation.md","audit/libraries/config.md","audit/libraries/core.md","audit/libraries/fixbase.md","audit/libraries/geneweb.md","audit/libraries/geneweb_def.md","audit/libraries/geneweb_gwdb.md","audit/libraries/geneweb_gwdb-legacy.md","audit/libraries/geneweb_util.md","audit/libraries/index.rst","audit/libraries/wserver.md","dev-doc/binaries/bench.md","dev-doc/binaries/cgl.md","dev-doc/binaries/connex.md","dev-doc/binaries/consang.md","dev-doc/binaries/export.md","dev-doc/binaries/fixbase_plugin.md","dev-doc/binaries/forum.md","dev-doc/binaries/ged2gwb.md","dev-doc/binaries/gwb2ged.md","dev-doc/binaries/gwc.md","dev-doc/binaries/gwd.md","dev-doc/binaries/gwdiff.md","dev-doc/binaries/gwfixbase.md","dev-doc/binaries/gwgc.md","dev-doc/binaries/gwrepl.md","dev-doc/binaries/gwu.md","dev-doc/binaries/gwxjg.md","dev-doc/binaries/index.rst","dev-doc/binaries/jingoo.md","dev-doc/binaries/lib_show.md","dev-doc/binaries/no_index.md","dev-doc/binaries/official_binaries.rst","dev-doc/binaries/plugins.rst","dev-doc/binaries/setup.md","dev-doc/binaries/test.md","dev-doc/binaries/tests.rst","dev-doc/binaries/update_nldb.md","dev-doc/binaries/v7.md","dev-doc/binaries/v7_im.md","dev-doc/binaries/welcome.md","dev-doc/binaries/xhtml.md","dev-doc/geneweb-lib/index.md","dev-doc/index.rst","dev-doc/installation/index.md","dev-doc/overview/database.md","dev-doc/overview/index.rst","index.rst"],objects:{},objnames:{},objtypes:{},terms:{"100":22,"1024":[31,52],"120":[36,39,60],"127":31,"16383":63,"1800":39,"189":26,"1999":12,"1st":63,"1st_name":44,"2021":17,"2316":[52,64],"2317":[31,39,52,64],"240":60,"2nd":63,"3rd":63,"400":39,"4000":26,"555sample16b":2,"555sample16l":2,"847":25,"\u00e9l\u00e9ment":19,"\u00e9tait":21,"\u00e9trang":19,"\u0439\u0442":[],"abstract":26,"boolean":[5,6],"byte":[26,60,62],"case":[3,4,5,6,16,25,26,38,60,62],"compl\u00e9ter":21,"default":[2,29,31,36,37,38,39,40,44,52,53,62,64],"enum":5,"export":[11,16,37,44,46,51,62,64],"final":26,"function":[3,4,9,11,20,22,23,24,25,26,27,28,39,60,61,62,64,65],"import":[11,18,36,64],"int":[31,38],"long":65,"m\u00eame":21,"new":[5,8,11,16,22,26,27,34,38,39,56,63,64],"null":43,"public":[18,36,37,44,62],"remarqu\u00e9":[19,21],"return":[3,4,5,6,24,25,26,31,37,39,44],"static":62,"switch":62,"true":[4,5,25,53,62],"try":[26,36],"utilis\u00e9":[19,21],"while":[4,5,11,16,18,25,26,51,64],AND:5,Adding:65,And:[22,26],But:[4,22],For:[4,5,11,15,16,20,25,43,63,64],NOT:16,One:[16,39,63,64],Such:27,That:[5,25,26,63],The:[0,1,2,3,4,5,6,10,11,12,16,18,21,24,39,44,52,53,60,61,62,63,64],Then:63,There:[1,5,8,9,39],These:[50,64],Use:[11,36,39,53,62,65],Used:[37,44,62],Using:[7,11],_build:[29,53,64],_piqi:18,abbrev_list:26,abl:[5,11],about:[0,4,25,26,60,62,63,64],absolut:53,acc:[25,26,63],accept:39,access:[4,5,6,7,16,22,25,39,60,63,65],accord:11,account:[25,64],acknowledg:[11,16],action:[5,39,62],activ:[39,62,64],actual:[4,5,25,26,64],actuali:25,add:[23,25,39,60,63,65],add_fam:39,add_fam_ok:39,add_ind:39,add_ind_ok:39,add_lexicon:39,add_par:[11,39],add_par_ok:39,add_person_by_nam:4,added:[39,60,63],adding:[2,8,16,25,39],addit:[14,18,39,44,60,64,65],addition:63,additionali:[],addr:39,address:[39,52],adef:[0,27,62],adition:60,adm_fil:5,admin:39,adopt:60,advanc:39,advic:65,advsearchok:27,affect:16,after:[17,25,36,39,53,63],age:22,ago:[37,44],ahnentafel:64,algorithm:5,alia:[18,36,62],alias:62,aliv:[36,39],all:[3,4,5,6,11,18,20,22,24,25,26,31,37,38,39,40,43,44,60,62,63,64],alloc:20,allow:[3,5,16,22,25,26,39,60,63,64],allowed_tag:39,alow:[],alphabet:2,alreadi:[0,5,11,26,36,38,62],also:[0,1,4,5,8,10,12,16,22,25,26,27,37,39,44,62],alter:2,alternatif:25,alwai:[2,4,5,18],among:[36,37],amount:11,analysi:16,ancestor:[6,37,39,44],andhebrew:60,ani:[0,4,6,16,36,39,41,60,63],anm:39,anniversari:39,anoth:[4,16,25,26,63],ansel:[36,37,44],ansi:[37,44],answer_i:39,antoth:25,anymor:[5,8,16],anyon:[5,22],anywher:25,api:11,appear:[4,26],appli:[16,34,41,64],applic:60,apply_patch:25,appveyor:[53,65],arab:26,architectur:[61,65],arduou:[5,11],arein:4,aren:[23,25],arg:[11,24],argument:[4,11,22,39,60,62,65],arrai:[2,8,16,20,25,60,63,64],artefact:62,as_ok:39,ascend:[37,39,40,44,63],ascii:[36,37,44,60],ask:[24,31],assert:[25,26],asset:[39,60,62,65],assocait:39,associ:[4,5,8,11,16,19,38,39,44,63,64],assum:5,atom:16,audit:[17,21,27],auth:39,authentif:[11,39],author:[39,52],autom:16,automat:[4,16,41,64],autoris:63,auxilarri:60,avaiabl:27,avail:[2,5,27,39,63,64],avoid:[3,5,11,15,65],b34f9cc756d5b21792ebbe8d6e9f9ebf01eddd27:17,back:[11,22],backend:[5,62],backward:44,baptism:39,bar:60,base1:40,base2:40,base64:27,base:[5,8,11,16,20,22,25,26,31,32,36,37,38,39,41,42,44,60,62,63,64,65],base_data:25,base_env:19,base_restricted_get:25,base_visible_get:25,basenam:39,basic:5,becaus:[0,5,11,16,26],becom:[36,44],bee:[25,26],been:[6,16,39],befor:[4,25,36,39,63],beforehand:7,begin:[25,36],behavior:38,behaviour:[3,4,5,27,51,64],being:[5,11,39,60],belong:[5,16,24],below:63,bench:[46,54,62],bench_fil:62,bench_nam:62,benchmark:[],benchmarklink:[],best:16,better:[4,18,22,23,25,26],between:[4,9,26,36,37,39,40,44,53,60,63,64],big:[5,11,15,16,22],big_int:64,bin:[16,39,52,62,64],binair:21,binari:[0,1,5,8,16,21,25,52,54,61,62,63,65],binary_int:63,bind:[10,16,60],bindir:52,binomi:60,birth:[36,39,63],birthdeath:27,birthplac:63,blang:39,blit:26,block:4,bname:5,bnote:[4,38],bodi:[4,5,16,22],born:[22,37,44],bottom:39,branch:[31,51],bring:11,broken:22,browser:39,buff:27,buffer:[2,26,60],bug:[16,23,27],build:[16,17,39,61,64,65],build_dir:62,build_distrib_dir:62,built:[11,26,64],burial:39,bypass:23,bytecod:62,cach:[25,39,53,63],cache_filenam:53,cache_lang:39,cal:39,calcul:[5,6,31,32,39,64],calendar:[27,39,62],call:[1,2,16,25,26,28,60,62],camlp5:62,camlp5o:62,camomil:60,can:[3,5,15,16,18,28,29,37,39,44,53,60,63,64],cancel:[2,36],cannot:[18,39],cat:43,caus:23,censor:[37,44],central:27,cgi:39,cgl:[11,46,51,62],champ:19,chang:[1,11,16,25,26,27,39,41,42,44,56],change_wiz_vi:39,changed_p:22,charact:[2,36,60],charcact:60,charset:[36,37,44],check:[7,11,16,22,24,25,36,38,39,40,41,53,64],check_bas:22,check_noloop:20,checkitem:22,chg_chn:39,chg_chn_ok:39,chg_evt_fam_ord:39,chg_evt_fam_ord_ok:39,chg_evt_ind_ord:39,chg_evt_ind_ord_ok:39,chg_fam_ord:39,chg_fam_ord_ok:39,children:[22,24,39,41,63],choos:53,choosen:53,chooser:53,chop_extens:5,chosen:62,chri:60,circular:64,clair:19,clarifi:0,clean:[4,18,22,25,39,62,64],clear:[11,25],clearer:5,client:39,close:16,cma:62,cmo:62,cmx:39,cnt:[31,39],code:[0,4,5,7,10,11,18,20,22,25,26,28,62,64,65],code_french_year:22,collect:[6,20,60,65],collector:[25,63],color:53,com:[],combin:39,come:17,comm:19,comma:[22,39],command:[11,18,25,62],comment:65,commit:[17,25,41,42,62,63,65],commit_patch:63,common:[11,26,60],commonli:25,commun:39,comp_famili:4,compact:63,compar:[16,60],comparison:22,compat:[11,16,27,44],compatible_expr:6,compil:[5,11,16,18,21,38,39,62],complement:[57,65],complet:[16,36,63],complex:27,compoment:[],compon:[31,61,64,65],compos:[11,60,63,64],comprehens:18,compress:60,comput:[4,6,37,38,39,44,60],concat:3,concaten:38,concern:[22,39,44],concert:16,condit:5,conf:[5,16,19,53],config:[5,11,16,22,62],configur:[16,39,53,61,65],confus:[1,4],conn_tmout:39,conn_wiz:39,connect:39,connex:[11,46,50,62,64],connexion:5,consang:[0,11,16,27,46,50,62,64],consanguin:[0,32,38,60,64],consid:[0,1,2,4,5,6,10,13,15,16,18,21,22,25,26,27,36,60,63],consist:[36,38,41,64,65],consol:53,constant:[11,63,64],construct:[60,62],constructor:[4,16,62],contain:[5,16,27,39,52,62,63,64],content:[5,11,16,17,27,28,38,44,59,60,61,62,65],context:[26,62],continu:4,contribut:22,control:[5,60],convers:44,convert:[26,36,37,60,64],convert_famili:4,convertor:64,copi:[25,26],copyr:22,core:[5,27,39,50,62,64],correct:[2,5,11,20,26],correctli:[18,22,26],correspond:[0,23,25,62,63],corrupt:[16,25],could:[4,5,6,8,9,10,11,12,16,20,24,25,26,27,28,62,63],couldn:26,count:[39,60],counter:[8,16],coupl:[39,60,63],cousin:[22,39],cousinsdisplai:22,cppo:62,cppo_d:62,cpu:29,creat:[4,8,13,16,18,20,22,25,26,36,38,44,62,63,64],critic:16,crush:[26,63],current:[5,16,18,25,38,39,60,62,63,64],custom:[5,11,50,62],customis:62,customiz:64,cut_spac:4,d3d861:[],daemon:[39,52],dag:39,dagdisplai:19,dai:[22,36,60],dan:19,danger:25,darwin:62,dat:63,data:[4,8,16,27,36,38,39,53,60,62,63,64],databas:[2,4,16,20,22,24,25,32,33,36,37,38,39,40,42,43,44,50,52,62,64],datatyp:64,date:[4,16,17,22,26,36,39,62,63],dates_dm:36,dates_md:36,db1link:11,dbdisk:62,dbname:63,dead:[16,18,22],deal:[4,25,26,60],death:[36,39],debug:[11,39,62],decid:25,declar:4,declin:26,decod:[2,26,36,60],dedic:34,def:[27,28,62,63,64],def_show:[48,62,64],default_particl:26,defaut:62,defect:11,defin:[0,2,4,5,16,21,23,24,25,26,28,37,39,40,44,51,52,56,60,61,62,63,64],defini:[],definit:[4,27,62,64],degre:60,del:31,del_fam:39,del_fam_ok:39,del_imag:57,del_image_ok:57,del_ind:39,del_ind_ok:39,delai:53,delet:[8,16,25,31,37,39,44],den:36,dep:62,depend:[11,25,33,36,39,52,62,64],deprec:[4,18,22,26,53],der:36,descend:[22,37,39,40,44,63],descenddisplai:22,descrept:22,describ:[4,5,9,61,62,63,64],descript:[11,22,62,63],desir:23,despit:[4,20,22,24,26],detail:[2,5,31,39,40,64],deux:21,dev:[18,43,62],develop:[5,11,51,65],developp:[],dichotom:63,dictionari:[8,16,39,42],did:39,die:36,died:22,diff:[6,39],differ:[9,11,16,18,28,34,39,40,60,61,62,63,64],difficult:[4,11,23],digest:39,dir:[39,44,52,53,62],direct:[22,39,62,63],directli:[4,6,22,26],directori:[5,26,27,39,44,52,53,60,62,63,64],disabl:62,disambigu:62,discard:[4,6,14,18,62],discourag:23,discov:16,disk:[4,25,60,63],displai:[5,18,22,26,27,31,39,44,53,62,63],distibut:62,distrib:[62,64],distrib_dir:62,distribut:[62,64],divid:64,divorc:41,dkei:11,dlevel:11,dmy_of_dmy2:26,doc:[18,41,56,62,64],doctyp:19,document:[4,9,11,15,16,18,24,50,64],doe:[0,1,2,5,11,16,25,60,63],doen:24,doesn:[4,22,25,26],don:[4,26,36,37,44],done:[5,8,14,16,20,22,24,62,63],driver:[27,62],drop:38,dry:[41,42],dsd:[],dsk_base:25,due:[18,26,62],dummi:[24,63],dummy_gwdb:62,dune:[23,64],dune_dirs_exclud:62,dune_profil:62,dupliat:39,duplic:39,durabl:[5,11],dure:16,dutil:24,dynam:64,dynlink:62,each:[2,4,5,8,16,18,39,62,63,64],easi:16,easili:[16,25],edit:39,effect:60,effici:[0,8,16,26],effort:27,efn:[2,36],eight:39,either:[4,18,25,39],elap:60,element:[2,25,60,63],elementari:28,elimin:[4,62],els:[22,28,44],emb:62,emit:62,empti:[22,38],enabl:[39,41,62],encod:[53,60,63],encompass:63,encor:21,end:[4,11,22,25,38,65],endian:11,engag:39,enhanc:10,enough:[11,16,22],enregistr:19,enthusiast:[36,37],entir:[20,26,62],entri:[4,8,16,39,42,64],env:22,environ:[5,22,62],epn:[2,36],eq_kei:26,eq_list:26,equal:[5,20,22],equival:[60,62],eras:[2,38],erron:25,error:[4,11,16,25,26,38,39,62],est:[19,21],etc:[22,39,62,63,64],evalu:22,even:[0,4,18,24,25,27,39],event:[4,11,22,37,39,44,63],everi:[16,22,25,26,39,62,63,64],everyth:62,exact:[5,31],exampl:[4,5,9,11,16,20,27,39,62,64],except:[4,16,25,26,39,60,62],exclud:[39,62],exe:[29,43,52,53,62,64],exec_if_verb_ok:5,execut:[2,4,9,11,29,39,43,53,63,65],exempl:60,exhaust:[5,11,65],exist:[25,26,36,38,62,63,65],expect:[5,11,26,39],explicit:16,expos:[16,26,60],ext:62,extend:63,extenis:60,extens:[62,63],extern:[5,11,26,62],externel:[],extra:33,extract:[36,37,44,62,63],extrat:[],extren:62,ez_fil:26,f_some:4,facilit:58,fact:18,factor:[3,5],fail:[2,5,39],failfirst:53,failur:[5,38],fait:21,fals:[3,25,53,62],famili:[4,22,24,25,36,37,38,39,41,44,63],fast:[32,41],faster:32,fastli:[],father:[24,60],featur:[27,39],feedback:62,fevent:41,field:[4,22,25,36,38,44,59,62,63,64],file:[2,4,6,11,22,23,25,26,27,31,36,37,38,39,44,52,53,60,64,65],file_nam:32,filenam:53,filter:0,filter_map:26,find:[22,23,25,62,63],find_compatible_person:6,find_compatible_union:6,find_person_by_global_nam:4,find_person_by_local_nam:4,find_top:6,findex:63,first:[5,36,37,38,39,44,60,63,65],firstli:[26,62,63],fix:[0,16,22,27,41,63,65],fixbas:[16,18,27,46,50,51,62,63,64],fixbase_ok:34,flag:[60,62],fly:[11,65],fname:[39,63],fne:36,focus:11,follow:[28,62],forc:[5,11,36,39],forget:16,form:39,format:[4,36,37,40,44,60,63,65],formatt:64,forum:[11,46,51,62],forum_add:35,forum_add_ok:35,forum_del:35,forum_p_p:35,forum_search:35,forum_v:35,forum_view:35,forumdisplai:[14,62],found:64,fragil:62,framework:11,french:60,friend:39,from:[4,5,11,16,17,22,25,26,27,32,34,39,44,45,50,60,62,63,64,65],ftruncat:26,full:53,full_major:53,fulli:4,functor:[26,64],funtion:25,further:[2,4,25,63],futil:27,g_error:4,g_patch_p:4,gain:16,garbag:[25,63,65],ged2gwb:[11,46,50,62,64],ged:[2,33,36,37,44],ged_date_dmi:3,gedcom:[2,16,36,37,64],gedplugindep:11,gen_:60,gen_ascend:0,gen_fam_event_nam:3,gen_pers_event_nam:3,gen_person:4,gen_person_misc_nam:60,genealog:[36,37,63,64],geneanet:[11,27],gener:[0,4,5,11,18,25,37,44,61,62,64,65],generated_files_dep:62,geneweb:[5,11,16,18,20,25,26,27,31,32,36,37,38,39,40,41,42,43,44,45,47,48,50,53,61,63],geneweb_cor:62,geneweb_def:[16,27,62],geneweb_def_show:62,geneweb_export:[62,64],geneweb_gwdb:[16,27,62],geneweb_gwdb_legaci:62,geneweb_sosa_arrai:62,geneweb_sosa_mli:62,geneweb_sosa_num:62,geneweb_sosa_zarith:62,geneweb_util:[16,24,27,62],get:[2,4,22,25,26,39,60,63],get_id:2,get_mar_d:4,get_optinal_baptd:4,get_optional_birthd:4,get_optional_event_d:4,get_optional_sex:4,get_to_eoln:2,getter:11,git:53,github:[],give:[5,22,23,25,60,63,65],given:[6,11,18,25,26,36,39,53,60,63],global:[5,6,10,11,25,26,65],goal:62,good:[4,16],grace:53,grammar:62,grandpar:22,grant:60,graph:[18,39],great:11,gregorian:60,grep:19,group:44,guess:5,gw_syntax:4,gwb2ge:62,gwb2ged:[11,46,50,62,64],gwb2ged_lib:62,gwb2gedlib:[62,64],gwb:[38,63,64],gwc:[11,16,46,50,62,63,64],gwcomp:11,gwd:[11,16,31,46,50,51,52,62,64],gwd_lib:[39,47,62,64],gwd_p:[31,52],gwdb:[4,20,27,62,63,64],gwdb_d:62,gwdb_driver:[16,62,63,64],gwdb_driver_mli:62,gwdb_legaci:62,gwdb_pkg:62,gwdiff:[11,46,50,62,64],gwdlog:[11,62],gwdplugin:[11,62],gwdplugindep:[39,62],gwdpluginmd5:62,gwdpluginmeta:[11,62],gwdrepl:43,gwexport:[36,62],gwexport_lib:62,gwf:39,gwfixbas:[11,41,62],gwgc:[11,16,46,50,62,63,64],gwlib:62,gwo:[4,38],gwrepl:[5,11,46,50,62,64],gwrepl_dep:62,gwrepl_force_unpack:43,gwrepl_noprompt:43,gwrepl_ppf:43,gwrepl_verbos:43,gwsetup:[39,52],gwu:[11,46,50,62,64],gwu_lib:62,gwulib:[10,62,64],gwxjg:[26,46,51,62],gwxjg_data:62,gwxjg_ezgw:62,gwxjg_lexicon_pars:62,gwxjg_tran:62,hand:25,handl:[5,11,39,57,64],hard:28,hardcod:64,harden:7,has:[0,3,4,6,10,16,18,39,62,64],hash:[8,10,16,39,62,63],hashtabl:[5,10],hashtbl:11,have:[6,11,22,25,26,36,39,63],head:16,header:[60,65],health:53,heavi:[5,11,15],help:[9,11,16,26,31,60,62,63],here:[6,7,39,60,62,63,64],hide:[5,26],high:25,higher:11,highli:16,highlight:39,him:22,his:[22,25,36,44,63],hist:39,hist_clean:39,hist_clean_ok:39,hist_diff:39,hist_search:39,histori:[22,39,63],histrorydiff:22,hold:[36,39,63],home:53,host:39,how:[15,60,61,63,65],howev:[11,28],html:[12,30,31,39,40,53,64,65],html_n:11,http:[12,22,28,44,60],http_redirect_temporarili:28,humain:4,ic2:25,ic2_first_name_start_po:25,ic2_surname_start_po:25,ident:39,identifi:[4,8,11,16,39,62,63,64],ids:63,ifam:63,ignor:[25,31],imag:[39,57],images_dir:39,images_url:39,imediatli:4,imh:39,impact:25,implement:[11,16,18,23,25,53,60,62,63,64],implementaion:62,impli:[25,37,44],implicit:62,implicitili:25,imposs:6,improov:[],improv:[5,20,27,65],includ:[35,62],inclus:11,inconsist:[5,36,63],incorrect:[2,5,39],increment:26,indend:5,indent:65,index:[22,25,26,37,39,41,56,58,60,62,63,65],index_out_of_bound:25,indic:62,individu:[32,36,37,39,44,64],inet:39,infer:4,info:39,inform:[2,4,25,26,29,60,62,63,64],init:2,initi:[2,36],initialis:[24,25],innocu:62,input:[11,16,63],input_binary_int:25,ins:[5,11,46,50,61,65],insert:[4,24],insert_famili:24,insert_person:4,insid:[4,16,18,20,22,25,26,60,62,63],instal:[5,11,16,17,39,52,61,64,65],instanc:25,instantli:[25,63],instead:[0,4,5,6,8,11,16,26,44,60],instruct:16,integ:64,intepret:39,interact:[9,43,64],interest:24,interfac:[16,26,39,63,65],intern:[8,16,23,25,26,60],interpret:36,interv:[36,53],introduct:17,intuit:25,inv_fam:39,inv_fam_ok:39,invalid:41,invalid_argu:26,invari:11,invert:39,involv:[5,27,37,44],inx:[25,63],iovalu:25,iper:[20,63],is_compat:6,is_primary_fev:3,is_primary_pev:3,isn:[4,22,25,26,62],iso_8859_1_of_utf_8:26,isol:44,issu:[5,16],istr:63,iter:[0,20,60,63],its:[4,5,8,10,16,18,26,36,39,42,60,62,63,64,65],itself:[5,11,63,64],januari:39,jingoo:[45,46,51,62],join:16,json:64,json_convert:62,json_export:62,julian:60,julien:[],junit:53,just:[4,7,20,21,22,25,39],kahn:5,keep:[16,36,63,64],kei:[11,22,24,37,39,41,44,60],kernel:62,kill:[39,53],kill_anc:39,kill_par:0,kind:[39,60,63,64],know:[36,62],knowledg:[5,11],known:64,label:[22,62],lang:[5,18,39,52,62],languag:39,larg:16,last:[16,39],later:22,latest:[25,39,41,63],launch:25,layer:60,lead:[16,39],least:[5,8,16],leav:16,legaci:[16,27,62,63,64],len:[2,25,26],length:[25,26,31,63],lens:16,less:[37,44],let:[51,63],letter:[11,36,60,65],level:[9,11,22,32,43,64],level_to_int:5,lex:4,lexer:62,lexicon:[39,60],lib:[18,19,23,27,62,63],lib_show:[46,51,62],librai:62,librari:[0,1,5,11,18,21,29,36,39,43,52,61,65],light:[11,64],lighten:[21,27],like:[4,5,22,23,25,26,36,53,60,62,63],limit:[39,44],line:[4,5,18,24,25,26,39,62],link:[4,8,16,30,39,62],linkal:62,linux:[26,62],list:[0,2,3,4,5,6,18,19,25,26,31,36,38,39,50,51,53,54,60,62,63],littl:11,live:39,load:[6,25,39,43,47,48,62,64],local:[4,18,26,39,62],locat:[27,28,39,53,63],location_of_gwsetup_dir:64,location_of_hd_dir:64,lock:[4,25,32,38,39],lockf:60,log:[6,36,39,53,60,62],log_level:39,logic:[0,16,26,27],login:39,login_tmout:39,longer:5,longest:39,look:36,loop:[2,62],lot:16,lowercas:36,lui:21,lunar:60,machin:62,made:[6,25],magic:[2,25,63],magic_gwb:63,mai:[2,11,16,19,22,27,39],main:[5,11,39,51,58,60,61,63,64,65],mainli:[63,65],maintain:[5,11,39,64],mainten:[5,11,27,39],major:[53,60],make:[2,4,5,11,25,26,28,29,53,60,62,63,64,65],make_subarrai:2,makefil:[],maker:62,malici:60,manag:[26,28,60],mandatori:40,mani:[26,28,60,64,65],manipualt:16,manipul:[16,26,43,50,60,63,64],manner:[5,11,16],manual:[4,25],map:60,map_couple_p:26,mark:62,marker:62,markup:[11,62],marri:36,marriag:[4,39,41,63],marshal:[62,63],match:[2,11,12,13,25,36,39,62,65],match_str:5,matter:11,max:39,max_client:39,maximum:[37,44],md5:[5,39],mean:[18,22,25,26,39,63],meant:11,mem:[32,37,38,40,44],memori:[4,16,20,25,32,37,38,40,41,44],menhir:4,mention:[62,63],menu:39,merg:[38,39,51],messag:[5,11,39,62],meta:[62,64],metadata:39,method:[3,16,53],min_disp_req:39,min_person:4,mind:64,minim:26,minimum:39,minor:20,minu:36,misc_not:39,misc_notes_search:39,miscellan:64,miss:[16,18,41,62],mission:65,mix:[60,63],mk_data:62,mk_gedpluginmd5:11,mk_gwdpluginmd5:[5,11,39,62],mli:[5,23,62,63,65],mll:62,mod:39,mod_data:39,mod_data_ok:39,mod_fam:39,mod_fam_ok:39,mod_ind:39,mod_ind_ok:39,mod_not:39,mod_notes_ok:39,mod_wiznot:39,mod_wiznotes_ok:39,mode:[22,25,32,39,41,52,53,62],modif:[22,24,39,64],modifi:[7,8,16,25,62,63],modifif:16,modul:[0,4,5,11,20,22,23,24,25,26,28,34,53,60,62,63,64,65],modules_without_implement:23,modulo:[39,63],month:[36,39],moon:60,mor:64,more:[0,4,5,6,8,11,16,22,26,32,39,40,41,60,62,63],most:[4,26,65],mother:60,move:[5,16,23,26,27],mrg:39,mrg_dup:39,mrg_dup_fam_y_n:39,mrg_dup_ind_y_n:39,mrg_fam:39,mrg_fam_ok:39,mrg_ind:39,mrg_ind_ok:39,mrg_mod_fam_ok:39,mrg_mod_ind_ok:39,msdo:36,much:[4,8,16,28],multi:26,multi_coupl:23,multi_par:23,multicor:25,multipl:[11,29,37,39,44,53,63],must:[16,24,37,39,44],mutabl:5,mutil:[4,27,53],my_plugin:[5,11],name:[0,2,4,11,18,22,25,27,31,36,37,39,44,53,62,63,64,65],namespac:4,nativ:62,natur:64,navig:22,nb_person:[25,63],nbd:41,necess:4,necessari:[11,43],need:[18,22,23,41,62],neg:36,neutr:4,never:[4,5,16],next:[38,63],next_family_fun_templ:4,nnn:[37,44],no_consang:0,no_efn:36,no_epn:[2,36],no_host_address:39,no_index:[46,51,62],no_nd:36,no_pit:36,nobil:36,node:62,nofail:38,nolock:[32,38,39],noment:36,nomin:[4,26],non:[5,11,53,62,65],none:[25,53,63],nopictur:[36,37,38,44],nor:[9,16],normal:[5,31,52,62],normalize_utf_8:26,note:[4,19,36,37,38,39,44,62,63],notes_d:63,noteslink:53,noth:28,notic:16,now:[1,5,11],nowadai:[4,62],nth:25,num:[37,39,44,62],number:[0,8,10,16,20,26,31,36,38,39,52,53,60,63,64],obj:[11,23,25],object:[38,62],obtain:62,ocaml:[4,5,11,16,17,23,25,43,51,60,62,64],ocamllex:62,ocamlpro:[64,65],ocamlrun:62,occ:[37,40,44],occupi:25,occur:[4,22,25,26,62],occurr:62,ocp:16,odir:44,odoc:[18,61,65],offici:[46,61,65],offset:63,often:[5,22,23,25],okasaki:60,old:[1,22,25,26,56],old_branch_of_sosa:22,old_gw:44,oldest:[22,39],onc:[5,8,16,20],one:[1,11,16,18,22,25,26,36,41,60,62,63,64,65],ones:[56,63],onli:[5,11,13,18,21,22,23,25,26,31,38,39,41,42,44,45,47,48,52,53,62,63,64],onlin:[11,32],oooooooooooooooooooooooooooooo:60,opam:[5,11,16,17,27,62],open:[9,11,14,25,62],opendb:25,oper:[4,60,63],opt:[37,44],optim:62,option:[1,31,32,33,36,37,38,39,40,41,42,44,52,53,60,62],order:[2,22,26,39,62,63],ordinar:62,org:[12,44],origin:[31,44,63,64],os_d:62,os_typ:62,other:[4,5,6,22,24,25,39,41,45,60,62,63],otherwis:64,ounit:[53,62],our:[16,50],out:[26,62],out_dir:11,outbas:[16,25],output:[11,16,25,29,31,36,37,38,39,44,53,60],output_arrai:25,output_fil:53,output_html_dir:53,output_junit_fil:53,outsid:5,ovenst:53,over:[18,20,60,62,63,65],overrid:36,overview:[61,65],own:[5,16,51],pa_extend:62,packag:[18,26,45,50,60,62],page:[11,16,22,30,39,58,63,65],pair:[37,44,64],paramet:62,parent:[4,22,26,39,41,60],parentship:[37,44],pari:22,pars:[2,4,5,16,60,62],parse_par:4,parser:[2,65],part:[5,16,25,36,51,64,65],particl:[26,36,38,63],particular:16,particulari:[60,62],particularli:62,pas:[19,21],pass:[4,24,62,63],passign:6,passwd:39,password:39,patch:[25,41,63,64],patch_:25,patch_person:25,path:[26,37,44,53,62],pattern:[11,12,39,62,65],pend:[25,63],per:39,perform:[4,5,6,8,16,20,25,27,39,62,63],performan:25,period:53,perman:39,person:[4,11,20,22,24,25,36,37,38,39,40,41,44,60,63,64],person_hash:4,persons_of_nam:25,pevent:41,pgocaml:16,phase:[60,62],pictur:[36,37,38,44],piec:20,pierr:[22,63],place:[5,22,26,39,53,63],platform:35,plu:39,plug:[5,11,46,50,61,65],plugin1:64,plugin2:64,plugin:[15,18,19,21,26,39,45,47,48,57,58,64],plugin_cgl:62,plugin_export:62,plugin_fixbas:62,plugin_forum:62,plugin_gwxjg:62,plugin_gwxjg_lib:62,plugin_jingoo:62,plugin_lib_show:62,plugin_no_index:62,plugin_v7:62,plugin_v7_im:62,plugin_v7_im_lib:62,plugin_v7_lib:62,plugin_welcom:62,point:[4,5],pointer:63,polymorph:[4,11,60,65],popul:39,popular:[36,37],port:[11,31,39,52,64],portal:[52,64],posit:[26,63],possibl:[5,10,11,16,36,39,44],post:39,pour:21,pps:62,ppx:62,ppx_blob:62,ppx_deriv:62,ppx_import:62,pqp_pyr:39,pr_o:62,practic:[4,16],pre:39,preced:[18,36],predefin:38,prefer:[16,25],prefix:[2,25,62],preproces:[],preprocess:62,preprocesso:62,preprocessor:62,prerequisit:62,present:[1,16,25,44],preserv:[0,25],pretti:[5,62],prevent:[6,28,60],previou:[2,25,26,36,39,53,63],previous:27,primari:11,prime:63,primit:11,princip:62,print:[2,4,5,11,22,25,36,38,39,41,42,60,62],print_str:16,printend:22,printer:[53,65],printing_st:28,prioriti:60,privat:62,probabl:[5,25,39],procedur:[16,17,61,65],process:[14,25,28,53],processor:39,produc:[31,62],profess:39,profil:[18,62],program:[4,5,25],progrbar:27,progress:60,project:[5,11,15,16,18,27,39,53,61,64,65],project_root:62,prone:16,properli:26,properti:26,proport:60,protect:5,provid:[0,5,22,26,27,37,44,45,50,60,62,63,64,65],psql:16,publicli:[16,27],pure:[60,65],purpos:[10,16,25,51,62],put:[10,36],pute:60,pyramid:39,q_mlast:62,qualifi:36,que:21,questionn:18,queue:60,quick:39,quiet:[32,38,41],quit:[25,63],rais:[4,5,16,25,26],random:[8,16],rather:[25,62],raw:[22,44],reach:[8,16],reachabl:39,read:[4,5,25,53,60,63,64],read_famili:4,read_family_0:4,read_lin:9,read_or_create_channel:26,readibl:10,readm:40,readthedoc:[],real:[25,62,63],reason:[4,16,60],rebuild:41,recomend:26,recommand:[16,17],recommend:[5,26,50,65],recompact:[],reconnect:44,record:[5,11,16,22,62,63],record_access:25,recurs:[0,4,11,26,60,62,65],redefin:[5,27,56,60],redefinit:11,redirect:[36,39],reduc:10,redund:[18,26],refactor:16,refer:[5,6,11,16,22,37,44,63],referenc:22,refin:4,regist:[30,33,34,35,39,56,57,58],register_plugin:5,register_s:39,regress:53,regroup:[60,62],regularli:27,reimplement:4,rel:[22,39],relat:[4,5,16,27,36,39],relationdisplai:19,relationship:[4,39,63],relativesdisplai:22,releas:[18,62],relev:13,remot:62,remov:[5,11,22,25,26,30,36,38,39,42,62,63,64],remove_dir:26,remplac:[4,22,62],renam:[11,22,25],rename:[],repetit:[5,11],replac:[0,5,6,10,11,16,26,28,30,37,44,59,62,63,64],repo:[],report:40,repositori:[5,11,51],reprent:0,repres:[4,60,64],represent:[23,25,26,60,63],request:[11,16,24,25,33,34,35,50,56,57,58,59,62,63,64],requir:[6,18,20,27,62,64],requrst:56,reset:53,respect:2,respons:[5,22,62,63],rest:39,restrict:[25,39,63],result:[16,25,26,39,53,62,65],rethink:16,retourn:19,revers:39,revis:[25,39,62],rework:65,rewrit:[32,62],rewritten:63,right:[5,11,62],risk:26,rlm:39,rm_rf:26,robot:[11,16,62],robot_error:[5,16],robot_excl:5,robot_xcl:39,roman:[26,36],root:[5,37,39,40,44,62],round:5,rs_no_ment:36,rule:[18,62],run:[41,42,43,53,60,62],runbench:62,runner:53,runtest:62,safe:[25,39],safer:[2,3,16],sai:[39,63],said:27,saint:26,same:[1,3,4,5,11,22,25,26,39,44,62,63,65],sampl:2,save:[8,16,32,37,38,40,44],scatter:11,scheme:39,scope:62,scratch:32,screen:[53,62],scrip_arg1:43,script:[5,9,11,16,43,51,62,64],sdn:60,seam:[22,24],search:[22,39,58,63,65],search_file_opt:26,searching_field:22,sec:39,second:[4,39,53],section:[16,62],secur:[27,39,65],sed:62,see:[2,40,44,62,63,64],seem:[0,11,23],select:[10,22,37,39,44,52,53,62,64],send:[22,39],sens:22,sep:[4,38,44],sep_limit:44,sep_only_fil:44,separ:[5,11,22,26,27,38,39,44,65],sequenti:53,serial:60,serv:5,server:[5,11,28,31,35,39,50,60,61,62,65],servic:[30,39],set:[4,5,10,18,25,36,37,38,39,44,62],sete:26,setter:11,setup:[46,50,62,64],setup_link:39,sever:[28,36,37,39,44,63,64],sex:4,shadow:62,shard:53,shard_id:53,share:[16,44],shift:38,shim:62,ship:39,should:[3,4,5,6,8,11,14,15,22,25,26,27,28,62,65],shouldn:26,show:[60,62],side:[5,60],sign:36,signatur:60,signifi:[25,63],similar:[11,16,60],simpl:[5,11,16,53,60],simpler:[2,6,28],simpli:[6,16],simplif:[11,16],simplifi:[4,8,11,16,18,27,42],simultan:60,sinc:[4,22,23,25,26,39],sindex:63,singl:[4,11,26,27],size:[20,26,31,44],skip:26,slightli:[27,63],slower:[4,32,37,38,40,44],sname:63,snd_imag:57,snd_image_ok:57,socket:[22,39],softwar:11,solv:5,some:[2,4,5,6,9,11,18,20,22,25,26,27,56,60,61,62,63,64],someon:[5,11,39],someth:22,sometim:[5,8,16,22,60],somewher:[],sosa:[53,62,64],sosa_arrai:[62,64],sosa_num:[62,64],sosa_pkg:62,sosa_zarith:[62,64],sourc:[9,16,36,37,38,39,44,63,65],souvent:19,space:[16,20,22,25,37,40,44],span:30,special_var:5,specif:[5,11,13,18,39,65],specifi:[5,18,31,39,52,62],spous:[24,37,44],src:[37,39,44,62],src_oc_ht:10,srcfiledisplai:5,stabl:[],stage:[4,25,60],staged_pp:62,stai:26,standard:[0,2,5,11,16,44,60],stanza:[18,23,62],start:[5,9,26,40,43,53,60,61,62,65],stat:[38,39],state:[11,18],statement:[5,62],statist:[4,31,38,39],statu:[28,36],stderr:[39,62],stdlib:[26,60,62],stdout:[25,37,39,44],step:16,still:[4,11,16,22,25,26,27,39],sting:26,stop:[4,53],storag:[25,65],store:[4,53,60,62,63,64],str:[26,38,53,62],stradonitz:64,straightforwardli:5,strang:11,strictli:31,string:[3,4,8,10,13,16,25,26,31,39,52,60,63,64],string_of_list:3,stringutil:16,strip:62,strong:[5,11],strongli:[5,11],structur:[4,16,27,45,63],sturctur:[],style:[53,65],sub:[26,63],subdirectori:62,submodul:16,subpart:[],subsect:63,subsequ:62,subset:11,succes:26,sucess:60,suffer:16,suffix:62,suggest:27,suit:[53,62],suite_nam:53,sum:[22,39],support:[26,60],suppos:36,suppres:[],suppress:63,sure:5,surnam:[26,36,37,39,44,60,63],surname_piec:26,surnnam:63,sustain:11,symbol:62,sync:16,synchro_famili:25,synchro_patch:63,synchro_person:25,synchron:32,synchronis:[4,25],syntax:62,syslog:[39,62],syslog_d:62,syslog_pkg:62,system:[5,11,16,17,23,25,26,45,60,61,65],sytax:4,tabl:[8,10,16,25,60,62,63],table_s:63,tabul:62,tag:[36,39],tail:[0,4,11,26,65],take:[22,25,60],taken:[25,64],target:[18,40,64],task:[5,11,16],team:[5,11],tell:[18,44,62],templ:[19,22],templ_pars:62,templast:62,templat:[22,39],temporari:4,term:[0,65],termin:62,test:[6,12,16,29,46,61,65],test_plac:53,test_sosa:53,test_util:53,test_wiki:53,testdata:53,testdata_dir:53,text:[39,63],thai:39,than:[5,6,11,16,37,39,44,62],thei:[2,11,15,16,25,26,36,39,50,51,64],them:[6,11,16,18,25,26,30,39,44,60,62],thi:[0,1,3,4,5,8,11,15,16,18,20,22,23,24,25,26,27,28,31,36,37,39,44,45,47,48,58,60,61,62,63,64,65],thing:5,those:[25,62,63],though:0,thread:53,three:[39,64],through:[5,11,14,64],throughout:[20,26,60],tidi:27,time:[5,8,11,16,20,37,39,44,60,62,63],timeout:39,timestamp:[25,63],titl:[5,16,36,39],tnd:36,todai:[11,39],todo:21,togeth:44,tool:[5,11,45,62,63,64,65],top:[9,43,64],toplevel:[4,62],total:[25,60],trace:[36,39],trace_failed_passwd:39,trackid:36,transform:13,translat:45,transmit:63,travi:53,treat:[22,39],treat_request:5,treatrd:[],tree:[6,39,62,64],tri:5,trim:4,troubl:2,truc:19,tsort:5,tupl:11,turn:65,tuxfamili:44,twice:[5,6],two:[2,20,34,39,40,60,62,64,65],txt:[39,60,62,63],type:[3,11,13,16,22,23,25,45,59,60,63,64],udi:36,uin:36,unaccent_utf_8:26,unclear:24,undefin:36,under:62,understand:[5,7,11,22],undocu:39,unidecod:[26,60,62],unifi:11,uninstal:62,union:[37,44,60,63],unit:[4,5],unix:[26,39,52,60,62],unknown:39,unless:[37,44],unlog:11,unnecessari:4,unsaf:[5,39,65],unsafe_of_str:26,until:[25,63],untreat:36,unus:[4,8,11,16,22,25,26,42,62,64],unverifi:39,unwant:26,updat:[3,5,11,12,15,25,27,39,51,62,63,64,65],update_nldb:[46,50,62],updhist_diff:39,updmenu:39,uppercas:36,url:39,usabl:[5,11,37,44],usag:[4,29,31,32,36,37,38,39,40,41,42,44,52,53,62],use:[2,4,5,11,16,18,23,25,26,32,39,43,44,45,53,58,60,62],used:[0,2,4,5,8,11,15,16,18,21,22,25,26,37,39,40,44,60,62,64],useful:[9,60,63,64],useless:11,user:[5,25,31,39,51,52,60],uses:[4,5,10,18,22,26,60,62],using:[0,4,5,11,25,26,28,44,60,62,65],usual:36,utf8:[27,41],utf:[11,37,44,53,60],utf_8_of_iso_8859_1:26,util:[11,24,27,34,39,50,53,62,64,65],utilitarian:64,uucp:62,uunf:62,uutf:62,v7_im:[46,51,62],v7_im_sendimag:62,v7_templ:19,valid:39,valu:[7,23,25,31,52,53,60,62,63,65],variabl:[4,5,16,22,25,39],variant:[5,11,65],variou:60,verbos:[4,37,38,44,53],veri:[4,22,25,32,41,60],verifi:26,version:[0,1,5,11,16,17,25,26,39,62,64],via:18,view:39,view_wiznot:39,virtual:[62,63,64],visibl:[25,63],w_lock:5,w_wizard:5,wai:[5,16,25],wait:53,want:11,warn:[0,5,16,22,45,47,48,62],web:[5,11,16,28,35,39,50,51,61,65],webpag:[],webserv:60,websit:2,weight:64,welcom:[39,46,51,62],well:[11,16,27,28,62],were:[5,25,51],werver:51,what:[0,5,16],when:[0,2,4,6,8,9,11,16,22,24,25,26,28,32,36,37,39,44,62,63],where:[5,15,25,26,36,38,39,52,62,63,64],which:[0,2,5,9,11,16,25,26,39,60,62,64],who:39,whole:[4,5,16,18],whose:[4,5,16,31,36,39,62,63],why:5,wide:62,wiki:[0,4,44,65],win:62,window:[26,39,62],wit:[4,41],with_logo:22,without:[5,11,22,24,25,26,36,38,44,62,63],wizard:[5,39],wiznot:39,wiznotes_search:39,wiznotesdisplai:5,wjf:39,won:[25,26],word:36,work:[5,11,16,26,28,44],worker:53,workspac:18,world:39,worst:60,would:[2,3,5,6,10,11,16,27],wrap:[11,16,62,64],write:[4,5,11,25,39,43,60,64],written:[16,39,63],wrong:2,wrongli:2,wserver:[16,27,39,52,62,64],wserver_oc:28,www:12,xhtml:[12,46,51],xml:59,year:[22,36,37,44],yet:[5,6],yml:65,you:[11,22,63,64],zarith:[62,64]},titles:["connex","consang","ged2gwb","gwb2ged","gwc","gwd","gwdiff","gwfixbase","gwgc","gwrepl","gwu","Binaries","cgl","export","forum","v7","Audit","<no title>","Installation procedure recommandations","Config module","Consang","Fixbase","Geneweb library audit","Geneweb_def library audit","Geneweb_gwdb library audit","Geneweb_gwdb-legacy library audit","Geneweb_util library audit","Libraries","Wserver","bench","cgl","connex","consang","export","fixbase","forum","ged2gwb","gwb2ged","gwc","gwd","gwdiff","fixbase","gwgc","gwrepl","gwu","gwxjg","Project binaries","jingoo","lib_show","no_index","Official binaries","Plug-ins","setup","test","Tests","update_nldb","v7","v7_im","welcome","xhtml","Geneweb library overview","Developer documentation","Installation procedure","Database overview","Overview","Welcome to Geneweb Audit\u2019s Documentation!"],titleterms:{"export":[5,13,33],"function":[0,2,5,6,7,10,15,16],"int":5,Adding:16,The:27,Use:[2,10,16,21,27],Using:6,access:11,addit:62,adef:[23,60],advsearchok:22,api:5,architectur:64,argument:[2,5,6,7,10,16],artifact:18,assert:5,audit:[16,22,23,24,25,26,65],authentif:5,avoid:16,bad:5,base:6,befor:5,begin:5,bench:29,benchmark:62,between:27,big:2,binari:[11,46,50,64],birthdeath:22,block:28,btree:60,buff:[26,60],bug:28,build:[18,62],calendar:[26,60],cgl:[12,30],channel:28,check:5,close:28,code:16,collect:16,command:5,config:[19,27],configur:[18,62,64],connect:28,connex:[0,31],consang:[1,20,32],constant:[0,12],convert:5,custom:39,cycl:5,data:5,databas:63,date:60,db1link:4,def:[23,60],defin:11,depend:[5,18,27],descript:2,dev:[],develop:61,developp:[],differ:6,document:[0,1,5,8,31,32,36,37,38,39,40,41,42,43,44,61,62,65],doe:28,driver:25,dune:[18,62],each:11,endian:2,entri:63,env:5,error:[2,5],event:3,exampl:63,execut:[5,62],exhaust:[3,16],expect:2,fals:5,field:5,file:[1,5,16,18,62,63],fixbas:[21,34,41],fly:[8,16],forc:2,fork:28,format:[5,16],forum:[14,35],from:28,full:5,futil:[26,60],garbag:16,ged2gwb:[2,36],gedplugindep:[5,39],gener:[16,27],geneweb:[22,60,62,64,65],geneweb_def:23,geneweb_gwdb:[24,25],geneweb_util:26,get:11,global:16,gwb2ged:[3,37],gwc:[4,38],gwcomp:4,gwd:[5,39],gwdb:[24,25,60],gwdiff:[6,40],gwdlog:[5,39],gwdplugin:[5,39],gwdpluginmd5:39,gwdpluginmeta:[5,39],gwfixbas:7,gwgc:[8,42],gwrepl:[9,43],gwu:[10,44],gwxjg:45,handl:[6,28],hashtbl:10,header:16,how:64,html:16,html_n:12,inclus:14,inconsist:28,incorrect:18,indent:16,indic:65,input:2,ins:[39,51,64],instal:[18,62],instead:10,intern:5,introduct:[],jingoo:47,lck:5,legaci:[25,60],letter:16,level:5,lib:60,lib_show:48,librari:[2,16,22,23,24,25,26,27,60,62,64],littl:2,lock:[5,60],log:11,main:[10,62],makefil:[18,62],manag:[5,11],mani:16,markup:12,match:[3,5,16,28],messag:[2,6],messi:28,meta:5,method:5,metric:11,mk_gedpluginmd5:5,mli:16,modif:63,modul:[12,14,16,19,21,27,39],mutil:[26,60],name:[1,5,16,26,60],nbase:5,no_index:49,non:[3,16],note:[5,11],obj:2,offici:[50,64],one:2,onli:2,onlin:[0,1],opam:18,open:4,opt:60,option:[2,4,5,9,10,11,18],our:[36,41,44],out:28,out_dir:10,over:5,overview:[60,63,64],page:5,parser:16,pattern:[3,5,16,28],plug:[39,51,64],plugin:[5,11,62],polymorph:[13,16],pqueue:60,primari:3,primit:3,print:28,printer:16,procedur:[18,62],progrbar:[26,60],project:[46,62],pure:16,recommand:18,recommend:[11,16,27,36,41,44],record:10,recurs:[2,6,9,16],redefinit:3,refer:[7,15],remov:[4,10],repetit:3,replac:12,request:[5,39],rework:[16,27],robot:[5,39],same:[6,16],secur:[26,60],separ:16,server:64,setup:52,should:16,simplif:8,simplifi:5,special:5,specif:16,standard:3,start:64,state:28,storag:[16,63,64],strang:2,string:5,style:16,suggest:28,system:[18,62],tabl:65,tail:[6,9,16],target:62,test:[53,54,62],thei:7,tool:16,tupl:10,two:16,type:[4,5,10,27],undocu:5,uniform:11,unknown:5,unlog:6,unsaf:16,unus:[0,2,5,6,10],updat:[2,7,16],update_nldb:55,usag:11,use:7,useless:13,utf8:[26,60],utf:2,util:[5,16,60],v7_im:57,valu:[2,5,10,11,16],variabl:62,variant:[13,16],version:18,view:5,w_base:5,warn:18,web:64,welcom:[58,65],when:5,who:5,workspac:62,wrap:2,write:28,wrong:5,wserver:28,xhtml:59}}) \ No newline at end of file diff --git a/lib/GWPARAM.ml b/lib/GWPARAM.ml index 1991ac58dc..93bc58e7b8 100644 --- a/lib/GWPARAM.ml +++ b/lib/GWPARAM.ml @@ -23,9 +23,9 @@ module Default = struct Secure.add_assets Filename.current_dir_name let base_path = fun pref bname -> - List.fold_right Filename.concat (Secure.bd () :: pref) bname + List.fold_right Filename.concat (Secure.base_dir () :: pref) bname - let bpath = fun bname -> Filename.concat (Secure.bd ()) bname + let bpath = fun bname -> Filename.concat (Secure.base_dir ()) bname (** [output_error ?headers ?content conf code] Send the http status [code], [headers] and diff --git a/lib/GWPARAM.mli b/lib/GWPARAM.mli new file mode 100644 index 0000000000..b475e68b4d --- /dev/null +++ b/lib/GWPARAM.mli @@ -0,0 +1,53 @@ +(** The level of log gravity (`LOG_WARNING the lightest). *) +type syslog_level = + [ `LOG_ALERT + | `LOG_CRIT + | `LOG_DEBUG + | `LOG_EMERG + | `LOG_ERR + | `LOG_INFO + | `LOG_NOTICE + | `LOG_WARNING ] + +(** Inititialise assets for gwd server (one in current directory one in /usr/share/geneweb) *) +val init : (unit -> unit) ref + +(** [!base_path pref fname] default function that returns path to [fname] inside base directory where [pref] + is a list of subdirectories between base directory and [fname]. *) +val base_path : (string list -> string -> string) ref + +(** [!bpath fname] default function that returns path to [fname] inside base directory *) +val bpath : (string -> string) ref + +(** [!output_error ?headers ?content conf status] default function that send the http status [status], [headers] and + [content] if provided. Otherwise send default content from {/etc/-.html} *) +val output_error : + (?headers:string list -> + ?content:string -> Config.config -> Def.httpStatus -> unit) + ref + +(** Calculate the access rights to the person's information in + according to his age. + Returns (in the order of the tests) : + - True if : requester is wizard or friend or person is public + - True if : person has at least one title and {i public_if_title} + is set to {i yes} in gwf config file + - False if : person is alive and {i private_years} > 0 + - True if : person is older (depending on the date of + birth or baptism date) then {i privates_years} + - False if : person is younger (depending on the date of + birth or baptism date) then {i privates_years} + - True if : person has been deceased for more than {i privates_years} + - False if : person has been deceased for less than {i privates_years} + - True if : person is between 80 and 120 years old and he is not beeing + private and {i public_if_no_date} is set to {i yes} in + gwf config file + - True if : person has been married for more than {i private_years} + - False otherwise *) +val p_auth : (Config.config -> Gwdb.base -> Gwdb.person -> bool) ref + +(** [!syslog level log] log message [log] with gravity level [level] on stderr. *) +val syslog : (syslog_level -> string -> unit) ref + +val wrap_output : + (Config.config -> string -> (unit -> unit) -> unit) ref \ No newline at end of file diff --git a/lib/advSearchOk.ml b/lib/advSearchOk.ml index 27daad7f45..866bdb3a4c 100644 --- a/lib/advSearchOk.ml +++ b/lib/advSearchOk.ml @@ -442,10 +442,6 @@ let advanced_search conf base max_answers = in List.rev list, len -(* - Returns a description string for the current advanced search results in the correct language. - e.g. "Search all Pierre, born in Paris, died in Paris" -*) let searching_fields conf base = let test_date x = reconstitute_date conf (x ^ "1") <> None diff --git a/lib/advSearchOk.mli b/lib/advSearchOk.mli new file mode 100644 index 0000000000..48f5900d7a --- /dev/null +++ b/lib/advSearchOk.mli @@ -0,0 +1,10 @@ + +(** [advanced_search conf base max_answers] extracts advaced request fields from environement [conf.env] and + returns at most [max_answers] persons from the [base] that match conditions described by those fields. Seond result + represents real number of matches (if less then [max_answers]). *) +val advanced_search : + Config.config -> Gwdb.base -> int -> Gwdb.person list * int + +(** Returns a description string for the current advanced search results in the correct language. + e.g. "Search all Pierre, born in Paris, died in Paris" *) +val searching_fields : Config.config -> Gwdb.base -> string \ No newline at end of file diff --git a/lib/advSearchOkDisplay.mli b/lib/advSearchOkDisplay.mli new file mode 100644 index 0000000000..39c6ef750f --- /dev/null +++ b/lib/advSearchOkDisplay.mli @@ -0,0 +1,3 @@ + +(** Displays the results of an advanced search *) +val print : Config.config -> Gwdb.base -> unit diff --git a/lib/alln.ml b/lib/alln.ml index f39eda7e3c..663f4f74d0 100644 --- a/lib/alln.ml +++ b/lib/alln.ml @@ -33,12 +33,6 @@ let first_letters base is_surnames = in loop (spi_first iii "") [] with Not_found -> [] -(** [select_names conf base is_surnames ini limit] - Select up to [limit] first names/surnames starting with [ini]. - If more values are available, return [Specify] with different - possible prefixes. - Otherwise, return the list of values as (key * text * person count). - *) let select_names conf base is_surnames ini limit = let inilen = Utf8.length ini + 1 in let cut k = Utf8.sub k 0 (min (Utf8.length k) inilen) in diff --git a/lib/alln.mli b/lib/alln.mli new file mode 100644 index 0000000000..2abb2b49e7 --- /dev/null +++ b/lib/alln.mli @@ -0,0 +1,36 @@ +(** Default number of names that could be printed simultaneously on the page *) +val default_max_cnt : int + +(** Type that represents result of name selection *) +type t = + | Result of (string * string * int) list (** Exhaustive result with the list of names + (key and printable name) with number of + persons that have giving name*) + | Specify of string list (** Not exhaustive result that specifies all existing names + prefixes (their length depends on initial searched prefix) *) + +(** Returns list of all first name's first letter present in the base (UTF8 encoded). + Used for fast access for base's names *) +val first_letters : Gwdb.base -> bool -> string list + +(** [select_names conf base is_surnames ini limit] + Select up to [limit] first names/surnames starting with [ini]. + If more values are available, return [Specify] with different + possible prefixes with the length at most equal to the length of [ini]+1 + (for empty [ini] specifies all first letters of existing names). + Otherwise, return the list of values [Result] with all first names + and number of persons that have giving name. *) +val select_names : + Config.config -> Gwdb.base -> bool -> string -> int -> t * int + +(** Returns prefix of length [len] of UTF8 encoded name *) +val ini : int -> string -> string + +(** [groupby_ini len results] returns alphabeticaly ordered list of grouped by name prefix (with length [len]) + results. *) +val groupby_ini : + int -> (string * 'a * 'b) list -> (string * ('a * 'b) list) list + +(** Returns ordered (from bigest to smallest) list of grouped by name frequency (number of persons + having the name) results. Shouldn't be used when results are represented with [Specify]. *) +val groupby_count : t -> (int * string list) list \ No newline at end of file diff --git a/lib/allnDisplay.mli b/lib/allnDisplay.mli new file mode 100644 index 0000000000..5e6553037d --- /dev/null +++ b/lib/allnDisplay.mli @@ -0,0 +1,13 @@ +(** Displays all persons surnames present in the base. Display could be different depending + on environement [conf.env]. These variables affect the display: + + - tri : "F" to display surnames by frequency, "S" to display + surnames regrouped by first letter (depends on variable "k") + otherwsise display surnames just ordered alphabeticaly + - k : Defines common prefix for surnames (empty for all) + - o : "A" to print all surnames (if less then [Alln.default_max_cnt]) + otherwise prints links to access different type of displaying *) +val print_surnames : Config.config -> Gwdb.base -> unit + +(** Same as [print_surnames] but dealing with first names. *) +val print_first_names : Config.config -> Gwdb.base -> unit \ No newline at end of file diff --git a/lib/ansel.mli b/lib/ansel.mli new file mode 100644 index 0000000000..98ddac176e --- /dev/null +++ b/lib/ansel.mli @@ -0,0 +1,6 @@ + +(** Convert ISO-8859-1 encoded string to ANSEL encoding used inside gedcom files *) +val of_iso_8859_1 : string -> string + +(** Convert ANSEL used inside gedcom files to ISO-8859-1 encoding *) +val to_iso_8859_1 : string -> string \ No newline at end of file diff --git a/lib/base64.mli b/lib/base64.mli new file mode 100644 index 0000000000..e8e7a5c2b4 --- /dev/null +++ b/lib/base64.mli @@ -0,0 +1,3 @@ + +(** Decode {i Base64} binary-to-text encoding used at the moment of basic autorization *) +val decode : string -> string diff --git a/lib/birthDeath.mli b/lib/birthDeath.mli new file mode 100644 index 0000000000..0d60c61f63 --- /dev/null +++ b/lib/birthDeath.mli @@ -0,0 +1,43 @@ + + +(** [select_person conf base get_date find_oldest] select 20 persons + from the base according to the one of their date (birth, death, + marriage, specific event, etc.) that could be get with [get_date]. + Returns sorted by date persons that have the latest (if [find_oldest] + is false) or oldest (otherwise) date. Selection could be different depending + on environement [conf.env]. These variables affect the selection: + k - allows to modify default value (20) of selected persons + by,bm,bd - allows to set reference date (all dates after the reference + one aren't selected) + Returns also the number of selected persons *) +val select_person : + Config.config -> + Gwdb.base -> + (Gwdb.person -> Def.date option) -> + bool -> (Gwdb.person * Def.dmy * Def.calendar) list * int + + +(** Same as [select_person] but dealing with families *) +val select_family : + Config.config -> + Gwdb.base -> + (Gwdb.family -> Def.date option) -> + bool -> (Gwdb.family * Def.dmy * Def.calendar) list * int + +(** Returns person's death date (if exists) *) +val death_date : Gwdb.person -> Adef.date option + +(** [make_population_pyramid nb_intervals interval interval at_date conf base] + Calculates population pyramid of all perons in the base. Population pyramid + consists of two separated arrays that regroups number of men's and women's born + in each time interval. One array has a size [nb_intervals + 1] and every element + is a number of persons born in the giving time interval that represents [interval] years. + Calculation starts at the date [at_date] and persons that are considered + in pyramid should be alive at this date. [limit] allows to limit persons + by age (those that has age greater then limit aren't taken into the account) *) +val make_population_pyramid : + nb_intervals:int -> + interval:int -> + limit:int -> + at_date:Def.dmy -> + Config.config -> Gwdb.base -> int array * int array \ No newline at end of file diff --git a/lib/birthDeathDisplay.mli b/lib/birthDeathDisplay.mli new file mode 100644 index 0000000000..3b2ea9ac82 --- /dev/null +++ b/lib/birthDeathDisplay.mli @@ -0,0 +1,27 @@ + +(** Lists the last births *) +val print_birth : Config.config -> Gwdb.base -> unit + +(** Lists the last deaths *) +val print_death : Config.config -> Gwdb.base -> unit + +(** Lists the persons who lived the longest *) +val print_longest_lived : Config.config -> Gwdb.base -> unit + +(** Displays the list of the oldest persons that are still alive or, if unknown, + whose death are not probable *) +val print_oldest_alive : Config.config -> Gwdb.base -> unit + +(** Lists the last marriages *) +val print_marriage : Config.config -> Gwdb.base -> unit + +(** Displays the list of the oldest couples that still exist *) +val print_oldest_engagements : Config.config -> Gwdb.base -> unit + +(** Displays several links for statistics: latest births, death, marriages, the + oldest couples, persons that are alive and who lived the longest, as well as + a population pyramid *) +val print_statistics : Config.config -> unit + +(** Displays a population pyramid from the base data *) +val print_population_pyramid : Config.config -> Gwdb.base -> unit diff --git a/lib/birthdayDisplay.mli b/lib/birthdayDisplay.mli new file mode 100644 index 0000000000..58d67c9257 --- /dev/null +++ b/lib/birthdayDisplay.mli @@ -0,0 +1,66 @@ + +(** [gen_print conf base month (next,txt_of) dead_people] displays anniversaries for a given month separated by day. + If [dead_people] is true then displays birth/death anniversaries for dead people with death reason. + Otherwise displays birthdays for alive people. [next] is function that returns next person from iterator and [txt_of] + text/link that describes person's information *) +val gen_print : + Config.config -> + Gwdb.base -> + int -> + (unit -> + Gwdb.person * + (Config.config -> Gwdb.base -> Gwdb.person -> string)) -> + bool -> unit + +(** Displays birthdays for alive people for a given month *) +val print_birth : Config.config -> Gwdb.base -> int -> unit + +(** Displays anniversaries for dead people for a given month *) +val print_dead : Config.config -> Gwdb.base -> int -> unit + +(** Displays marriage anniversaries for a given month *) +val print_marriage : Config.config -> Gwdb.base -> int -> unit + +(** [gen_print_menu_birth conf base (next,txt_of) mode] displays the main birthdays menu for alive people + that contains: + - Persons that has their birthdays today + - Persons that has their birthdays tomorrow + - Persons that has their birthdays after today + - Form to select the month of birthdays we want to see. + [next] is function that returns next person from iterator, [txt_of] text/link that + describes person's information and [mode] that add some additional hidden inputs in the month form *) +val gen_print_menu_birth : + Config.config -> + Gwdb.base -> + (unit -> + Gwdb.person * + (Config.config -> Gwdb.base -> Gwdb.person -> string)) -> + (unit -> 'a) -> unit + +(** Displays the main birthdays menu considering all alive people *) +val print_menu_birth : Config.config -> Gwdb.base -> unit + +(** [gen_print_menu_dead conf base (next,txt_of) mode] displays the main anniversaries menu for dead people + that contains: + - Persons that has their anniversaries today + - Persons that has their anniversaries tomorrow + - Persons that has their anniversaries after today + - Form to select the month of anniversaries we want to see. + [next] is function that returns next person from iterator, [txt_of] text/link that + describes person's information and [mode] that add some additional hidden inputs in the month form *) +val gen_print_menu_dead : + Config.config -> + Gwdb.base -> + (unit -> + Gwdb.person * + (Config.config -> Gwdb.base -> Gwdb.person -> string)) -> + (unit -> 'a) -> unit + +(** Displays the main anniversaries menu considering all dead people *) +val print_menu_dead : Config.config -> Gwdb.base -> unit + +(** Displays the main wedding anniversaries menu *) +val print_menu_marriage : Config.config -> Gwdb.base -> unit + +(** Displays the menu of anniversaries selection *) +val print_anniversaries : Config.config -> unit diff --git a/lib/changeChildren.mli b/lib/changeChildren.mli new file mode 100644 index 0000000000..74431c6c9e --- /dev/null +++ b/lib/changeChildren.mli @@ -0,0 +1,26 @@ + +(** Returns digest (using md5 algorithm) of concatenated for every childran first name, surname and occurence number *) +val digest_children : Gwdb.base -> Gwdb.iper list -> string + +(** Checks if children digest in environement [conf.env] corresponds to specified digest. Other print error page. *) +val check_digest : Config.config -> string -> unit + +(** Exception raised when childran change defines a new childran information (new first name, new surname and new occurence number) are in + conflict with another person already existing in the base *) +exception ChangeChildrenConflict of Gwdb.person * Gwdb.person + +(** Exception raised when childran change removes it first name *) +exception FirstNameMissing of Gwdb.iper + +(** Change all person's children by looking up information to update inside [conf.env] that was send by the form. + Changes also childran's personal image name. Could raise [ChangeChildrenConflict] if new childran's key + is in conflict with another and [FirstNameMissing] if new childran's first name is empty. If surname modification is + requested but absent then childran takes parent's surname. Returns informations used by [Update] module to record + children's update operation. *) +val change_children : + Config.config -> + Gwdb.base -> + string -> + Gwdb.iper list -> + ((string * string * int * Gwdb.iper) * (string * string * int * Gwdb.iper)) + list \ No newline at end of file diff --git a/lib/changeChildrenDisplay.ml b/lib/changeChildrenDisplay.ml index 5b376f633f..ce72f980a5 100644 --- a/lib/changeChildrenDisplay.ml +++ b/lib/changeChildrenDisplay.ml @@ -148,7 +148,7 @@ let print_conflict conf base ip_var p = Hutil.rheader conf title; Update.print_error conf base @@ Update.UERR_already_defined (base, p, ""); let free_n = - Gutil.find_free_occ base (p_first_name base p) (p_surname base p) 0 + Gutil.find_free_occ base (p_first_name base p) (p_surname base p) in Output.print_string conf "
    \n"; Output.print_string conf "
  • "; diff --git a/lib/changeChildrenDisplay.mli b/lib/changeChildrenDisplay.mli new file mode 100644 index 0000000000..81e466171f --- /dev/null +++ b/lib/changeChildrenDisplay.mli @@ -0,0 +1,9 @@ + +(** Displays a form where all person's children with their first names, surnames and occurence numbers are listed + and could be modified on submit. Id of person should be mentionned in environement [conf.env] with binding {i "ip"=id} + otherwise displays Bad request page. *) +val print : Config.config -> Gwdb.base -> unit + +(** Performs and displays results of children modification requested by form submiting. If changes of one of children raises an error + displays corresponding error page that either just informs user about error source either propose to fix up solution. *) +val print_ok : Config.config -> Gwdb.base -> unit \ No newline at end of file diff --git a/lib/check.ml b/lib/check.ml index 44c2f57e57..02d703bb4e 100644 --- a/lib/check.ml +++ b/lib/check.ml @@ -227,6 +227,7 @@ let min_year_of p = let dummy_date = CheckInfered (CheckOther max_int) +(* check ad print warning if ancestors is born before person *) let rec check_ancestors base warning year year_tab ip ini_p = let infer = function | CheckBefore i -> CheckInfered (CheckBefore (pred i)) diff --git a/lib/check.mli b/lib/check.mli index 69343c20d1..45c92cb7a0 100644 --- a/lib/check.mli +++ b/lib/check.mli @@ -4,9 +4,15 @@ open Gwdb +(** Print database specification error on the giving channel *) val print_base_error : out_channel -> base -> CheckItem.base_error -> unit + +(** Print database specification warning on the giving channel *) val print_base_warning : out_channel -> base -> CheckItem.base_warning -> unit +(** [check_base base onwarning onerror _] makes full database proprety check. Checks every person and family separetely + with corresponding function inside [CheckItem] module. Checks also person's graph in order to find cycles (if person + is own ancestor). *) val check_base : ?verbose:bool -> ?mem:bool diff --git a/lib/checkItem.ml b/lib/checkItem.ml index 9027683d6f..a2eb12de42 100644 --- a/lib/checkItem.ml +++ b/lib/checkItem.ml @@ -644,8 +644,11 @@ let check_person_dates_as_witness base warning p = end related_pers let check_pevents base warning p = + (* check order of events *) check_order_pevents warning p ; + (* check person's witnesses *) check_witness_pevents base warning p; + (* check another witness dates where person is a witness *) check_person_dates_as_witness base warning p let check_siblings ?(onchange = true) base warning (ifam, fam) callback = @@ -785,31 +788,44 @@ let check_parent_marriage_age warning fam p = loop (get_fevents fam) let check_parents warning fam fath moth = + (* check father's marriage date *) check_parent_marriage_age warning fam fath ; + (* check mother's marriage date *) check_parent_marriage_age warning fam moth ; + (* check age difference between spouses *) check_difference_age_between_cpl warning fath moth (* main *) let person ?(onchange = true) base warning p = + (* check personal events *) check_pevents base warning p; + (* check person's age *) check_person_age warning p; + (* check titles dates *) List.iter (title_dates warning p) (get_titles p); + (* check order of personal events *) if onchange then changed_pevents_order warning p ; related_sex_is_coherent base warning p let family ?(onchange = true) base warning ifam fam = let fath = poi base @@ get_father fam in let moth = poi base @@ get_mother fam in + (* check order of familial events *) check_order_fevents base warning fam ; + (* check family's witnesses *) check_witness_fevents base warning fam ; + (* check parents marraige *) check_parents warning fam fath moth ; + (* check children *) check_children ~onchange base warning (ifam, fam) fath moth ; if onchange then begin changed_fevents_order warning (ifam, fam); let father = poi base (get_father fam) in let mother = poi base (get_mother fam) in + (* change order of father's families *) changed_marriages_order base warning father; + (* change order of mother's families *) changed_marriages_order base warning mother end diff --git a/lib/checkItem.mli b/lib/checkItem.mli index ae8b94898f..1b5b9e0e1e 100644 --- a/lib/checkItem.mli +++ b/lib/checkItem.mli @@ -3,17 +3,25 @@ open Gwdb +(** Database specification error *) type base_error = person Def.error + +(** Database specification warning *) type base_warning = (iper, person, ifam, family, title, pers_event, fam_event) Def.warning + +(* *) type base_misc = (person, family, title) Def.misc +(** Event name that unites personal and familial event names *) type 'string event_name = - Psort of 'string Def.gen_pers_event_name - | Fsort of 'string Def.gen_fam_event_name + | Psort of 'string Def.gen_pers_event_name (** Personal event name *) + | Fsort of 'string Def.gen_fam_event_name (** Familial event name *) +(** Sort events (both peronal and familial) by their date and their name *) val sort_events : ('a -> 'string event_name) -> ('a -> Adef.cdate) -> 'a list -> 'a list +(** Merge two sorted event lists (returns sorted list) *) val merge_events : ('a -> 'string event_name) -> ('a -> Adef.cdate) -> 'a list -> 'a list -> 'a list @@ -29,6 +37,14 @@ val check_siblings : -> (person -> unit) -> unit +(** [person onchange base warn p] checks person's properties: + + - personal events + - person's age + - person's titles dates + - etc. + If [onchange] is set then sort person's events + Calls [warn] on corresponding [base_warning] when find some inconsistencies. *) val person : ?onchange:bool -> base @@ -36,6 +52,14 @@ val person -> person -> (iper * person * Def.sex option * relation list option) list option +(** [family onchange base warn f] checks family properties like : + + - familial events + - parents marraige + - children age gap and birth + - etc. + If [onchange] is set then sort family's events + Calls [warn] on corresponding [base_warning] when find some inconsistencies. *) val family : ?onchange:bool -> base @@ -44,14 +68,21 @@ val family -> family -> unit +(** Unlike [person] who checks directly the properties of a person, checks the properties + of a person in relation to other people (his children, parents, spouses, witnesses, etc). + Calls [warn] on corresponding [base_warning] when find some inconsistencies. + *) val on_person_update : base -> (base_warning -> unit) -> person -> unit +(** Sort array of children by their birth date from oldest to youngest. + Returns old array and sorted version. *) val sort_children : base -> iper array -> (iper array * iper array) option +(** Cheks if family, father and mother have sources. Otherwise call [misc] on [base_misc] *) val check_other_fields : base -> (base_misc -> unit) -> ifam -> family -> unit diff --git a/lib/config.ml b/lib/config.ml index 291ce680aa..54d4d17bd1 100644 --- a/lib/config.ml +++ b/lib/config.ml @@ -7,6 +7,7 @@ type auth_scheme_kind = NoAuth | TokenAuth of token_auth_scheme | HttpAuth of http_auth_scheme + and token_auth_scheme = { ts_user : string; ts_pass : string } and http_auth_scheme = Basic of basic_auth_scheme @@ -66,6 +67,7 @@ type config = env : (string * string) list; mutable senv : (string * string) list; mutable henv : (string * string) list; + (* content of .gwf file *) base_env : (string * string) list; allowed_titles : string list Lazy.t; denied_titles : string list Lazy.t; @@ -82,13 +84,13 @@ type config = today_wd : int; time : int * int * int; ctime : float; + (* HTTP printer *) mutable output_conf : output_conf ; (* prefix for image urls: the value of argument -images_url if specified, otherwise command ^ "?m=IM&v=" in CGI mode "images" otherwise *) image_prefix : string; - (* if true, the base name is in the b argument of the query string: ?b=BASE&... if false, the base name is the last element of the uri path: .../base?... *) cgi : bool @@ -97,8 +99,8 @@ type config = } (**/**) -(** A dummy {!type:config} value, with uninitialized fields. - Used for testing purpose *) +(** A dummy {!type:config} value, with uninitialized fields. + Used for testing purpose *) let empty = { from = "" ; manitou = false diff --git a/lib/config.mli b/lib/config.mli new file mode 100644 index 0000000000..230a9b1412 --- /dev/null +++ b/lib/config.mli @@ -0,0 +1,115 @@ +open Def +open Gwdb + +(** Authentication scheme data type *) +type auth_scheme_kind = + NoAuth + | TokenAuth of token_auth_scheme + | HttpAuth of http_auth_scheme + +(** Authentication via security token *) +and token_auth_scheme = { ts_user : string; ts_pass : string; } + +(** Authentication via HTTP *) +and http_auth_scheme = + Basic of basic_auth_scheme + | Digest of digest_auth_scheme + +(** Basic authentication scheme inside {i Autorization} HTTP header *) +and basic_auth_scheme = { + bs_realm : string; + bs_user : string; + bs_pass : string; +} + +(** Digest authentication scheme inside {i Autorization} HTTP header *) +and digest_auth_scheme = { + ds_username : string; + ds_realm : string; + ds_nonce : string; + ds_meth : string; + ds_uri : string; + ds_qop : string; + ds_nc : string; + ds_cnonce : string; + ds_response : string; +} + +(** HTTP printer, that prints and sends requests on the user's socket *) +type output_conf = { + status : Def.httpStatus -> unit; + header : string -> unit; + body : string -> unit; + flush : unit -> unit; +} + +(** Geneweb configuration data type *) +type config = + { from : string; + api_mode : bool; + manitou : bool; + supervisor : bool; + wizard : bool; + is_printed_by_template : bool; + debug : bool; + friend : bool; + just_friend_wizard : bool; + user : string; + username : string; + auth_scheme : auth_scheme_kind; + command : string; + indep_command : string; + highlight : string; + lang : string; + default_lang : string; + default_sosa_ref : iper * Gwdb.person option; + multi_parents : bool; + authorized_wizards_notes : bool; + public_if_titles : bool; + public_if_no_date : bool; + mutable setup_link : bool; + access_by_key : bool; + private_years : int; + hide_names : bool; + use_restrict : bool; + no_image : bool; + no_note : bool; + bname : string; + cgi_passwd : string; + env : (string * string) list; + mutable senv : (string * string) list; + mutable henv : (string * string) list; + (* content of .gwf file *) + base_env : (string * string) list; + allowed_titles : string list Lazy.t; + denied_titles : string list Lazy.t; + request : string list; + lexicon : (string, string) Hashtbl.t; + mutable charset : string; + is_rtl : bool; + left : string; + right : string; + auth_file : string; + border : int; + mutable n_connect : (int * int * int * (string * float) list) option; + today : dmy; + today_wd : int; + time : int * int * int; + ctime : float; + (* HTTP printer *) + mutable output_conf : output_conf ; + (* prefix for image urls: + the value of argument -images_url if specified, otherwise + command ^ "?m=IM&v=" in CGI mode + "images" otherwise *) + image_prefix : string; + (* if true, the base name is in the b argument of the query string: ?b=BASE&... + if false, the base name is the last element of the uri path: .../base?... *) + cgi : bool + ; forced_plugins : string list + ; plugins : string list +} + +(** A dummy {!type:config} value, with uninitialized fields. + Used for testing purpose *) +val empty : config \ No newline at end of file diff --git a/lib/core/consang.ml b/lib/core/consang.ml index 202f359480..344a1e55f1 100644 --- a/lib/core/consang.ml +++ b/lib/core/consang.ml @@ -38,7 +38,10 @@ type relationship_info = let half x = x *. 0.5 -type visit = NotVisited | BeingVisited | Visited +type visit = + | NotVisited (* not visited person *) + | BeingVisited (* visited person but visit of ascendants haven't been terminated *) + | Visited (* visited person and his ascendants *) let rec noloop_aux base error tab i = match Gwdb.Marker.get tab i with @@ -94,6 +97,7 @@ let topological_sort base poi = | _ -> () ) persons ; + (* starting from the leaf vertex of graph (persons without childs) *) let todo = Gwdb.Collection.fold (fun acc i -> if Gwdb.Marker.get tab i = 0 then i :: acc else acc diff --git a/lib/core/consang.mli b/lib/core/consang.mli index f3126c7482..b684c26540 100644 --- a/lib/core/consang.mli +++ b/lib/core/consang.mli @@ -3,8 +3,12 @@ open Def open Gwdb +(* TODOOCP: doc *) + +(** Relation with ancestor status *) type anc_stat +(** Consanguinity information attached to person (relationship between parents) *) type relationship = { mutable weight1 : float; mutable weight2 : float; @@ -16,24 +20,39 @@ type relationship = mutable anc_stat1 : anc_stat; mutable anc_stat2 : anc_stat } +(** Computation consanguinity state for every person in the base *) type relationship_info = - { tstab : (Gwdb.iper, int) Gwdb.Marker.t + { + (* Information about topological rank for each person *) + tstab : (Gwdb.iper, int) Gwdb.Marker.t ; reltab : (Gwdb.iper, relationship) Gwdb.Marker.t ; mutable queue : Gwdb.iper list array } +(** Error that could occure while topological sorting, and raised when person is ancestor of himself. *) exception TopologicalSortError of person +(** Returns result of topological sort of persons. Result is represented as marker that associates to every person in the base his + topologic rank (let's suppose [r]). Global rule is : if person p1 is ancestor of p2 then r(p1) > r(p2). For example, all leaf + persons (without children) have rank 0, their parents (if no another child that has child themself) - rank 1, parents of their + parents - rank 2, etc. Raises [TopologicalSortError] if person is directly or undirectly is ancestor of himself (cycle). *) val topological_sort : Gwdb.base -> (Gwdb.base -> Gwdb.iper -> Gwdb.person) -> (Gwdb.iper, int) Gwdb.Marker.t +(** Initialise relationship info. *) val make_relationship_info : base -> (Gwdb.iper, int) Gwdb.Marker.t -> relationship_info +(* Returns relationship rate between two person and common ancestors (is exists). *) val relationship_and_links : base -> relationship_info -> bool -> Gwdb.iper -> Gwdb.iper -> float * Gwdb.iper list +(** [check_noloop base onerror] scans database person's oriented graph (vertex is a person and edge is parenthood from child to parent). If + cycle is found (person is directly or undirectly is ancestor of himself) calls [onerror] with [OwnAncestor] error. Array of + ascendants should be load in the memory. *) val check_noloop : base -> (person error -> unit) -> unit + +(** Same as [check_noloop] but scans only specified list of persons and their ancestors instead of entire database. *) val check_noloop_for_person_list : base -> (person error -> unit) -> Gwdb.iper list -> unit diff --git a/lib/core/consangAll.ml b/lib/core/consangAll.ml index 51e0328064..a62a69c216 100644 --- a/lib/core/consangAll.ml +++ b/lib/core/consangAll.ml @@ -68,6 +68,7 @@ let compute ?(verbosity = 2) base from_scratch = Opt.iter (fun ifam -> Gwdb.Marker.set consang_tab ifam cg) (fget i) ; if cg = Adef.no_consang then incr cnt end persons ; + (* number of persons which need consanguinity to be computed *) let max_cnt = !cnt in let most = ref None in if verbosity >= 1 then Printf.eprintf "To do: %d persons\n" max_cnt; @@ -81,39 +82,46 @@ let compute ?(verbosity = 2) base from_scratch = while !running do running := false ; Gwdb.Collection.iter (fun i -> + (* if person's consanguinity wasn't calculated *) if cget i = Adef.no_consang then match fget i with - Some ifam -> - let pconsang = Gwdb.Marker.get consang_tab ifam in - if pconsang = Adef.no_consang then - let cpl = foi base ifam in - let ifath = get_father cpl in - let imoth = get_mother cpl in - if cget ifath != Adef.no_consang - && cget imoth != Adef.no_consang - then - let consang = relationship base tab ifath imoth in - trace verbosity !cnt max_cnt; - decr cnt; - let cg = Adef.fix_of_float consang in - cset i cg; - Gwdb.Marker.set consang_tab ifam cg; - (if verbosity >= 2 then - if match !most with Some m -> cg > cget m | None -> true then - begin - Printf.eprintf "\nMax consanguinity %g for %s... " - consang - (Gutil.designation base (poi base i)); - flush stderr; - most := Some i - end) - else running := true - else - begin trace verbosity !cnt max_cnt; decr cnt; cset i pconsang end - | None -> - trace verbosity !cnt max_cnt; - decr cnt; - cset i (Adef.fix_of_float 0.0) + (* if person has parents *) + | Some ifam -> + let pconsang = Gwdb.Marker.get consang_tab ifam in + (* if parent's family's consanguinity wasn't calculated *) + if pconsang = Adef.no_consang then + let cpl = foi base ifam in + let ifath = get_father cpl in + let imoth = get_mother cpl in + (* if parent's consanguinity was calculated *) + if cget ifath != Adef.no_consang + && cget imoth != Adef.no_consang + then + let consang = relationship base tab ifath imoth in + trace verbosity !cnt max_cnt; + decr cnt; + let cg = Adef.fix_of_float consang in + cset i cg; + Gwdb.Marker.set consang_tab ifam cg; + (if verbosity >= 2 then + if match !most with Some m -> cg > cget m | None -> true then + begin + Printf.eprintf "\nMax consanguinity %g for %s... " + consang + (Gutil.designation base (poi base i)); + flush stderr; + most := Some i + end) + (* if it wasn't makes further another run over persons *) + else running := true + (* if it was then set to person his family's consanguinity *) + else + begin trace verbosity !cnt max_cnt; decr cnt; cset i pconsang end + (* if he doesn't then set his consanguinity to 0 *) + | None -> + trace verbosity !cnt max_cnt; + decr cnt; + cset i (Adef.fix_of_float 0.0) ) persons ; done; if max_cnt <> 0 then diff --git a/lib/core/consangAll.mli b/lib/core/consangAll.mli index 06c86e96f3..0296ad396f 100644 --- a/lib/core/consangAll.mli +++ b/lib/core/consangAll.mli @@ -4,7 +4,8 @@ open Gwdb (** [compute base from_scratch] [?verbosity] may be 0, 1 or 2 (default is 2) - + Compute consanguinity for each person in the base. If [from_scratch] is set then recompute + consanguinity for entire database. Return [true] if base has been patched, [false] otherwise. *) val compute : ?verbosity:int -> base -> bool -> bool diff --git a/lib/cousins.mli b/lib/cousins.mli new file mode 100644 index 0000000000..d3a9251317 --- /dev/null +++ b/lib/cousins.mli @@ -0,0 +1,28 @@ +(** Default number of relatives that could be listed at the same page *) +val default_max_cnt : int + +(** Retruns list of children of the giving family *) +val children_of_fam : Gwdb.base -> Gwdb.ifam -> Gwdb.iper list + +(** Returns list of person's siblings that includes also half-blood siblings. Every sibling + is annotated with parent's id and parent's sex. For common father's and mother's + childran father's annotation is preserved. *) +val siblings : + Config.config -> + Gwdb.base -> Gwdb.iper -> (Gwdb.iper * (Gwdb.iper * Def.sex)) list + +(** [has_desc_lev conf base lev p] tells if person [p] has descendants at the level [lev]. + [lev] 2 represents his children, 3 represents grandchildren, etc. *) +val has_desc_lev : + Config.config -> Gwdb.base -> int -> Gwdb.person -> bool + +(** Tells if two family branches don't itersect *) +val br_inter_is_empty : ('a * 'b) list -> ('a * 'c) list -> bool + +(** Same as [has_desc_lev] but used for a person's sibling as returned by [siblings]. *) +val sibling_has_desc_lev : + Config.config -> Gwdb.base -> int -> Gwdb.iper * 'a -> bool + +(** Returns sosa number calculated from the giving ancestors list. *) +val sosa_of_persons : + Config.config -> Gwdb.base -> Gwdb.iper list -> int \ No newline at end of file diff --git a/lib/cousinsDisplay.mli b/lib/cousinsDisplay.mli new file mode 100644 index 0000000000..8c105a9efc --- /dev/null +++ b/lib/cousinsDisplay.mli @@ -0,0 +1,15 @@ + +(** Displays the menu that lists all person's relatives depending on ancestor and his descandant levels + specified by [conf.env] variables {i v1} (for ancestor) and {i v2} for his descandant. For exemple : + + "v1" = 1, "v2" = 1 - Displays all person's siblings (mount to the person's parent (ancestor of level 1) + and lists all his children (descandant of level 1)); + "v1" = 2, "v2" = 2 - Displays all cousins; + "v1" = 2, "v2" = 1 - Displays all uncles/aunts; + "v1" = 1, "v2" = 2 - Displays all nieces/nephews; + etc. + + Variable "t" is used to display anniversaries for relatives like [BirthdayDisplay.gen_print]. + If nor of those variables are defined, prints menu that allows to access the most common relatives (except for direct relatives) + like cousins, siblings, uncles/aunts, etc. *) +val print : Config.config -> Gwdb.base -> Gwdb.person -> unit diff --git a/lib/dag.mli b/lib/dag.mli new file mode 100644 index 0000000000..e200903a7f --- /dev/null +++ b/lib/dag.mli @@ -0,0 +1,17 @@ +(* TODOCP *) +module Pset : + sig + type t = Gwdb.iper list + type elt = Gwdb.iper + val add : 'a -> 'a list -> 'a list + val empty : 'a list + val elements : 'a list -> 'a list + val mem : 'a -> 'a list -> bool + end + +val get_dag_elems : Config.config -> Gwdb.base -> Gwdb.iper list +type ('a, 'b) sum = ('a, 'b) Def.choice +val make_dag : + Config.config -> + Gwdb.base -> + Gwdb.iper list -> (Gwdb.iper, int) Def.choice Dag2html.dag \ No newline at end of file diff --git a/lib/dag2html.mli b/lib/dag2html.mli index fafd587fb2..30e85a4e81 100644 --- a/lib/dag2html.mli +++ b/lib/dag2html.mli @@ -1,13 +1,12 @@ (* $Id: dag2html.mli,v 5.0 2005-12-13 11:51:26 ddr Exp $ *) +(* TODOCP *) type 'a dag = { mutable dag : 'a node array } and 'a node = { mutable pare : idag list; valu : 'a; mutable chil : idag list } and idag - external int_of_idag : idag -> int = "%identity" external idag_of_int : int -> idag = "%identity" - type 'a table = { mutable table : 'a data array array } and 'a data = { mutable elem : 'a elem; mutable span : span_id } and 'a elem = @@ -16,7 +15,6 @@ and 'a elem = | Nothing and span_id and ghost_id - type align = LeftA | CenterA | RightA type ('a, 'b) table_data = TDitem of 'a @@ -26,10 +24,8 @@ type ('a, 'b) table_data = | TDnothing type ('a, 'b) html_table_line = (int * align * ('a, 'b) table_data) array type ('a, 'b) html_table = ('a, 'b) html_table_line array - val html_table_struct : ('a node -> 'b) -> ('a node -> 'c) -> ('a node -> bool) -> 'a dag -> idag table -> (int * align * ('b, 'c) table_data) array array - val table_of_dag : ('a node -> bool) -> bool -> bool -> bool -> 'a dag -> idag table diff --git a/lib/dagDisplay.mli b/lib/dagDisplay.mli new file mode 100644 index 0000000000..b8fea7d37e --- /dev/null +++ b/lib/dagDisplay.mli @@ -0,0 +1,32 @@ +(* TODOOCP *) +val image_txt : Config.config -> Gwdb.base -> Gwdb.person -> string +type item = Item of Gwdb.person * string +val make_tree_hts : + Config.config -> + Gwdb.base -> + (Gwdb.person -> item) -> + (Gwdb.iper -> string) -> + bool -> + Gwdb.iper list -> + (Gwdb.iper * (Gwdb.iper * Gwdb.ifam option)) list -> + (Gwdb.iper, 'a) Def.choice Dag2html.dag -> + (int * Dag2html.align * + (string, string) Dag2html.table_data) + array array +type dag_item = string +val print_slices_menu_or_dag_page : + Config.config -> + string -> + (int * Dag2html.align * + (dag_item, string) Dag2html.table_data) + array array -> string -> unit +val make_and_print_dag : + Config.config -> + Gwdb.base -> + (Gwdb.person -> item) -> + (Gwdb.iper -> string) -> + bool -> + Gwdb.iper list -> + (Gwdb.iper * (Gwdb.iper * Gwdb.ifam option)) list -> + string -> string -> unit +val print : Config.config -> Gwdb.base -> unit \ No newline at end of file diff --git a/lib/dateDisplay.mli b/lib/dateDisplay.mli index 1903bcc8d3..6f89067ede 100644 --- a/lib/dateDisplay.mli +++ b/lib/dateDisplay.mli @@ -8,19 +8,53 @@ open Gwdb Return the day of the week for this [date] *) val get_wday : config -> date -> string +(** Returns textual representation of the date translated to the current language. Uses different encodings depending on day's, + month's and year's accessibility. Doesn't consider precision. *) val code_dmy : config -> dmy -> string -val string_of_ondate : ?link:bool -> config -> date -> string + +(** Converts and translate date to the textual representation for the giving language. Considers precision. *) +val string_of_dmy : Config.config -> Def.dmy -> string + +(** If date is [Dgreg] calls for [string_of_dmy] to convert date to the string else returns content of [Dtext]. + Difference between calendars is not taken into the acount. *) val string_of_date : config -> date -> string + +(** Converts and translate date with considering different calendars with prefix "on" before dates (changes for other languages). + Date precision is much more verbose then with [string_of_date]. Decline phrase if needed. + If [link] is true then encapsulates result in HTML link to the page calendar's date converter. *) +val string_of_ondate : ?link:bool -> config -> date -> string + +(** Returns date in format dd/mm/yyyy. Format could be different for other languages (defined by [!dates order] + keyword in the lexicon). *) val string_slash_of_date : config -> date -> string + +(** Returns textual representation of the age represented by [dmy]. *) val string_of_age : config -> dmy -> string + +(** Returns textual representation of date's precision and year. *) val prec_year_text : config -> dmy -> string + +(** Returns textual representation of date's precision *) val prec_text : config -> dmy -> string -val day_text : dmy -> string + +(** Returns textual representation of date's month number. *) val month_text : dmy -> string + +(** Returns textual representation of date's year. *) val year_text : dmy -> string + +(** Returns concatenation of person's birth and death dates (if exists). Precision is mentionned for each date. + For example : + + * 1700-1780 (birth - death) + * 1700- (birth - death but don't know when) + * 1700 (birth - alive) + * †1780 (unknown birth date - death) + * † (unknown birth date - death but don't know when) *) val short_dates_text : config -> base -> person -> string + +(** Retruns year of marriage for given spouses with its precision. *) val short_marriage_date_text : config -> base -> family -> person -> person -> string -val print_dates : config -> base -> person -> unit (** [death_symbol conf] Return the value associated to ["death_symbol"] in [.gwf] file @@ -28,16 +62,12 @@ val print_dates : config -> base -> person -> unit *) val death_symbol : config -> string -val string_of_dmy : config -> dmy -> string -val string_of_on_french_dmy : config -> dmy -> string -val string_of_on_hebrew_dmy : config -> dmy -> string -val string_of_prec_dmy : config -> string -> string -> dmy -> string -val gregorian_precision : config -> dmy -> string -val french_month : config -> int -> string +(** Returns roman number of the year of French calendar *) val code_french_year : config -> int -> string -val code_hebrew_date : config -> int -> int -> int -> string -(** [string_of_date_aux ~dmy:string_of_dmy ~sep:" " conf d] *) +(** Same as [string_of_ondate] except : + - Conversion function for [Def.dmy] could be passed in in [dmy] argument + - Doesn't consider phrase declination as [string_of_ondate] does. *) val string_of_date_aux : ?link:bool -> ?dmy:(Config.config -> Def.dmy -> string) diff --git a/lib/def/adef.ml b/lib/def/adef.ml index a277107f0a..db3e23068c 100644 --- a/lib/def/adef.ml +++ b/lib/def/adef.ml @@ -35,6 +35,7 @@ type cdate = | Cdate of date | Cnone +(* compress concrete date if it's possible *) let compress d = let simple = match d.prec with @@ -70,6 +71,7 @@ let cdate_of_date d = end | Dtext t -> Ctext t +(* uncompress concrete date *) let uncompress x = let (year, x) = x mod 2500, x / 2500 in let (month, x) = x mod 13, x / 13 in diff --git a/lib/def/adef.mli b/lib/def/adef.mli index b603c4b81d..471b8827f7 100644 --- a/lib/def/adef.mli +++ b/lib/def/adef.mli @@ -1,17 +1,40 @@ (* $Id: adef.mli,v 5.6 2007-02-21 18:14:01 ddr Exp $ *) (* Copyright (c) 1998-2007 INRIA *) +(** Consanguinity rate *) type fix -type cdate -type 'person gen_couple +(** Returns float coefficient of consanguinity rate *) +val float_of_fix : fix -> float + +(** Returns consanguinity rate from its float coefficient *) +val fix_of_float : float -> fix + +(** [fix] from int *) +external fix : int -> fix = "%identity" + +(** [fix] to int *) +external fix_repr : fix -> int = "%identity" + +(** No consanguinity *) +val no_consang : fix + +(** Date data type that can be either concrete date associated to a calendar or a textual form of the date. *) type date = Dgreg of dmy * calendar | Dtext of string + +(** Supported calendars *) and calendar = Dgregorian | Djulian | Dfrench | Dhebrew + +(** Concrete date with precision. *) and dmy = { day : int; month : int; year : int; prec : precision; delta : int } + +(** Concrete date without precision. *) and dmy2 = { day2 : int; month2 : int; year2 : int; delta2 : int } + +(** Precision attached to the concrete date. *) and precision = Sure | About @@ -21,25 +44,45 @@ and precision = | OrYear of dmy2 | YearInt of dmy2 -val float_of_fix : fix -> float -val fix_of_float : float -> fix -external fix : int -> fix = "%identity" -external fix_repr : fix -> int = "%identity" - -val no_consang : fix +(** Compressed date *) +type cdate +(** Convert [cdate] to [date] *) val date_of_cdate : cdate -> date + +(** Convert [date] to [cdate] *) val cdate_of_date : date -> cdate +(** Absent compressed date *) val cdate_None : cdate + +(** Optional date from [cdate] *) val od_of_cdate : cdate -> date option + +(** Optional date to [cdate] *) val cdate_of_od : date option -> cdate +(** Polymorphic type to represent a family's couple. + Couple consists of the father and of the mother. *) +type 'person gen_couple + +(** Get father from couple *) val father : 'a gen_couple -> 'a + +(** Get mother from couple *) val mother : 'a gen_couple -> 'a + +(** [couple f m] creates a couple from father [f] and mother [m] *) val couple : 'a -> 'a -> 'a gen_couple + +(** Create [gen_couple] from array. First element of array should be father, second - mother *) val parent : 'a array -> 'a gen_couple + +(** Returns array from [gen_couple]. First element of array is father, second - mother *) val parent_array : 'a gen_couple -> 'a array +(** @deprecated Use [couple] instead *) val multi_couple : 'a -> 'a -> 'a gen_couple + +(** @deprecated Use [parent] instead *) val multi_parent : 'a array -> 'a gen_couple diff --git a/lib/def/def.ml b/lib/def/def.ml index aa91f1b539..34becdcc28 100644 --- a/lib/def/def.ml +++ b/lib/def/def.ml @@ -1,5 +1,6 @@ (* Copyright (c) 1998-2007 INRIA *) +(** Http response status *) type httpStatus = | OK (* 200 *) | Moved_Temporarily (* 302 *) @@ -11,21 +12,33 @@ type httpStatus = | Internal_Server_Error (* 500 *) | Service_Unavailable (* 503 *) +(** Type that represents 2 possible choices *) type ('a, 'b) choice = Left of 'a | Right of 'b +(** Alias to [Adef.cdate] *) type cdate = Adef.cdate +(** Alias to [Adef.date] *) type date = Adef.date = - Dgreg of dmy * calendar + Dgreg of dmy * calendar + (* textual form of the date *) | Dtext of string + +(** Alias to [Adef.calendar] *) and calendar = Adef.calendar = Dgregorian | Djulian | Dfrench | Dhebrew + +(** Alias to [Adef.dmy] *) and dmy = Adef.dmy = { day : int; month : int; year : int; prec : precision; delta : int } + +(** Alias to [Adef.dmy2] *) and dmy2 = Adef.dmy2 = { day2 : int; month2 : int; year2 : int; delta2 : int } + +(** Alias to [Adef.precision] *) and precision = Adef.precision = Sure @@ -34,8 +47,10 @@ and precision = | Before | After | OrYear of dmy2 + (* inteval *) | YearInt of dmy2 +(** Relation kind between couple in the family *) type relation_kind = | Married | NotMarried @@ -49,12 +64,16 @@ type relation_kind = | Pacs | Residence +(** Divorce status *) type divorce = | NotDivorced | Divorced of cdate | Separated +(** Death reason *) type death_reason = Killed | Murdered | Executed | Disappeared | Unspecified + +(** Death status *) type death = NotDead | Death of death_reason * cdate @@ -63,17 +82,22 @@ type death = | DontKnowIfDead | OfCourseDead +(** Burial information *) type burial = UnknownBurial | Buried of cdate | Cremated of cdate +(** Rights for access to the personal data *) type access = IfTitles | Public | Private +(** Title name *) type 'string gen_title_name = Tmain | Tname of 'string | Tnone + +(** Type that represents information about nobility title of a person *) type 'string gen_title = { t_name : 'string gen_title_name; t_ident : 'string; @@ -82,8 +106,10 @@ type 'string gen_title = t_date_end : cdate; t_nth : int } +(** Witness kind for an event *) type witness_kind = Witness | Witness_GodParent | Witness_Officer +(** Personal event name. *) type 'string gen_pers_event_name = Epers_Birth | Epers_Baptism @@ -136,6 +162,8 @@ type 'string gen_pers_event_name = | Epers_VenteBien | Epers_Will | Epers_Name of 'string + +(** Personal event information *) type ('person, 'string) gen_pers_event = { epers_name : 'string gen_pers_event_name; epers_date : cdate; @@ -145,6 +173,7 @@ type ('person, 'string) gen_pers_event = epers_src : 'string; epers_witnesses : ('person * witness_kind) array } +(** Event name pertaining a family. *) type 'string gen_fam_event_name = Efam_Marriage | Efam_NoMarriage @@ -159,6 +188,8 @@ type 'string gen_fam_event_name = | Efam_PACS | Efam_Residence | Efam_Name of 'string + +(** Event information pertaining a family. *) type ('person, 'string) gen_fam_event = { efam_name : 'string gen_fam_event_name; efam_date : cdate; @@ -168,18 +199,21 @@ type ('person, 'string) gen_fam_event = efam_src : 'string; efam_witnesses : ('person * witness_kind) array } - +(** Relation type with parent (if not native) *) type relation_type = Adoption | Recognition | CandidateParent | GodParent | FosterParent +(** Relation information with parents (if not native) *) type ('person, 'string) gen_relation = { r_type : relation_type; r_fath : 'person option; r_moth : 'person option; r_sources : 'string } +(** Sex of person *) type sex = Male | Female | Neuter +(** Place information *) type place = { other : string; town : string; @@ -190,8 +224,7 @@ type place = region : string; country : string } -(* person *) - +(** Polymorphic type describing information about person. *) type ('iper, 'person, 'string) gen_person = { first_name : 'string; surname : 'string; @@ -203,7 +236,10 @@ type ('iper, 'person, 'string) gen_person = first_names_aliases : 'string list; surnames_aliases : 'string list; titles : 'string gen_title list; + (* relations with not native parents *) rparents : ('person, 'string) gen_relation list; + (* related persons like (father of witnessed family, + concerned person of witnessed event, adopted child, etc.) *) related : 'person list; occupation : 'string; sex : sex; @@ -229,13 +265,17 @@ type ('iper, 'person, 'string) gen_person = psources : 'string; key_index : 'iper } - +(** Person's ascendants (family where he is a childran) with its consangunity rate + (equal to relationship betwen his parents). *) type 'family gen_ascend = { parents : 'family option; consang : Adef.fix } +(* Person's families to which he belongs as parent (union of families) *) type 'family gen_union = { family : 'family array } -(* family *) +(** Children of the family *) +type 'person gen_descend = { children : 'person array } +(** Polymorphic type describing information about family. *) type ('person, 'ifam, 'string) gen_family = { marriage : cdate; marriage_place : 'string; @@ -246,63 +286,74 @@ type ('person, 'ifam, 'string) gen_family = divorce : divorce; fevents : ('person, 'string) gen_fam_event list; comment : 'string; - origin_file : 'string; + origin_file : 'string; (* .gw filename where family is defined *) fsources : 'string; fam_index : 'ifam } +(** Alias to [Adef.gen_couple] *) type 'person gen_couple = 'person Adef.gen_couple -type 'person gen_descend = { children : 'person array } - +(** Database errors describing bad specification of the person *) type 'person error = - AlreadyDefined of 'person - | OwnAncestor of 'person + | AlreadyDefined of 'person + | OwnAncestor of 'person (** Person is his own ancestor *) | BadSexOfMarriedPerson of 'person +(** Database warnings attached to the specification of the person, family, relation, etc. *) type ('iper, 'person, 'family, 'descend, 'title, 'pevent, 'fevent) warning = - | BigAgeBetweenSpouses of 'person * 'person * dmy - | BirthAfterDeath of 'person - | IncoherentSex of 'person * int * int - | ChangedOrderOfChildren of 'family * 'descend * 'iper array * 'iper array - | ChangedOrderOfMarriages of 'person * 'family array * 'family array - | ChangedOrderOfFamilyEvents of 'family * 'fevent list * 'fevent list - | ChangedOrderOfPersonEvents of 'person * 'pevent list * 'pevent list - | ChildrenNotInOrder of 'family * 'descend * 'person * 'person - | CloseChildren of 'family * 'person * 'person - | DeadOld of 'person * dmy - | DeadTooEarlyToBeFather of 'person * 'person - | DistantChildren of 'family * 'person * 'person - | FEventOrder of 'person * 'fevent * 'fevent - | FWitnessEventAfterDeath of 'person * 'fevent * 'family - | FWitnessEventBeforeBirth of 'person * 'fevent * 'family - | IncoherentAncestorDate of 'person * 'person - | MarriageDateAfterDeath of 'person - | MarriageDateBeforeBirth of 'person - | MotherDeadBeforeChildBirth of 'person * 'person - | ParentBornAfterChild of 'person * 'person - | ParentTooOld of 'person * dmy * 'person - | ParentTooYoung of 'person * dmy * 'person - | PEventOrder of 'person * 'pevent * 'pevent + | BigAgeBetweenSpouses of 'person * 'person * dmy (* Age differece between couples is greater then 50 years *) + | BirthAfterDeath of 'person (** Person is born after his death *) + | IncoherentSex of 'person * int * int (** Incoherent sex of person *) + | ChangedOrderOfChildren of 'family * 'descend * 'iper array * 'iper array (** Children order has been modified *) + | ChangedOrderOfMarriages of 'person * 'family array * 'family array (** Person's marriages order has been modified *) + | ChangedOrderOfFamilyEvents of 'family * 'fevent list * 'fevent list (** Family's events order has been modified *) + | ChangedOrderOfPersonEvents of 'person * 'pevent list * 'pevent list (** Person's events order has been modified *) + | ChildrenNotInOrder of 'family * 'descend * 'person * 'person (** Children aren't ordered *) + | CloseChildren of 'family * 'person * 'person (** Age difference between two child is less then 7 month (except for twins) *) + | DeadOld of 'person * dmy (** Dead old (at the age older then 109 after 1900 year and older then 100 before) *) + | DeadTooEarlyToBeFather of 'person * 'person (** Childran is born in more then 1 year after his father's death *) + | DistantChildren of 'family * 'person * 'person (** Age gap between two of siblings greater then 50 years *) + | FEventOrder of 'person * 'fevent * 'fevent (** Familial events haven't been ordered correctly *) + | FWitnessEventAfterDeath of 'person * 'fevent * 'family (** Witness is dead before familial event date *) + | FWitnessEventBeforeBirth of 'person * 'fevent * 'family (** Witness is born after familial event date *) + | IncoherentAncestorDate of 'person * 'person (** Ancestor is born after person's birth *) + | MarriageDateAfterDeath of 'person (** Person is married after his death *) + | MarriageDateBeforeBirth of 'person (** Person is married before his birth *) + | MotherDeadBeforeChildBirth of 'person * 'person (** Childran is born after his mother's death *) + | ParentBornAfterChild of 'person * 'person (** Parent is born after one of his childran *) + | ParentTooOld of 'person * dmy * 'person (** Person became a parent at age older then 55 years for mother and 70 for father *) + | ParentTooYoung of 'person * dmy * 'person (** Person became a parent at age younger then 11 years old *) + | PEventOrder of 'person * 'pevent * 'pevent (** Personal events haven't been ordered correctly *) | PossibleDuplicateFam of 'family * 'family - | PWitnessEventAfterDeath of 'person * 'pevent * 'person - | PWitnessEventBeforeBirth of 'person * 'pevent * 'person - | TitleDatesError of 'person * 'title - | UndefinedSex of 'person - | YoungForMarriage of 'person * dmy * 'family - | OldForMarriage of 'person * dmy * 'family - + | PWitnessEventAfterDeath of 'person * 'pevent * 'person (** Witness is dead before personal event date *) + | PWitnessEventBeforeBirth of 'person * 'pevent * 'person (** Witness is born after personal event date *) + | TitleDatesError of 'person * 'title (** Title's start date is after end date or person is born after title dates *) + | UndefinedSex of 'person (** Person has undefined sex (Neuter) *) + | YoungForMarriage of 'person * dmy * 'family (** Person is married before he was 12 years old *) + | OldForMarriage of 'person * dmy * 'family (** Person is married after he was 100 years old *) + +(** Missing sources warning *) type ('person, 'descend, 'title) misc = MissingSources -type rn_mode = RnAll | Rn1Ln | RnDeg +(** Database note/page reading mode *) +type rn_mode = + | RnAll (** Read all content *) + | Rn1Ln (** Read first line *) + | RnDeg (** If file isn't empty returns a space *) +(** Database note/page explorer structure *) type base_notes = - { nread : string -> rn_mode -> string + { + (* read content of the page with giving mode. + Page "" represent database note *) + nread : string -> rn_mode -> string + (* origin .gw filename *) ; norigin_file : string + (* returns list of extended pages *) ; efiles : unit -> string list } -(* Historique des modifications *) - +(** Update modification used for history tracking *) type ('iper, 'person, 'family, 'string) base_changed = U_Add_person of ('iper, 'person, 'string) gen_person | U_Modify_person of @@ -330,10 +381,13 @@ type ('iper, 'person, 'family, 'string) base_changed = | U_Add_parent of ('iper, 'person, 'string) gen_person * ('person, 'family, 'string) gen_family | U_Kill_ancestors of ('iper, 'person, 'string) gen_person + (* Modification U_Multi used when multiple persons are modified successively. Separation with U_Modify_person is necessary to inform foreign notify_change script + about database change without creating process for every person. *) | U_Multi of ('iper, 'person, 'string) gen_person * ('iper, 'person, 'string) gen_person * bool | U_Notes of int option * string +(** TODOOCP : doc *) module NLDB = struct type ('a, 'b) page = | PgInd of 'a diff --git a/lib/descendDisplay.mli b/lib/descendDisplay.mli new file mode 100644 index 0000000000..4e52854cd6 --- /dev/null +++ b/lib/descendDisplay.mli @@ -0,0 +1,70 @@ + +(* Public functions for API (plugin v7_descend) *) + +(** Displays only descendants for specified level in unordered lists *) +val display_descendants_level : + Config.config -> Gwdb.base -> int -> Gwdb.person -> unit + +(** Displays descendants with numerated by letter list. Title links to descendats index *) +val display_descendants_with_numbers : + Config.config -> Gwdb.base -> int -> Gwdb.person -> unit + +(** Displays index of descendants *) +val display_descendant_index : + Config.config -> Gwdb.base -> int -> Gwdb.person -> unit + +(** Displays index of descendant's spouses *) +val display_spouse_index : + Config.config -> Gwdb.base -> int -> Gwdb.person -> unit + +(** Displays descendants in the table where rows are ordered by D'Aboville number. *) +val display_descendant_with_table : + Config.config -> Gwdb.base -> int -> Gwdb.person -> unit + +(** Displays tree of descendants *) +val print_tree : + Config.config -> Gwdb.base -> int -> Gwdb.person -> unit + +(** Displays descendants as follows : + + person + | desc1 + | desc2 + | | desc21 + | desc3 + + *) +val print_aboville : + Config.config -> Gwdb.base -> int -> Gwdb.person -> unit + +(** Prints form that allows to customise display of descendants *) +val desmenu_print : + Config.config -> Gwdb.base -> Gwdb.person -> unit + +(** Displays the descendants of the selected in [conv.env] person. Descendants could be displayed by different ways + depending on variable {i t} in [conv.env] environement: + + - "L" dispalying descendants in unordered list + - "F" same as "L" but displays only female line + - "M" same as "L" but displays only female line + - "H" table dispalying + - "I" table dispalying with spouses information + - "A" numerated list (d'Aboville) + - "V" displaying a tree of descendants + + Previous dispalyings are done by template evaluation. Next ones are done by functions inside this module: + + - "B" for [print_aboville] + - "S" for [display_descendants_level] + - "K" for [display_descendant_with_table] + - "N" for [display_descendants_with_numbers] + - "G" for [display_descendant_index] + - "C" for [display_spouse_index] + - "T" for [print_tree] + + Variable {i v} is used to select maximal level to descend for descendant displaying (1 for children, 2 for + grandchildren, etc). If {i t} variable isn't defined, then displays the form that allows + customising of display. + + *) +val print : Config.config -> Gwdb.base -> Gwdb.person -> unit diff --git a/lib/fixbase.ml b/lib/fixbase.ml index e00a84ce28..0ee8fd77ed 100644 --- a/lib/fixbase.ml +++ b/lib/fixbase.ml @@ -193,7 +193,7 @@ let check_persons_parents ?report progress base = let ip = get_iper p in let fam = Gwdb.foi base ifam in if get_ifam fam = dummy_ifam then begin - patch_ascend base ip {parents = None; consang = Adef.fix (-1)}; + patch_ascend base ip {parents = None; consang = Adef.no_consang}; match report with | Some fn -> fn (Fix_ParentDeleted ip) | None -> () diff --git a/lib/fixbase.mli b/lib/fixbase.mli index 9de62f5ca4..7f02a04038 100644 --- a/lib/fixbase.mli +++ b/lib/fixbase.mli @@ -11,7 +11,8 @@ [Gwdb.commit_patches] *) - +(** All possible patches that could be automatically deducted from incontinent + or absent information in the database *) type patch = | Fix_NBDS of Gwdb.iper | Fix_AddedUnion of Gwdb.iper @@ -27,24 +28,39 @@ type patch = | Fix_WrongUTF8Encoding of Gwdb.ifam option * Gwdb.iper option * (Gwdb.istr * Gwdb.istr) option | Fix_UpdatedOcc of Gwdb.iper * int * int +(** For every person in the base synchronise his birth, death, baptism and burial events with + his fields and vice versa. *) val check_NBDS : ?report:(patch -> unit) -> (int -> int -> unit) -> Gwdb.base -> unit +(** For every family's parent in the base add current family to the parent's union (if absent). *) val check_families_parents : ?report:(patch -> unit) -> (int -> int -> unit) -> Gwdb.base -> unit +(** For every family's childran in the base add current family to the childran's ascendants (if absent). + Doesn't modify consanguinity rate. *) val check_families_children : ?report:(patch -> unit) -> (int -> int -> unit) -> Gwdb.base -> unit +(** For every person checks it ascendants. If it references to the dummy family, then remove this reference. + Otherwise add person to the family's children if he is absent. *) val check_persons_parents : ?report:(patch -> unit) -> (int -> int -> unit) -> Gwdb.base -> unit +(** For every person in the base removes all duplicate families and families where person isn't a parent *) val check_persons_families : ?report:(patch -> unit) -> (int -> int -> unit) -> Gwdb.base -> unit +(** For every person's event's witness add current person to the list of related persons if absent. *) val check_pevents_witnesses : ?report:(patch -> unit) -> (int -> int -> unit) -> Gwdb.base -> unit +(** For every family's event's witness add family's father to the list of related persons if absent. *) val check_fevents_witnesses : ?report:(patch -> unit) -> (int -> int -> unit) -> Gwdb.base -> unit +(** For every family in the base synchronise its fields with marriage and divorce events. *) val fix_marriage_divorce : ?report:(patch -> unit) -> (int -> int -> unit) -> Gwdb.base -> unit +(** For every family's missing parent (or spouse) fix his id and add current family to the parent's union *) val fix_missing_spouses : ?report:(patch -> unit) -> (int -> int -> unit) -> Gwdb.base -> unit +(** For every person's and family's string remplace it with normalized UTF8 version *) val fix_utf8_sequence : ?report:(patch -> unit) -> (int -> int -> unit) -> Gwdb.base -> unit +(** For every person in the base update his occurence number if someone with same name and same occurence + number already exists in the base. *) val fix_key : ?report:(patch -> unit) -> (int -> int -> unit) -> Gwdb.base -> unit diff --git a/lib/gwdb-legacy/btree.mli b/lib/gwdb-legacy/btree.mli new file mode 100644 index 0000000000..ac58e45f7d --- /dev/null +++ b/lib/gwdb-legacy/btree.mli @@ -0,0 +1,34 @@ + +(** Input signature of the functor [Btree.Make]. *) +module type OrderedType = sig type t val compare : t -> t -> int end + +(** Output signature of the functor [Btree.Make]. *) +module type S = + sig + + (** Same as {!Stdlib.Map.S.key} *) + type key + + (** Same as {!Stdlib.Map.S.t} *) + type +'a t + + (** Same as {!Stdlib.Map.S.mem} *) + val mem : key -> 'a t -> bool + + (** Same as {!Stdlib.Map.S.add} *) + val add : key -> 'a -> 'a t -> 'a t + + (** Same as {!Stdlib.Map.S.find} *) + val find : key -> 'a t -> 'a + + (** [key_after f_compare m] browse map [m] to find the key [k] which + gives [f_compare k = 0]. Raise [Not_found] if such key doesn't exists. *) + val key_after : (key -> int) -> 'a t -> key + + (** [next k bt] returns the smallest key that is bigger then [k] inside [bt]. *) + val next : key -> 'a t -> key + end + +(** Functor building an implementation of the map structure given a + totally ordered type. *) +module Make : functor (Ord : OrderedType) -> S with type key = Ord.t \ No newline at end of file diff --git a/lib/gwdb-legacy/database.ml b/lib/gwdb-legacy/database.ml index ba849c8316..f1b3c87210 100644 --- a/lib/gwdb-legacy/database.ml +++ b/lib/gwdb-legacy/database.ml @@ -51,20 +51,22 @@ let move_with_backup src dst = names.inx - index for names, strings of first names and surnames offset to sindex : binary_int offset to findex : binary_int - 1st index (names) : value + 1st index (mixes between names) : value array, length = "table_size", associating: - a hash value of a "crushed" (module "Name") name (modulo length) - to the array of indexes of the corresponding persons - 2nd index (surnames strings) : value + 2nd index (surnames sub-strings) : value array, length = "table_size", associating: - - a hash value of the "crushed" (module "Name") first name or - surname (modulo length) - - to the array of the corresponding string indexes - 3rd index (surnames strings) : value + - a hash value of the "crushed" (module "Name") surname sub-string + (modulo length) + - to the array of the corresponding surnnames (string indexes) that contains + giving surname substring + 3rd index (first name sub-strings) : value array, length = "table_size", associating: - - a hash value of the "crushed" (module "Name") first name or - surname (modulo length) - - to the array of the corresponding string indexes + - a hash value of the "crushed" (module "Name") first name sub-string + (modulo length) + - to the array of the corresponding string indexes that contains + giving fiest name substring names.acc - direct accesses to arrays inside names.inx @@ -76,10 +78,10 @@ let move_with_backup src dst = - to its index in the string array strings list array (length = string array length) - associating a string index - - to the index of the next index holding the same hash value + - to the index of the next index (previus value) holding the same hash value snames.inx - index for surnames - binary tree + array ordered by surname - associating the string index of a surname - to a pointer (int) to snames.dat @@ -87,19 +89,29 @@ let move_with_backup src dst = table of list of persons holding a surname fnames.inx - index for first names - binary tree + array ordered by first name - associating the string index of a first name - to a pointer (int) to fnames.dat fnames.dat - data associated with fnames.inx table of list of persons holding a first name -the corresponding list of persons holding this surname + notes - text file containing data base notes. + + notes_d - directory containing .txt for each extended page + + particles.txt - text file with autorised name's particles patches - patches When updated, none of the previous files are modified. Only this one is written and rewritten. It holds a record of type "patches", composed of association lists "index" - "new value". + + nb_persons - number of real persons (with those added by patches) + + synchro_patches - timestamped history of base's modification. + + restrict - defines visibility of each person in the base *) exception Found of int @@ -275,6 +287,7 @@ let binary_search_next arr cmp = let new_persons_of_first_name_or_surname cmp_str cmp_istr base_data params = let (proj, person_patches, names_inx, names_dat, bname) = params in let fname_dat = Filename.concat bname names_dat in + (* content of "snames.inx" *) let bt = lazy begin let fname_inx = Filename.concat bname names_inx in @@ -284,6 +297,7 @@ let new_persons_of_first_name_or_surname cmp_str cmp_istr base_data params = bt end in + (* ordered by string name's ids attached to the patched persons *) let patched = (* This is not useful to keep the list of ipers here because [patched] is not used by [find] but only by [cursor] and [next] *) @@ -595,12 +609,12 @@ let make_visible_record_access bname persons = (* Synchro: - - synchro_person contient la liste des ip des personnes patches. - - synchro_family contient la liste des ifam des familles patches. + - synchro_person contient la liste des ip des personnes patch�es. + - synchro_family contient la liste des ifam des familles patch�es. - synchro_patch contient : * le timestamp de la modification - * la liste des personnes modifies - * la liste des familles modifies + * la liste des personnes modifi�es + * la liste des familles modifi�es *) let synchro_person = ref [] let synchro_family = ref [] @@ -829,6 +843,7 @@ let opendb bname = flush stderr; None in + (* skipping array length *) let ic2_string_start_pos = match version with | GnWb0024 | GnWb0023 | GnWb0022 -> Dutil.int_size @@ -931,6 +946,8 @@ let opendb bname = close_out oc ; !cnt in + (* read person number without considering pended patches from the "nb_persons". If + file doesn't exists then count and write it to the file *) let nbp_read () = if Sys.file_exists nbp_fname then begin @@ -941,6 +958,7 @@ let opendb bname = end else npb_init () in let commit_patches () = + (* read real person number (considering pending patches) *) let nbp = Hashtbl.fold begin fun ip p acc -> try @@ -966,6 +984,7 @@ let opendb bname = Hashtbl.iter (Hashtbl.replace ht) ht' ; Hashtbl.clear ht' ; in + (* commit every pending patch *) aux patches.h_person pending.h_person ; aux patches.h_ascend pending.h_ascend ; aux patches.h_union pending.h_union ; @@ -973,6 +992,7 @@ let opendb bname = aux patches.h_couple pending.h_couple ; aux patches.h_descend pending.h_descend ; aux patches.h_string pending.h_string ; + (* update "pathes" file *) let tmp_fname = Filename.concat bname "1patches" in let fname = Filename.concat bname "patches" in let oc9 = Secure.open_out_bin tmp_fname in @@ -1155,6 +1175,7 @@ let opendb bname = ; func = base_func ; version } +(* record_acces of the given table (already present in the memory) *) let record_access_of tab = { Dbdisk.load_array = (fun () -> ()) ; get = (fun i -> tab.(i)) @@ -1188,6 +1209,8 @@ let make bname particles ((persons, families, strings, bnotes) as _arrays) : Dbd ; bnotes = bnotes ; bdir = bdir } in + (* since this function as called exclusively to create a database, it doesn't + needs for functionalities over arays *) let func : Dbdisk.base_func = { person_of_key = (fun _ -> assert false) ; persons_of_name = (fun _ -> assert false) diff --git a/lib/gwdb-legacy/database.mli b/lib/gwdb-legacy/database.mli index 9bd4aae06e..f4fb9940e0 100644 --- a/lib/gwdb-legacy/database.mli +++ b/lib/gwdb-legacy/database.mli @@ -1,7 +1,15 @@ (* Copyright (c) 1998-2007 INRIA *) +(** Initialise [dsk_base] from the database situated in the specified directory. + Initialises both data and functionallity part. *) val opendb : string -> Dbdisk.dsk_base +(** [make bname particles ((persons, ascendants, unions) (families, couples, + descendants) strings base_notes)] returns initialised with giving data + [dsk_base]. This function is called exclusively for database creating + purpose. It means that, it contains only data without functionalities. + Either call [opendb] on existing database or call [Gwdb.make], if you + want to make requests. *) val make : string -> string list @@ -16,6 +24,11 @@ val make -> Dbdisk.dsk_base (* Ajout pour l'API *) + +(** List of commited modifications inside the database. First element is a timestamp of a commit, + second - changed/added by considered commit person ids, third - changed/added by considered commit families ids. *) type synchro_patch = { mutable synch_list : (string * int list * int list) list } + +(** Get [synchro_patch] from the giving database directory. *) val input_synchro : string -> synchro_patch diff --git a/lib/gwdb-legacy/dbdisk.mli b/lib/gwdb-legacy/dbdisk.mli index ed749012e9..8c951ecfb4 100644 --- a/lib/gwdb-legacy/dbdisk.mli +++ b/lib/gwdb-legacy/dbdisk.mli @@ -1,3 +1,5 @@ +(** {1 Aliases to [Def] and [Adef]} *) + type fix = Adef.fix (* FIXME: expose its type *) type cdate = Def.cdate (* FIXME: expose its type *) @@ -241,74 +243,167 @@ type 'person gen_couple = 'person Def.gen_couple (* FIXME: expose its type *) type 'person gen_descend = 'person Def.gen_descend = { children : 'person array } +(** Extended person's entry in the base *) type dsk_person = (int, int, int) gen_person + +(** Person's ascendants entry in the base *) type dsk_ascend = int gen_ascend + +(** Person's union entry in the base *) type dsk_union = int gen_union + +(** Family's entry in the base *) type dsk_family = (int, int, int) gen_family + +(** Family's couple entry in the base *) type dsk_couple = int gen_couple + +(** Family's descendants entry in the base *) type dsk_descend = int gen_descend +(** Nobility title in the base *) type dsk_title = int gen_title +(** Type that define the functions to use to access and manipulate with + database arrays. *) type 'a record_access = - { load_array : unit -> unit - ; get : int -> 'a - ; get_nopending : int -> 'a - ; set : int -> 'a -> unit - ; mutable len : int - ; output_array : out_channel -> unit - ; clear_array : unit -> unit + { + (* Load array in the memory and cache it so it could be accessed + instantly by other functions unless [clear_array] is called. *) + load_array : unit -> unit; + (* Get the nth element of array. In details, it searches for an element in + the following order: + - Search inside the pending patches + - Search inside the commited patches + - Search insede the loaded in memory array + - Search inside the "base" file *) + get : int -> 'a; + (* Same as [get] but doesn't consider pending patches *) + get_nopending : int -> 'a; + (* Set the nth element of array *) + set : int -> 'a -> unit; + (* Return length of an array that by default takes into account + commited patches *) + mutable len : int; + (* Output array with applied commited patches to the giving chanel *) + output_array : out_channel -> unit; + (* Remove array from the memory *) + clear_array : unit -> unit } +(** Data structure for optimised search throughout index by name + (surname or first name). Considers also patched persons. *) type string_person_index = - { find : int -> int list - ; cursor : string -> int - ; next : int -> int + { + (* Find all person's ids that has giving surname/first name. *) + find : int -> int list; + (* Return surname's/first name's id. If it doen't present return id of the next + name by alphabetical order *) + cursor : string -> int; + (* Return surname's/first name's id. If it doen't present return id of the next + name by alphabetical order *) + next : int -> int } +(* unused *) type visible_record_access = { v_write : unit -> unit; v_get : (dsk_person -> bool) -> int -> bool } +(** Data part of database *) type base_data = - { persons : dsk_person record_access - ; ascends : dsk_ascend record_access - ; unions : dsk_union record_access - ; visible : visible_record_access - ; families : dsk_family record_access - ; couples : dsk_couple record_access - ; descends : dsk_descend record_access - ; strings : string record_access - ; particles_txt : string list - ; particles : Re.re Lazy.t - ; bnotes : Def.base_notes - ; bdir : string + { + (* Array of persons *) + persons : dsk_person record_access; + (* Array of persons' ascendants *) + ascends : dsk_ascend record_access; + (* Array of persons' unions *) + unions : dsk_union record_access; + (* unused *) + visible : visible_record_access; + (* Array of families *) + families : dsk_family record_access; + (* Array of families' couples *) + couples : dsk_couple record_access; + (* Array of families' descendants *) + descends : dsk_descend record_access; + (* Array of strings *) + strings : string record_access; + (* Array of autorised to use surname's particles *) + particles_txt : string list; + (* Regular expression that matches particles in [particles_txt] *) + particles : Re.re Lazy.t; + (* Data base notes and extended page structure *) + bnotes : Def.base_notes; + (* Directory where database's files are stored *) + bdir : string } +(** Functioallity part of database. Every modification of the base is stored in {i patches} file. + Note that, every modification firstly is pendent and should be commited + to apply them and to update {i patches} file with [commit_patches]. *) type base_func = - { person_of_key : string -> string -> int -> int option - ; persons_of_name : string -> int list - ; strings_of_sname : string -> int list - ; strings_of_fname : string -> int list - ; persons_of_surname : string_person_index - ; persons_of_first_name : string_person_index - ; patch_person : int -> dsk_person -> unit - ; patch_ascend : int -> dsk_ascend -> unit - ; patch_union : int -> dsk_union -> unit - ; patch_family : int -> dsk_family -> unit - ; patch_couple : int -> dsk_couple -> unit - ; patch_descend : int -> dsk_descend -> unit - ; patch_name : string -> int -> unit - ; insert_string : string -> int - ; commit_patches : unit -> unit - ; commit_notes : string -> string -> unit - ; cleanup : unit -> unit - ; nb_of_real_persons : unit -> int - ; iper_exists : int -> bool - ; ifam_exists : int -> bool + { + (* Return person's id from the giving key (first name, surname and occurene number). + If person doesn't exists return None. Doesn't consider pending patches *) + person_of_key : string -> string -> int -> int option; + (* Return list of person ids that have giving name + (could be one of the mix). Doesn't consider pending patches *) + persons_of_name : string -> int list; + (* Return list of surnames (string ids) that contain giving person's surname or surname substring. + Consider also surnames of pathed persons. Doesn't consider pending patches *) + strings_of_sname : string -> int list; + (* Return list of first names (string ids) that contain giving person's first name or first name's + substring. Consider also first names of pathed persons. Doesn't consider pending patches *) + strings_of_fname : string -> int list; + (* Search functionalities throughout index by surname *) + persons_of_surname : string_person_index; + (* Search functionalities throughout index by first name *) + persons_of_first_name : string_person_index; + (* Insert or modify person with a giving id. Added inside pending patches. *) + patch_person : int -> dsk_person -> unit; + (* Insert or modify ascendants of a person with a giving id. + Added inside pending patches. *) + patch_ascend : int -> dsk_ascend -> unit; + (* Insert or modify union of a person with a giving id. + Added inside pending patches. *) + patch_union : int -> dsk_union -> unit; + (* Insert or modify family with a giving id. Added inside pending patches. *) + patch_family : int -> dsk_family -> unit; + (* Insert or modify couple of a family with a giving id. + Added inside pending patches. *) + patch_couple : int -> dsk_couple -> unit; + (* Insert or modify descendants of a family with a giving id. + Added inside pending patches. *) + patch_descend : int -> dsk_descend -> unit; + (* Associate person to the another name inside the index. + Added directly inside commited patches. *) + patch_name : string -> int -> unit; + (* Insert new string inside the pending patches and returns its id. + If string already exists return its id. *) + insert_string : string -> int; + (* Commit pending patches and write a patches' new state inside "patches" + file. "nb_persons" are also updated. *) + commit_patches : unit -> unit; + (* Update content (second arg) of the notes' file (first arg) if exists. *) + commit_notes : string -> string -> unit; + (* Close every opened channel. *) + cleanup : unit -> unit; + (* Returns real number of persons inside the base + (without bogus ? ? definition). Pendent patches aren't considered. *) + nb_of_real_persons : unit -> int; + (* Tells if person with giving id exists in the base. + Pendent patches are also considered *) + iper_exists : int -> bool; + (* Tells if family with giving id exists in the base. + Pendent patches are also considered *) + ifam_exists : int -> bool } +(** Geneweb database version *) type base_version = GnWb0020 | GnWb0021 | GnWb0022 | GnWb0023 | GnWb0024 +(** Database represntation in the memory that regroups + data and basic requests over this data. *) type dsk_base = { data : base_data ; func : base_func ; version : base_version } diff --git a/lib/gwdb-legacy/dutil.ml b/lib/gwdb-legacy/dutil.ml index 3342b8cc4e..6e74720394 100644 --- a/lib/gwdb-legacy/dutil.ml +++ b/lib/gwdb-legacy/dutil.ml @@ -72,12 +72,13 @@ let int_size = 4 let output_value_no_sharing oc v = Marshal.to_channel oc v [Marshal.No_sharing] +(* (*UNUSED*) let output_array_no_sharing oc arr_get arr_len = let header_pos = Iovalue.create_output_value_header oc in Iovalue.output_block_header oc 0 arr_len; for i = 0 to arr_len - 1 do Iovalue.output oc (arr_get i) done; let pos_end = Iovalue.patch_output_value_header oc header_pos in - seek_out oc pos_end + seek_out oc pos_end*) module IntHT = Hashtbl.Make (struct type t = int diff --git a/lib/gwdb-legacy/dutil.mli b/lib/gwdb-legacy/dutil.mli index 2b860369c1..5b34a8c908 100644 --- a/lib/gwdb-legacy/dutil.mli +++ b/lib/gwdb-legacy/dutil.mli @@ -2,34 +2,66 @@ open Dbdisk +(** Index for all kind of mix between person's names (first index inside {i names.inx}) *) type name_index_data = int array array + +(** Index for sub-strings of person's surame and first name (second and third index respectively inside {i names.inx}) *) type strings_of_fsname = int array array +(** Header for the {i base} file (version 0020) *) val magic_GnWb0020 : string + +(** Header for the {i base} file (version 0021) *) val magic_GnWb0021 : string + +(** Header for the {i base} file (version 0022) *) val magic_GnWb0022 : string + +(** Header for the {i base} file (version 0023) *) val magic_GnWb0023 : string + +(** Header for the latest version of {i base} file *) val magic_GnWb0024 : string + +(** Maximal size of hash table for name indexation (inside {i names.inx}) *) val table_size : int +(** [compare_fnames_i base i1 i2] compare two first names that have indexes [i1] and [i2] inside the [base]. *) val compare_fnames_i : Dbdisk.base_data -> int -> int -> int + +(** [compare_fnames] compare two first names. *) val compare_fnames : string -> string -> int +(** [compare_snames_i base i1 i2] compare two surnames that have indexes [i1] and [i2] inside the [base]. *) val compare_snames_i : Dbdisk.base_data -> int -> int -> int + +(** [compare_snames_i base s1 s2] compare two surnames according to the principe specified by [Mutil.compare_after_particle]. *) val compare_snames : Dbdisk.base_data -> string -> string -> int +(** [dsk_person_misc_names base p nobtit] computes various mix between all kind of names of a person's entry [p] + from the database [base]. [nobtit] is used to return a title entries for passed in argument person. *) val dsk_person_misc_names : dsk_base -> dsk_person -> (dsk_person -> dsk_title list) -> string list +(** [poi base i] returns person's entry with index [i] from [base]. *) val poi : dsk_base -> int -> dsk_person + +(** [poi base i] returns string with index [i] from [base]. *) val sou : dsk_base -> int -> string + +(** Returns person's first name from the given person's entry. *) val p_first_name : dsk_base -> dsk_person -> string + +(** Returns person's surname from the given person's entry. *) val p_surname : dsk_base -> dsk_person -> string +(** Output given value to the channel. Uses [Marshall.to_channel] with [No_sharing] flag. *) val output_value_no_sharing : out_channel -> _ -> unit -val output_array_no_sharing : out_channel -> (int -> _) -> int -> unit + +(** Size of integer value inside the Geneweb's binary files *) val int_size : int +(** Hastable that has unhashed int as a key. *) module IntHT : sig include module type of Hashtbl.Make (struct type t = int @@ -40,6 +72,6 @@ end (** [name_index s] Compute the index of crush_lowered version of s - in an array of size {!val:table_size}. + in an array of size [table_size]. *) val name_index : string -> int diff --git a/lib/gwdb-legacy/gwdb_driver.ml b/lib/gwdb-legacy/gwdb_driver.ml index 1b5c0d328e..711fa12a1f 100644 --- a/lib/gwdb-legacy/gwdb_driver.ml +++ b/lib/gwdb-legacy/gwdb_driver.ml @@ -536,7 +536,7 @@ module Marker = struct ; set : 'k -> 'v -> unit } - let make (k : 'a -> 'k) (c : 'a Collection.t) (i : 'v) : ('a, 'v) t = + let make (k : 'a -> int) (c : 'a Collection.t) (i : 'v) : ('a, 'v) t = let a = Array.make c.Collection.length i in { get = (fun x -> Array.get a (k x) ) ; set = (fun x v -> Array.set a (k x) v) } diff --git a/lib/gwdb-legacy/gwdb_gc.ml b/lib/gwdb-legacy/gwdb_gc.ml index 214a5200a7..02b2ff6d8c 100644 --- a/lib/gwdb-legacy/gwdb_gc.ml +++ b/lib/gwdb-legacy/gwdb_gc.ml @@ -71,6 +71,7 @@ let gc ?(dry_run = true) base = for i = 0 to base.data.families.len - 1 do if Array.get mf i then begin let f = base.data.families.get i in + (* if family wasn't deleted *) if f.fam_index <> dummy_ifam then begin let _ = Futil.map_family_ps markp markf marks f in let _ = Futil.map_couple_p false markp @@ base.data.couples.get i in @@ -79,12 +80,14 @@ let gc ?(dry_run = true) base = end end done ; + (* [p1;p2:p3;p4] [true;false;true;false] -> [0;0;1;1] *) let dst_i src m = let off = ref 0 in Array.init src.len begin fun i -> if Array.get m i then i - !off else begin incr off ; i - !off end end in + (* 2 [true;false;true;false] -> [0;2] *) let src_i len m = let off = ref 0 in let a = Array.make len (-1) in diff --git a/lib/gwdb-legacy/gwdb_gc.mli b/lib/gwdb-legacy/gwdb_gc.mli new file mode 100644 index 0000000000..4755c0dbff --- /dev/null +++ b/lib/gwdb-legacy/gwdb_gc.mli @@ -0,0 +1,7 @@ +(** [gc ~dry_run base] launch garbage collector over that analyse database [base] that detects + all deleted or empty elements that aren't referenced by anyone (unmarked element). If [dry_run] + is unset then performs memory compacting by eliminating all unmarked elements from all database + arrays and update corresponding database on the disk. Otherwise, it just perform computing stage + without database update. Returns [(deletedp,deletedf,deleteds)] where [deletedp] is ids of all unmarked + persons, [deletedf] is ids of all unmarked families and [deleteds] is ids of all unmarked strings *) +val gc : ?dry_run:bool -> Dbdisk.dsk_base -> int list * int list * int list \ No newline at end of file diff --git a/lib/gwdb-legacy/iovalue.ml b/lib/gwdb-legacy/iovalue.ml index c5df073604..50fbaedea3 100644 --- a/lib/gwdb-legacy/iovalue.ml +++ b/lib/gwdb-legacy/iovalue.ml @@ -242,6 +242,7 @@ let create_output_value_header oc = size_64 := 0; pos_header, pos_out oc +(* making a header for input_value like output_value does *) let patch_output_value_header oc (pos_header, pos_start) = let pos_end = pos_out oc in if Sys.word_size = 64 && diff --git a/lib/gwdb-legacy/iovalue.mli b/lib/gwdb-legacy/iovalue.mli index 79ffe28195..67d6af8b45 100644 --- a/lib/gwdb-legacy/iovalue.mli +++ b/lib/gwdb-legacy/iovalue.mli @@ -1,35 +1,18 @@ (* $Id: iovalue.mli,v 5.5 2012-01-27 08:53:53 ddr Exp $ *) (* Copyright (c) 1998-2007 INRIA *) -val input : in_channel -> 'a -val output : out_channel -> 'a -> unit - +(** Size of long integer value inside the Geneweb's binary files *) val sizeof_long : int -val sign_extend : int -> int - -(* making a header for input_value like output_value does *) - -type header_pos -val create_output_value_header : out_channel -> header_pos -val patch_output_value_header : out_channel -> header_pos -> int - -(* generic functions *) - -type 'a in_funs = - { input_byte : 'a -> int; - input_binary_int : 'a -> int; - input : 'a -> bytes -> int -> int -> unit } -val gen_input : 'a in_funs -> 'a -> 'b - -type 'a out_funs = - { output_byte : 'a -> int -> unit; - output_binary_int : 'a -> int -> unit; - output : 'a -> string -> int -> int -> unit } -val gen_output : 'a out_funs -> 'a -> 'b -> unit +(** Input a value from the giving channel. Identical to [Marshal.from_channel]. *) +val input : in_channel -> 'a -val output_block_header : out_channel -> int -> int -> unit -val size_32 : int ref -val size_64 : int ref +(** Output a value to the giving channel. Identical to [Marshal.to_channel] with [No_sharing] flag. *) +val output : out_channel -> 'a -> unit +(** [output_array_acces oc getf arr_get arr_len pos] prints to the channel + [oc] position for each element (that could be obtained with [arr_get]) + in the binary file where marshalled array is stored. Array should be + of length [arr_len] and should start at the position [pos] inside the + binary file. Returns a position just after the end of array. *) val output_array_access : out_channel -> (int -> 'a) -> int -> int -> int diff --git a/lib/gwdb-legacy/outbase.ml b/lib/gwdb-legacy/outbase.ml index 147fe7397c..03440dc485 100644 --- a/lib/gwdb-legacy/outbase.ml +++ b/lib/gwdb-legacy/outbase.ml @@ -22,6 +22,7 @@ let count_error computed found = let output_index_aux oc_inx oc_inx_acc ni = let bpos = pos_out oc_inx in + (* output name index (circular hash table) in the "names.inx" and position for hashed value in the "names.acc" *) Dutil.output_value_no_sharing oc_inx ni ; let epos = Iovalue.output_array_access oc_inx_acc (Array.get ni) (Array.length ni) @@ -33,6 +34,7 @@ let make_name_index base = let t = Array.make Dutil.table_size [] in for i = 0 to base.data.persons.len - 1 do let p = base.data.persons.get i in + (* not ? ? *) if p.first_name <> 1 && p.first_name <> 1 then begin List.iter (fun i -> Array.set t i @@ p.key_index :: Array.get t i) @@ @@ -108,7 +110,9 @@ let output_strings_hash tmp_strings_inx base = let tabl = Array.make strings_array.len (-1) in for i = 0 to strings_array.len - 1 do let ia = Hashtbl.hash (base.data.strings.get i) mod Array.length taba in - tabl.(i) <- taba.(ia); taba.(ia) <- i + (* store last associated value associated to the same hash *) + tabl.(i) <- taba.(ia); + taba.(ia) <- i done; output_binary_int oc (Array.length taba); for i = 0 to Array.length taba - 1 do output_binary_int oc taba.(i) done; @@ -129,14 +133,15 @@ let output_name_index_aux cmp get base names_inx names_dat = done ; let a = Array.make (Dutil.IntHT.length ht) (0, []) in ignore @@ Dutil.IntHT.fold (fun k v i -> Array.set a i (k, v) ; succ i) ht 0 ; + (* sort by name behind the int order *) Array.sort (fun (k, _) (k', _) -> cmp k k') a ; let oc_n_dat = Secure.open_out_bin names_dat in let bt2 = - Array.map begin fun (i, ipl) -> + Array.map begin fun (k, ipl) -> let off = pos_out oc_n_dat in output_binary_int oc_n_dat (List.length ipl) ; List.iter (output_binary_int oc_n_dat) ipl ; - (i, off) + (k, off) end a in close_out oc_n_dat ; @@ -166,8 +171,10 @@ let output_particles_file particles fname = close_out oc let output base = + (* create database directory *) let bname = base.data.bdir in if not (Sys.file_exists bname) then Unix.mkdir bname 0o755 ; + (* temporary files *) let tmp_particles = Filename.concat bname "1particles.txt" in let tmp_base = Filename.concat bname "1base" in let tmp_base_acc = Filename.concat bname "1base.acc" in @@ -196,6 +203,7 @@ let output base = if epos <> pos_out oc then count_error epos (pos_out oc) in begin try + (* output header of "base" *) output_string oc Dutil.magic_GnWb0024; output_binary_int oc base.data.persons.len; output_binary_int oc base.data.families.len; @@ -209,6 +217,7 @@ let output base = output_binary_int oc 0; output_binary_int oc 0; Dutil.output_value_no_sharing oc (base.data.bnotes.Def.norigin_file : string); + (* output arrays in the "base" and position for each element in the "base.acc" *) let persons_array_pos = pos_out oc in output_array "persons" base.data.persons; let ascends_array_pos = pos_out oc in @@ -223,6 +232,7 @@ let output base = output_array "descends" base.data.descends; let strings_array_pos = pos_out oc in output_array "strings" base.data.strings; + (* output arrays position in the header *) seek_out oc array_start_indexes; output_binary_int oc persons_array_pos; output_binary_int oc ascends_array_pos; diff --git a/lib/gwdb-legacy/outbase.mli b/lib/gwdb-legacy/outbase.mli new file mode 100644 index 0000000000..c39e1b72cd --- /dev/null +++ b/lib/gwdb-legacy/outbase.mli @@ -0,0 +1,29 @@ + +(** Flag that enables memory saving by calling gc sometimes *) +val save_mem : bool ref + +(** [output base] uses data section of the [base] to store database on the disk in the files: + + - {i base} main file that stores all the arrays of the database + - {i base.acc} direct accesses to arrays inside {i base} (list of offsets for every array) + - {i names.inx} 3 different name indexes : + + - For all kind of mix between person's names. Associate hash value + of the name to the array of persons (index of its entry) containing the given name + - sub-strings of surname. Associate hash value of the sub-string to + the array of string indexes of the names that contains mentionned sub string. + - sub-strings of first name. Same storage principe as for surname sub-strings. + - {i names.acc} direct accesses to arrays inside {i names.inx}. + - {i strings.inx} strings index. Associate hash of the string to the index + of its entry in the base's string array. Save also previus value in the case of + collision of hash. + - {i snames.inx} ordered index for surnames. Associate index of surname to its offset in + {i snames.dat}. + - {i snames.dat} For a giving surname give associated list of perosons (index of its entry) + - {i fnames.inx} and {i fnames.dat} same as for {i snames.inx} and {i snames.dat} but deals + with first names + - {i notes} text file containing data base notes. + - {i notes_d} directory containing .txt for each extended page. + - {i particles.txt} text file with autorised name's particles. + *) +val output : Dbdisk.dsk_base -> unit \ No newline at end of file diff --git a/lib/gwdb/gutil.ml b/lib/gwdb/gutil.ml index 6c8d0ab2e6..b84d09ca6d 100644 --- a/lib/gwdb/gutil.ml +++ b/lib/gwdb/gutil.ml @@ -258,7 +258,7 @@ let sort_person_list = sort_person_list_aux List.sort let sort_uniq_person_list = sort_person_list_aux List.sort_uniq -let find_free_occ base f s _i = +let find_free_occ base f s = let ipl = persons_of_name base (f ^ " " ^ s) in let first_name = Name.lower f in let surname = Name.lower s in diff --git a/lib/gwdb/gutil.mli b/lib/gwdb/gutil.mli index edde5e00e2..faafdaabca 100644 --- a/lib/gwdb/gutil.mli +++ b/lib/gwdb/gutil.mli @@ -3,33 +3,71 @@ open Def open Gwdb +(** [spouse p f] returns spouse of giving person inside the family. *) val spouse : iper -> family -> iper +(** Returns list of persons having the giving name as one of the misc names. *) val person_not_a_key_find_all : base -> string -> iper list + +(** Returns list of persons from the giving key. If key has form {i "firstname.occ surname"} + then returns list of one corresponding person. Otherwise calls [person_not_a_key_find_all] *) val person_ht_find_all : base -> string -> iper list + +(** [person_of_string_key base key] try to find a key inside [key] string of + the form {i "firstname.occ surname"} and returns a corresponding person. + If person doesn't exists or key isn't found then returns [None] *) val person_of_string_key : base -> string -> iper option + +(** Returns list of persons having the same first name and surname + as the specified person *) val find_same_name : base -> person -> person list -(* Pour les personnes avec plein de '.' dans le prénom ou le nom. *) -val person_of_string_dot_key : base -> string -> iper option +(** Returns person's key that has form {i "firstname.occ surname"} *) val designation : base -> person -> string +(** Trim at the end of string *) val trim_trailing_spaces : string -> string + +(** Compare two UTF-8 encoded strings by alphabetic order *) val alphabetic_utf_8 : string -> string -> int + +(** Compare two ISO-8859-1 encoded strings by alphabetic order *) val alphabetic : string -> string -> int + +(** Same as [alphabetic_utf_8] *) val alphabetic_order : string -> string -> int +(** Parse line and extract separated arguments ("" and '' are used to indlude spaces + inside the argument) *) val arg_list_of_string : string -> string list +(** Sort list of persons by comparison with following order: + - Compare by birth and death date + - Compare by surname + - Compare by first name + - Compare by occurence number + - Compare by id *) val sort_person_list : base -> person list -> person list + +(** Same as [sort_person_list] but also remove duplicates *) val sort_uniq_person_list : base -> person list -> person list +(** Same as [Adef.father] *) val father : 'a gen_couple -> 'a + +(** Same as [Adef.mother] *) val mother : 'a gen_couple -> 'a + +(** [couple multi f m] creates a couple from father [f] and mother [m]. If + [multi] true uses multiparent functionality *) val couple : bool -> 'a -> 'a -> 'a gen_couple + +(** Same as [Adef.parent_array] *) val parent_array : 'a gen_couple -> 'a array -val find_free_occ : base -> string -> string -> int -> int +(** Find first free occurence number for the person with specified first name + and surname. *) +val find_free_occ : base -> string -> string -> int (** [get_birth_death p] diff --git a/lib/gwdb/gwdb.ml b/lib/gwdb/gwdb.ml index 77b2462ca6..762225c472 100644 --- a/lib/gwdb/gwdb.ml +++ b/lib/gwdb/gwdb.ml @@ -2,10 +2,10 @@ open Def include Gwdb_driver -(** [insert_person base per] - Add a new person with the same properties as [per] in [base], - returning the fresh new {!type:iper} for this person. - [per] SHOULD be defined using [dummy_iper]. +(** [insert_person base p a u] + Add a new person with its union and ascendants in the [base]. + Allocate and returns the fresh new id for this person. + [p] SHOULD be defined using [dummy_iper]. *) let insert_person base p a u = let iper = Gwdb_driver.new_iper base in @@ -15,10 +15,10 @@ let insert_person base p a u = Gwdb_driver.insert_person base iper p ; iper -(** [insert_family base fam] - Add a new family with the same properties as [fam] in [base], - returning the fresh new {!type:ifam} for this family. - [fam] SHOULD be defined using [dummy_ifam]. +(** [insert_family base f c d] + Add a new family with its couple and descendants the in the [base]. + Allocate and returns the fresh new id for this family. + [f] SHOULD be defined using [dummy_ifam]. *) let insert_family base f c d = let ifam = Gwdb_driver.new_ifam base in @@ -50,10 +50,13 @@ let rec delete_person excl base ip = ^ "])" ) ; let a = get_gen_ascend base ip in + (* if person is the single childran ad his parents are empty persons then [ipers] contains father and mother and [ifams] contains family *) let ipers, ifams = match a.parents with | Some ifam -> + (* delete ascendants *) Gwdb_driver.delete_ascend base ip ; + (* remove person id from family descendants *) let children = (get_gen_descend base ifam).children |> Mutil.array_except ip @@ -181,6 +184,9 @@ let delete_family base ifam = ignore @@ delete_family ([], []) base ifam (**/**) (** Misc *) +(** [nobtit base allowed_titles denied_titles p] returns list of titles of a person [p] + that apprears in [allowed_titles] and doesn't appears in [denied_titles]. If [allowed_titles] + is empty the every title is allowed *) let nobtit base allowed_titles denied_titles p = let list = get_titles p in match Lazy.force allowed_titles with @@ -215,9 +221,14 @@ let nobtit base allowed_titles denied_titles p = else true) list +(** Returns first name of person with giving id *) let p_first_name base p = Mutil.nominative (sou base (get_first_name p)) + +(** Returns surname of person with giving id *) let p_surname base p = Mutil.nominative (sou base (get_surname p)) +(** Returns array of surnames of person's husbands. First element of a couple in the array is husband's surname, + second - is a husband's surname aliases *) let husbands base gp = let p = poi base gp.key_index in Array.map begin fun ifam -> @@ -228,7 +239,9 @@ let husbands base gp = husband_surname, husband_surnames_aliases end (get_family p) -let father_titles_places base p nobtit = + +(** Return person's father titles *) +let father_titles_places base p (nobtit : person -> title list) = match get_parents (poi base p.key_index) with | Some ifam -> let fam = foi base ifam in @@ -246,9 +259,12 @@ let gen_gen_person_misc_names base p nobtit nobtit_fun = (father_titles_places base p nobtit_fun) |> List.map Name.lower +(** [person_misc_names base p nobtit] computes various mix between all kind of names of a person's entry [p] + from the database [base]. [nobtit] is used to return a title entries for passed in argument person. *) let person_misc_names base p nobtit = gen_gen_person_misc_names base (gen_person_of_person p) (nobtit p) nobtit +(** Returns list of children ids for every family for giving person *) let children_of_p base p = Array.fold_right (fun ifam -> Array.fold_right List.cons (get_children @@ foi base ifam) ) diff --git a/lib/gwdb_driver.mli/gwdb_driver.mli b/lib/gwdb_driver.mli/gwdb_driver.mli index 3e57869890..e7e2007cb0 100644 --- a/lib/gwdb_driver.mli/gwdb_driver.mli +++ b/lib/gwdb_driver.mli/gwdb_driver.mli @@ -1,211 +1,531 @@ (* Copyright (c) 1998-2007 INRIA *) -type iper -type ifam +(** String id *) type istr +(** Family id *) +type ifam + +(** Person id *) +type iper + +(** Convert [iper] to string *) val string_of_iper : iper -> string + +(** Convert [ifam] to string *) val string_of_ifam : ifam -> string + +(** Convert [istr] to string *) val string_of_istr : istr -> string +(** Convert [iper] from string *) val iper_of_string : string -> iper + +(** Convert [ifam] from string *) val ifam_of_string : string -> ifam + +(** Convert [istr] from string *) val istr_of_string : string -> istr +(** Person data structure *) type person + +(** Family data structure *) type family +(** Database implementation for [Def.gen_relation] *) type relation = (iper, istr) Def.gen_relation + +(** Database implementation for [Def.gen_title] *) type title = istr Def.gen_title + +(** Database implementation for [Def.pers_event] *) type pers_event = (iper, istr) Def.gen_pers_event + +(** Database implementation for [Def.fam_event] *) type fam_event = (iper, istr) Def.gen_fam_event +(** Data structure for optimised search throughout index by name + (surname or first name). *) type string_person_index +(** Database represntation in the memory that regroups + data and basic requests over this data. *) type base +(** Open database situated in the specified directory. *) val open_base : string -> base + +(** Close database memory representation. *) val close_base : base -> unit +(** Dummy person id *) val dummy_iper : iper + +(** Dummy family id *) val dummy_ifam : ifam +(** Says if strings with the giving ids are equal *) val eq_istr : istr -> istr -> bool + +(** Says if string with the giving id is empty ("") *) val is_empty_string : istr -> bool + +(** Says if string with the giving id is a question mark ("?") *) val is_quest_string : istr -> bool + +(** Id of the empty string ("") *) val empty_string : istr + +(** Id of the question mark ("?") *) val quest_string : istr + +(** Returns unitialised person with the giving id. *) val empty_person : base -> iper -> person + +(** Returns unitialised family with the giving id. *) val empty_family : base -> ifam -> family +(** Tells if person with giving id exists in the base. *) val iper_exists : base -> iper -> bool + +(** Tells if family with giving id exists in the base. *) val ifam_exists : base -> ifam -> bool +(** {2 Getters} + Getters are used to extract information about person and family. + If corresponding information part isn't present, driver load it from + the disk and cache it so further gets will return result immediately. *) + +(** Get rights that defines access to person's data *) val get_access : person -> Def.access + +(** Get person's aliases ids *) val get_aliases : person -> istr list + +(** Get person's baptism date *) val get_baptism : person -> Def.cdate + +(** Get person's baptism note id *) val get_baptism_note : person -> istr + +(** Get person's baptism place id *) val get_baptism_place : person -> istr + +(** Get person's baptism source id *) val get_baptism_src : person -> istr + +(** Get person's birth date *) val get_birth : person -> Def.cdate + +(** Get person's birth note id *) val get_birth_note : person -> istr + +(** Get person's birth place id *) val get_birth_place : person -> istr + +(** Get person's birth source id *) val get_birth_src : person -> istr + +(** Get information about person's burial *) val get_burial : person -> Def.burial + +(** Get person's burial note id *) val get_burial_note : person -> istr + +(** Get person's burial place id *) val get_burial_place : person -> istr + +(** Get person's burial source id *) val get_burial_src : person -> istr + +(** Get array of family's children ids *) val get_children : family -> iper array + +(** Get family's comment id *) val get_comment : family -> istr + +(** Get person's consanguinity degree with his ascendants *) val get_consang : person -> Adef.fix + +(** Get person's death status *) val get_death : person -> Def.death + +(** Get person's death note id *) val get_death_note : person -> istr + +(** Get person's death place id *) val get_death_place : person -> istr + +(** Get person's death source id *) val get_death_src : person -> istr + +(** Get family's divorce status *) val get_divorce : family -> Def.divorce + +(** Get array of family's ids to which a person belongs as parent (person's union) *) val get_family : person -> ifam array + +(** Get family's father id (from the family's couple) *) val get_father : family -> iper + +(** Get family's event list *) val get_fevents : family -> fam_event list + +(** Get person's first name id *) val get_first_name : person -> istr + +(** Get list of person's first name aliases ids *) val get_first_names_aliases : person -> istr list + +(** Get family's sources id *) val get_fsources : family -> istr + +(** Get family's id *) val get_ifam : family -> ifam + +(** Get id of path to person's image *) val get_image : person -> istr + +(** Get person's id *) val get_iper : person -> iper + +(** Get family's marriage date *) val get_marriage : family -> Def.cdate + +(** Get family's marriage note id *) val get_marriage_note : family -> istr + +(** Get family's marriage place id *) val get_marriage_place : family -> istr + +(** Get family's marriage source id *) val get_marriage_src : family -> istr + +(** Get family's mother id (from the family's couple) *) val get_mother : family -> iper + +(** Get person's notes id *) val get_notes : person -> istr + +(** Get person's occurence number *) val get_occ : person -> int + +(** Get person's occupation id *) val get_occupation : person -> istr + +(** Get family's origin file (.gw filename where family is defined) id *) val get_origin_file : family -> istr + +(** Get family's parents ids (father and mother from family's couple) *) val get_parent_array : family -> iper array + +(** Get person's family id to which his parents belong (as family's couple) *) val get_parents : person -> ifam option + +(** Get person's event list *) val get_pevents : person -> pers_event list + +(** Get person's sources id *) val get_psources : person -> istr + +(** Get person's public name id *) val get_public_name : person -> istr + +(** Get list of person's qualifiers ids *) val get_qualifiers : person -> istr list + +(** Get person's related persons ids *) val get_related : person -> iper list + +(** Get relation kind between couple in the family *) val get_relation : family -> Def.relation_kind + +(** Get person's relations with not native parents *) val get_rparents : person -> relation list + +(** Get person's sex *) val get_sex : person -> Def.sex + +(** Get person's surname id *) val get_surname : person -> istr + +(** Get person's surname aliases ids *) val get_surnames_aliases : person -> istr list + +(** Get list of person's nobility titles *) val get_titles : person -> title list + +(** Get array of family's witnesses ids *) val get_witnesses : family -> iper array +(** Extract [gen_couple] from [family]. *) val gen_couple_of_family : family -> iper Def.gen_couple + +(** Extract [gen_descend] from [family]. *) val gen_descend_of_family : family -> iper Def.gen_descend + +(** Extract [gen_family] from [family]. *) val gen_family_of_family : family -> (iper, ifam, istr) Def.gen_family + +(** Extract [gen_person] from [person]. *) val gen_person_of_person : person -> (iper, iper, istr) Def.gen_person + +(** Extract [gen_ascend] from [person]. *) val gen_ascend_of_person : person -> ifam Def.gen_ascend + +(** Extract [gen_union] from [person]. *) val gen_union_of_person : person -> ifam Def.gen_union +(** Create [family] from associated values. *) val family_of_gen_family : base -> (iper, ifam, istr) Def.gen_family * iper Def.gen_couple * iper Def.gen_descend -> family + +(** Create [person] from associated values. *) val person_of_gen_person : base -> (iper, iper, istr) Def.gen_person * ifam Def.gen_ascend * ifam Def.gen_union -> person +(** Create uninitialised person with giving id *) val poi : base -> iper -> person + +(** Create uninitialised family with giving id *) val foi : base -> ifam -> family + +(** Returns string that has giving id from the base *) val sou : base -> istr -> string +(** Returns unitialised [gen_person] with giving id *) val no_person : iper -> (iper, iper, istr) Def.gen_person + +(** Returns unitialised [gen_ascend] *) val no_ascend : ifam Def.gen_ascend + +(** Returns unitialised [gen_union] *) val no_union : ifam Def.gen_union + +(** Returns unitialised [gen_family] with giving id *) val no_family : ifam -> (iper, ifam, istr) Def.gen_family + +(** Returns unitialised [gen_descend] *) val no_descend :iper Def.gen_descend + +(** Returns unitialised [gen_couple] *) val no_couple : iper Def.gen_couple +(** Returns number of persons inside the database *) val nb_of_persons : base -> int + +(** Returns number of defined persons (without bogus definition "? ?") + inside the database *) val nb_of_real_persons : base -> int + +(** Returns number of families inside the database *) val nb_of_families : base -> int + +(** Returns database name *) val bname : base -> string +(** Modify/add person with the giving id in the base. New names are added + to the patched name index for the cosidered person and for evey member of family to + which he belongs. Modification stay blocked until call of [commit_patches]. *) val patch_person : base -> iper -> (iper, iper, istr) Def.gen_person -> unit + +(** Modify/add ascendants of a person with a giving id. Modification stay blocked until + call of [commit_patches]. *) val patch_ascend : base -> iper -> ifam Def.gen_ascend -> unit + +(** Modify/add union of a person with a giving id. Modification stay blocked until + call of [commit_patches]. *) val patch_union : base -> iper -> ifam Def.gen_union -> unit + +(** Modify/add family with a giving id. Modification stay blocked until + call of [commit_patches]. *) val patch_family : base -> ifam -> (iper, ifam, istr) Def.gen_family -> unit + +(** Modify/add descendants of a family with a giving id. Modification stay blocked until + call of [commit_patches]. *) val patch_descend : base -> ifam -> iper Def.gen_descend -> unit + +(** Modify/add couple of a family with a giving id. Modification stay blocked until + call of [commit_patches]. *) val patch_couple : base -> ifam -> iper Def.gen_couple -> unit +(** Modify/add string with a giving id. If string already exists return its id. + Modification stay blocked until call of [commit_patches]. *) val insert_string : base -> string -> istr + +(** Commit blocked modifications (patches) and update database files in order to + apply modifications on the disk. *) val commit_patches : base -> unit + +(** [commit_notes fname s] Update content of the notes/extended page file [fname] if exists. *) val commit_notes : base -> string -> string -> unit +(** Retruns new unused person's id *) val new_iper : base -> iper + +(** Retruns new unused family's id *) val new_ifam : base -> ifam +(** Same as [patch_person] *) val insert_person : base -> iper -> (iper, iper, istr) Def.gen_person -> unit + +(** Same as [patch_ascend] *) val insert_ascend : base -> iper -> ifam Def.gen_ascend -> unit + +(** Same as [patch_union] *) val insert_union : base -> iper -> ifam Def.gen_union -> unit + +(** Same as [patch_family] *) val insert_family : base -> ifam -> (iper, ifam, istr) Def.gen_family -> unit + +(** Same as [patch_couple] *) val insert_descend : base -> ifam -> iper Def.gen_descend -> unit + +(** Same as [patch_descend] *) val insert_couple : base -> ifam -> iper Def.gen_couple -> unit +(** Remplace person with the giving id by bogus definition and clear + person's data structure. *) val delete_person : base -> iper -> unit + +(** Clear person's ascendants data structure *) val delete_ascend : base -> iper -> unit + +(** Clear person's union data structure *) val delete_union : base -> iper -> unit + +(** Remplace family with the giving id by dummy family and clear + family's data structure. *) val delete_family : base -> ifam -> unit + +(** Clear family's descendants data structure *) val delete_descend : base -> ifam -> unit + +(** Clear family's couple data structure *) val delete_couple : base -> ifam -> unit -(** [person_of_key first_name surname occ] *) +(** [person_of_key first_name surname occ] returns person from his key information + (first name, surname and occurence number) *) val person_of_key : base -> string -> string -> int -> iper option + +(** Return list of person ids that have giving name (could be one of the mix). *) val persons_of_name : base -> string -> iper list + +(** Returns data structure that allows to make optimised search throughout + index by first name *) val persons_of_first_name : base -> string_person_index + +(** Returns data structure that allows to make optimised search throughout + index by surname *) val persons_of_surname : base -> string_person_index -(** first [first/sur]name starting with that string *) +(** Returns first [first/sur]name id starting with that string *) val spi_first : string_person_index -> string -> istr -(** next [first/sur]name by Gutil.alphabetical order *) +(** Retruns next [first/sur]name id that follows giving name's id by + Gutil.alphabetical order *) val spi_next : string_person_index -> istr -> istr -(** all persons having that [first/sur]name *) +(** Retruns all persons id having that [first/sur]name. *) val spi_find : string_person_index -> istr -> iper list +(** [base_visible_get base fct ip] get visibility of person [ip] ([true] for not visible + (restrited)) from the [base]. If file {i restrict} is present then read it to get + visibility information. If person's visibility isn't known, then set it with [fct]. + Used when mode `use_restrict` is ativated *) val base_visible_get : base -> (person -> bool) -> iper -> bool + +(** Write updated visibility information to the {i restricted} file. *) val base_visible_write : base -> unit + +(** Return regular expression that matches all defined in the [base] particles. *) val base_particles : base -> Re.re (** [base_strings_of_first_name base x] - Return the list of first names (as [istr]) being equal to [x] - using {!val:Name.crush_lower} comparison. + Return the list of first names (as [istr]) being equal or to [x] + using {!val:Name.crush_lower} comparison. [x] could be also a substring + of the matched first name. *) val base_strings_of_first_name : base -> string -> istr list (** [base_strings_of_surname base x] Return the list of surnames (as [istr]) being equal to [x] - using {!val:Name.crush_lower} comparison. + using {!val:Name.crush_lower} comparison. [x] could be also a substring + of the matched surname. *) val base_strings_of_surname : base -> string -> istr list +(** Load array of ascendants in the memory and cache it so it could be accessed + instantly by other functions unless [clear_ascends_array] is called. *) val load_ascends_array : base -> unit + +(** Load array of unions in the memory and cache it so it could be accessed + instantly by other functions unless [clear_unions_array] is called. *) val load_unions_array : base -> unit + +(** Load array of couples in the memory and cache it so it could be accessed + instantly by other functions unless [clear_couples_array] is called. *) val load_couples_array : base -> unit + +(** Load array of descendants in the memory and cache it so it could be accessed + instantly by other functions unless [clear_descends_array] is called. *) val load_descends_array : base -> unit + +(** Load array of strings in the memory and cache it so it could be accessed + instantly by other functions unless [clear_strings_array] is called. *) val load_strings_array : base -> unit + +(** Load array of persons in the memory and cache it so it could be accessed + instantly by other functions unless [clear_persons_array] is called. *) val load_persons_array : base -> unit + +(** Load array of families in the memory and cache it so it could be accessed + instantly by other functions unless [clear_families_array] is called. *) val load_families_array : base -> unit +(** Remove array of ascendants from the memory *) val clear_ascends_array : base -> unit + +(** Remove array of unions from the memory *) val clear_unions_array : base -> unit + +(** Remove array of couples from the memory *) val clear_couples_array : base -> unit + +(** Remove array of descendants from the memory *) val clear_descends_array : base -> unit + +(** Remove array of strings from the memory *) val clear_strings_array : base -> unit + +(** Remove array of persons from the memory *) val clear_persons_array : base -> unit + +(** Remove array of families from the memory *) val clear_families_array : base -> unit +(** [base_notes_read base fname] read and return content of [fname] note + (either database note either extended page). *) val base_notes_read : base -> string -> string + +(** [base_notes_read base fname] read and return first line of [fname] note *) val base_notes_read_first_line : base -> string -> string + +(** Says if note has empty content *) val base_notes_are_empty : base -> string -> bool + +(** Retruns origin file (.gw file) of the note *) val base_notes_origin_file : base -> string + +(** Directory where extended pages are stored *) val base_notes_dir : base -> string + +(** Directory where wizard notes are stored *) val base_wiznotes_dir : base -> string +(** Returns last modification time of the database on disk *) val date_of_last_change : base -> float +(** Collections of elemetns *) module Collection : sig (** Collections are sets of elements you want to traverse. *) @@ -254,6 +574,7 @@ module Collection : sig end +(** Markers for elements inside [Collection.t] *) module Marker : sig (** Markers are way to annotate (add extra information to) elements of a {!val:Collection.t}. *) @@ -273,9 +594,16 @@ end (** {2 Useful collections} *) +(** Collection of person's ids *) val ipers : base -> iper Collection.t + +(** Collection of persons *) val persons : base -> person Collection.t + +(** Collection of family's ids *) val ifams : ?select:(ifam -> bool) -> base -> ifam Collection.t + +(** Collection of families *) val families : ?select:(family -> bool) -> base -> family Collection.t (** [dummy_collection x] create a dummy collection with no element. @@ -285,7 +613,12 @@ val dummy_collection : 'a -> 'a Collection.t (** {2 Useful markers} *) +(** [iper_marker c v] create marker over collection of person's ids and initialise it + for every element with [v] *) val iper_marker : iper Collection.t -> 'a -> (iper, 'a) Marker.t + +(** [ifam_marker c v] create marker over collection of family's ids and initialise it + for every element with [v] *) val ifam_marker : ifam Collection.t -> 'a -> (ifam, 'a) Marker.t (** [dummy_marker k v] create a dummy collection with no element. @@ -309,6 +642,7 @@ val make * Def.base_notes ) -> base +(** TODOOCP : doc *) val read_nldb : base -> (iper, ifam) Def.NLDB.t val write_nldb : base -> (iper, ifam) Def.NLDB.t -> unit diff --git a/lib/history.mli b/lib/history.mli index 9a395a7a35..170352bddb 100644 --- a/lib/history.mli +++ b/lib/history.mli @@ -5,14 +5,29 @@ open Config open Def open Gwdb +(** Retruns path to the file where history of updates is stored *) val file_name : config -> string +(** [record conf change action] records new modification in the history files (global file and specific for each concerned by modification person). + Additionaly it does: + + - Updates [conf.default_sosa_ref] if concered by modification person is referenced by default_sosa_ref + - Notify foreign {i notify_change} about modification on the base (doesn't notify if multiple modifications are done succesively) *) val record : config -> base -> (iper, iper, ifam, string) base_changed -> string -> unit + +(** [notify conf base action] Explicit notification of foreign script {i notify_change} that modification action [action] was executed on the database. + Since [record] already does notify script about unary modification on the base, this function is used exclusively to send notification about multiple + modifications and avoid creating indefinite amount of processes for each modification (for example for each concerned person in the list of modified persons). *) val notify : config -> base -> string -> unit +(** Displays an history of updates *) val print : config -> base -> unit -val print_search : config -> base -> unit +(** Same as `print`, but simultaneously searches for text inside the history and higlhight all found matches. + Search pattern is available with {i s} variable in environement [conf.env]. *) +val print_search : config -> base -> unit (* Ajout pour l'API *) + +(** Parses one line of history file that delimits one modification record. *) val line_fields : string -> (string * string * string * string option) option diff --git a/lib/historyDiff.mli b/lib/historyDiff.mli new file mode 100644 index 0000000000..3e65f1a346 --- /dev/null +++ b/lib/historyDiff.mli @@ -0,0 +1,23 @@ +(** Type that represnets one update record stored in the history file for concerned person. *) +type gen_record = { + date : string; + wizard : string; + gen_p : (Gwdb.iper, Gwdb.iper, string) Def.gen_person; + gen_f : (Gwdb.iper, Gwdb.ifam, string) Def.gen_family list; + gen_c : Gwdb.iper array list; +} + +(** Returns history filename for the person with the given key. Has format : {i firstname.occ.surname} *) +val history_file : string -> string -> int -> string + +(** Returns path to the history file inside {i history_d} with given filename *) +val history_path : Config.config -> string -> string + +(** [record_diff conf base change] records new updated information [change] inside the history files of concerned by [change] persons. *) +val record_diff : + Config.config -> + Gwdb.base -> + (Gwdb.iper, Gwdb.iper, Gwdb.ifam, string) Def.base_changed -> unit + +(** Loadlist of modification records for a giving person's history file. The most recent modification is at the head of the list *) +val load_person_history : Config.config -> string -> gen_record list \ No newline at end of file diff --git a/lib/historyDiffDisplay.mli b/lib/historyDiffDisplay.mli new file mode 100644 index 0000000000..433d0fe8e8 --- /dev/null +++ b/lib/historyDiffDisplay.mli @@ -0,0 +1,10 @@ + +(** Displays page that allows to select all revision of the history file in argument that user may want to clean *) +val print_clean : Config.config -> unit + +(** Cleans the history associated to the history file in argument *) +val print_clean_ok : Config.config -> unit + +(** Displays the page that allows to select (with variable {i t} = "SUM") and to view (with variable {i t} = "DIFF") the difference between all revisions of + history file of concerned person in variable {i f}. Intepretate the template file {i updhist_diff.txt} *) +val print : Config.config -> Gwdb.base -> unit diff --git a/lib/hutil.mli b/lib/hutil.mli index e1b2fca737..68c7693b83 100644 --- a/lib/hutil.mli +++ b/lib/hutil.mli @@ -3,26 +3,70 @@ open Config +(** [header_without_http conf title] pritns HTML page header in the body of the current response on the socket. + HTML page header consists of : + + - Declaration + - tag where : + + - content of tag is get with [title true] + - <meta> and <link> tags are filled due to [conf] + - content of <style> tag is evaluated and send by interpretation of template {i etc/css.txt} + + - Opening <body> tag with its attributes + - If user is a wizard or a friend, then includes all messages send to him. *) +val header_without_http : config -> (bool -> unit) -> unit + +(** [gen_trailer with_logo] prints HTML page trailer in the body of the current response on the socket. + HTML page header consists of : + + - Copyright message from template {i etc/copyr.txt} with inserted logo if [with_logo] is true + - Scripts JS from template {i etc/js.txt} + - Closing <body> and <html> tags *) +val gen_trailer : bool -> config -> unit + +(** Calls for [Util.html] to print HTTP header and for [header_without_http] to print HTML page header. Additionaly + prints opening container <div> tag on the socket. *) +val header_without_page_title : config -> (bool -> unit) -> unit + +(** [header conf title] calls for [header_without_page_title] to print HTTP header and HTML page header. Additionaly + prints page title with [title true] (false to print browser tab title). *) val header : config -> (bool -> unit) -> unit + +(** Same as [header] but takes page title from [conf.env]. *) +val header_no_page_title : config -> (bool -> unit) -> unit + +(** Pritns HTML page header (without HTTP headers) and opens fluid container <div> (see Bootstrap). *) val header_fluid : config -> (bool -> unit) -> unit + +(** Same as [header] but insert links to previous and home pages (with [print_link_to_welcome]) before page title. *) val header_link_welcome : config -> (bool -> unit) -> unit -val print_link_to_welcome : config -> bool -> unit + +(** Same as [gen_trailer true]. *) val trailer : config -> unit -val header_without_page_title : config -> (bool -> unit) -> unit -val header_without_http : config -> (bool -> unit) -> unit -val header_no_page_title : config -> (bool -> unit) -> unit +(** Same as [header] except page's title informs about an occured error (red title). *) val rheader : config -> (bool -> unit) -> unit + +(** Returns the HTML link to the previous (referer) page *) val link_to_referer : config -> string + +(** [gen_print_link_to_welcome f conf right_alined] prints links to previous and to home pages. [f] is used to print additional + content before links. *) val gen_print_link_to_welcome : (unit -> unit) -> config -> bool -> unit -val gen_trailer : bool -> config -> unit +(** Calls [gen_print_link_to_welcome] with empty function [f]. *) +val print_link_to_welcome : config -> bool -> unit + +(** Sends [Bad Request] HTTP response (same as [GWPARAM.output_error conf Bad_Request]) *) val incorrect_request : config -> unit +(* TODOOCP *) val interp : config -> string -> ('a, 'b) Templ.interp_fun -> 'a Templ.env -> 'b -> unit - val interp_no_header : config -> string -> ('a, 'b) Templ.interp_fun -> 'a Templ.env -> 'b -> unit +(** Displays the calendar; if no key is set, it will use today's date. + Based on template file calendar.txt *) val print_calendar : config -> unit diff --git a/lib/imageDisplay.ml b/lib/imageDisplay.ml index 9a25f0de2d..391a37962d 100644 --- a/lib/imageDisplay.ml +++ b/lib/imageDisplay.ml @@ -56,6 +56,12 @@ let print_image_type conf fname ctype = (* ************************************************************************** *) (* [Fonc] print_image_file : string -> bool *) +(** [Description] : Affiche une image (avec ses en-têtes) en réponse HTTP en + utilisant Wserver. Le type MIME de l'image est deviné à + partir de l'extension contenu dans le nom du fichier. + [Args] : + - fname : le nom du fichier image + [Retour] : True si l'image a pu être affichée *) (* ************************************************************************** *) let print_image_file conf fname = List.exists diff --git a/lib/imageDisplay.mli b/lib/imageDisplay.mli index 0b53050f83..82bdb10c2f 100644 --- a/lib/imageDisplay.mli +++ b/lib/imageDisplay.mli @@ -1,19 +1,11 @@ -(** [Description] : Affiche une image (avec ses en-têtes) en réponse HTTP en - utilisant Wserver. Le type MIME de l'image est deviné à - partir de l'extension contenu dans le nom du fichier. - [Args] : - - fname : le nom du fichier image - [Retour] : True si l'image a pu être affichée *) -val print_image_file : Config.config -> string -> bool +(** [print_image_file conf fname] send HTTP respose with content of an image file at the path [fname]. + MIME type of an image is deducted from [fname] extension. Returns [false] if image + wasn't found or couldn't be send. *) +val print_image_file : Config.config -> string -> bool -(** [Description] : Traite une requête image. - [Args] : - - config : configuration de la requête - - base : base de donnée sélectionnée *) +(** Searhes image's filename in the environement [conf.env] and sends HTTP respose with its content on the socket. If filename isn't presented, looks up + personal image for person's mentionned in [conf.env] *) val print : Config.config -> Gwdb.base -> unit -(** [Description] : Affiche une image seule dans une page HTML. - [Args] : - - conf : configuration de la requête - - base : argument non utilisé *) +(** Sends HTTP respose with HTML page containg just image specified in arguments. *) val print_html : Config.config -> unit diff --git a/lib/json_export/json_converter.mli b/lib/json_export/json_converter.mli new file mode 100644 index 0000000000..c3ab3a61c4 --- /dev/null +++ b/lib/json_export/json_converter.mli @@ -0,0 +1,86 @@ + +(** Json converter driver *) +module type ConverterDriver = sig + + (** Json value *) + type t + + (** Convert to JSON string *) + val str : string -> t + + (** Convert to JSON integer *) + val int : int -> t + + (** Convert to JSON object *) + val obj : (string * t) array -> t + + (** Convert to JSON null value *) + val null : t + + (** Convert array to JSON list *) + val array : 't array -> t + + (** Convert list to JSON list *) + val list : 't list -> t + + (** Convert to JSON boolean *) + val bool : bool -> t +end + +(** Functor building JSON convertion functions of the Geneweb data types. *) +module Make : + functor (D : ConverterDriver) -> + sig + + (** Convert [dmy] to JSON *) + val conv_dmy : Def.dmy -> D.t + + (** Convert [dmy2] to JSON *) + val conv_dmy2 : Def.dmy2 -> D.t + + (** Convert [cdate] to JSON *) + val conv_cdate : Def.cdate -> D.t + + (** Convert [gen_pers_event_name] to JSON *) + val conv_pevent_name : string Def.gen_pers_event_name -> D.t + + (** Convert [witness_kind] to JSON *) + val conv_event_witness_kind : Def.witness_kind -> D.t + + (** Convert [gen_pers_event] to JSON *) + val conv_pevent : (Gwdb_driver.iper, string) Def.gen_pers_event -> D.t + + (** Convert [gen_title_name] to JSON *) + val conv_title_name : string Def.gen_title_name -> D.t + + (** Convert [gen_title] to JSON *) + val conv_title : string Def.gen_title -> D.t + + (** Convert [relation_kind] to JSON *) + val conv_relation_kind : Def.relation_kind -> D.t + + (** Convert [gen_fam_event_name] to JSON *) + val conv_fevent_name : string Def.gen_fam_event_name -> D.t + + (** Convert [gen_fam_event] to JSON *) + val conv_fevent : (Gwdb_driver.iper, string) Def.gen_fam_event -> D.t + + (** Convert [divorce] to JSON *) + val conv_divorce : Def.divorce -> D.t + + (** Convert [relation_type] to JSON *) + val conv_relation_type : Def.relation_type -> D.t + + (** Convert [gen_relation] to JSON *) + val conv_rparent : (Gwdb_driver.iper, string) Def.gen_relation -> D.t + + (** Convert [death] to JSON *) + val conv_death : Def.death -> D.t + + (** Convert [person] to JSON *) + val conv_person : Gwdb.base -> Gwdb.person -> D.t + + (** Convert [family] to JSON *) + val conv_family : Gwdb.base -> Gwdb.family -> D.t + + end \ No newline at end of file diff --git a/lib/mergeDisplay.mli b/lib/mergeDisplay.mli index 040532b435..257f3207be 100644 --- a/lib/mergeDisplay.mli +++ b/lib/mergeDisplay.mli @@ -4,7 +4,11 @@ open Gwdb open Config +(** Prints person's key on the socket *) val print_someone : config -> base -> person -> unit + +(** Displays a menu for merging two persons *) val print : config -> base -> person -> unit +(** Prints link on the page to continue merging two persons (or two duplications). *) val print_possible_continue_merging : config -> base -> unit diff --git a/lib/mergeDupDisplay.mli b/lib/mergeDupDisplay.mli new file mode 100644 index 0000000000..48d38173d7 --- /dev/null +++ b/lib/mergeDupDisplay.mli @@ -0,0 +1,10 @@ + +(** Displays a menu for merging possible duplications of persons *) +val main_page : Config.config -> Gwdb.base -> unit + +(** Either displays the merge dupliate menu if `answer_y` is not a key of the + request, or a form for merging two persons *) +val answ_ind_y_n : Config.config -> Gwdb.base -> unit + +(** Same than `answ_ind_y_n` but for families *) +val answ_fam_y_n : Config.config -> Gwdb.base -> unit diff --git a/lib/mergeFamDisplay.mli b/lib/mergeFamDisplay.mli new file mode 100644 index 0000000000..6352aec467 --- /dev/null +++ b/lib/mergeFamDisplay.mli @@ -0,0 +1,11 @@ + +(** Displays differences between couples ; relation kind, marriage, marriage place + and divorce. *) +val print_differences : + Config.config -> + Gwdb.base -> + (Gwdb.iper * Gwdb.iper) list -> + Gwdb.ifam * Gwdb.family -> Gwdb.ifam * Gwdb.family -> unit + +(** Displays a menu for merging families. Couples must be identical (modulo reversion). *) +val print : Config.config -> Gwdb.base -> unit diff --git a/lib/mergeFamOk.mli b/lib/mergeFamOk.mli new file mode 100644 index 0000000000..78606b56c7 --- /dev/null +++ b/lib/mergeFamOk.mli @@ -0,0 +1,4 @@ + +(* TODOOCP *) +val print_merge : Config.config -> Gwdb.base -> unit +val print_mod_merge : Config.config -> Gwdb.base -> unit \ No newline at end of file diff --git a/lib/mergeInd.mli b/lib/mergeInd.mli new file mode 100644 index 0000000000..6dc7c489e1 --- /dev/null +++ b/lib/mergeInd.mli @@ -0,0 +1,25 @@ + +(* TODOOCP *) +val reparent_ind : + Gwdb.base -> + (CheckItem.base_warning -> unit) -> Gwdb.iper -> Gwdb.iper -> unit +exception Error_loop of Gwdb.person +exception Same_person +exception Different_sexes of Gwdb.person * Gwdb.person +val merge : + Config.config -> + Gwdb.base -> + Gwdb.person -> + Gwdb.person -> + (Config.config -> + Gwdb.base -> + (Gwdb.iper * Gwdb.iper) list -> Gwdb.person -> Gwdb.person -> 'a) -> + (Config.config -> + Gwdb.base -> + (Gwdb.iper * Gwdb.iper) list -> + Gwdb.ifam * Gwdb.family -> + Gwdb.ifam * Gwdb.family -> Gwdb.person -> Gwdb.person -> 'b) -> + bool * CheckItem.base_warning list +val kill_ancestors : + Config.config -> + Gwdb.base -> bool -> Gwdb.person -> int ref -> int ref -> unit \ No newline at end of file diff --git a/lib/mergeIndDisplay.mli b/lib/mergeIndDisplay.mli new file mode 100644 index 0000000000..5ec15e9208 --- /dev/null +++ b/lib/mergeIndDisplay.mli @@ -0,0 +1,6 @@ + +(** Displays a form for merging two persons *) +val print : Config.config -> Gwdb.base -> unit + +(** Displays the page for killing ancestors (undocumented feature) *) +val print_kill_ancestors : Config.config -> Gwdb.base -> unit diff --git a/lib/mergeIndOk.mli b/lib/mergeIndOk.mli new file mode 100644 index 0000000000..14ab5dfe2a --- /dev/null +++ b/lib/mergeIndOk.mli @@ -0,0 +1,24 @@ +(* TODOOCP *) +val reconstitute : + Config.config -> + Gwdb.base -> + Gwdb.person -> + Gwdb.person -> + (Gwdb.iper, string * string * int * Update.create * string, string) + Def.gen_person +val effective_mod_merge : + Config.config -> + Gwdb.base -> + (Gwdb.iper, Gwdb.iper, string) Def.gen_person -> + (Gwdb.iper, Gwdb.iper, string) Def.gen_person -> + (Gwdb.iper, Update.key, string) Def.gen_person -> + (Config.config -> + Gwdb.base -> + CheckItem.base_warning list -> + (Gwdb.iper, Gwdb.iper, Gwdb.istr) Def.gen_person -> + (Gwdb.iper, Gwdb.ifam) Def.NLDB.page list -> + string -> + string -> + int -> + (Gwdb.iper, Gwdb.ifam) Def.NLDB.page list -> string -> string -> int -> 'a) -> + 'a \ No newline at end of file diff --git a/lib/mergeIndOkDisplay.mli b/lib/mergeIndOkDisplay.mli new file mode 100644 index 0000000000..654c25ea44 --- /dev/null +++ b/lib/mergeIndOkDisplay.mli @@ -0,0 +1,3 @@ +(* TODOOCP *) +val print_merge : Config.config -> Gwdb.base -> unit +val print_mod_merge : Config.config -> Gwdb.base -> unit \ No newline at end of file diff --git a/lib/notesDisplay.mli b/lib/notesDisplay.mli new file mode 100644 index 0000000000..16a193f718 --- /dev/null +++ b/lib/notesDisplay.mli @@ -0,0 +1,21 @@ + +(** Displays the page list in argument *) +val print_linked_list : + Config.config -> + Gwdb.base -> + (Gwdb.iper, Gwdb.ifam) Def.NLDB.page list -> unit + +(** Displays the base notes *) +val print : Config.config -> Gwdb.base -> unit + +(** Displays a text form for writing notes *) +val print_mod : Config.config -> Gwdb.base -> unit + +(** Updates notes *) +val print_mod_ok : Config.config -> Gwdb.base -> unit + +(** Displays a menu to search in notes *) +val print_misc_notes : Config.config -> Gwdb.base -> unit + +(** Same as `print_misc_notes`, with a default search *) +val print_misc_notes_search : Config.config -> Gwdb.base -> unit diff --git a/lib/output.mli b/lib/output.mli new file mode 100644 index 0000000000..bfad54a75c --- /dev/null +++ b/lib/output.mli @@ -0,0 +1,17 @@ + +(** [status conf answer] print HTTP status line to the socket where [answer] is a HTTP status. *) +val status : Config.config -> Def.httpStatus -> unit + +(** Formatter printing of the HTTP header (header line) to the socket. If used without seting HTTP status with [status], + it is set OK. *) +val header : Config.config -> ('a, unit, string, unit) format4 -> 'a + +(** Printing the part of HTTP response body on the socket. If used without seting HTTP status with [status], it is set OK. *) +val print_string : Config.config -> string -> unit + +(** Formatter printing of the part of HTTP response body on the socket. If used without seting HTTP status with [status], + it is set OK. *) +val printf : Config.config -> ('a, unit, string, unit) format4 -> 'a + +(** Flushes (send) printed previously content of the buffer on the socket. *) +val flush : Config.config -> unit \ No newline at end of file diff --git a/lib/perso.mli b/lib/perso.mli index 4a4c2f2dd6..8bcf58077b 100644 --- a/lib/perso.mli +++ b/lib/perso.mli @@ -18,8 +18,13 @@ val interp_templ_with_menu : val interp_notempl_with_menu : (bool -> unit) -> string -> config -> base -> person -> unit +(** Displays the HTML page of a person *) val print : ?no_headers:bool -> config -> base -> person -> unit + +(** Displays the ascendants of the selected person *) val print_ascend : config -> base -> person -> unit + +(** Displays links to pages associated to the person *) val print_what_links : config -> base -> person -> unit val links_to_ind @@ -29,8 +34,15 @@ val links_to_ind -> string * string * int -> (iper, ifam) Def.NLDB.page list +(** Construts from the giving person sosa table strored in the cache. Sosa table contains association + {i person_id -> sosa number} for each person in the base. Person has sosa [Sosa.one] and his ancestors have sosa > [Sosa.one]. + For non ancestor person sosa number is set to [Sosa.zero]. *) val build_sosa_tree_ht : config -> base -> person -> unit + +(** Extract referenced person from environement and constructs for him sosa table wiht [build_sosa_tree_ht]. *) val build_sosa_ht : config -> base -> unit + + val get_sosa_person : person -> Sosa.t val get_single_sosa : config -> base -> person -> Sosa.t val print_sosa : config -> base -> person -> bool -> unit diff --git a/lib/searchName.mli b/lib/searchName.mli index 4f81d72517..d714745a0c 100644 --- a/lib/searchName.mli +++ b/lib/searchName.mli @@ -3,14 +3,27 @@ open Config open Gwdb - +(** [search_key_aux aux conf base str] search persons by misc name [str] (could be one of the mix). If result is empty tries to + it search names with roman number instead of numerals (if they are present in the name). Applies [aux] on the result and removes + all dublicates. Empty persons, persons with private names or persons to which there are no rights to access are not listed. *) val search_key_aux : (config -> base -> person list -> string -> person list) -> config -> base -> string -> person list + +(** Search persons by name that has format {i "firstname surname"}. Dublicates are possible. Empty persons, persons with private + names or persons to which there are no rights to access are not listed. *) val search_by_name : config -> base -> string -> person list + +(** Calls [search_key_aux] with [aux] fonction that makes calls to [search_by_name] if result is empty. *) val search_partial_key : config -> base -> string -> person list + val search_by_sosa : config -> base -> string -> person list + +(** Same as [search_by_name] but search by key that has format {i "firstname.occ surname"}. *) val search_by_key : config -> base -> string -> person list + +(** Calls [search_key_aux] with [aux] fonction that filter result list and only persons whose + [fname^sname] or one of their misc names are equal to key. *) val search_approx_key : config -> base -> string -> person list val print : diff --git a/lib/show/def_show.mli b/lib/show/def_show.mli new file mode 100644 index 0000000000..e42edf495f --- /dev/null +++ b/lib/show/def_show.mli @@ -0,0 +1,609 @@ +type date = Adef.date = Dgreg of dmy * calendar | Dtext of string +and calendar = Adef.calendar = Dgregorian | Djulian | Dfrench | Dhebrew +and dmy = + Adef.dmy = { + day : int; + month : int; + year : int; + prec : precision; + delta : int; +} +and dmy2 = + Adef.dmy2 = { + day2 : int; + month2 : int; + year2 : int; + delta2 : int; +} +and precision = + Adef.precision = + Sure + | About + | Maybe + | Before + | After + | OrYear of dmy2 + | YearInt of dmy2 + +(** Printer for [date] *) +val pp_date : Format.formatter -> date -> unit + +(** Convert [date] to string. *) +val show_date : date -> string + +(** Printer for [calendar] *) +val pp_calendar : + Format.formatter -> + calendar -> unit + +(** Convert [calendar] to string *) +val show_calendar : calendar -> string + +(** Printer for [dmy] *) +val pp_dmy : + Format.formatter -> dmy -> unit + +(** Convert [dmy] to string *) +val show_dmy : dmy -> string + +(** Printer for [dmy2] *) +val pp_dmy2 : + Format.formatter -> dmy2 -> unit + +(** Convert [dmy2] to string *) +val show_dmy2 : dmy2 -> string + +(** Printer for [precision] *) +val pp_precision : + Format.formatter -> + precision -> unit + +(** Convert [precision] to string *) +val show_precision : precision -> string + +type cdate = Adef.cdate + +(** Printer for [cdate] *) +val pp_cdate : + Format.formatter -> + Adef.cdate -> unit + +(** Convert [cdate] to string *) +val show_cdate : Adef.cdate -> string + +type relation_kind = + Def.relation_kind = + Married + | NotMarried + | Engaged + | NoSexesCheckNotMarried + | NoMention + | NoSexesCheckMarried + | MarriageBann + | MarriageContract + | MarriageLicense + | Pacs + | Residence + +(** Printer for [relation_kind] *) +val pp_relation_kind : + Format.formatter -> + relation_kind -> unit + +(** Convert [relation_kind] to string *) +val show_relation_kind : relation_kind -> string + +type divorce = Def.divorce = NotDivorced | Divorced of cdate | Separated + +(** Printer for [divorce] *) +val pp_divorce : + Format.formatter -> + divorce -> unit + +(** Convert [divorce] to string *) +val show_divorce : divorce -> string + +type death_reason = + Def.death_reason = + Killed + | Murdered + | Executed + | Disappeared + | Unspecified + +(** Printer for [death_reason] *) +val pp_death_reason : + Format.formatter -> + death_reason -> unit + +(** Convert [death_reason] to string *) +val show_death_reason : death_reason -> string + +type death = + Def.death = + NotDead + | Death of death_reason * cdate + | DeadYoung + | DeadDontKnowWhen + | DontKnowIfDead + | OfCourseDead + +(** Printer for [death] *) +val pp_death : + Format.formatter -> death -> unit + +(** Convert [death] to string *) +val show_death : death -> string + +type burial = + Def.burial = + UnknownBurial + | Buried of cdate + | Cremated of cdate + +(** Printer for [burial] *) +val pp_burial : + Format.formatter -> + burial -> unit + +(** Convert [burial] to string *) +val show_burial : burial -> string + +type access = Def.access = IfTitles | Public | Private + +(** Printer for [access] *) +val pp_access : + Format.formatter -> + access -> unit + +(** Convert [access] to string *) +val show_access : access -> string + +type 'string gen_title_name = + 'string Def.gen_title_name = + Tmain + | Tname of 'string + | Tnone + +(** Printer for [gen_title_name] *) +val pp_gen_title_name : + (Format.formatter -> + 'string -> unit) -> + Format.formatter -> + 'string gen_title_name -> unit + +(** Convert [gen_title_name] to string *) +val show_gen_title_name : + (Format.formatter -> + 'string -> unit) -> + 'string gen_title_name -> string + +type 'string gen_title = + 'string Def.gen_title = { + t_name : 'string gen_title_name; + t_ident : 'string; + t_place : 'string; + t_date_start : cdate; + t_date_end : cdate; + t_nth : int; +} + +(** Printer for [gen_title] *) +val pp_gen_title : + (Format.formatter -> + 'string -> unit) -> + Format.formatter -> + 'string gen_title -> unit + +(** Convert [gen_title] to string *) +val show_gen_title : + (Format.formatter -> + 'string -> unit) -> + 'string gen_title -> string + +type witness_kind = + Def.witness_kind = + Witness + | Witness_GodParent + | Witness_Officer + +(** Printer for [witness_kind] *) +val pp_witness_kind : + Format.formatter -> + witness_kind -> unit + +(** Convert [witness_kind] to string *) +val show_witness_kind : witness_kind -> string + +type 'string gen_pers_event_name = + 'string Def.gen_pers_event_name = + Epers_Birth + | Epers_Baptism + | Epers_Death + | Epers_Burial + | Epers_Cremation + | Epers_Accomplishment + | Epers_Acquisition + | Epers_Adhesion + | Epers_BaptismLDS + | Epers_BarMitzvah + | Epers_BatMitzvah + | Epers_Benediction + | Epers_ChangeName + | Epers_Circumcision + | Epers_Confirmation + | Epers_ConfirmationLDS + | Epers_Decoration + | Epers_DemobilisationMilitaire + | Epers_Diploma + | Epers_Distinction + | Epers_Dotation + | Epers_DotationLDS + | Epers_Education + | Epers_Election + | Epers_Emigration + | Epers_Excommunication + | Epers_FamilyLinkLDS + | Epers_FirstCommunion + | Epers_Funeral + | Epers_Graduate + | Epers_Hospitalisation + | Epers_Illness + | Epers_Immigration + | Epers_ListePassenger + | Epers_MilitaryDistinction + | Epers_MilitaryPromotion + | Epers_MilitaryService + | Epers_MobilisationMilitaire + | Epers_Naturalisation + | Epers_Occupation + | Epers_Ordination + | Epers_Property + | Epers_Recensement + | Epers_Residence + | Epers_Retired + | Epers_ScellentChildLDS + | Epers_ScellentParentLDS + | Epers_ScellentSpouseLDS + | Epers_VenteBien + | Epers_Will + | Epers_Name of 'string + +(** Printer for [gen_pers_event_name] *) +val pp_gen_pers_event_name : + (Format.formatter -> + 'string -> unit) -> + Format.formatter -> + 'string gen_pers_event_name -> unit + +(** Convert [gen_pers_event_name] to string *) +val show_gen_pers_event_name : + (Format.formatter -> + 'string -> unit) -> + 'string gen_pers_event_name -> string + +type ('person, 'string) gen_pers_event = + ('person, 'string) Def.gen_pers_event = { + epers_name : 'string gen_pers_event_name; + epers_date : cdate; + epers_place : 'string; + epers_reason : 'string; + epers_note : 'string; + epers_src : 'string; + epers_witnesses : ('person * witness_kind) array; +} + +(** Printer for [gen_pers_event] *) +val pp_gen_pers_event : + (Format.formatter -> + 'person -> unit) -> + (Format.formatter -> + 'string -> unit) -> + Format.formatter -> + ('person, 'string) gen_pers_event -> unit + +(** Convert [gen_pers_event] to string *) +val show_gen_pers_event : + (Format.formatter -> + 'person -> unit) -> + (Format.formatter -> + 'string -> unit) -> + ('person, 'string) gen_pers_event -> string + +type 'string gen_fam_event_name = + 'string Def.gen_fam_event_name = + Efam_Marriage + | Efam_NoMarriage + | Efam_NoMention + | Efam_Engage + | Efam_Divorce + | Efam_Separated + | Efam_Annulation + | Efam_MarriageBann + | Efam_MarriageContract + | Efam_MarriageLicense + | Efam_PACS + | Efam_Residence + | Efam_Name of 'string + +(** Printer for [gen_fam_event_name] *) +val pp_gen_fam_event_name : + (Format.formatter -> + 'string -> unit) -> + Format.formatter -> + 'string gen_fam_event_name -> unit + +(** Convert [gen_fam_event_name] to string *) +val show_gen_fam_event_name : + (Format.formatter -> + 'string -> unit) -> + 'string gen_fam_event_name -> string + +type ('person, 'string) gen_fam_event = + ('person, 'string) Def.gen_fam_event = { + efam_name : 'string gen_fam_event_name; + efam_date : cdate; + efam_place : 'string; + efam_reason : 'string; + efam_note : 'string; + efam_src : 'string; + efam_witnesses : ('person * witness_kind) array; +} + +(** Printer for [gen_fam_event] *) +val pp_gen_fam_event : + (Format.formatter -> + 'person -> unit) -> + (Format.formatter -> + 'string -> unit) -> + Format.formatter -> + ('person, 'string) gen_fam_event -> unit + +(** Convert [gen_fam_event] to string *) +val show_gen_fam_event : + (Format.formatter -> + 'person -> unit) -> + (Format.formatter -> + 'string -> unit) -> + ('person, 'string) gen_fam_event -> string + +type relation_type = + Def.relation_type = + Adoption + | Recognition + | CandidateParent + | GodParent + | FosterParent + +(** Printer for [relation_type] *) +val pp_relation_type : + Format.formatter -> + relation_type -> unit + +(** Convert [relation_type] to string *) +val show_relation_type : relation_type -> string + +type ('person, 'string) gen_relation = + ('person, 'string) Def.gen_relation = { + r_type : relation_type; + r_fath : 'person option; + r_moth : 'person option; + r_sources : 'string; +} + +(** Printer for [gen_relation] *) +val pp_gen_relation : + (Format.formatter -> + 'person -> unit) -> + (Format.formatter -> + 'string -> unit) -> + Format.formatter -> + ('person, 'string) gen_relation -> unit + +(** Convert [gen_relation] to string *) +val show_gen_relation : + (Format.formatter -> + 'person -> unit) -> + (Format.formatter -> + 'string -> unit) -> + ('person, 'string) gen_relation -> string + +type sex = Def.sex = Male | Female | Neuter + +(** Printer for [sex] *) +val pp_sex : + Format.formatter -> sex -> unit + +(** Convert [sex] to string *) +val show_sex : sex -> string + +type place = + Def.place = { + other : string; + town : string; + township : string; + canton : string; + district : string; + county : string; + region : string; + country : string; +} + +(** Printer for [place] *) +val pp_place : + Format.formatter -> place -> unit + +(** Convert [place] to string *) +val show_place : place -> string + +type ('iper, 'person, 'string) gen_person = + ('iper, 'person, 'string) Def.gen_person = { + first_name : 'string; + surname : 'string; + occ : int; + image : 'string; + public_name : 'string; + qualifiers : 'string list; + aliases : 'string list; + first_names_aliases : 'string list; + surnames_aliases : 'string list; + titles : 'string gen_title list; + rparents : ('person, 'string) gen_relation list; + related : 'person list; + occupation : 'string; + sex : sex; + access : access; + birth : cdate; + birth_place : 'string; + birth_note : 'string; + birth_src : 'string; + baptism : cdate; + baptism_place : 'string; + baptism_note : 'string; + baptism_src : 'string; + death : death; + death_place : 'string; + death_note : 'string; + death_src : 'string; + burial : burial; + burial_place : 'string; + burial_note : 'string; + burial_src : 'string; + pevents : ('person, 'string) gen_pers_event list; + notes : 'string; + psources : 'string; + key_index : 'iper; +} + +(** Printer for [gen_person] *) +val pp_gen_person : + (Format.formatter -> + 'iper -> unit) -> + (Format.formatter -> + 'person -> unit) -> + (Format.formatter -> + 'string -> unit) -> + Format.formatter -> + ('iper, 'person, 'string) gen_person -> unit + +(** Convert [gen_person] to string *) +val show_gen_person : + (Format.formatter -> + 'iper -> unit) -> + (Format.formatter -> + 'person -> unit) -> + (Format.formatter -> + 'string -> unit) -> + ('iper, 'person, 'string) gen_person -> string + +type fix = Adef.fix + +(** Printer for [fix] *) +val pp_fix : Format.formatter -> Adef.fix -> unit + +(** Convert [fix] to string *) +val show_fix : Adef.fix -> string + +type 'family gen_ascend = + 'family Def.gen_ascend = { + parents : 'family option; + consang : fix; +} + +(** Printer for [gen_ascend] *) +val pp_gen_ascend : + (Format.formatter -> + 'family -> unit) -> + Format.formatter -> + 'family gen_ascend -> unit + +(** Convert [gen_ascend] to string *) +val show_gen_ascend : + (Format.formatter -> + 'family -> unit) -> + 'family gen_ascend -> string + +type 'family gen_union = 'family Def.gen_union = { family : 'family array; } + +(** Printer for [gen_union] *) +val pp_gen_union : + (Format.formatter -> + 'family -> unit) -> + Format.formatter -> + 'family gen_union -> unit + +(** Convert [gen_union] to string *) +val show_gen_union : + (Format.formatter -> + 'family -> unit) -> + 'family gen_union -> string + +type ('person, 'ifam, 'string) gen_family = + ('person, 'ifam, 'string) Def.gen_family = { + marriage : cdate; + marriage_place : 'string; + marriage_note : 'string; + marriage_src : 'string; + witnesses : 'person array; + relation : relation_kind; + divorce : divorce; + fevents : ('person, 'string) gen_fam_event list; + comment : 'string; + origin_file : 'string; + fsources : 'string; + fam_index : 'ifam; +} + +(** Printer for [gen_family] *) +val pp_gen_family : + (Format.formatter -> + 'person -> unit) -> + (Format.formatter -> + 'ifam -> unit) -> + (Format.formatter -> + 'string -> unit) -> + Format.formatter -> + ('person, 'ifam, 'string) gen_family -> unit + +(** Convert [gen_family] to string *) +val show_gen_family : + (Format.formatter -> + 'person -> unit) -> + (Format.formatter -> + 'ifam -> unit) -> + (Format.formatter -> + 'string -> unit) -> + ('person, 'ifam, 'string) gen_family -> string + +type 'person gen_couple = 'person Adef.gen_couple + +(** Printer for [gen_couple] *) +val pp_gen_couple : + (Format.formatter -> + 'person -> unit) -> + Format.formatter -> + 'person gen_couple -> unit + +(** Convert [gen_couple] to string *) +val show_gen_couple : + (Format.formatter -> + 'person -> unit) -> + 'person gen_couple -> string + +type 'person gen_descend = + 'person Def.gen_descend = { + children : 'person array; +} + +(** Printer for [gen_descend] *) +val pp_gen_descend : + (Format.formatter -> + 'person -> unit) -> + Format.formatter -> + 'person gen_descend -> unit + +(** Convert [gen_descend] to string *) +val show_gen_descend : + (Format.formatter -> + 'person -> unit) -> + 'person gen_descend -> string \ No newline at end of file diff --git a/lib/some.mli b/lib/some.mli new file mode 100644 index 0000000000..f772104666 --- /dev/null +++ b/lib/some.mli @@ -0,0 +1,39 @@ + +val surname_not_found : Config.config -> string -> unit + +(** [persons_of_fsname conf base base_strings_of_fsname find proj x] function where: + + - [base_strings_of_fsname base x] is a function that returns the list of first/surnames (as istr) being equal to [x] + - [find istr] is a function that returns the list of persons having [istr] as a first/surname id + - [proj iper] is a function that returns first/surname id from the giving person id. + - [x] is a first/surname or its substring. + + Returns [(l,inj)] where [l] is a list of [(str,istr,iperl)] where [istr] is id of [str] and [iperl] is a list of persons + found that has [istr] as a first/surname such that [str = inj x]*) +val persons_of_fsname : + Config.config -> + Gwdb.base -> + (Gwdb.base -> string -> Gwdb.istr list) -> + (Gwdb.istr -> Gwdb.iper list) -> + (Gwdb.person -> Gwdb.istr) -> + string -> (string * Gwdb.istr * Gwdb.iper list) list * (string -> string) + +val first_name_print : Config.config -> Gwdb.base -> string -> unit + +val surname_print : + Config.config -> + Gwdb.base -> (Config.config -> string -> unit) -> string -> unit + +val search_surname : + Config.config -> Gwdb.base -> string -> Gwdb.iper list + +val search_surname_print : + Config.config -> + Gwdb.base -> (Config.config -> string -> unit) -> string -> unit + +val search_first_name : + Config.config -> + Gwdb.base -> string -> (string * (Mutil.StrSet.t * Gwdb.iper list)) list + +val search_first_name_print : + Config.config -> Gwdb.base -> string -> unit diff --git a/lib/sosa.mli/sosa.mli b/lib/sosa.mli/sosa.mli index 4487c1d01c..84f1be95e0 100644 --- a/lib/sosa.mli/sosa.mli +++ b/lib/sosa.mli/sosa.mli @@ -2,21 +2,54 @@ type t +(** Initial sosa value *) val zero : t + +(** Sosa number describing the subject itself *) val one : t + +(** Equality between 2 sosa *) val eq : t -> t -> bool + +(** Tells if one sosa number is greater then another *) val gt : t -> t -> bool + +(** Comparison function between sosa numbers *) val compare : t -> t -> int + +(** Addition of 2 sosa numbers *) val add : t -> t -> t + +(** Substraction of 2 sosa numbers *) val sub : t -> t -> t + +(** Returns sosa number multiplied by 2. Represents father's sosa number of + person with the giving sosa. *) val twice : t -> t + +(** Returns sosa number divided by 2. If person has a child then result number + will be child's sosa number. *) val half : t -> t + +(** Tells if sosa number is even. Even numbers describe fathers, odd - mothers for each generation. *) val even : t -> bool + +(** Addition of sosa number with a integer *) val inc : t -> int -> t + +(** Multiply sosa number with an integer *) val mul : t -> int -> t + +(** The power of the sosa number *) val exp : t -> int -> t + +(** Divide sosa number by an integer *) val div : t -> int -> t + +(** Calculate module of sosa number comparing to integer *) val modl : t -> int -> t + +(** Retruns generation of sosa number. *) val gen : t -> int (** [branches sosa] @@ -26,8 +59,13 @@ val gen : t -> int *) val branches : t -> int list +(** Converts sosa from integer *) val of_int : int -> t + +(** Converts sosa from string *) val of_string : string -> t + +(** Converts sosa to string *) val to_string : t -> string (** See {!val:Mutil.string_of_int_sep} *) diff --git a/lib/srcfileDisplay.mli b/lib/srcfileDisplay.mli index 927675e299..604e0df010 100644 --- a/lib/srcfileDisplay.mli +++ b/lib/srcfileDisplay.mli @@ -12,6 +12,7 @@ val print_start : config -> base -> unit val incr_welcome_counter : config -> (int * int * string) option val incr_request_counter : config -> (int * int * string) option +(** Compute administration file path with giving name (search inside {i cnt} directory) *) val adm_file : string -> string val source_file_name : config -> string -> string diff --git a/lib/stats.mli b/lib/stats.mli new file mode 100644 index 0000000000..351c458cdc --- /dev/null +++ b/lib/stats.mli @@ -0,0 +1,30 @@ + +(** Statistic about persons in database *) +type stats = { + (* Number of men *) + mutable men : int; + (* Number of women *) + mutable women : int; + (* Number of persons with neuter sex *) + mutable neutre : int; + (* Number of persons with unknown first name and surname *) + mutable noname : int; + (* Oldest father with age when he became father *) + mutable oldest_father : int * Gwdb.person; + (* Oldest mother with age when she became mother *) + mutable oldest_mother : int * Gwdb.person; + (* Youngest father with age when he became father *) + mutable youngest_father : int * Gwdb.person; + (* Youngest mother with age when he became mother *) + mutable youngest_mother : int * Gwdb.person; + (* Oldest dead person with his age when he died *) + mutable oldest_dead : int * Gwdb.person; + (* Oldest person that is still alive with his age *) + mutable oldest_still_alive : int * Gwdb.person; +} + +(** Compute [stats] from the database's persons *) +val stat_base : Gwdb.base -> stats + +(** Prints statistic [stats] on stdout *) +val print_stats : Gwdb.base -> stats -> unit \ No newline at end of file diff --git a/lib/templ.mli b/lib/templ.mli index fd2d66f14a..6705e11305 100644 --- a/lib/templ.mli +++ b/lib/templ.mli @@ -25,7 +25,11 @@ val interp_ast : (**) val input_templ : config -> string -> ast list option + +(** Evaluates and prints content of {i cpr} template. If template wasn't found prints basic copyrigth HTML structure. *) val print_copyright : config -> unit + +(** Calls [print_copyright] with config where variable {i with_logo} is set to "yes" *) val print_copyright_with_logo : config -> unit val include_hed_trl : config -> string -> unit val copy_from_templ : config -> string env -> in_channel -> unit diff --git a/lib/update.ml b/lib/update.ml index fc99ec41e5..5f1944b9aa 100644 --- a/lib/update.ml +++ b/lib/update.ml @@ -1096,7 +1096,7 @@ let print_create_conflict conf base p var = prerr conf err @@ fun () -> Output.print_string conf (string_of_error conf err) ; let free_n = - Gutil.find_free_occ base (p_first_name base p) (p_surname base p) 0 + Gutil.find_free_occ base (p_first_name base p) (p_surname base p) in Output.printf conf "<form method=\"post\" action=\"%s\">\n" conf.command; List.iter diff --git a/lib/updateData.ml b/lib/updateData.ml index 9462377b8b..7257626695 100644 --- a/lib/updateData.ml +++ b/lib/updateData.ml @@ -237,7 +237,7 @@ let update_person conf base old new_input p = else if old = s_first_name then new_istr, Gutil.find_free_occ base (sou base new_istr) - (sou base (get_surname p)) 0 + (sou base (get_surname p)) else first_name, get_occ p in let first_names_aliases = get_first_names_aliases p in @@ -268,7 +268,7 @@ let update_person conf base old new_input p = else if old = s_surname then new_istr, Gutil.find_free_occ base (sou base (get_first_name p)) - (sou base new_istr) 0 + (sou base new_istr) else surname, get_occ p in let surnames_aliases = get_surnames_aliases p in diff --git a/lib/updateDataDisplay.mli b/lib/updateDataDisplay.mli new file mode 100644 index 0000000000..7979016ec8 --- /dev/null +++ b/lib/updateDataDisplay.mli @@ -0,0 +1,7 @@ + +(** Displays a menu for updating Geneweb's dictionary of names, last names, + locations, sources and professions. *) +val print_mod : Config.config -> Gwdb.base -> unit + +(** Updates persons linked to the updated data. *) +val print_mod_ok : Config.config -> Gwdb.base -> unit diff --git a/lib/updateFam.ml b/lib/updateFam.ml index 27e339d238..3333d51792 100644 --- a/lib/updateFam.ml +++ b/lib/updateFam.ml @@ -840,7 +840,13 @@ let change_order u ifam n = [] -> if i = n then [ifam] else [] | fam :: faml -> if ifam = fam then - if i = n then ifam :: loop (i + 1) (fam :: faml) else loop i faml + (* S: The following code is strange: if i=n, fam is added to the iterated list; + at the next iteration, we reach the same block and i = n+1, hence fam is removed *) + if i = n + then ifam :: loop (i + 1) (fam :: faml) + else loop i faml + + (* S: Same remark than before: fam is added to the iterated list, hence discarded after *) else if i = n then ifam :: loop (i + 1) (fam :: faml) else fam :: loop (i + 1) faml in diff --git a/lib/updateFam.mli b/lib/updateFam.mli index f1276c67ec..94a6fb655b 100644 --- a/lib/updateFam.mli +++ b/lib/updateFam.mli @@ -5,22 +5,40 @@ open Config open Def open Gwdb +(** Returns the update key associated to a person *) val person_key : base -> iper -> Update.key +(** The main page for updating families. *) val print_update_fam : config -> base -> (Update.key, ifam, string) gen_family * Update.key gen_couple * Update.key gen_descend -> string -> unit +(** Displays the form for adding families *) val print_add : config -> base -> unit + +(** Displays a form for updating a family *) val print_mod : config -> base -> unit + +(** Displays a page for validating the deletion of the family in argument *) val print_del : config -> base -> unit + +(** Displays a menu for inverting the order of two families *) val print_inv : config -> base -> unit + +(** Associates parents to a person *) val print_add_parents : config -> base -> unit +(** [change_order p f i] + Returns the families of `p` where `f` is at the ith position. + `i` must not be 0. *) val change_order : person -> ifam -> int -> ifam list + +(** Displays a menu to change the family order *) val print_change_order : config -> base -> unit + +(** Displays the form for changing the order of events for a family *) val print_change_event_order : config -> base -> unit val string_family_of : diff --git a/lib/updateFamOk.ml b/lib/updateFamOk.ml index cb8f3ef974..1733b482b4 100644 --- a/lib/updateFamOk.ml +++ b/lib/updateFamOk.ml @@ -24,6 +24,7 @@ let raw_get conf key = Some v -> v | None -> failwith (key ^ " unbound") +(* This is exactly the same function than before! *) let get conf key = match p_getenv conf.env key with Some v -> v @@ -325,15 +326,29 @@ let rec reconstitute_sorted_fevents conf cnt = let el = reconstitute_sorted_fevents conf (cnt + 1) in (id, pos) :: el | _ -> [] -let reconstitute_from_fevents nsck empty_string fevents = +(* S: + * why is marriage record transformed into a tuple? + *) +let reconstitute_from_fevents + (nsck : bool) + (empty_string : 'string) + (fevents : ('person, 'string) Def.gen_fam_event list) = (* On tri les évènements pour être sûr. *) let fevents = CheckItem.sort_events (fun evt -> CheckItem.Fsort evt.efam_name) (fun evt -> evt.efam_date) fevents in - let found_marriage = ref None in - let found_divorce = ref None in + let found_marriage : ( + Def.relation_kind + * Def.cdate + * 'string + * 'string + * 'string + * ('person * Def.witness_kind) array + ) option ref = ref None in + + let found_divorce : Def.divorce option ref = ref None in let mk_marr evt kind = let e = Some (kind, evt.efam_date, evt.efam_place, evt.efam_note, evt.efam_src, evt.efam_witnesses) in match !found_marriage with @@ -579,7 +594,7 @@ let print_err_parents conf base p = (Update.string_of_error conf err) (Utf8.capitalize_fst (transl conf "first free number")) (Util.transl conf ":") - (Gutil.find_free_occ base (p_first_name base p) (p_surname base p) 0); + (Gutil.find_free_occ base (p_first_name base p) (p_surname base p)); Update.print_return conf let print_err_sex conf base p = diff --git a/lib/updateFamOk.mli b/lib/updateFamOk.mli new file mode 100644 index 0000000000..ce912a8921 --- /dev/null +++ b/lib/updateFamOk.mli @@ -0,0 +1,82 @@ + +(** [reconstitute_from_fevents nsck empty_string family_events] + Iterate over family's events and returns a tuple with: + + - marriage information (relation kind, date, place, notes, source); + - divorce information; + - marriage witnesses; + + Boolean `nsck' is true if no check have been made on the married + persons sex. + *) +val reconstitute_from_fevents : + bool -> + 'string -> + ('person, 'string) Def.gen_fam_event list -> + (Def.relation_kind * Def.cdate * 'string * 'string * 'string) * Def.divorce * + ('person * Def.witness_kind) array + +val effective_mod : + Config.config -> + Gwdb.base -> + bool -> + (Update.key, Gwdb.ifam, string) Def.gen_family -> + Update.key Def.gen_couple -> + Update.key Def.gen_descend -> + Gwdb.ifam * (Gwdb.iper, Gwdb.ifam, Gwdb.istr) Def.gen_family * + Gwdb.iper Def.gen_couple * Gwdb.iper Def.gen_descend + +(** Removes a family from the base *) +val effective_del : + Config.config -> Gwdb.base -> Gwdb.iper -> Gwdb.family -> unit + +val all_checks_family : + Config.config -> + Gwdb.base -> + Gwdb.ifam -> + (Gwdb.iper, Gwdb.ifam, Gwdb.istr) Def.gen_family -> + Gwdb.iper Def.gen_couple -> + Gwdb.iper Def.gen_descend -> + Update.key Def.gen_couple * + Update.key Def.gen_descend * + (('i array * 'j array) * ('i array * 'j array)) option -> + CheckItem.base_warning list * CheckItem.base_misc list + +(** Displays a family page in HTML after an update. + Used by MergeFamOk *) +val print_family : + Config.config -> + Gwdb.base -> + CheckItem.base_warning list * CheckItem.base_misc list -> + Gwdb.iper Adef.gen_couple -> Gwdb.iper Def.gen_descend -> unit + +(** Deletes a family and displays a page confirming its deletion *) +val print_del : Config.config -> Gwdb.base -> unit + +(** Displays the page after validating the addition of a family in the base *) +val print_add : Config.config -> Gwdb.base -> unit + +val print_add_parents : Config.config -> Gwdb.base -> unit + +val print_mod_aux : + Config.config -> + Gwdb.base -> + ((string * string * int * Update.create * string, Gwdb.ifam, + string) + Def.gen_family -> + (string * string * int * Update.create * string) Def.gen_couple -> + (string * string * int * Update.create * string) Def.gen_descend -> + unit) -> + unit + +val print_mod : Config.config -> Gwdb.base -> unit + +(** Reverses families *) +val print_inv : Config.config -> Gwdb.base -> unit + +(** Changes the family order for a person *) +val print_change_order_ok : Config.config -> Gwdb.base -> unit + +(** Changes the evenements order for a family *) +val print_change_event_order : Config.config -> Gwdb.base -> unit + diff --git a/lib/updateInd.ml b/lib/updateInd.ml index 763dea9107..fdedd65984 100644 --- a/lib/updateInd.ml +++ b/lib/updateInd.ml @@ -759,6 +759,7 @@ let print_foreach print_ast _eval_expr = in print_foreach +(* S: check on `m` should be made beforehand; what about plugins? *) let print_update_ind conf base p digest = match p_getenv conf.env "m" with Some ("MRG_IND_OK" | "MRG_MOD_IND_OK") | Some ("MOD_IND" | "MOD_IND_OK") | diff --git a/lib/updateInd.mli b/lib/updateInd.mli index 419fa93077..2928552470 100644 --- a/lib/updateInd.mli +++ b/lib/updateInd.mli @@ -7,11 +7,20 @@ open Gwdb val string_person_of : base -> person -> (iper, Update.key, string) gen_person +(** The main HTML page displayed after an update. + Based on template updind.txt *) val print_update_ind : config -> base -> (iper, Update.key, string) gen_person -> string -> unit +(** Displays an HTML form with empty fields for adding a person *) val print_add : config -> base -> unit + +(** Displays a page for validating the deletion of a person *) val print_del : config -> base -> unit + +(** Displays a form for updating a person *) val print_mod : config -> base -> unit + +(** Displays the form for changing the order of events for a person *) val print_change_event_order : config -> base -> unit diff --git a/lib/updateIndOk.ml b/lib/updateIndOk.ml index 56d79be8f3..7bc6db8a77 100644 --- a/lib/updateIndOk.ml +++ b/lib/updateIndOk.ml @@ -766,7 +766,7 @@ let print_conflict conf base p = Update.prerr conf err @@ fun () -> Update.print_error conf base (Update.UERR_already_defined (base, p, "")); let free_n = - Gutil.find_free_occ base (p_first_name base p) (p_surname base p) 0 + Gutil.find_free_occ base (p_first_name base p) (p_surname base p) in Output.print_string conf "<ul>\n"; Output.print_string conf "<li>"; diff --git a/lib/updateIndOk.mli b/lib/updateIndOk.mli index bac392e1af..4c9db3817d 100644 --- a/lib/updateIndOk.mli +++ b/lib/updateIndOk.mli @@ -2,8 +2,10 @@ open Config open Def open Gwdb +(** Removes a person from the base *) val effective_del_no_commit : base -> (iper, iper, string) gen_person -> unit +(** Adds to the diff the deletion of a person *) val effective_del_commit : config -> base -> (iper, iper, string) gen_person -> unit (** [effective_del] applies [effective_del_no_commit] and [effective_del_commit] *) @@ -18,21 +20,33 @@ val effective_mod -> (iper, Update.key, string) gen_person -> (iper, iper, istr) gen_person +(** Tries to modifies a person and displays a success page if successful *) val print_mod : ?prerr:(config -> base -> Update.update_error -> unit) -> config -> base -> unit +(** Patches the informations of a person by checking the order of events: + for example, a birth should happen before the death of a mother. *) val all_checks_person : base -> (iper, iper, istr) gen_person -> ifam gen_ascend -> - ifam gen_union -> CheckItem.base_warning list + ifam gen_union -> CheckItem.base_warning list + val print_mod_aux : config -> base -> ((iper, Update.key, string) gen_person -> unit) -> unit +(** Renames the image associated to a person *) val rename_image_file : config -> base -> person -> (iper, iper, string) gen_person -> unit +(** Tries to add a person to the base and displays a success HTML page if + successful *) val print_add : config -> base -> unit + +(** Tries to remove a person from the base and displays a success HTML page if + successful *) val print_del : config -> base -> unit +(** Tries to change the order of events for a person and displays a success HTML + page if successful *) val print_change_event_order : config -> base -> unit (* Ajout pour l'API *) diff --git a/lib/util.ml b/lib/util.ml index c7a842f198..7ceec8dec9 100644 --- a/lib/util.ml +++ b/lib/util.ml @@ -1890,13 +1890,13 @@ let source_image_file_name bname str = List.fold_right Filename.concat [base_path ["src"] bname; "images"] str in let fname2 = - List.fold_right Filename.concat [Secure.bd (); "src"; "images"] str + List.fold_right Filename.concat [Secure.base_dir (); "src"; "images"] str in if Sys.file_exists fname1 then fname1 else fname2 let image_file_name str = let fname1 = - List.fold_right Filename.concat [Secure.bd (); "images"] str + List.fold_right Filename.concat [Secure.base_dir (); "images"] str in if Sys.file_exists fname1 then fname1 else search_in_assets (Filename.concat "images" str) diff --git a/lib/util.mli b/lib/util.mli index 4f24f71848..8189c76095 100644 --- a/lib/util.mli +++ b/lib/util.mli @@ -4,111 +4,241 @@ open Config open Def open Gwdb +(** Returns the current dir + (changed by `gwd` if geneweb is running on windows) *) val cnt_dir : string ref + +(** Returns the image prefix (conf.image_prefix) *) val image_prefix : config -> string + +(** Alias for !GWPARAM.base_path *) val base_path : string list -> string -> string + +(** Alias for !GWPARAM.bpath *) val bpath : string -> string +(** Checks that the file in argument belong to one of the asserts dir + (defined in the Secure module) *) val search_in_assets : string -> string +(** Returns the path to the template file in parameter *) val etc_file_name : config -> string -> string +(** Returns the date of the base directory last update *) val escache_value : base -> string + +(** Commits the patches and logs the modification *) val commit_patches : config -> base -> unit + val update_wf_trace : config -> string -> unit +(** Get referer (the page you came from to the current page) page from HTTP request *) val get_referer : config -> string val no_html_tags : string -> string val clean_html_tags : string -> string list -> string +(** Prints HTTP response headers with giving content type (default : {i text/html}) on the socket. *) val html : ?content_type:string -> config -> unit + +(** Prints HTTP response with code 401 (Unauthorized) and error page with giving message *) val unauthorized : config -> string -> unit val string_of_ctime : config -> string +(** Returns link to the current command (database name after domain name and port in url) with query string + that containts bindings from [conf.henv] and [conf.senv]. Doesn't add binding [(k,v)] when: + - k = "oc" or "ocz" and v = "0" + - v = "" *) val commd : config -> string + +(** Same as [commd] but returns without separator '&' at the end. *) val commd_2 : config -> string val prefix_base : config -> string val prefix_base_password : config -> string val prefix_base_2 : config -> string val prefix_base_password_2 : config -> string + +(** Creates a hidden HTML input for every key and value in [conf.henv] and [conf.senv]. + Used to include immutable environement bindings in the HTML form. *) val hidden_env : config -> unit +(** [nobtit conf base p] returns list of titles of [p] from the [base] + that respects constraints imposed by [conf.allowed_titles] and + [conf.denied_titles] *) val nobtit : config -> base -> person -> title list val strictly_after_private_years : config -> dmy -> bool + +(** Alias to !GWPARAM.p_auth *) val authorized_age : config -> base -> person -> bool val is_old_person : config -> (iper, iper, istr) gen_person -> bool val start_with_vowel : string -> bool +(** Returns URL query string to access nth person *) val acces_n : config -> base -> string -> person -> string val acces : config -> base -> person -> string val wprint_hidden_person : config -> base -> string -> person -> unit + +(** Tells if person could be accessed by his first name and surname *) val accessible_by_key : config -> base -> person -> string -> string -> bool +(** [geneweb_link conf href s] Returns HTML link to actual geneweb's command (database name) with additional (to those defind by [commd]) + argument [href] and [s] as textual content of the link. *) val geneweb_link : config -> string -> string -> string + +(** Prints on the socket link created by [geneweb_link]. *) val wprint_geneweb_link : config -> string -> string -> unit +(** Tells if person is restrited to acccess. If mode `use_restrict` is + disabled returns always [false]. *) val is_restricted : config -> base -> iper -> bool + +(** Tells if person is hiden (if his surname is empty) *) val is_hidden : person -> bool +(** Returns person with giving id from the base. If person is restrited to +acccess returns empty person with giving id. *) val pget : config -> base -> iper -> person + +(** Remplaces string ids inside person's entry by real strings *) val string_gen_person : base -> (iper, iper, istr) gen_person -> (iper, iper, string) gen_person + +(** Remplaces string ids inside family's entry by real strings *) val string_gen_family : base -> (iper, ifam, istr) gen_family -> (iper, ifam, string) gen_family +(** Type that defines couple of functions allowing to access to person's first name + and surname. *) type p_access = (base -> person -> string) * (base -> person -> string) + +(** Standard access (p_first_name, p_surname). *) val std_access : p_access + +(** Raw access (sou + get_name). *) val raw_access : p_access -(* Fonctions d'écriture du nom et prénom d'un individu en fonction de : *) -(* - son/ses titre de noblesse *) -(* - son/ses nom public *) -(* - son/ses sobriquets ... *) +(** Returns person's first name and surname HTML description depending on : + - his public name + - his qualifiers + If person is hiden returns ".....". If person's names are hiden + or access to them is denied returns "x x" *) val gen_person_text : p_access -> config -> base -> person -> string + +(** Same as [gen_person_text] but doesn't encapsulates description in HTML + tag <em>. *) val gen_person_text_no_html : p_access -> config -> base -> person -> string + +(** Returns either person's first name and surname either title and qualifiers + HTML description *) val gen_person_text_without_title : p_access -> config -> base -> person -> string + +(** [gen_person_title_text reference paccess conf base p] returns HTML structure + of person that describes person's first name surname and main title. [reference] + is used to either encapsulate structure in the link (or other type + of maniplations). *) val gen_person_title_text : (config -> base -> person -> string -> string) -> p_access -> config -> base -> person -> string + +(** Makes call to [gen_person_text] with [std_access] *) val person_text : config -> base -> person -> string + +(** Makes call to [gen_person_text_no_html] with [std_access] *) val person_text_no_html : config -> base -> person -> string + +(** Same as [gen_person_text] but doesn't display surname *) val person_text_without_surname : config -> base -> person -> string + +(** Same as [gen_person_text] but : + - doesn't display surname + - returns HTML description even if person's names are hiden + or access to them is denied (don't print "x x") *) val person_text_no_surn_no_acc_chk : config -> base -> person -> string + +(** Makes call to [gen_person_text_without_title] with [std_access] *) val person_text_without_title : config -> base -> person -> string + +(** Returns main person's title. If person doesn't have it, then returns first title + from the list. *) val main_title : config -> base -> person -> title option + +(** Returns person's first name and surname text description depending on + person's title *) val titled_person_text : config -> base -> person -> title -> string + +(** Returns HTML representation of title's identifier with its place (if exists) *) val one_title_text : base -> title -> string + +(** Returns HTML structure of person that describes person's first name surname + and main title. Calls [gen_person_title_text] with [no_reference]. *) val person_title_text : config -> base -> person -> string + +(** Returns HTML representation of person's main title (or first title if + main doesn't exists). If person doesn't have a title or if access to + person isn't granted returns empty string *) val person_title : config -> base -> person -> string val child_of_parent : config -> base -> person -> string +(** [reference conf base p desc] returns HTML link to the person + where [desc] is content of the link (generaly his first name and + surname description). If person is hidden returns [desc] (do not + create link). *) val reference : config -> base -> person -> string -> string + +(** Same as [reference] but link doesn't has "id" field *) val reference_noid : config -> base -> person -> string -> string + +(** [reference conf base p desc] returns [desc] without creating a link *) val no_reference : config -> base -> person -> string -> string + +(** Retruns HTML link to the person that contains its first name, surname and person's + nobility title. Calls [gen_person_title_text] with [reference]. *) val referenced_person_title_text : config -> base -> person -> string + +(** Returns HTML link to the person that contains its first name and surname. *) val referenced_person_text : config -> base -> person -> string + +(** Returns HTML link to the person that contains its first name. *) val referenced_person_text_without_surname : config -> base -> person -> string val update_family_loop : config -> base -> person -> string -> string +(** Returns value associated to the label in environnement *) val p_getenv : (string * string) list -> string -> string option + +(** Returns integer value associated to the label in environnement *) val p_getint : (string * string) list -> string -> int option + +(** Create association list from two types of string. First has format : [[k1=v1;k2=v2]]. Second : [[k1=v1&k2=v2]]. + For both returns list [[("k1","v1"); ("k2","v2")]]. *) val create_env : string -> (string * string) list +(** [open_etc_file fname] search for template {i etc/fname.txt} inside the base directory or inside one of assets directories. + Returns input channel and the path to giving template. *) val open_etc_file : string -> (in_channel * string) option val open_hed_trl : config -> string -> in_channel option -val open_templ : config -> string -> in_channel option + +(** [open_etc_file fname] search for template {i etc/fname.txt} using [config] or inside one of assets directories. + Returns input channel and the path to giving template. *) val open_templ_fname : config -> string -> (in_channel * string) option + +(** Same as [open_templ_fname] but returns only input channel of the giving template file *) +val open_templ : config -> string -> in_channel option val string_of_place : config -> string -> string val place_of_string : config -> string -> place option val allowed_tags_file : string ref + +(** Returns additional attributes for <body> tag from [config]. *) val body_prop : config -> string + +(** Prints all messages send to wizard (or friend) on the socket. Messages are located in + {i <basename>/etc/mess_wizzard.txt} (messages destinated to all wizards) and in + {i <basename>/etc/mess_wizzard_<user>.txt} (messages destinated to considered wizard). *) val message_to_wizard : config -> unit val of_course_died : config -> person -> bool @@ -136,13 +266,23 @@ type ('a, 'b) format2 = ('a, unit, string, 'b) format4 val check_format : ('a, 'b) format2 -> string -> ('a, 'b) format2 option val valid_format : ('a, 'b) format2 -> string -> ('a, 'b) format2 +(** Find translation of given english word in [conf.lexicon] *) val transl : config -> string -> string + +(** [transl_nth conf w n] translate word [w] and returns [n]'th field of its translation (with [nth_field]). *) val transl_nth : config -> string -> int -> string val transl_decline : config -> string -> string -> string val ftransl : config -> ('a, 'b) format2 -> ('a, 'b) format2 val ftransl_nth : config -> ('a, 'b) format2 -> int -> ('a, 'b) format2 val fdecline : ('a, 'b) format2 -> string -> ('a, 'b) format2 val fcapitale : ('a, 'b) format2 -> ('a, 'b) format2 + +(** [nth_field str n] gets [n]'th field of string that separate its fields with "/". + Example : + - nth_field "a/b/</c>/d" 0 = a + - nth_field "a/b/</c>/d" 1 = b + - nth_field "a/b/</c>/d" 2 = </c> + - nth_field "a/b/</c>/d" 3 = d *) val nth_field : string -> int -> string val cftransl : config -> string -> string list -> string val translate_eval : string -> string @@ -155,8 +295,10 @@ val translate_eval : string -> string val transl_a_of_b : config -> string -> string -> string -> string val transl_a_of_gr_eq_gen_lev : config -> string -> string -> string -> string +(** Colorise HTML element with [conf.highlight] color. *) val std_color : config -> string -> string +(** Sex index (0 for male, 1 for female, 2 for neuter) *) val index_of_sex : sex -> int val string_of_pevent_name : @@ -187,7 +329,7 @@ val husband_wife : config -> base -> person -> bool -> string (** [find_person_in_env conf base suff] Reconstitutes the key of a person from [conf.env], - using ["i" ^ suff] or ["n" ^ suffix] + ["p" ^ suff] + ["oc" ^ suff] + using ["i" ^ suff] or ["n" ^ suff] + ["p" ^ suff] + ["oc" ^ suff] *) val find_person_in_env : config -> base -> string -> person option @@ -202,7 +344,11 @@ val default_sosa_ref : config -> base -> person option val find_sosa_ref : config -> base -> person option val update_gwf_sosa : config -> base -> iper * (string * string * int) -> unit +(** Returns server host name with its port number (if different from 80). *) val get_server_string : config -> string + +(** Returns request string. Request string has format {i scriptname?querystring} where + scriptname is a path to the script in URI. *) val get_request_string : config -> string val create_topological_sort : config -> base -> (iper, int) Gwdb.Marker.t @@ -213,7 +359,7 @@ val create_topological_sort : config -> base -> (iper, int) Gwdb.Marker.t val p_of_sosa : config -> base -> Sosa.t -> person -> person option (** [branch_of_sosa conf base sosa p0] - Get all the lineage to go from [p0]'s sosa [sosa] to [p0] + Get all the lineage to go from [p0]'s ancestor with sosa number [sosa] to [p0] *) val branch_of_sosa : config -> base -> Sosa.t -> person -> person list option @@ -231,22 +377,45 @@ val old_branch_of_sosa : config -> base -> iper -> Sosa.t -> (iper * sex) list o val old_sosa_of_branch : config -> base -> (iper * sex) list -> Sosa.t val has_image : config -> base -> person -> bool + +(** [image_file_name fname] search for image {i images/fname} inside the base and assets directories. + Retrun the path to found file or [fname] if file isn't found. *) val image_file_name : string -> string + +(** Returns path to the image file with the giving name in directory {i sources}. *) val source_image_file_name : string -> string -> string +(** Returns width and height of an image. *) val image_size : string -> (int * int) option + +(** [limited_image_size max_wid max_hei fname defsize] returns image size of [fname]. If width and height are greater + then their limits [max_wid] and [max_hei] then returns reduced size with the same proportions. [defsize] is returned + if image filename is empty. *) val limited_image_size : int -> int -> string -> (int * int) option -> (int * int) option + +(** Returns path to the personal image. In details, returns [(is_filename,source,size)] where [is_filename] tells if [source] + is a filename or URL and [size] is a size of image (width x height). *) val image_and_size : config -> base -> person -> (string -> (int * int) option -> (int * int) option) -> (bool * string * (int * int) option) option +(** Returns default image name calculated from person's first name, surname + and occurence number. For example : Jean Claude DUPOND 3 => jean_claude.3.dupond *) val default_image_name_of_key : string -> string -> int -> string + +(** Returns default image name calculated from person's key. *) val default_image_name : base -> person -> string + +(** Searchs personal image (portrait) inside the base directory by looking up its default name + and tryig to deduce its extension. Returns path to the image if found. *) val auto_image_file : config -> base -> person -> string option +(** Trims and remplaces all non-printable characters by spaces in the given string. *) val only_printable : string -> string + +(** Same as [only_printable] but also accepts '\n'. *) val only_printable_or_nl : string -> string val relation_type_text : config -> relation_type -> int -> string @@ -258,7 +427,10 @@ val browser_doesnt_have_tables : config -> bool val doctype : config -> string +(** Prints on the socket beginning of the <table> tag untill first opened <td> where the text is centred *) val begin_centered : config -> unit + +(** Prints on the socket end of the column and table opened by [begin_centered] *) val end_centered : config -> unit val print_alphab_list @@ -275,10 +447,13 @@ val short_f_month : int -> string (* Reading password file *) +(** Authenticated user from from authorization file. *) type auth_user = { au_user : string; au_passwd : string; au_info : string } +(** Read all authenticated users with their passwords from authorization file (associated to {i "wizard_passwd_file"} in [conf.base_env]) *) val read_gen_auth_file : string -> auth_user list +(** [is_that_user_and_password auth_sheme user paswd] verify if given user with his password correspond to the authentication scheme. *) val is_that_user_and_password : auth_scheme_kind -> string -> string -> bool (* Searching *) @@ -291,14 +466,18 @@ val html_highlight : bool -> string -> string -> string val wprint_in_columns : config -> ('a -> string) -> ('a -> unit) -> 'a list -> unit -(* Variable that use also private flag of person *) +(** Tells if person's names are hiden (if person's access is [Private] or if mode [conf.hide_names] is enabled). *) val is_hide_names : config -> person -> bool +(** [reduce_list n l] takes [n] first elements from the list [l] *) val reduce_list : int -> 'a list -> 'a list val print_reference : config -> string -> int -> string -> unit +(** Print a tip with the specified text *) val gen_print_tips : config -> string -> unit + +(** Print a tip that tells to {i Click an individual below to calculate the family link.} *) val print_tips_relationship : config -> unit val print_image_sex : config -> person -> int -> unit @@ -359,16 +538,18 @@ module IperSet : sig include Set.S with type elt = iper end module IfamSet : sig include Set.S with type elt = ifam end (**/**) -(* [copy_from_templ_ref] is for internal usage only. Use copy_from_templ *) + +(** Reference by default [Templ.copy_from_templ] *) val copy_from_templ_ref : (config -> (string * string) list -> in_channel -> unit) ref + (* [copy_from_templ_ref] is for internal usage only. Use copy_from_templ *) (**/**) -(** [include_template failure conf env fname] - Search [fname] in templates path en interpret it with [env] provided. - - If the file can not be found, failure is called. +(** [include_template conf env fname failure] + Search [fname] in templates path and interpret it with global environnement [env] provided. + Interpretation of template write directly its results in the socket. + If the file can not be found, [failure] is called. *) val include_template : config diff --git a/lib/util/buff.ml b/lib/util/buff.ml index 2b42d59045..520a70c5d6 100644 --- a/lib/util/buff.ml +++ b/lib/util/buff.ml @@ -2,12 +2,16 @@ module Make () = struct + let buff = ref (Bytes.create 80) + let store len x = if len >= Bytes.length !buff then buff := Bytes.extend !buff 0 (Bytes.length !buff); Bytes.set !buff len x; succ len + + (* gstore and mstore axillary function *) let unsafe_gstore len s si slen = let newlen = len + slen in if newlen > Bytes.length !buff then @@ -16,16 +20,25 @@ module Make () = end; Bytes.blit_string s si !buff len slen; newlen + let mstore len s = unsafe_gstore len s 0 (String.length s) + let gstore len s si slen = unsafe_gstore len s si (min slen (String.length s - si)) + let get len = Bytes.sub_string !buff 0 len + end +(* Global buffer *) module BB = Make (struct end) -let get = BB.get +let buff = BB.buff + let store = BB.store -let gstore = BB.gstore + let mstore = BB.mstore -let buff = BB.buff + +let gstore = BB.gstore + +let get = BB.get diff --git a/lib/util/buff.mli b/lib/util/buff.mli new file mode 100644 index 0000000000..af07d67f3b --- /dev/null +++ b/lib/util/buff.mli @@ -0,0 +1,46 @@ + +(** Functor building a local implementation of the buffer. *) +module Make : + functor () -> + sig + (** Internal representation of the buffer *) + val buff : bytes ref + + (** [store i c] stores a character [c] at the position [i] inside the buffer. + Automatically extends buffer if needed. Returns the position that follows + inserted character ([i+1]) in buffer. Should be used either with position + 0 or with position returned by previous calls of store functions. *) + val store : int -> char -> int + + (** [mstore i s] stores a string [s] starting from the postion [i] inside the + buffer. Automatically extends buffer if needed. Returns the position that + follows inserted string in buffer. Should be used either with position 0 + or with position returned by previous calls of store functions.*) + val mstore : int -> string -> int + + (** [gstore i s si len] stores substring of [s] from [si] position with length + [len] inside the buffer starting from the postion [i]. Automatically extends + buffer if needed. Returns the position that follows inserted substring in + buffer. Should be used either with position 0 or with position returned + by previous calls of store functions.*) + val gstore : int -> string -> int -> int -> int + + (** [get len] returns buffer's content until position [len] *) + val get : int -> string + + end + +(** Variable [buff] for the global buffer *) +val buff : bytes ref + +(** Function [get] for the global buffer. *) +val get : int -> string + +(** Function [store] for the global buffer. *) +val store : int -> char -> int + +(** Function [mstore] for the global buffer. *) +val mstore : int -> string -> int + +(** Function [gstore] for the global buffer. *) +val gstore : int -> string -> int -> int -> int diff --git a/lib/util/calendar.ml b/lib/util/calendar.ml index efc8344d66..01cfe1924b 100644 --- a/lib/util/calendar.ml +++ b/lib/util/calendar.ml @@ -1,5 +1,8 @@ +(** Convert [Adef.date] to Calendars.d *) let to_calendars : Def.dmy -> Calendars.d = fun { Def.day ; month ; year ; delta ; _ } -> { Calendars.day ; month ; year ; delta } + +(** Convert Calendars.d to [Adef.date] *) let of_calendars : ?prec:Def.precision -> Calendars.d -> Def.dmy = fun ?(prec = Def.Sure) { Calendars.day ; month ; year ; delta } -> { Def.day ; month ; year ; delta ; prec } diff --git a/lib/util/calendar.mli b/lib/util/calendar.mli index 3430d0a5b2..e52a50a545 100644 --- a/lib/util/calendar.mli +++ b/lib/util/calendar.mli @@ -1,20 +1,51 @@ +(** Returns date of gregorian calendar from SDN and specified precision. *) val gregorian_of_sdn : Def.precision -> int -> Def.dmy + +(** Returns date of julian calendar from SDN and specified precision. *) val julian_of_sdn : Def.precision -> int -> Def.dmy + +(** Returns date of french calendar from SDN and specified precision. *) val french_of_sdn : Def.precision -> int -> Def.dmy + +(** Returns date of hebrew calendar from SDN and specified precision. *) val hebrew_of_sdn : Def.precision -> int -> Def.dmy +(** Returns SDN of the date of gregorian calendar. *) val sdn_of_gregorian : Def.dmy -> int + +(** Returns SDN of the date of julian calendar. *) val sdn_of_julian : Def.dmy -> int + +(** Returns SDN of the date of french calendar. *) val sdn_of_french : Def.dmy -> int + +(** Returns SDN of the date of hebrew calendar. *) val sdn_of_hebrew : Def.dmy -> int +(** Converts julian calendar's date to gregorian. *) val gregorian_of_julian : Def.dmy -> Def.dmy + +(** Converts gregorian calendar's date to julian date. *) val julian_of_gregorian : Def.dmy -> Def.dmy + +(** Converts french calendar's date to gregorian date. *) val gregorian_of_french : Def.dmy -> Def.dmy + +(** Converts gregorian calendar's date to french date. *) val french_of_gregorian : Def.dmy -> Def.dmy + +(** Converts hebrew calendar's date to gregorian date. *) val gregorian_of_hebrew : Def.dmy -> Def.dmy + +(** Converts gregorian calendar's date to hebrew date. *) val hebrew_of_gregorian : Def.dmy -> Def.dmy +(** Moon phases *) type moon_phase = NewMoon | FirstQuarter | FullMoon | LastQuarter +(** Returns information about moon phase from the given SDN. + Result [(Some (mph,h,m), day)] describes moon phase [mph], hour [h] and minute + [m] when this phase appears and days [day] since last New Moon phase (moon's age). + If result is [(None,_)], it tells that there wasn't any moon's phase (one + of the mentionned in [moon_phase]) this day. *) val moon_phase_of_sdn : int -> (moon_phase * int * int) option * int diff --git a/lib/util/date.mli b/lib/util/date.mli index 08ffaab717..a5cc588a0b 100644 --- a/lib/util/date.mli +++ b/lib/util/date.mli @@ -2,8 +2,11 @@ open Def +(** Says if the given year is a leap year. *) val leap_year : int -> bool +(** Returns number of days for the given month and year for + gregorian calendar. Takes into account leap years. *) val nb_days_in_month : int -> int -> int (** [time_elapsed start stop] @@ -15,6 +18,7 @@ val nb_days_in_month : int -> int -> int - [Before] for "less than" duration - [After] for "more than" duration - [Maybe] for other cases + Used to compare only gregorian calendar's dates. *) val time_elapsed : Def.dmy -> Def.dmy -> Def.dmy @@ -23,6 +27,7 @@ val time_elapsed : Def.dmy -> Def.dmy -> Def.dmy (e.g. time_elapsed_opt /1839 /1859). *) val time_elapsed_opt : Def.dmy -> Def.dmy -> Def.dmy option +(** Returns date of death if present. *) val date_of_death : Def.death -> Adef.date option (** [dmy_of_dmy2 dmy2] @@ -31,8 +36,8 @@ val dmy_of_dmy2 : dmy2 -> dmy (** [Not_comparable] is raised by [compare_dmy] and [compare_date] when [strict] mode is used and precision of dates are incompatibles to - have a reliable result - (e.g. is [compare_dmy 2019 07/2019]) *) + have a reliable result (e.g. is [compare_dmy 2019 07/2019]) or when + one of the date in [compare_date] is [Dtext]. *) exception Not_comparable (** [compare_dmy ?strict d1 d2] diff --git a/lib/util/futil.mli b/lib/util/futil.mli index 5a74d5c54d..4edc345197 100644 --- a/lib/util/futil.mli +++ b/lib/util/futil.mli @@ -2,12 +2,20 @@ open Def +(** Convert generic type used to represent name, id and the place of [Def.gen_title] into + another one. If [fd] is present, apply it on the date of the start and date of the end of a title *) val map_title_strings : ?fd:(Def.date -> Def.date) -> ('a -> 'b) -> 'a gen_title -> 'b gen_title +(** Convert: + + - Generic type used to represent witnesses of [Def.gen_pers_event] into another one. + - Generic type used to represent name, place, reason, note and source of [Def.gen_pers_event] + into another one. + If [fd] is present, apply it on date of the personal event. *) val map_pers_event : ?fd:(Def.date -> Def.date) -> ('a -> 'c) @@ -15,6 +23,12 @@ val map_pers_event -> ('a, 'b) gen_pers_event -> ('c, 'd) gen_pers_event +(** Convert: + + - Generic type used to represent witnesses of [Def.gen_fam_event] into another one. + - Generic type used to represent name, place, reason, note and source of [Def.gen_fam_event] + into another one. + If [fd] is present, apply it on date of the familial event. *) val map_fam_event : ?fd:(Def.date -> Def.date) -> ('a -> 'c) @@ -22,9 +36,21 @@ val map_fam_event -> ('a, 'b) gen_fam_event -> ('c, 'd) gen_fam_event +(** Convert: + + - Generic type used to represent father and mother inside [Def.gen_relation] into another one. + - Generic type used to represent sources of [Def.gen_relation] into another one. *) val map_relation_ps : ('a -> 'c) -> ('b -> 'd) -> ('a, 'b) gen_relation -> ('c, 'd) gen_relation +(** Convert: + + - Generic type used to represent related persons (parents, witnesses of a personal event, etc.) + of [Def.gen_person] into another one. + - Generic type used to represent another large part of information of [Def.gen_person] + into another one. + If [fd] is present, apply it on every date (birth, death, titles,, personal events, etc.). + Generic type that is used to represent indexation key isn't converted. *) val map_person_ps : ?fd:(Def.date -> Def.date) -> ('b -> 'd) @@ -32,9 +58,22 @@ val map_person_ps -> ('a, 'b, 'c) gen_person -> ('a, 'd, 'e) gen_person +(** Convert generic type used to represent family inside [Def.gen_ascend] into + another one. *) val map_ascend_f : ('a -> 'b) -> 'a gen_ascend -> 'b gen_ascend + +(** Convert generic type used to represent one of the famillies inside [Def.gen_union] into + another one. *) val map_union_f : ('a -> 'b) -> 'a gen_union -> 'b gen_union +(** Convert: + + - Generic type used to represent faimily indexation key into another one. + - Generic type used to represent witnesses (of the marriage or of a famillial events, etc.) + of [Def.gen_family] into another one. + - Generic type used to represent another large part of information of [Def.gen_family] + into another one. + If [fd] is present, apply it on it on every date (marriage, divorce, famillial events, etc.).*) val map_family_ps : ?fd:(Def.date -> Def.date) -> ('a -> 'b) @@ -43,16 +82,30 @@ val map_family_ps -> ('a, 'c, 'e) gen_family -> ('b, 'd, 'f) gen_family +(** Convert generic type used to represent father and mother inside [Def.gen_couple] into + another one. If first argument is true then use multi-parent functionality. *) val map_couple_p : bool -> ('a -> 'b) -> 'a gen_couple -> 'b gen_couple + +(** @deprecated Use [Adef.parent] instead. *) +val parent : bool -> 'a array -> 'a gen_couple + +(** Convert generic type used to represent children inside [Def.gen_descend] into + another one.*) val map_descend_p : ('a -> 'b) -> 'a gen_descend -> 'b gen_descend +(** Says if two lists with different element's type are equal with given comparison + function. *) val eq_lists : ('a -> 'b -> bool) -> 'a list -> 'b list -> bool + +(** Says if two titles with different types are equal with given comparison + function. *) val eq_titles : ('a -> 'b -> bool) -> 'a gen_title -> 'b gen_title -> bool + +(** Says if two title names with different types are equal with given comparison + function. *) val eq_title_names : ('a -> 'b -> bool) -> 'a gen_title_name -> 'b gen_title_name -> bool -val parent : bool -> 'a array -> 'a gen_couple - (** Return a list of string corresponding to various mix between all kind of names. It can contain duplicates. Strings are used raw (not lowered). diff --git a/lib/util/lock.mli b/lib/util/lock.mli index d35152a046..65749472ba 100644 --- a/lib/util/lock.mli +++ b/lib/util/lock.mli @@ -1,12 +1,20 @@ +(** Flag that indicates if the lock should be used. *) val no_lock_flag : bool ref +(** Print lock error message and terminate program. *) val print_error_and_exit : unit -> unit +(** Print message about locked database. *) val print_try_again : unit -> unit +(** [control ~onerror lname wait f] opens file [lname], puts a write lock on it and then calls [f]. + If [wait] is true then if it tries to access locked file it will be blocked until these lock is removed. Otherwise + it will fail, and function [onerror] will be called. If flag [no_lock_flag] is set then returns [f ()] immediatly. *) val control : onerror:(unit -> 'a) -> string -> bool -> (unit -> 'a) -> 'a +(** Tries to call [control] without blocking. If it fail (lock is put) then call again [control] + and waits untill lock is removed. If it fails with another reason calls [onerror]. *) val control_retry : onerror:(unit -> 'a) -> string -> (unit -> 'a) -> 'a diff --git a/lib/util/mutil.ml b/lib/util/mutil.ml index 24efefba4c..3b78fb99bb 100644 --- a/lib/util/mutil.ml +++ b/lib/util/mutil.ml @@ -472,6 +472,7 @@ let input_lexicon lang ht open_fname = || (String.unsafe_get a i = String.unsafe_get b i && aux a b (i - 1) ) in + (* find header *) let rec key () = match input_line ic with | exception End_of_file -> close_in ic @@ -484,6 +485,7 @@ let input_lexicon lang ht open_fname = && String.unsafe_get line 3 = ' ' then trad (String.sub line 4 (len - 4)) else key () + (* find a line corresponding to a language *) and trad k = match input_line ic with | exception End_of_file -> close_in ic @@ -1023,6 +1025,7 @@ let rev_input_line ic pos (rbuff, rpos) = decr rpos; Bytes.unsafe_get !rbuff !rpos in + (* reverse buffer *) let get_n_reset () = let s = Buffer.to_bytes rev in let n = Bytes.length s in diff --git a/lib/util/mutil.mli b/lib/util/mutil.mli index d985d309e6..2ba13d16a6 100644 --- a/lib/util/mutil.mli +++ b/lib/util/mutil.mli @@ -1,14 +1,32 @@ (* Copyright (c) 2006-2007 INRIA *) -val int_size : int +(** Global variable that indicates either + servers should be in verbose mode. *) val verbose : bool ref +(** [list_iter_first f l] iter over first element with [f true] and over others with [f false]. *) val list_iter_first : (bool -> 'a -> unit) -> 'a list -> unit + +(** Remove all trailing spaces in string *) val strip_all_trailing_spaces : string -> string +(* [decline dform dformat] encode name that could be declined (like in the czech language) + and its declination form in more comprehensible for computer format. + Declination form [dform] is one of the follows: + - 'n' for nominative + - 'a' for accusative + - 'g' for genitif + Declination format [dformat] describes how does a name changes throughout different + declination forms comparing to the nominative form. + See {{: https://geneweb.tuxfamily.org/wiki/declension }Declination in Geneweb} for more details. + Example 1: [decline 'a' "Vladana:a:Vladanu:g:Vladany"] returns encoding "@(@(a)@(a?Vladanu:g?Vladany:Vladana))." + Example 2: [decline 'a' "Vladana:a:-u:g:-y"] returns encoding "@(@(a)Vladan@(a?u:g?y:a))" + @deprecated *) val decline : char -> string -> string -val nominative : string -> string +(** Encodes name for nominative declination format. + @deprecated *) +val nominative : string -> string (** [mkdir_p ?perm dir] Create the directory [dir]. @@ -16,35 +34,58 @@ val nominative : string -> string *) val mkdir_p : ?perm:int -> string -> unit +(** Remove every file in the directory and then remove the directory itself *) val remove_dir : string -> unit + +(** Returns the name of a lock file (with extension .lck). Result is generally used as an + argument for [Lock.control] function. *) val lock_file : string -> string -val name_key : string -> string +(** Returns position of first capital letter in the name (0 if no capitals). *) val initial : string -> int + +(** [input_particles fname] read file and returns list of lines. + Empty lines are skipped. *) val input_particles : string -> string list + +(** Divide surnames on pieces. Every separated word that contains at least 4 character + forms one piece. Words that contains less than 4 characters or words "saint" and "sainte" + are considered as the particles and are attached to the another word to form a piece. + If string contains less than two pieces, returns an empty list. *) val surnames_pieces : string -> string list +(** Convert encoded string with ISO 8859-1 to UTF 8 *) val utf_8_of_iso_8859_1 : string -> string + +(** Convert encoded string with UTF 8 to ISO 8859-1 *) val iso_8859_1_of_utf_8 : string -> string +(** Convert arabic number (int) to roman (string). Number should be < 4000. *) val roman_of_arabian : int -> string + +(** Convert roman number (string) to arabic (int). Number should be less or equal + to MMMCMXCIX (3999). *) val arabian_of_roman : string -> int +(** [input_lexicon lang ht open_file] open {i lexicon.txt} file with [open_file ()], + parse it and fill [ht] where key is a section name (in english) and value is + a coresponding traduction associated to a [lang] language code. If traduction + line has a form [->: sect] it associates to the current section name the value + associated to [sect] section name inside [ht]. *) val input_lexicon : string -> (string, string) Hashtbl.t -> (unit -> in_channel) -> unit +(** Set of strings *) module StrSet : Set.S with type elt = string (** [tr c1 c2 str] Return a new string which is the same as [str] with all occurences of [c1] - replaced by [c2]. - If [str] does not contain [c1]. [str] is returned intouched. - + replaced by [c2]. If [str] does not contain [c1] [str] is returned untouched. *) val tr : char -> char -> string -> string (** [unsafe_tr c1 c2 str] - Update [str] in place. Replace all occurences of [c1] replaced by [c2]. + Update [str] in place. Replace all occurences of [c1] by [c2]. *) val unsafe_tr : char -> char -> string -> string @@ -99,7 +140,9 @@ val compile_particles : string list -> Re.re If no such [p] exists, empty string [""] is returned. *) val get_particle : Re.re -> string -> string -(** [compare_after_particle particles s1 s2] *) +(** [compare_after_particle particles s1 s2] + compare strings [s1] [s2] starting from the first character after + particle's match. If they are equal, compare particles. *) val compare_after_particle : Re.re -> string -> string -> int (** [rm fname] @@ -153,7 +196,8 @@ val list_slice : int -> int -> 'a list -> 'a list *) val check_magic : string -> in_channel -> bool -(** Magic string generated from the md5sum of the running executable. +(** Magic string are either get from {i GW_EXECUTABLE_MAGIC} environement variable + either generated from the md5sum of the running executable. It can be used for volatile files which can be easily corrupted by any change in program or data representation. *) @@ -187,7 +231,9 @@ val array_forall2 : ('a -> 'b -> bool) -> 'a array -> 'b array -> bool *) val list_replace : 'a -> 'a -> 'a list -> 'a list -(** [list_except old_v new_v list] *) +(** [list_except x list] + Return a list containing all the elements from [list] + except the first occurence of [x]. *) val list_except : 'a -> 'a list -> 'a list (** [list_index element list] @@ -220,8 +266,12 @@ val input_file_ic : in_channel -> string *) val normalize_utf_8 : string -> string +(** [list_map_sort_uniq f l] apply [f] to every element and return + sorted with Merge Sort algorithm list where every element is unique. *) val list_map_sort_uniq : ('a -> 'b) -> 'a list -> 'b list +(** [list_rev_map_append f l1 l2] apply [f] to every element in [l1], reverse it and + concat with [l2]. *) val list_rev_map_append : ('a -> 'b) -> 'a list -> 'b list -> 'b list (** [read_or_create_channel ?magic fname read write] @@ -267,6 +317,7 @@ val read_or_create_value *) val bench : string -> (unit -> 'a) -> 'a +(** Prints call stack on stderr with at most [max] entries. *) val print_callstack : ?max:int -> unit -> unit (** [encode s] @@ -297,7 +348,8 @@ val gen_decode : bool -> string -> string Answers the empty string if the parameter is not found. *) val extract_param : string -> char -> string list -> string -(** Print a date using "%04d-%02d-%02d %02d:%02d:%02d" format. *) +(** Print a date using "%04d-%02d-%02d %02d:%02d:%02d" format + Example : 2021-12-13 22:35:08. *) val sprintf_date : Unix.tm -> string (** [rev_input_line ic pos (rbytes, rpos)] @@ -307,7 +359,11 @@ val sprintf_date : Unix.tm -> string character at the end, and the position of the first character of the returned line (to be used with next [rev_input_line] call). - [rpos] and [rbytes] must be the same in each subsequents calls + [rpos] and [rbytes] are intermediate between [ic] and reading functions. + At the beginig when [!rpos = 0] and [rbytes] is empty, initialise buffer with + the size = 1024, then reads last 1024 characters from [ci]. When [rpos] comes + down to 0, resize buffer *2 and reads 2048 characters before 1024 last + characters. [rpos] and [rbytes] must be the same in each subsequents calls Raises [End_of_file] if the beginning of the file is reached at the beginning of line. @@ -330,7 +386,7 @@ val search_asset_opt : string -> string option *) val eq_key : (string * string * int) -> (string * string * int) -> bool -(** [ls_rs dirs] +(** [ls_r dirs] List directories (and subdirectories) contents of [dirs], including [dirs] themselves. *) val ls_r : string list -> string list diff --git a/lib/util/name.ml b/lib/util/name.ml index 745c440fd6..55386a4c32 100644 --- a/lib/util/name.ml +++ b/lib/util/name.ml @@ -28,6 +28,12 @@ let next_chars_if_equiv s i t j = let (t1, j1) = unaccent_utf_8 true t j in if s1 = t1 then Some (i1, j1) else None +(* Name.lower: + - uppercase -> lowercase + - no accents + - chars no letters and no numbers (except '.') => spaces (stripped) + Key comparison (first name, surname, number) applies "lower" equality + on first names and surnames *) let lower s = let rec copy special i len = if i = String.length s then Buff.get len @@ -57,11 +63,15 @@ let title s = (* Name.abbrev *) +(* List of abbreviations. If abbreviation is mapped to [Some s] should be remplaced by + [s]. If mapped to None, it should be removed from name. *) let abbrev_list = ["a", None; "af", None; "d", None; "de", None; "di", None; "ier", Some "i"; "of", None; "saint", Some "st"; "sainte", Some "ste"; "van", None; "von", None; "zu", None; "zur", None] +(* Checks if words are the same (starting from given position for both of them). + In [s] word should end with space. *) let rec is_word s i p ip = if ip = String.length p then if i = String.length s then true else if s.[i] = ' ' then true else false @@ -69,6 +79,7 @@ let rec is_word s i p ip = else if s.[i] = p.[ip] then is_word s (i + 1) p (ip + 1) else false +(* Checks if word that starts at position [i] in [s] is one of abbreviation *) let rec search_abbrev s i = function (w, a) :: pl -> @@ -76,6 +87,7 @@ let rec search_abbrev s i = else search_abbrev s i pl | [] -> None +(* Name.abbrev: suppress lowercase particles, shorten "saint" into "st" *) let abbrev s = let rec copy can_start_abbrev i len = if i >= String.length s then Buff.get len @@ -94,6 +106,7 @@ let abbrev s = (* Name.strip *) +(* Name.strip_c = name without the charater c given as parameter *) let strip_c s c = let rec copy i len = if i = String.length s then Buff.get len @@ -104,7 +117,7 @@ let strip_c s c = let strip s = strip_c s ' ' - +(* String without any forbidden caracters defined in forbidden_char *) (* ******************************************************************** *) (* [Fonc] purge : string -> string *) (** [Description] : Supprime tous les caractères interdits (défini par @@ -116,11 +129,13 @@ let strip s = strip_c s ' ' - string : retourne la chaîne délestée des caractères interdits [Rem] : Exporté en clair hors de ce module. *) (* ******************************************************************** *) -let purge s = List.fold_left (fun s c -> strip_c s c) s forbidden_char +let purge s = List.fold_left strip_c s forbidden_char (* Name.crush *) +(* If string starting from [i] contains roman number then returns the next position, + else returns None. *) let roman_number s i = let rec loop i = if i = String.length s then Some i @@ -132,6 +147,18 @@ let roman_number s i = in if i = 0 || s.[i-1] = ' ' then loop i else None +(*Name.crush: + - no spaces + - roman numbers are keeped + - vowels are suppressed, except in words starting with a vowel, + where this vowel is converted into "e" + - "k" and "q" replaced by "c" + - "y" replaced by "i" + - "z" replaced by "s" + - "ph" replaced by "f" + - others "h" deleted + - s at end of words are deleted + - no double lowercase consons *) let crush s = let rec copy i len first_vowel = if i = String.length s then Buff.get len @@ -185,12 +212,17 @@ let crush s = (* strip_lower *) +(* strip_lower = strip o lower, as first comparison of names. + First names and Surnames comparison is strip_lower equality. *) let strip_lower s = strip (lower s) (* crush_lower *) +(* crush_lower = crush o abbrev o lower, as second comparison of names. + In index by names, the "names" are crush_lowers *) let crush_lower s = crush (abbrev (lower s)) +(* concat two strings using Bytes module *) let concat_aux fn l1 sn l2 = let b = Bytes.create (l1 + l2 + 1) in Bytes.blit_string fn 0 b 0 l1 ; diff --git a/lib/util/name.mli b/lib/util/name.mli index 3dafa7bd59..e504dd3905 100644 --- a/lib/util/name.mli +++ b/lib/util/name.mli @@ -1,24 +1,42 @@ (* Copyright (c) 1998-2007 INRIA *) -(** Apply uppercasing to the first letter of each name part, +(** List of forbidden to use characters *) +val forbidden_char : char list + +(** [unaccent_utf_8 lower s i] checks UTF-8 characher that starts at position [i] inside [s] + and returns couple (cs,np) where [cs] is ASCII representation of this character (characters + between 0x00 and 0x7F) and [np] it's a position of next utf8 character inside [s]. If [lower] + is true then [cs] will contain only lowercase letters. + Example : unaccent_utf_8 "aÈa" 1 -> ("e",3) *) +val unaccent_utf_8 : bool -> string -> int -> string * int + +(** [next_chars_if_equiv s1 i1 s2 i2] checks if UTF-8 characters that start at position + [i1] inside [s1] and at [i2] inside [s2] are equivalent (have the same ASCII representation). + In this case returns position of the next charecter for each of them. Otherwise, returns None. *) +val next_chars_if_equiv : string -> int -> string -> int -> (int * int) option + +(** Convert every letter to lowercase and use *unidecode* library to + represent unicode characters with ASCII. Non-alphanumeric characters + (except '.') are remplaced by space. *) +val lower : string -> string + +(** Apply uppercasing to the first letter of each name (sequence of alphabetic characters) part, and lowercasing to the rest of the text. *) val title : string -> string -val lower : string -> string - (* Name.lower: - - uppercase -> lowercase - - no accents - - chars no letters and no numbers (except '.') => spaces (stripped) - Key comparison (first name, surname, number) applies "lower" equality - on first names and surnames *) +(** Remplace by an abbreviation or remove particles inside the name *) val abbrev : string -> string - (* Name.abbrev: suppress lowercase particles, shorten "saint" into "st" *) + +(** Removes all the spaces inside the name *) val strip : string -> string - (* Name.strip = name without spaces *) + +(** [strip_c s c] removes all the occurences of [c] inside the name *) val strip_c : string -> char -> string - (* Name.strip_c = name without the charater c given as parameter *) -val crush : string -> string - (* Name.crush: + +(** Removes all the forbiden characters from [forbidden_char] inside the name *) +val purge : string -> string + +(** Converts name to the following format: - no spaces - roman numbers are keeped - vowels are suppressed, except in words starting with a vowel, @@ -28,29 +46,33 @@ val crush : string -> string - "z" replaced by "s" - "ph" replaced by "f" - others "h" deleted - - s at end of words are deleted + - s at thr end of words are deleted - no double lowercase consons *) - +val crush : string -> string + +(** Equivalent to [strip o lower]. Used as: + - First comparison of names. + - Comparison for first names and surnames. *) val strip_lower : string -> string - (* strip_lower = strip o lower, as first comparison of names. - First names and Surnames comparison is strip_lower equality. *) - -val purge : string -> string - (* String without any forbidden caracters defined in forbidden_char *) +(** Equivalent to [crush o abbrev o lower]. Used as: + - Second comparison of names. + - Key when index by names *) val crush_lower : string -> string - (* crush_lower = crush o abbrev o lower, as second comparison of names. - In index by names, the "names" are crush_lowers *) - -val next_chars_if_equiv : string -> int -> string -> int -> (int * int) option - -val unaccent_utf_8 : bool -> string -> int -> string * int - -val forbidden_char : char list (** [concat fn sn] is [fn ^ " " ^ sn] but faster. *) val concat : string -> string -> string +(** [split_sname_callback fn s] + Same as [split_sname], but call [fn] with substring indexes instead of building + a list *) +val split_sname_callback : (int -> int -> unit) -> string -> unit + +(** [split_fname_callback fn s] + Same as [split_fname], but call [fn] with substring indexes instead of building + a list *) +val split_fname_callback : (int -> int -> unit) -> string -> unit + (** [split_sname s] split the surname [s] in parts composing it. e.g. [split_sname base "Foo-Bar"] is [[ "Foo" ; "Bar"]] *) val split_sname : string -> string list @@ -58,16 +80,4 @@ val split_sname : string -> string list (** [split_fname s] split the string [s] representing multiple first names into this list of firstname. e.g. [split_fname base "Foo-Bar Baz"] is [[ "Foo-Bar" ; "Baz"]] *) -val split_fname : string -> string list - -(** [split_sname_callback fn s] - Same as [split_sname], but call [fn] with substring indices instead of building - a list -*) -val split_sname_callback : (int -> int -> unit) -> string -> unit - -(** [split_fname_callback fn s] - Same as [split_fname], but call [fn] with substring indices instead of building - a list -*) -val split_fname_callback : (int -> int -> unit) -> string -> unit +val split_fname : string -> string list \ No newline at end of file diff --git a/lib/util/opt.mli b/lib/util/opt.mli new file mode 100644 index 0000000000..4dc6645a74 --- /dev/null +++ b/lib/util/opt.mli @@ -0,0 +1,14 @@ +(** [iter f o] if [o=Some x] then executes [f x]. *) +val iter : ('a -> unit) -> 'a option -> unit + +(** [map f o] if [o=Some x] then returns [Some (f x)] otherwise returns None. *) +val map : ('a -> 'b) -> 'a option -> 'b option + +(** [map_default d f o] if [o=Some x] then returns [(f x)] otherwise returns [d]. *) +val map_default : 'a -> ('b -> 'a) -> 'b option -> 'a + +(** [default d o] if [o=Some x] then returns [x] otherwise returns [d]. *) +val default : 'a -> 'a option -> 'a + +(** [to_string so] if [so=Some s] then returns [s] otherwise returns empty string. *) +val to_string : string option -> string \ No newline at end of file diff --git a/lib/util/pqueue.mli b/lib/util/pqueue.mli index fb9f9d930a..76f14743b4 100644 --- a/lib/util/pqueue.mli +++ b/lib/util/pqueue.mli @@ -5,7 +5,7 @@ (** This module implements priority queues, given a total ordering function over the elements inserted. All operations are purely applicative (no side effects). - The implementation uses binomial queues from Chris Okasak. + The implementation uses binomial queues from Chris Okasaki. "add", "take" and "union" are in o(log n) in the worst case. *) (** The input signature of the functor [Pqueue.Make]. @@ -15,6 +15,7 @@ first argument is less or equal to the second one. *) module type OrderedType = sig type t val leq : t -> t -> bool end +(** Output signature for priority queue *) module type S = sig (** Type of elementes contained in priority queues. *) @@ -36,8 +37,10 @@ module type S = sig raises [Not_found] when [x] is empty. *) val take : t -> elt * t + (** [union q1 q2] returns heap constructed by union of [q1] [q2] *) val union : t -> t -> t end +(** Functor that creates instance of priority queue from given element type. *) module Make (Ord : OrderedType) : S with type elt = Ord.t diff --git a/lib/util/progrBar.ml b/lib/util/progrBar.ml index 960e57fcd1..e5dd04b35b 100644 --- a/lib/util/progrBar.ml +++ b/lib/util/progrBar.ml @@ -1,6 +1,8 @@ (* $Id: progrBar.ml,v 5.4 2007-02-01 10:28:55 ddr Exp $ *) +(* bar size in characters *) let size = 60 + let draw_rep = 5 let draw = "|/-\\" let empty = ref '.' diff --git a/lib/util/progrBar.mli b/lib/util/progrBar.mli index 9250acb386..eee0f1c99a 100644 --- a/lib/util/progrBar.mli +++ b/lib/util/progrBar.mli @@ -1,11 +1,24 @@ (* $Id: progrBar.mli,v 5.3 2007-02-01 10:28:55 ddr Exp $ *) +(** Character that represents not passed part of progression bar *) +val empty : char ref + +(** Character that represents passed part of progression bar *) +val full : char ref + +(** Prints empty bar with carriage return. *) val start : unit -> unit + +(** [run i len] modifies progression bar that is now filled proportionally to + [i] by comparison with [len]. *) val run : int -> int -> unit + +(** Stop printing progression bar and prints a new line. *) val finish : unit -> unit +(** Stop printing progression bar and prints a new line. *) val suspend : unit -> unit -val restart : int -> int -> unit -val empty : char ref -val full : char ref +(** [restart i len] restart progression bar. It's equivalent to call successively + [run] from 0 to [i]. *) +val restart : int -> int -> unit diff --git a/lib/util/secure.ml b/lib/util/secure.ml index a59c2f58c1..bdabcb0866 100644 --- a/lib/util/secure.ml +++ b/lib/util/secure.ml @@ -9,6 +9,8 @@ let ok_r = ref [] let assets_r = ref [] let bd_r = ref Filename.current_dir_name +(* [decompose: string -> string list] decompose a path into a list of + directory and a basename. "a/b/c" -> [ "a" ; "b"; "c" ] *) let decompose = let rec loop r s = let b = Filename.basename s in @@ -22,19 +24,24 @@ let decompose = in loop [] +(* add asset to the list of allowed to acces assets *) let add_assets d = assets_r := d :: !assets_r ; ok_r := decompose d :: !ok_r +(* set base dir to which acces could be allowed *) let set_base_dir d = let ok = decompose d in bd_r := d ; ok_r := ok :: (List.filter ((<>) ok)) !ok_r +(* get all assets *) let assets () = !assets_r -let bd () = !bd_r +let base_dir () = !bd_r -let suffix d df = +(* [list_check_prefix d df] returns either [None] if [d] is not a prefix of + [df], or [Some suffix], where [df = d @ suffix] *) +let list_check_prefix d df = let rec loop = function x :: xl, y :: yl -> if x = y then loop (xl, yl) else None @@ -43,15 +50,21 @@ let suffix d df = in loop (d, df) +(** Check if a filename is safe to read: + * it must not contain the '\000' character + * it must either be relative to the local directory OR + included in one of the allowed directories (base_dir or assets) + * the relative part does not contain the '..' directory +*) let check fname = if String.contains fname '\000' then false else let df = decompose fname in let rec loop = function | d :: dl -> - begin match suffix d df with - | Some bf -> not (List.mem Filename.parent_dir_name bf) - | None -> loop dl + begin match list_check_prefix d df with + | Some bf when not (List.mem Filename.parent_dir_name bf) -> true + | _ -> loop dl end | [] -> if Filename.is_relative fname @@ -60,7 +73,7 @@ let check fname = in loop !ok_r -let check_open fname = +let check_open do_open fname = if not (check fname) then begin if Sys.unix then begin @@ -68,11 +81,14 @@ let check_open fname = flush stderr end; raise (Sys_error "invalid access") - end + end ; + do_open fname -let open_in fname = check_open fname; Stdlib.open_in fname -let open_in_bin fname = check_open fname; Stdlib.open_in_bin fname -let open_out fname = check_open fname; Stdlib.open_out fname -let open_out_bin fname = check_open fname; Stdlib.open_out_bin fname -let open_out_gen mode perm fname = - check_open fname; Stdlib.open_out_gen mode perm fname +(* The following functions perform a [check] before opening the file, + preventing potential attacks on the system. +*) +let open_in = check_open Stdlib.open_in +let open_in_bin = check_open Stdlib.open_in_bin +let open_out = check_open Stdlib.open_out +let open_out_bin = check_open Stdlib.open_out_bin +let open_out_gen mode perm = check_open (Stdlib.open_out_gen mode perm) diff --git a/lib/util/secure.mli b/lib/util/secure.mli index 7a35f3ea3a..e308501868 100644 --- a/lib/util/secure.mli +++ b/lib/util/secure.mli @@ -1,18 +1,36 @@ (* Copyright (c) 1998-2007 INRIA *) +(** Returns list of allowed to acces assets *) val assets : unit -> string list -val bd : unit -> string +(** Returns directory where databases are installed to which acces is allowed *) +val base_dir : unit -> string + +(** Add new asset to the [assets] list *) val add_assets : string -> unit + +(** Set base directory *) val set_base_dir : string -> unit -(** Check if a filename is safe to read or not - (i.e. will not read in a location it is not supposed to read). +(** Check if a filename is safe to read: + - it must not contain the '\000' character + - it must either be relative to the local directory OR + included in one of the allowed directories (base_dir or assets) + - the relative part does not contain the '..' directory *) val check : string -> bool +(** Secured version of [open_in] *) val open_in : string -> in_channel + +(** Secured version of [open_in_bin] *) val open_in_bin : string -> in_channel + +(** Secured version of [open_out] *) val open_out : string -> out_channel + +(** Secured version of [open_out_bin] *) val open_out_bin : string -> out_channel + +(** Secured version of [open_out_gen] *) val open_out_gen : open_flag list -> int -> string -> out_channel diff --git a/lib/util/utf8.ml b/lib/util/utf8.ml index 069de3933f..fa4638c94f 100644 --- a/lib/util/utf8.ml +++ b/lib/util/utf8.ml @@ -1,6 +1,5 @@ (* TODO: replace with Unidecode.nbc when version constraint [= 0.2.0] will be removed *) -(** Return the number of bytes composing the UTF8 character starting with [c] *) let nbc c = if Char.code c < 0x80 then 1 else if Char.code c < 0xC0 then invalid_arg "nbc" @@ -11,24 +10,15 @@ let nbc c = else if Char.code c < 0xFE then 6 else invalid_arg "nbc" -(** [Utf8.next s i] returns the index of the character comming after - the one which starts at [i]. -*) let next s i = i + nbc s.[i] -(** [Utf8.get s n] returns the index where the [n]-th character - starts in string [s]. -*) let get s i = let rec loop i k = if k = 0 then i else loop (next s i) (pred k) in loop 0 i -(** Return the length (number of characters, not bytes) - of the given string. -*) let length s = let rec loop i len = if i < String.length s @@ -36,11 +26,6 @@ let length s = else len in loop 0 0 -(** [sub ?pad s start len] - Return a fresh UTF8-friendly substring of [len] characters, padded if needed. - Be careful [start] is the index of the byte where to start in [s], - not the [start-th] UTF8-character. -*) let sub ?pad str start len = let strlen = String.length str in let n, i = @@ -97,6 +82,8 @@ module C = struct type t = Str of string | Chr of char | Empty + (* Creates [t] from the UTF-8 character that starts at position [i] in [s]. + If characher isn't the one that exists in ASCII char set, use unidecode library *) let unaccent trimmed s i0 len = let rec loop i = if i < len then match @@ -159,6 +146,7 @@ module C = struct let m = Char.code (String.unsafe_get s (i + 3)) in n' lsl 6 lor (0x7f land m) + (* compare bytes (UTF-8 charachter) delimited by intevals [i1,j1] and [i2,j2] *) let cmp_substring s1 i1 j1 s2 i2 j2 = let l1 = j1 - i1 in let l2 = j2 - i2 in @@ -175,6 +163,7 @@ module C = struct let c2 = match Uucp.Case.Fold.fold c2 with `Self -> [c2] | `Uchars us -> us in Stdlib.compare c1 c2 + (* See [Utf8.compare] *) let compare n1 n2 = let trimmed1 = ref false in let trimmed2 = ref false in @@ -215,12 +204,4 @@ module C = struct end -(** [compare a b] compare normalized version of [a] and [b] - It is case insensitive. - It starts with unaccented comparison of [a] and [b], - and refine the result with accents comparison. - - Here is an exemple of how letters would be sorted: - [A À Á  B C Ç Č D E É L Ł Ô Ö Ø Œ P Q R * . ?] - *) let compare = C.compare diff --git a/lib/util/utf8.mli b/lib/util/utf8.mli new file mode 100644 index 0000000000..3ecbabf25b --- /dev/null +++ b/lib/util/utf8.mli @@ -0,0 +1,49 @@ + +(** Return the number of bytes composing the UTF8 character starting with [c] *) +val nbc : char -> int + +(** [Utf8.next s i] returns the index of the character comming after + the one which starts at [i]. *) +val next : string -> int -> int + +(** [Utf8.get s n] returns the index where the [n]-th character + starts in string [s]. *) +val get : string -> int -> int + +(** Return the length (number of characters, not bytes) + of the given string. *) +val length : string -> int + +(** [sub ?pad s start len] + Return a fresh UTF8-friendly substring of [len] characters, padded if needed. + Be careful [start] is the index of the byte where to start in [s], + not the [start-th] UTF8-character. *) +val sub : ?pad:char -> string -> int -> int -> string + +(** [cmap_utf_8 cmap s] returns the UTF-8 encoded string + resulting from applying the character map [cmap] to every character + of the UTF-8 encoded string [s]. *) +val cmap_utf_8 : + (Uchar.t -> [< `Self | `Uchars of Uchar.t list ]) -> string -> string + +(** Returns UTF-8 encoded string with all uppercase letters translated to lowercase *) +val lowercase : string -> string + +(** Returns UTF-8 encoded string with all lowercase letters translated to uppercase *) +val uppercase : string -> string + +(** Returns UTF-8 encoded string where the first letter is capitalised *) +val capitalize_fst : string -> string + +(** Returns UTF-8 encoded string where the first letter is capitalised and others minimalised *) +val capitalize : string -> string + +(** [compare a b] compare normalized version of [a] and [b] + It is case insensitive. + It starts with unaccented comparison of [a] and [b], + and refine the result with accents comparison. + + Here is an exemple of how letters would be sorted: + [A À Á  B C Ç Č D E É L Ł Ô Ö Ø Œ P Q R * . ?] + *) +val compare : string -> string -> int \ No newline at end of file diff --git a/lib/version.mli b/lib/version.mli new file mode 100644 index 0000000000..bb8f6545ac --- /dev/null +++ b/lib/version.mli @@ -0,0 +1,6 @@ + +(** Current Geneweb version *) +val txt : string + +(** List of all supported by Geneweb languages (abbreviations). *) +val available_languages : string list \ No newline at end of file diff --git a/lib/wiki.ml b/lib/wiki.ml index 74119a3ffc..002fdfe50a 100644 --- a/lib/wiki.ml +++ b/lib/wiki.ml @@ -61,7 +61,7 @@ let notes_aliases conf = let rec loop list = match try Some (input_line ic) with End_of_file -> None with Some s -> - let list = + let list = (* S: is it replacable by `String.split_on_char ' '` s? *) try let i = String.index s ' ' in (String.sub s 0 i, @@ -565,22 +565,38 @@ let html_with_summary_of_tlsw conf wi edit_opt s = let s2 = string_of_modify_link conf 0 (s = "") edit_opt in s2 ^ s else s -let rev_extract_sub_part s v = +(* v = 0 -> keeps the last lines until a title occurs, discards the rest *) +(* v = 1 -> *) +let rev_extract_sub_part (s : string) (v : int) : string list = let (lines, _) = lines_list_of_string s in - let rec loop lines lev cnt = - function + let rec loop + (lines : string list) (* The accumulator of lines *) + (lev : int) (* The section level *) + (cnt : int) (* A counter of titles *) + : string list -> string list + = function s :: sl -> - let len = String.length s in - if len > 2 && s.[0] = '=' && s.[len-1] = '=' then - if v = first_cnt - 1 then lines + let len = String.length s in + if len > 2 && s.[0] = '=' && s.[len-1] = '=' then + (* This line is a title *) + if v = first_cnt - 1 then lines + (* S: previous condition is a strange way to write `if v = 0` *) + else + let nlev = section_level s len in + if cnt = v (* *) + then loop (s :: lines) nlev (cnt + 1) sl else - let nlev = section_level s len in - if cnt = v then loop (s :: lines) nlev (cnt + 1) sl - else if cnt > v then - if nlev > lev then loop (s :: lines) lev (cnt + 1) sl else lines + if cnt > v + then + if nlev > lev + then loop (s :: lines) lev (cnt + 1) sl + else lines else loop lines lev (cnt + 1) sl - else if cnt <= v then loop lines lev cnt sl - else loop (s :: lines) lev cnt sl + else + (* This line is not a title *) + if cnt <= v + then loop lines lev cnt sl (* Line is in an ignored section *) + else loop (s :: lines) lev cnt sl (* Keeping the line *) | [] -> lines in loop [] 0 first_cnt lines @@ -744,6 +760,7 @@ let insert_sub_part s v sub_part = in String.concat "\n" (List.rev_append lines sl) +(* TODO: simplify with Str *) let rec find_env s i = match try Some (String.index_from s i '=', String.index_from s i '\n') with diff --git a/lib/wiki.mli b/lib/wiki.mli index 5400383686..54b98cb278 100644 --- a/lib/wiki.mli +++ b/lib/wiki.mli @@ -3,6 +3,35 @@ open Config +(* TLSW: Text Language Stolen to Wikipedia + = title level 1 = + == title level 2 == + ... + ====== title level 6 ====== + * list ul/li item + * list ul/li item + ** list ul/li item 2nd level + ** list ul/li item 2nd level + ... + # list ol/li item + : indentation list dl/dd item + ; list dl dt item ; dd item + ''italic'' + '''bold''' + '''''bold+italic''''' + [[first_name/surname/oc/text]] link; 'text' displayed + [[first_name/surname/text]] link (oc = 0); 'text' displayed + [[first_name/surname]] link (oc = 0); 'first_name surname' displayed + [[[notes_subfile/text]]] link to a sub-file; 'text' displayed + [[[notes_subfile]]] link to a sub-file; 'notes_subfile' displayed + empty line : new paragraph + lines starting with space : displayed as they are (providing 1/ there + are at least two 2/ there is empty lines before and after the group + of lines). + __TOC__ : summary + __SHORT_TOC__ : short summary (unnumbered) + __NOTOC__ : no (automatic) numbered summary *) + type wiki_info = { wi_mode : string; wi_file_path : string -> string; @@ -11,24 +40,49 @@ type wiki_info = val syntax_links : config -> wiki_info -> string -> string +(** Parses a whole TLSW text to a list of strings *) val html_of_tlsw : config -> string -> string list + +(** HTML displaying a table of content for a TLSW file *) val html_with_summary_of_tlsw : config -> wiki_info -> (bool * string * string) option -> string -> string +(** [extract_sub_part tlsw i] + Extracts the `i`th first TLSW sections of `tlsw` *) val extract_sub_part : string -> int -> string list + +(** + The argument is expected to have the form "KEY=value\n"... + This function calculates each Key/value pair and puts it in a list; + except for the key TITLE, that is the second element of the returned tuple. + If there is no title defined, checks if the first line is not empty and does + not start with '=' nor contains '<' nor '[', in which case it is choosen as a + first line. Otherwise, the title is the empty string. +*) val split_title_and_text : string -> (string * string) list * string +(** Prints an exctracted sub part *) val print_sub_part : config -> wiki_info -> bool -> string -> string -> int -> string list -> - unit + unit + +(** Prints an editable part *) val print_mod_view_page : config -> bool -> string -> string -> (bool -> unit) -> (string * string) list -> string -> unit + +(** Commits the changes of a page *) val print_mod_ok : config -> wiki_info -> (string -> string option) -> (string option -> string) -> (string -> (string * string) list * string) -> (string -> string -> unit) -> (string -> string) -> bool -> unit +(*S: shouldn't the following functions be defined elsewhere? *) + +(** Reads the notes alias file (conf.base_env.notes_alias_file or base_path/notes.alias). + File format is "KEY value\n...", returns the list of (KEY,value) *) val notes_aliases : config -> (string * string) list + +(** Given an alias list, finds the corresponding alias for a given string *) val map_notes : (string * string) list -> string -> string diff --git a/lib/wiznotesDisplay.ml b/lib/wiznotesDisplay.ml index 1853862077..3b7730ce96 100644 --- a/lib/wiznotesDisplay.ml +++ b/lib/wiznotesDisplay.ml @@ -461,6 +461,7 @@ let print_mod conf base = else Hutil.incorrect_request conf | None -> Hutil.incorrect_request conf +(* S: merge with print_mod *) let print_view conf base = let auth_file = match diff --git a/lib/wiznotesDisplay.mli b/lib/wiznotesDisplay.mli index ff67aeccb4..99421eb5be 100644 --- a/lib/wiznotesDisplay.mli +++ b/lib/wiznotesDisplay.mli @@ -4,13 +4,34 @@ open Config open Gwdb +(** Returns the path to the wizard notes files associated to the base. *) val dir : config -> base -> string +(** Prints the HTML page displaying the wizard notes. + Fails if wizard authentification is incorrect *) val print : config -> base -> unit + +(** Prints the HTML page displaying editable wizard notes. + Fails if wizard authentification is incorrect or if current user cannot + edit. *) val print_mod : config -> base -> unit + +(** Commits the modification and displays the `OK` page. + Fails if wizard authentification is incorrect *) val print_mod_ok : config -> base -> unit + +(** Same as `print_mod`, but works even if user cannot edit. + It still fails in case of wrong authentification. *) val print_view : config -> base -> unit + +(** Same as `print` but highlights HTML with the speficied + string searched (environment key of search is `s`). + If no search is specified, just prints the wizard notes. *) val print_search : config -> base -> unit +(** Displays the connected wizards. *) val connected_wizards : config -> base -> unit + +(** Same as `connected_wizards`, but starts by updating the wizard + visibility. *) val change_wizard_visibility : config -> base -> unit diff --git a/plugins/v7_im/v7_im_sendImage.ml b/plugins/v7_im/v7_im_sendImage.ml index d52ad91f72..7dd4642348 100644 --- a/plugins/v7_im/v7_im_sendImage.ml +++ b/plugins/v7_im/v7_im_sendImage.ml @@ -297,7 +297,7 @@ let effective_send_ok conf base p file = let bfdir = Util.base_path ["images"] conf.bname in if Sys.file_exists bfdir then bfdir else - let d = Filename.concat (Secure.bd ()) "images" in + let d = Filename.concat (Secure.base_dir ()) "images" in let d1 = Filename.concat d conf.bname in (try Unix.mkdir d 0o777 with Unix.Unix_error (_, _, _) -> ()); (try Unix.mkdir d1 0o777 with Unix.Unix_error (_, _, _) -> ());