Gnosygnu has submitted this change and it was merged.

Change subject: v1.12.2.1
......................................................................


v1.12.2.1

Change-Id: I9fbcc79d3e6c2b1d41cd90c0564f37fd1710ccc3
---
M 100_core/src_110_primitive/gplx/Array_.java
M 100_core/src_110_primitive/gplx/Bool_obj_ref.java
M 140_dbs/src_100_core/gplx/dbs/Db_rdr.java
M 140_dbs/src_100_core/gplx/dbs/Db_rdr_.java
M 140_dbs/src_100_core/gplx/dbs/Db_rdr__basic.java
M 140_dbs/src_110_dbQry/gplx/dbs/Db_qry__select_in_tbl.java
A 400_xowa/src/gplx/dbs/Gfdb_db_base.java
A 400_xowa/src/gplx/dbs/schemas/Schema_db_mgr.java
A 400_xowa/src/gplx/dbs/schemas/Schema_itm_tid.java
A 400_xowa/src/gplx/dbs/schemas/Schema_loader_mgr.java
A 400_xowa/src/gplx/dbs/schemas/Schema_loader_mgr_.java
A 400_xowa/src/gplx/dbs/schemas/Schema_tbl_itm.java
A 400_xowa/src/gplx/dbs/schemas/Schema_tbl_mgr.java
A 400_xowa/src/gplx/dbs/schemas/updates/Schema_update_cmd.java
A 400_xowa/src/gplx/dbs/schemas/updates/Schema_update_cmd_.java
A 400_xowa/src/gplx/dbs/schemas/updates/Schema_update_mgr.java
A 400_xowa/src/gplx/dbs/schemas/updates/Schema_update_mgr_tst.java
A 400_xowa/src/gplx/threads/Gfo_async_cmd_itm.java
A 400_xowa/src/gplx/threads/Gfo_async_cmd_mkr.java
A 400_xowa/src/gplx/threads/Gfo_async_mgr.java
M 400_xowa/src/gplx/xowa/Xoa_app.java
M 400_xowa/src/gplx/xowa/Xoa_app_.java
M 400_xowa/src/gplx/xowa/gui/history/Xog_history_stack.java
M 400_xowa/src/gplx/xowa/gui/history/Xog_history_stack_tst.java
M 400_xowa/src/gplx/xowa/gui/views/Xog_tab_itm.java
M 400_xowa/src/gplx/xowa/hdumps/Db_provider_mkr.java
M 400_xowa/src/gplx/xowa/hdumps/Xogv_tab_base.java
M 400_xowa/src/gplx/xowa/hdumps/core/Hdump_page.java
M 400_xowa/src/gplx/xowa/hdumps/saves/Hdump_stats_tbl.java
M 400_xowa/src/gplx/xowa/html/hzips/Xow_hzip_itm__header_tst.java
M 400_xowa/src/gplx/xowa/specials/Xows_mgr.java
A 400_xowa/src/gplx/xowa/specials/xowa/file_browsers/Xosp_fbrow_special.java
M 400_xowa/src/gplx/xowa/specials/xowa/system_data/System_data_page.java
M 400_xowa/src/gplx/xowa/users/Xou_user.java
A 400_xowa/src/gplx/xowa/users/data/Xoud_bmk_row.java
A 400_xowa/src/gplx/xowa/users/data/Xoud_bmk_tbl.java
A 400_xowa/src/gplx/xowa/users/data/Xoud_data_mgr.java
A 400_xowa/src/gplx/xowa/users/data/Xoud_history_mgr.java
A 400_xowa/src/gplx/xowa/users/data/Xoud_history_row.java
A 400_xowa/src/gplx/xowa/users/data/Xoud_history_special.java
A 400_xowa/src/gplx/xowa/users/data/Xoud_history_tbl.java
A 400_xowa/src/gplx/xowa/users/data/Xoud_regy_mgr.java
A 400_xowa/src/gplx/xowa/users/data/Xoud_regy_row.java
A 400_xowa/src/gplx/xowa/users/data/Xoud_regy_tbl.java
A 400_xowa/src/gplx/xowa/users/data/Xoud_site_row.java
A 400_xowa/src/gplx/xowa/users/data/Xoud_site_tbl.java
M 400_xowa/src/gplx/xowa/users/history/Xou_history_mgr.java
M 400_xowa/src_310_url/gplx/xowa/Xoa_url.java
M 400_xowa/src_310_url/gplx/xowa/Xoa_url_arg_hash.java
M tst/400_xowa/root/file/en.wikipedia.org/fsdb.user/fsdb.atr.00.sqlite3
M tst/400_xowa/root/file/en.wikipedia.org/wiki.orig#00.sqlite3
M tst/400_xowa/root/wiki/en.wikipedia.org/en.wikipedia.org.002.sqlite3
52 files changed, 1,233 insertions(+), 33 deletions(-)

Approvals:
  Gnosygnu: Verified; Looks good to me, approved



diff --git a/100_core/src_110_primitive/gplx/Array_.java 
b/100_core/src_110_primitive/gplx/Array_.java
index 41e1e2b..293069a 100644
--- a/100_core/src_110_primitive/gplx/Array_.java
+++ b/100_core/src_110_primitive/gplx/Array_.java
@@ -47,8 +47,6 @@
                        rv[i + curReplacePos] = add[i];
                for (int i = curReplacePos + addInsertPos; i < curLen; i++)     
        // copy old after curReplacePos
                        rv[i + newLen] = cur[i];
-//                     tst_ReplaceInsert(ary_obj(0, 1, 4, 5)   , ary_obj(1, 2, 
3), 1, 1, ary_obj(0, 1, 2, 3, 4, 5));
-//                     tst_ReplaceInsert(ary_obj(0, 1, 2, 4, 5), ary_obj(1, 2, 
3), 1, 2, ary_obj(0, 1, 2, 3, 4, 5));//3,4 -> 4,5
                return rv;
        }
        public static Object Resize(Object src, int trgLen) {           
diff --git a/100_core/src_110_primitive/gplx/Bool_obj_ref.java 
b/100_core/src_110_primitive/gplx/Bool_obj_ref.java
index 4fa8672..194fcfe 100644
--- a/100_core/src_110_primitive/gplx/Bool_obj_ref.java
+++ b/100_core/src_110_primitive/gplx/Bool_obj_ref.java
@@ -18,6 +18,8 @@
 package gplx;
 public class Bool_obj_ref {
        public boolean Val() {return val;} private boolean val;
+       public boolean Val_y() {return val;}
+       public boolean Val_n() {return !val;}
        public Bool_obj_ref Val_y_() {val = true; return this;}
        public Bool_obj_ref Val_n_() {val = false; return this;}
        public Bool_obj_ref Val_(boolean v) {val = v; return this;}
diff --git a/140_dbs/src_100_core/gplx/dbs/Db_rdr.java 
b/140_dbs/src_100_core/gplx/dbs/Db_rdr.java
index 8659150..472776c 100644
--- a/140_dbs/src_100_core/gplx/dbs/Db_rdr.java
+++ b/140_dbs/src_100_core/gplx/dbs/Db_rdr.java
@@ -26,5 +26,6 @@
        long            Read_long(int i);
        float           Read_float(int i);
        double          Read_double(int i);
+       DateAdp         Read_date_by_str(int i);
        void            Close();
 }
diff --git a/140_dbs/src_100_core/gplx/dbs/Db_rdr_.java 
b/140_dbs/src_100_core/gplx/dbs/Db_rdr_.java
index c2fb81b..0cb78db 100644
--- a/140_dbs/src_100_core/gplx/dbs/Db_rdr_.java
+++ b/140_dbs/src_100_core/gplx/dbs/Db_rdr_.java
@@ -25,6 +25,7 @@
        public byte[]           Read_bry_by_str(int i)  {return Bry_.Empty;}
        public byte                     Read_byte(int i)                {return 
Byte_.MaxValue_127;}
        public String           Read_str(int i)                 {return 
String_.Empty;}
+       public DateAdp          Read_date_by_str(int i) {return 
DateAdp_.MinValue;}
        public int                      Read_int(int i)                 {return 
Int_.MinValue;}
        public long             Read_long(int i)                {return 
Long_.MinValue;}
        public float            Read_float(int i)               {return 
Float_.NaN;}
diff --git a/140_dbs/src_100_core/gplx/dbs/Db_rdr__basic.java 
b/140_dbs/src_100_core/gplx/dbs/Db_rdr__basic.java
index cdf9a5d..8cf7f6f 100644
--- a/140_dbs/src_100_core/gplx/dbs/Db_rdr__basic.java
+++ b/140_dbs/src_100_core/gplx/dbs/Db_rdr__basic.java
@@ -28,6 +28,7 @@
        public byte[]           Read_bry(int i)                 {try {return 
rdr.getBytes(i + 1);} catch (Exception e) {throw Err_.new_("read failed: i={0} 
type={1} err={2}", i, Bry_.Cls_name, Err_.Message_lang(e));}} 
        public byte[]           Read_bry_by_str(int i)  {try {return 
Bry_.new_utf8_(rdr.getString(i + 1));} catch (Exception e) {throw 
Err_.new_("read failed: i={0} type={1} err={2}", i, String_.Cls_name, 
Err_.Message_lang(e));}} 
        public String           Read_str(int i)                 {try {return 
rdr.getString(i + 1);} catch (Exception e) {throw Err_.new_("read failed: i={0} 
type={1} err={2}", i, String_.Cls_name, Err_.Message_lang(e));}} 
+       public DateAdp          Read_date_by_str(int i) {return 
DateAdp_.parse_iso8561(Read_str(i));}
        public int                      Read_int(int i)                 {try 
{return rdr.getInt(i + 1);} catch (Exception e) {throw Err_.new_("read failed: 
i={0} type={1} err={2}", i, Int_.Cls_name, Err_.Message_lang(e));}} 
        public long             Read_long(int i)                {try {return 
rdr.getLong(i + 1);} catch (Exception e) {throw Err_.new_("read failed: i={0} 
type={1} err={2}", i, Long_.Cls_name, Err_.Message_lang(e));}} 
        public float            Read_float(int i)               {try {return 
rdr.getFloat(i + 1);} catch (Exception e) {throw Err_.new_("read failed: i={0} 
type={1} err={2}", i, Float_.Cls_name, Err_.Message_lang(e));}} 
diff --git a/140_dbs/src_110_dbQry/gplx/dbs/Db_qry__select_in_tbl.java 
b/140_dbs/src_110_dbQry/gplx/dbs/Db_qry__select_in_tbl.java
index 8f7f76c..56abcff 100644
--- a/140_dbs/src_110_dbQry/gplx/dbs/Db_qry__select_in_tbl.java
+++ b/140_dbs/src_110_dbQry/gplx/dbs/Db_qry__select_in_tbl.java
@@ -37,7 +37,6 @@
        public String Having_sql() {return having_sql;} private final String 
having_sql;
        public String Order_by_sql() {return order_by_sql;} public 
Db_qry__select_in_tbl Order_by_sql_(String v) {order_by_sql = v; return this;} 
private String order_by_sql;
        public String Limit_sql() {return limit_sql;} private final String 
limit_sql;
-       public static Db_qry__select_in_tbl new_(String tbl_name, String[] 
where_flds, String[] select_flds) {return new Db_qry__select_in_tbl(tbl_name, 
select_flds, where_flds, null, null, null, null);}
        public String KeyOfDb_qry() {return "select_in_tbl";}
        public boolean ExecRdrAble() {return true;}
        public String XtoSql() {return Xto_sql();}
@@ -57,5 +56,6 @@
                if (limit_sql           != null) sb.Add(limit_sql);
                return sb.XtoStr();
        }
+       public static Db_qry__select_in_tbl new_(String tbl_name, String[] 
where_flds, String[] select_flds) {return new Db_qry__select_in_tbl(tbl_name, 
select_flds, where_flds, null, null, null, null);}
        public static final String[] Where_flds__all = null;
 }
diff --git a/400_xowa/src/gplx/dbs/Gfdb_db_base.java 
b/400_xowa/src/gplx/dbs/Gfdb_db_base.java
new file mode 100644
index 0000000..1422c54
--- /dev/null
+++ b/400_xowa/src/gplx/dbs/Gfdb_db_base.java
@@ -0,0 +1,27 @@
+/*
+XOWA: the XOWA Offline Wiki Application
+Copyright (C) 2012 gnosy...@gmail.com
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package gplx.dbs; import gplx.*;
+import gplx.dbs.schemas.*;
+public class Gfdb_db_base {
+       public Db_provider Provider() {return provider;} private Db_provider 
provider;
+       public Schema_db_mgr Schema() {return schema;} private Schema_db_mgr 
schema = new Schema_db_mgr();
+       public void Init(Db_provider provider) {
+               this.provider = provider;
+               schema.Init(provider);
+       }
+}
diff --git a/400_xowa/src/gplx/dbs/schemas/Schema_db_mgr.java 
b/400_xowa/src/gplx/dbs/schemas/Schema_db_mgr.java
new file mode 100644
index 0000000..6d84a22
--- /dev/null
+++ b/400_xowa/src/gplx/dbs/schemas/Schema_db_mgr.java
@@ -0,0 +1,28 @@
+/*
+XOWA: the XOWA Offline Wiki Application
+Copyright (C) 2012 gnosy...@gmail.com
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package gplx.dbs.schemas; import gplx.*; import gplx.dbs.*;
+import gplx.dbs.schemas.updates.*;
+public class Schema_db_mgr {
+       public Schema_loader_mgr Loader() {return loader;} public void 
Loader_(Schema_loader_mgr v) {loader = v;} private Schema_loader_mgr loader;
+       public Schema_update_mgr Updater() {return updater;} private final 
Schema_update_mgr updater = new Schema_update_mgr();
+       public Schema_tbl_mgr Tbl_mgr() {return tbl_mgr;} private final 
Schema_tbl_mgr tbl_mgr = new Schema_tbl_mgr();
+       public void Init(Db_provider provider) {
+               loader.Load(this, provider);
+               updater.Update(this, provider);
+       }
+}
diff --git a/400_xowa/src/gplx/dbs/schemas/Schema_itm_tid.java 
b/400_xowa/src/gplx/dbs/schemas/Schema_itm_tid.java
new file mode 100644
index 0000000..f00cff1
--- /dev/null
+++ b/400_xowa/src/gplx/dbs/schemas/Schema_itm_tid.java
@@ -0,0 +1,28 @@
+/*
+XOWA: the XOWA Offline Wiki Application
+Copyright (C) 2012 gnosy...@gmail.com
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package gplx.dbs.schemas; import gplx.*; import gplx.dbs.*;
+public class Schema_itm_tid {
+       public static final int Tid_table = 1, Tid_index = 2;
+       public static final String Key_table = "table", Key_index = "index";
+       public static int Xto_int(String s) {
+               s = String_.Lower(s);
+               if              (String_.Eq(s, Key_table))      return 
Tid_table;
+               else if (String_.Eq(s, Key_index))      return Tid_index;
+               else                                                            
throw Err_.unhandled(s);
+       }
+}
diff --git a/400_xowa/src/gplx/dbs/schemas/Schema_loader_mgr.java 
b/400_xowa/src/gplx/dbs/schemas/Schema_loader_mgr.java
new file mode 100644
index 0000000..2babf6c
--- /dev/null
+++ b/400_xowa/src/gplx/dbs/schemas/Schema_loader_mgr.java
@@ -0,0 +1,21 @@
+/*
+XOWA: the XOWA Offline Wiki Application
+Copyright (C) 2012 gnosy...@gmail.com
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package gplx.dbs.schemas; import gplx.*; import gplx.dbs.*;
+public interface Schema_loader_mgr {
+       void Load(Schema_db_mgr db_mgr, Db_provider provider);
+}
diff --git a/400_xowa/src/gplx/dbs/schemas/Schema_loader_mgr_.java 
b/400_xowa/src/gplx/dbs/schemas/Schema_loader_mgr_.java
new file mode 100644
index 0000000..7c4abea
--- /dev/null
+++ b/400_xowa/src/gplx/dbs/schemas/Schema_loader_mgr_.java
@@ -0,0 +1,47 @@
+/*
+XOWA: the XOWA Offline Wiki Application
+Copyright (C) 2012 gnosy...@gmail.com
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package gplx.dbs.schemas; import gplx.*; import gplx.dbs.*;
+public class Schema_loader_mgr_ {
+        public static final Schema_loader_mgr Null = new 
Schema_loader_mgr__null();
+        public static final Schema_loader_mgr Sqlite = new 
Schema_loader_mgr__sqlite();
+}
+class Schema_loader_mgr__null implements Schema_loader_mgr {
+       public void Load(Schema_db_mgr db_mgr, Db_provider provider) {}
+}
+class Schema_loader_mgr__sqlite implements Schema_loader_mgr {
+       public void Load(Schema_db_mgr db_mgr, Db_provider provider) {
+               Gfo_usr_dlg_._.Log_many("", "", "db.schema.load.bgn: 
provider=~{0}", provider.Conn_info().Str_api());
+               Schema_tbl_mgr tbl_mgr = db_mgr.Tbl_mgr();
+               Db_qry__select_in_tbl qry = 
Db_qry__select_in_tbl.new_("sqlite_master", null, String_.Ary("type", "name", 
"sql"));
+               Db_stmt stmt = Db_stmt_.new_select_as_rdr(provider, qry);
+               Db_rdr rdr = stmt.Exec_select_as_rdr();
+               while (rdr.Move_next()) {
+                       int type = Schema_itm_tid.Xto_int(rdr.Read_str(0));
+                       switch (type) {
+                               case Schema_itm_tid.Tid_table:
+                                       Schema_tbl_itm tbl_itm = new 
Schema_tbl_itm(rdr.Read_str(1), rdr.Read_str(2));
+                                       tbl_mgr.Add(tbl_itm);
+                                       break;
+                               case Schema_itm_tid.Tid_index:  break;  // noop 
for now
+                               default:                                        
        throw Err_.unhandled(type);
+                       }
+               }
+               rdr.Close();
+               Gfo_usr_dlg_._.Log_many("", "", "db.schema.load.end");
+       }
+}
diff --git a/400_xowa/src/gplx/dbs/schemas/Schema_tbl_itm.java 
b/400_xowa/src/gplx/dbs/schemas/Schema_tbl_itm.java
new file mode 100644
index 0000000..7b16d18
--- /dev/null
+++ b/400_xowa/src/gplx/dbs/schemas/Schema_tbl_itm.java
@@ -0,0 +1,23 @@
+/*
+XOWA: the XOWA Offline Wiki Application
+Copyright (C) 2012 gnosy...@gmail.com
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package gplx.dbs.schemas; import gplx.*; import gplx.dbs.*;
+public class Schema_tbl_itm {
+       public Schema_tbl_itm(String name, String sql) {this.name = name; 
this.sql = sql;}
+       public String Name() {return name;} private final String name;
+       public String Sql() {return sql;} private final String sql;
+}
diff --git a/400_xowa/src/gplx/dbs/schemas/Schema_tbl_mgr.java 
b/400_xowa/src/gplx/dbs/schemas/Schema_tbl_mgr.java
new file mode 100644
index 0000000..72377aa
--- /dev/null
+++ b/400_xowa/src/gplx/dbs/schemas/Schema_tbl_mgr.java
@@ -0,0 +1,24 @@
+/*
+XOWA: the XOWA Offline Wiki Application
+Copyright (C) 2012 gnosy...@gmail.com
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package gplx.dbs.schemas; import gplx.*; import gplx.dbs.*;
+public class Schema_tbl_mgr {
+       private OrderedHash hash = OrderedHash_.new_();
+       public void Add(Schema_tbl_itm itm) {hash.Add(itm.Name(), itm);}
+       public boolean Has(String name) {return hash.Has(name);}
+       public Schema_tbl_itm Get(String name) {return 
(Schema_tbl_itm)hash.Fetch(name);}
+}
diff --git a/400_xowa/src/gplx/dbs/schemas/updates/Schema_update_cmd.java 
b/400_xowa/src/gplx/dbs/schemas/updates/Schema_update_cmd.java
new file mode 100644
index 0000000..eba15f4
--- /dev/null
+++ b/400_xowa/src/gplx/dbs/schemas/updates/Schema_update_cmd.java
@@ -0,0 +1,23 @@
+/*
+XOWA: the XOWA Offline Wiki Application
+Copyright (C) 2012 gnosy...@gmail.com
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package gplx.dbs.schemas.updates; import gplx.*; import gplx.dbs.*; import 
gplx.dbs.schemas.*;
+public interface Schema_update_cmd {
+       String Name();
+       boolean Exec_is_done();
+       void Exec(Schema_db_mgr mgr, Db_provider provider);
+}
diff --git a/400_xowa/src/gplx/dbs/schemas/updates/Schema_update_cmd_.java 
b/400_xowa/src/gplx/dbs/schemas/updates/Schema_update_cmd_.java
new file mode 100644
index 0000000..ca1cf47
--- /dev/null
+++ b/400_xowa/src/gplx/dbs/schemas/updates/Schema_update_cmd_.java
@@ -0,0 +1,36 @@
+/*
+XOWA: the XOWA Offline Wiki Application
+Copyright (C) 2012 gnosy...@gmail.com
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package gplx.dbs.schemas.updates; import gplx.*; import gplx.dbs.*; import 
gplx.dbs.schemas.*;
+public class Schema_update_cmd_ {
+       public static Schema_update_cmd Make_tbl_create(String tbl_name, String 
tbl_sql, Db_idx_itm... tbl_idxs) {return new 
Schema_update_cmd__tbl_create(tbl_name, tbl_sql, tbl_idxs);}
+}
+class Schema_update_cmd__tbl_create implements Schema_update_cmd {
+       private final String tbl_sql; private final Db_idx_itm[] tbl_idxs;
+       public Schema_update_cmd__tbl_create(String tbl_name, String tbl_sql, 
Db_idx_itm... tbl_idxs) {
+               this.tbl_name = tbl_name; this.tbl_sql = tbl_sql; this.tbl_idxs 
= tbl_idxs;
+       }
+       public String Name() {return "schema.tbl.create." + tbl_name;} private 
final String tbl_name;
+       public boolean Exec_is_done() {return exec_is_done;} private boolean 
exec_is_done;
+       public void Exec(Schema_db_mgr db_mgr, Db_provider provider) {
+               if (db_mgr.Tbl_mgr().Has(tbl_name)) return;
+               Gfo_usr_dlg_._.Log_many("", "", "schema.tbl.create: tbl=~{0}", 
tbl_name);
+               Sqlite_engine_.Tbl_create(provider, tbl_name, tbl_sql);
+               Sqlite_engine_.Idx_create(provider, tbl_idxs);
+               exec_is_done = true;
+       }
+}
diff --git a/400_xowa/src/gplx/dbs/schemas/updates/Schema_update_mgr.java 
b/400_xowa/src/gplx/dbs/schemas/updates/Schema_update_mgr.java
new file mode 100644
index 0000000..1d7c898
--- /dev/null
+++ b/400_xowa/src/gplx/dbs/schemas/updates/Schema_update_mgr.java
@@ -0,0 +1,32 @@
+/*
+XOWA: the XOWA Offline Wiki Application
+Copyright (C) 2012 gnosy...@gmail.com
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package gplx.dbs.schemas.updates; import gplx.*; import gplx.dbs.*; import 
gplx.dbs.schemas.*;
+public class Schema_update_mgr {               
+       private ListAdp cmds = ListAdp_.new_();
+       public void Add(Schema_update_cmd cmd) {cmds.Add(cmd);}
+       public void Update(Schema_db_mgr schema_mgr, Db_provider provider) {
+               int cmds_len = cmds.Count();
+               for (int i = 0; i < cmds_len; ++i) {
+                       Schema_update_cmd cmd = 
(Schema_update_cmd)cmds.FetchAt(i);
+                       try {cmd.Exec(schema_mgr, provider);}
+                       catch (Exception e) {
+                               Gfo_usr_dlg_._.Warn_many("", "", "failed to run 
update cmd; name=~{0} err=~{1}", cmd.Name(), Err_.Message_gplx_brief(e));
+                       }
+               }
+       }
+}
diff --git a/400_xowa/src/gplx/dbs/schemas/updates/Schema_update_mgr_tst.java 
b/400_xowa/src/gplx/dbs/schemas/updates/Schema_update_mgr_tst.java
new file mode 100644
index 0000000..6351eb3
--- /dev/null
+++ b/400_xowa/src/gplx/dbs/schemas/updates/Schema_update_mgr_tst.java
@@ -0,0 +1,55 @@
+/*
+XOWA: the XOWA Offline Wiki Application
+Copyright (C) 2012 gnosy...@gmail.com
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package gplx.dbs.schemas.updates; import gplx.*; import gplx.dbs.*; import 
gplx.dbs.schemas.*;
+import org.junit.*; import gplx.dbs.*;
+public class Schema_update_mgr_tst {
+       @Before public void init() {fxt.Clear();} private Schema_update_mgr_fxt 
fxt = new Schema_update_mgr_fxt();
+       @Test   public void Create() {
+               fxt.Test_exec_y(new Schema_update_cmd__mock());
+       }
+       @Test   public void Delete() {
+               fxt.Init_itm(Schema_itm_tid.Tid_table, 
Schema_update_cmd__mock.Tbl_name);
+               fxt.Test_exec_n(new Schema_update_cmd__mock());
+       }
+}
+class Schema_update_mgr_fxt {
+       private Schema_update_mgr update_mgr; private Schema_db_mgr db_mgr;
+       public void Clear() {
+               update_mgr = new Schema_update_mgr();
+               db_mgr = new Schema_db_mgr();
+       }
+       public void Init_itm(int tid, String name) {
+               db_mgr.Tbl_mgr().Add(new Schema_tbl_itm(name, "sql"));
+       }
+       public void Test_exec_y(Schema_update_cmd cmd) {Test_exec(cmd, 
Bool_.Y);}
+       public void Test_exec_n(Schema_update_cmd cmd) {Test_exec(cmd, 
Bool_.N);}
+       private void Test_exec(Schema_update_cmd cmd, boolean expd) {
+               update_mgr.Add(cmd);
+               update_mgr.Update(db_mgr, Db_provider_.Null);
+               Tfds.Eq(expd, cmd.Exec_is_done());
+       }
+}
+class Schema_update_cmd__mock implements Schema_update_cmd {
+       public String Name() {return "xowa.user.cfg_regy.create";}
+       public boolean Exec_is_done() {return exec_is_done;} private boolean 
exec_is_done;
+       public void Exec(Schema_db_mgr mgr, Db_provider provider) {
+               if (mgr.Tbl_mgr().Has(Tbl_name)) return;
+               exec_is_done = true;
+       }
+       public static final String Tbl_name = "tbl_mock";
+}
diff --git a/400_xowa/src/gplx/threads/Gfo_async_cmd_itm.java 
b/400_xowa/src/gplx/threads/Gfo_async_cmd_itm.java
new file mode 100644
index 0000000..55e115f
--- /dev/null
+++ b/400_xowa/src/gplx/threads/Gfo_async_cmd_itm.java
@@ -0,0 +1,42 @@
+/*
+XOWA: the XOWA Offline Wiki Application
+Copyright (C) 2012 gnosy...@gmail.com
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package gplx.threads; import gplx.*;
+public class Gfo_async_cmd_itm implements GfoInvkAble {
+       private GfoInvkAble invk; private String invk_key; private GfoMsg msg = 
GfoMsg_.new_cast_("");
+       public Gfo_async_cmd_itm Init(GfoInvkAble invk, String invk_key, 
Object... args) {
+               this.invk = invk; this.invk_key = invk_key;
+               msg.Args_reset();
+               msg.Clear();
+               int len = args.length; 
+               for (int i = 0; i < len; i += 2) {
+                       String key = (String)args[i];
+                       Object val = args[i + 1];
+                       msg.Add(key, val);
+               }
+               return this;
+       }
+       public void Exec() {
+               GfoInvkAble_.InvkCmd_msg(invk, invk_key, msg);
+       }
+       public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) {
+               if              (ctx.Match(k, Invk_exec))               Exec();
+               else    return GfoInvkAble_.Rv_unhandled;
+               return this;
+       }       private static final String Invk_exec = "exec";
+       public static final Gfo_async_cmd_itm[] Ary_empty = new 
Gfo_async_cmd_itm[0];
+}
diff --git a/400_xowa/src/gplx/threads/Gfo_async_cmd_mkr.java 
b/400_xowa/src/gplx/threads/Gfo_async_cmd_mkr.java
new file mode 100644
index 0000000..d7cb054
--- /dev/null
+++ b/400_xowa/src/gplx/threads/Gfo_async_cmd_mkr.java
@@ -0,0 +1,34 @@
+/*
+XOWA: the XOWA Offline Wiki Application
+Copyright (C) 2012 gnosy...@gmail.com
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package gplx.threads; import gplx.*;
+class Gfo_async_cmd_mkr {
+//             private Gfo_async_cmd_itm[] free = Gfo_async_cmd_itm.Ary_empty, 
used = Gfo_async_cmd_itm.Ary_empty;
+//             private int free_bgn = 0, free_end = 0, ary_len = 0;
+//             public void Resize(int v) {
+//                     free = (Gfo_async_cmd_itm[])Array_.Resize(free, v);
+//                     used = (Gfo_async_cmd_itm[])Array_.Resize(used, v);
+//                     ary_len = v;
+//             }
+       public Gfo_async_cmd_itm Get(GfoInvkAble invk, String invk_key, 
Object... args) {
+               Gfo_async_cmd_itm rv = new Gfo_async_cmd_itm();
+               rv.Init(invk, invk_key, args);
+               return rv;
+       }
+       public void Rls(Gfo_async_cmd_itm cmd) {
+       }
+}
diff --git a/400_xowa/src/gplx/threads/Gfo_async_mgr.java 
b/400_xowa/src/gplx/threads/Gfo_async_mgr.java
new file mode 100644
index 0000000..e2f36f4
--- /dev/null
+++ b/400_xowa/src/gplx/threads/Gfo_async_mgr.java
@@ -0,0 +1,57 @@
+/*
+XOWA: the XOWA Offline Wiki Application
+Copyright (C) 2012 gnosy...@gmail.com
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package gplx.threads; import gplx.*;
+public class Gfo_async_mgr implements GfoInvkAble {
+       private ListAdp queue = ListAdp_.new_();
+       private Bool_obj_ref running = Bool_obj_ref.n_();
+       private Gfo_async_cmd_mkr cmd_mkr = new Gfo_async_cmd_mkr();
+       public void Queue(GfoInvkAble invk, String invk_key, Object... args) {
+               Gfo_async_cmd_itm cmd = cmd_mkr.Get(invk, invk_key, args);
+               synchronized (queue) {
+                       queue.Add(cmd);
+               }
+               synchronized (running) {
+                       if (running.Val_n()) {
+                               running.Val_y_();
+                               gplx.threads.ThreadAdp_.invk_(Invk_run, this, 
Invk_run).Start();
+                       }
+               }
+       }
+       public void Run() {
+               Gfo_async_cmd_itm cmd = null;
+               try {
+                       while (true) {
+                               synchronized (queue) {
+                                       if (queue.Count() == 0) break;
+                                       cmd = 
(Gfo_async_cmd_itm)ListAdp_.Pop(queue);
+                                       cmd.Exec();
+                               }
+                       }
+               }
+               finally {
+                       synchronized (running) {
+                               running.Val_n_();
+                       }
+               }
+       }
+       public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) {
+               if              (ctx.Match(k, Invk_run))                Run();
+               else    return GfoInvkAble_.Rv_unhandled;
+               return this;
+       }       private static final String Invk_run = "run";
+}
diff --git a/400_xowa/src/gplx/xowa/Xoa_app.java 
b/400_xowa/src/gplx/xowa/Xoa_app.java
index af8edff..11c9dae 100644
--- a/400_xowa/src/gplx/xowa/Xoa_app.java
+++ b/400_xowa/src/gplx/xowa/Xoa_app.java
@@ -129,6 +129,7 @@
        public Xof_math_subst_regy      Math_subst_regy() {return 
math_subst_regy;} private Xof_math_subst_regy math_subst_regy = new 
Xof_math_subst_regy();
        public Gfo_usr_dlg                      Gui_wtr() {return 
gui_mgr.Browser_win().Usr_dlg();}
        public Launcher_app_mgr         Launcher() {return launcher;} private 
Launcher_app_mgr launcher;
+       public Gfo_async_mgr            Async_mgr() {return async_mgr;} private 
Gfo_async_mgr async_mgr = new Gfo_async_mgr();
 
        public Xoi_setup_mgr            Setup_mgr() {return setup_mgr;} private 
Xoi_setup_mgr setup_mgr;
        public Gfo_msg_log                      Msg_log() {return msg_log;} 
private Gfo_msg_log msg_log = new Gfo_msg_log(Xoa_app_.Name);
diff --git a/400_xowa/src/gplx/xowa/Xoa_app_.java 
b/400_xowa/src/gplx/xowa/Xoa_app_.java
index 3f1b0c3..ae26daa 100644
--- a/400_xowa/src/gplx/xowa/Xoa_app_.java
+++ b/400_xowa/src/gplx/xowa/Xoa_app_.java
@@ -24,7 +24,7 @@
                boot_mgr.Run(args);
        }
        public static final String Name = "xowa";
-       public static final String Version = "1.12.1.1";
+       public static final String Version = "1.12.2.1";
        public static String Build_date = "2012-12-30 00:00:00";
        public static String Op_sys;
        public static String User_agent = "";
diff --git a/400_xowa/src/gplx/xowa/gui/history/Xog_history_stack.java 
b/400_xowa/src/gplx/xowa/gui/history/Xog_history_stack.java
index 7b25c98..0af0ddf 100644
--- a/400_xowa/src/gplx/xowa/gui/history/Xog_history_stack.java
+++ b/400_xowa/src/gplx/xowa/gui/history/Xog_history_stack.java
@@ -32,15 +32,15 @@
        }
        public Xog_history_itm Go_bwd() {
                if (list.Count() == 0) return Xog_history_itm.Null;
+               if (cur_pos == 0) return Xog_history_itm.Null;
                --cur_pos;
-               if (cur_pos < 0) cur_pos = 0; 
                return this.Cur_itm();
        }
        public Xog_history_itm Go_fwd() {
                int list_count = list.Count();
                if (list_count == 0) return Xog_history_itm.Null;
+               if (cur_pos == list_count - 1) return Xog_history_itm.Null;
                ++cur_pos;
-               if (cur_pos == list_count) cur_pos = list_count - 1;
                return this.Cur_itm();
        }
        private void Del_from(int from) {
diff --git a/400_xowa/src/gplx/xowa/gui/history/Xog_history_stack_tst.java 
b/400_xowa/src/gplx/xowa/gui/history/Xog_history_stack_tst.java
index 1a6c30d..b0aecff 100644
--- a/400_xowa/src/gplx/xowa/gui/history/Xog_history_stack_tst.java
+++ b/400_xowa/src/gplx/xowa/gui/history/Xog_history_stack_tst.java
@@ -28,7 +28,7 @@
        @Test  public void Add_3_bwd_add()              {fxt.Exec_add_many("A", 
"B", 
"C").Exec_go_bwd().Exec_add_many("D").Test_len(3).Test_cur("D").Test_pos(2);}
        @Test  public void Add_3_bwd_bwd_add()  {fxt.Exec_add_many("A", "B", 
"C").Exec_go_bwd().Exec_go_bwd().Exec_add_many("D").Test_len(2).Test_cur("D").Test_pos(1);}
        @Test  public void Add_dif_ns()                 {fxt.Exec_add_many("A", 
"Help:A").Test_cur("Help:A");}  // PURPOSE.fix: page_stack was only 
differtiating by Page_db, not Full; EX: Unicode -> Category:Unicode
-       @Test  public void Add_qargs() {// PURPOSE.fix: page_stack was only 
differtiating by qtxt args
+       @Test  public void Add_qargs() {// PURPOSE.fix: page_stack was only 
differentiating by qtxt args
                fxt     .Exec_add_one("Special:AllPages", "?from=A")
                        .Exec_add_one("Special:AllPages", "?from=B")
                        .Exec_add_many("B")
diff --git a/400_xowa/src/gplx/xowa/gui/views/Xog_tab_itm.java 
b/400_xowa/src/gplx/xowa/gui/views/Xog_tab_itm.java
index 616cbdc..8bcd875 100644
--- a/400_xowa/src/gplx/xowa/gui/views/Xog_tab_itm.java
+++ b/400_xowa/src/gplx/xowa/gui/views/Xog_tab_itm.java
@@ -145,7 +145,10 @@
                        if (page.Ttl().Anch_bgn() != Bry_.NotFound) 
page.Url().Anchor_bry_(page.Ttl().Anch_txt());      // NOTE: occurs when page 
is a redirect to an anchor; EX: w:Duck race -> Rubber duck#Races
                        history_mgr.Add(page);
                        Xog_tab_itm_read_mgr.Show_page(this, page, true);
-                       if (app.Api_root().Usr().History().Enabled()) 
app.User().History_mgr().Add(page);
+                       if (app.Api_root().Usr().History().Enabled()) {
+                               app.User().History_mgr().Add(page);
+                               
app.User().Data_mgr().History_mgr().Update_async(app.Async_mgr(), ttl, url);
+                       }
                        usr_dlg.Prog_none("", "", "rendering html");
                        //      win.Page__async__bgn(this);
                        app.Thread_mgr().File_load_mgr().Add_at_end(new 
Load_files_wkr(this)).Run();
diff --git a/400_xowa/src/gplx/xowa/hdumps/Db_provider_mkr.java 
b/400_xowa/src/gplx/xowa/hdumps/Db_provider_mkr.java
index 65edba1..1bdd4b9 100644
--- a/400_xowa/src/gplx/xowa/hdumps/Db_provider_mkr.java
+++ b/400_xowa/src/gplx/xowa/hdumps/Db_provider_mkr.java
@@ -51,6 +51,7 @@
        public long     Read_long(int i)                        {return 
Long_.cast_(rows[idx].Get_at(i));}
        public float    Read_float(int i)                       {return 
Float_.cast_(rows[idx].Get_at(i));}
        public double   Read_double(int i)                      {return 
Double_.cast_(rows[idx].Get_at(i));}
+       public DateAdp  Read_date_by_str(int i)         {return 
DateAdp_.parse_iso8561(Read_str(i));}
        public void             Close()                                         
{rows = null;}
        public static Mem_db_rdr new_by_rows(Mem_db_row[] rows) {return new 
Mem_db_rdr(rows);}
        public static Mem_db_rdr new_by_obj_ary(Object[][] obj_arys) {
diff --git a/400_xowa/src/gplx/xowa/hdumps/Xogv_tab_base.java 
b/400_xowa/src/gplx/xowa/hdumps/Xogv_tab_base.java
index 16b8738..1989702 100644
--- a/400_xowa/src/gplx/xowa/hdumps/Xogv_tab_base.java
+++ b/400_xowa/src/gplx/xowa/hdumps/Xogv_tab_base.java
@@ -38,6 +38,7 @@
                return Fetch_page_and_show(old_itm, new_itm);
        }
        private Hdump_page Fetch_page_and_show(Xog_history_itm old_itm, 
Xog_history_itm new_itm) {
+               if (new_itm == Xog_history_itm.Null) return new 
Hdump_page().Exists_n_();
                Hdump_page new_hpg = Fetch_page(new_itm.Wiki(), new_itm.Page());
                Show_page(old_itm, new_itm, new_hpg);
                return new_hpg;
diff --git a/400_xowa/src/gplx/xowa/hdumps/core/Hdump_page.java 
b/400_xowa/src/gplx/xowa/hdumps/core/Hdump_page.java
index 8d4e032..a5bcf9a 100644
--- a/400_xowa/src/gplx/xowa/hdumps/core/Hdump_page.java
+++ b/400_xowa/src/gplx/xowa/hdumps/core/Hdump_page.java
@@ -18,6 +18,7 @@
 package gplx.xowa.hdumps.core; import gplx.*; import gplx.xowa.*; import 
gplx.xowa.hdumps.*;
 import gplx.xowa.pages.*; import gplx.xowa.pages.skins.*;
 public class Hdump_page {
+       public boolean                          Exists() {return exists;} 
public Hdump_page Exists_n_() {exists = false; return this;} private boolean 
exists = true;
        public int                              Page_id() {return page_id;} 
private int page_id;
        public Xoa_url                  Page_url() {return page_url;} private 
Xoa_url page_url;
        public Xoa_ttl                  Page_ttl() {return page_ttl;} private 
Xoa_ttl page_ttl;
diff --git a/400_xowa/src/gplx/xowa/hdumps/saves/Hdump_stats_tbl.java 
b/400_xowa/src/gplx/xowa/hdumps/saves/Hdump_stats_tbl.java
index bdc0a67..42931e8 100644
--- a/400_xowa/src/gplx/xowa/hdumps/saves/Hdump_stats_tbl.java
+++ b/400_xowa/src/gplx/xowa/hdumps/saves/Hdump_stats_tbl.java
@@ -67,7 +67,7 @@
        , Fld_js_math = "js_math", Fld_js_imap = "js_imap", Fld_js_packed = 
"js_packed", Fld_js_hiero = "js_hiero"
        , Fld_a_rhs = "a_rhs", Fld_lnki_text_n = "lnki_text_n", Fld_lnki_text_y 
= "lnki_text_y"
        , Fld_lnke_txt = "lnke_txt", Fld_lnke_brk_text_n = "lnke_brk_text_n", 
Fld_lnke_brk_text_y = "lnke_brk_text_y"
-       , Fld_hdr_1 = "hdr_1", Fld_hdr_2 = "hdr_2", Fld_hdr_3 = "hdr_3", 
Fld_hdr_4 = "hdr_", Fld_hdr_5 = "hdr_", Fld_hdr_6 = "hdr_6"
+       , Fld_hdr_1 = "hdr_1", Fld_hdr_2 = "hdr_2", Fld_hdr_3 = "hdr_3", 
Fld_hdr_4 = "hdr_4", Fld_hdr_5 = "hdr_5", Fld_hdr_6 = "hdr_6"
        , Fld_img_full = "img_full"
        ;
        private static final String[] Flds__all = new String[] 
diff --git a/400_xowa/src/gplx/xowa/html/hzips/Xow_hzip_itm__header_tst.java 
b/400_xowa/src/gplx/xowa/html/hzips/Xow_hzip_itm__header_tst.java
index 6055b7c..617aed6 100644
--- a/400_xowa/src/gplx/xowa/html/hzips/Xow_hzip_itm__header_tst.java
+++ b/400_xowa/src/gplx/xowa/html/hzips/Xow_hzip_itm__header_tst.java
@@ -19,12 +19,20 @@
 import org.junit.*; import gplx.xowa.html.*; import gplx.xowa.hdumps.srls.*;
 public class Xow_hzip_itm__header_tst {
        @Before public void init() {fxt.Clear();} private Xow_hzip_mgr_fxt fxt 
= new Xow_hzip_mgr_fxt();
-       @Test   public void Srl_hdr() {
+       @Test   public void Srl_basic() {
                byte[][] brys = Bry_.Ary(Xow_hzip_dict.Bry_hdr_lhs, 
Bry_.ints_(2), Bry_.new_ascii_("A"), Xow_hzip_dict.Escape_bry);
                fxt.Test_save(brys, "<h2><span class='mw-headline' 
id='A'>A<!--xo_hdr_end--></span></h2>");
                fxt.Test_load(brys, "<h2><span class='mw-headline' 
id='A'>A</span></h2>");
        }
-       @Test   public void Html_hdr() {
+       @Test   public void Html_basic() {
                fxt.Test_html("==A==", "<h2><span class='mw-headline' 
id='A'>A<!--xo_hdr_end--></span></h2>\n");
        }
+       @Test   public void Srl_anchor() {
+               byte[][] brys = Bry_.Ary(Xow_hzip_dict.Bry_hdr_lhs, 
Bry_.ints_(2), Bry_.new_ascii_("A <a xtid='a_lnki_text_n' href=\"/wiki/B\" 
xowa_redlink='1'>b</a> c"), Xow_hzip_dict.Escape_bry);
+               fxt.Test_save(brys, "<h2><span class='mw-headline' id='A_b_c'>A 
<a xtid='a_lnki_text_n' href=\"/wiki/B\" xowa_redlink='1'>b</a> 
c<!--xo_hdr_end--></span></h2>");
+//                     fxt.Test_load(brys, "<h2><span class='mw-headline' 
id='A_b_c'>A <a xtid='a_lnki_text_n' href=\"/wiki/B\" xowa_redlink='1'>b</a> 
c<!--xo_hdr_end--></span></h2>");
+       }
+       @Test   public void Html_anchor() {
+               fxt.Test_html("==A [[b]] c==", "<h2><span class='mw-headline' 
id='A_b_c'>A <a xtid='a_lnki_text_n' href=\"/wiki/B\" xowa_redlink='1'>b</a> 
c<!--xo_hdr_end--></span></h2>\n");
+       }
 }
diff --git a/400_xowa/src/gplx/xowa/specials/Xows_mgr.java 
b/400_xowa/src/gplx/xowa/specials/Xows_mgr.java
index 4e1abfe..8ff1371 100644
--- a/400_xowa/src/gplx/xowa/specials/Xows_mgr.java
+++ b/400_xowa/src/gplx/xowa/specials/Xows_mgr.java
@@ -21,6 +21,7 @@
 import gplx.xowa.specials.allPages.*; import gplx.xowa.specials.search.*; 
import gplx.xowa.specials.nearby.*; import gplx.xowa.specials.randoms.*; import 
gplx.xowa.specials.statistics.*; import gplx.xowa.xtns.translates.*; import 
gplx.xowa.specials.movePage.*;
 import gplx.xowa.specials.xowa.system_data.*; import 
gplx.xowa.specials.xowa.default_tab.*; import 
gplx.xowa.specials.xowa.popup_history.*;
 import gplx.xowa.xtns.wdatas.specials.*;
+import gplx.xowa.users.data.*;
 public class Xows_mgr {
        private Hash_adp_bry hash;
        public Xows_mgr(Xow_wiki wiki, Xol_lang lang) {
@@ -35,6 +36,7 @@
        public Xows_page_random                         Page_random() {return 
page_random;} private Xows_page_random page_random;
        public Xop_randomRootPage_page          Page_randomRootPage() {return 
page_randomRootPage;} private Xop_randomRootPage_page page_randomRootPage = new 
Xop_randomRootPage_page();
        public Xou_history_html                         Page_history() {return 
page_history;} private Xou_history_html page_history = new Xou_history_html();
+       public Xoud_history_special                     Page_history2() {return 
page_history2;} private Xoud_history_special page_history2 = new 
Xoud_history_special();
        public Nearby_mgr                                       Page_nearby() 
{return page_nearby;} Nearby_mgr page_nearby = new Nearby_mgr();
        public Xop_mylanguage_page                      Page_mylanguage() 
{return page_mylanguage;} private Xop_mylanguage_page page_mylanguage = new 
Xop_mylanguage_page();
        public Wdata_itemByTitle_page           Page_itemByTitle() {return 
page_itemByTitle;} Wdata_itemByTitle_page page_itemByTitle = new 
Wdata_itemByTitle_page();
@@ -52,6 +54,7 @@
                hash.Add_str_obj("randompage"                                   
                , page_random);
                hash.Add_str_obj("randomrootpage"                               
                , page_randomRootPage);
                hash.Add_bry_obj(Xou_history_mgr.Ttl_name                       
        , page_history);
+               hash.Add_bry_obj(Xoud_history_special.Ttl_name                  
, page_history2);
                hash.Add_bry_obj(Nearby_mgr.Ttl_name                            
        , page_nearby);
                hash.Add_str_obj("mylanguage"                                   
                , page_mylanguage);
                hash.Add_str_obj("itembytitle"                                  
                , page_itemByTitle);
diff --git 
a/400_xowa/src/gplx/xowa/specials/xowa/file_browsers/Xosp_fbrow_special.java 
b/400_xowa/src/gplx/xowa/specials/xowa/file_browsers/Xosp_fbrow_special.java
new file mode 100644
index 0000000..a25f616
--- /dev/null
+++ b/400_xowa/src/gplx/xowa/specials/xowa/file_browsers/Xosp_fbrow_special.java
@@ -0,0 +1,70 @@
+/*
+XOWA: the XOWA Offline Wiki Application
+Copyright (C) 2012 gnosy...@gmail.com
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package gplx.xowa.specials.xowa.file_browsers; import gplx.*; import 
gplx.xowa.*; import gplx.xowa.specials.*; import gplx.xowa.specials.xowa.*;
+import gplx.xowa.specials.*;
+public class Xosp_fbrow_special implements Bry_fmtr_arg, Xows_page {
+       // private Xoa_url_arg_hash url_args = new Xoa_url_arg_hash();
+       public void Special_gen(Xoa_url url, Xoa_page page, Xow_wiki wiki, 
Xoa_ttl ttl) {
+               /*
+               url_args.Init_enum("cmd").Val("view", Tid_view).Val("select", 
Tid_select);
+               url_args.Load(url_args.Args_bry);
+               switch (url_args.Read_val_as_byte("cmd")) {
+                       case Tid_view: Write_view();
+               }
+               String x = url_args.Read_key_as_str("path");
+               */
+//                     url_args.Load(url);
+//                     byte[] cmd_bry = arg_hash.Get_val_bry_or(Arg_cmd, 
null); if (cmd_bry == null) return;
+//                     byte cmd_tid = 
((Byte_obj_ref)arg_cmds.Get_by_bry(cmd_bry)).Val();
+//                     switch (cmd_tid) {
+//                             case Arg_cmd_view:              Write_view(); 
break;
+//                             case Arg_cmd_select:    break;
+//                     }
+       }
+//             private void Write_view() {
+//                     byte[] path_bry = arg_hash.Get_val_bry_or(Arg_path, 
null); if (path_bry == null) return;
+//                     Io_url path_url = 
Io_url_.new_any_(String_.new_utf8_(path_bry));
+//                     Io_mgr._.QueryDir_args(path_url).ExecAsDir();
+//             }
+       public void XferAry(Bry_bfr bfr, int idx) {
+       }
+//             private static final byte[] Arg_cmd = Bry_.new_ascii_("cmd"), 
Arg_path = Bry_.new_ascii_("path");
+//             private static final byte Arg_cmd_view = 1, Arg_cmd_select = 2;
+//             private static final Hash_adp_bry arg_cmds = 
Hash_adp_bry.ci_ascii_()
+//             .Add_str_byte("view"                    , Arg_cmd_view)
+//             .Add_str_byte("select"                  , Arg_cmd_select)
+//             ;
+//             private static Bry_fmtr html_grp = 
Bry_fmtr.new_(String_.Concat_lines_nl_skip_last
+//             ( "<table class='sortable'>"
+//             , "  <tr>"
+//             , "    <th><!--icon--></th>"
+//             , "    <th>name</th>"
+//             , "  </tr>~{itms}"
+//             , "</table>"
+//             ), "itms"
+//             );
+//             private static Bry_fmtr html_itm = 
Bry_fmtr.new_(String_.Concat_lines_nl_skip_last
+//             ( ""
+//             , "  <tr>"
+//             , "    <td></td>"
+//             , "    <td>~{name}</td>"
+//             , "  </tr>"
+//             ), "itm_name"
+//             );
+//             public static final byte[] Ttl_name = 
Bry_.new_ascii_("XowaFileBrowser");
+}
diff --git 
a/400_xowa/src/gplx/xowa/specials/xowa/system_data/System_data_page.java 
b/400_xowa/src/gplx/xowa/specials/xowa/system_data/System_data_page.java
index 8706aeb..e3a2bed 100644
--- a/400_xowa/src/gplx/xowa/specials/xowa/system_data/System_data_page.java
+++ b/400_xowa/src/gplx/xowa/specials/xowa/system_data/System_data_page.java
@@ -44,7 +44,6 @@
 
        private static final byte[] Arg_type = Bry_.new_ascii_("type");
        private static final byte Type_log_session = 1, Type_cfg_app = 2, 
Type_cfg_lang = 3, Type_cfg_user = 4, Type_cfg_custom = 5, Type_usr_history = 6;
-
        private static final Hash_adp_bry type_hash = Hash_adp_bry.cs_()
        .Add_str_byte("log_session"             , Type_log_session)
        .Add_str_byte("cfg_app"                 , Type_cfg_app)
diff --git a/400_xowa/src/gplx/xowa/users/Xou_user.java 
b/400_xowa/src/gplx/xowa/users/Xou_user.java
index aa21e1e..dfb00c6 100644
--- a/400_xowa/src/gplx/xowa/users/Xou_user.java
+++ b/400_xowa/src/gplx/xowa/users/Xou_user.java
@@ -17,6 +17,7 @@
 */
 package gplx.xowa.users; import gplx.*; import gplx.xowa.*;
 import gplx.xowa.wikis.*; import gplx.xowa.wikis.xwikis.*; import 
gplx.xowa.users.dbs.*; import gplx.xowa.users.history.*; import 
gplx.xowa.xtns.scribunto.*;
+import gplx.xowa.users.data.*;
 public class Xou_user implements GfoEvMgrOwner, GfoInvkAble {
        public Xou_user(Xoa_app app, Io_url user_dir) {
                this.evMgr = GfoEvMgr.new_(this);
@@ -33,6 +34,7 @@
        public byte[] Key_bry() {return key_bry;} private byte[] key_bry;
        public void Key_str_(String v) {this.key_str = v; this.key_bry = 
Bry_.new_utf8_(v);}
        public GfoEvMgr EvMgr() {return evMgr;} private final GfoEvMgr evMgr;
+       public Xoud_data_mgr Data_mgr() {return data_mgr;} private 
Xoud_data_mgr data_mgr = new Xoud_data_mgr();
        public Xol_lang Lang() {if (lang == null) {lang = 
app.Lang_mgr().Get_by_key_or_new(app.Sys_cfg().Lang()); lang.Init_by_load();} 
return lang;} private Xol_lang lang;            
        public void Lang_(Xol_lang v) {
                lang = v;
@@ -58,6 +60,7 @@
                if (!Env_.Mode_testing()) {
                        db_mgr.App_init();
                        this.Available_from_fsys();
+                       data_mgr.Init_by_app(app);
                }
        }
        public void App_term() {
diff --git a/400_xowa/src/gplx/xowa/users/data/Xoud_bmk_row.java 
b/400_xowa/src/gplx/xowa/users/data/Xoud_bmk_row.java
new file mode 100644
index 0000000..f5fc02e
--- /dev/null
+++ b/400_xowa/src/gplx/xowa/users/data/Xoud_bmk_row.java
@@ -0,0 +1,31 @@
+/*
+XOWA: the XOWA Offline Wiki Application
+Copyright (C) 2012 gnosy...@gmail.com
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package gplx.xowa.users.data; import gplx.*; import gplx.xowa.*; import 
gplx.xowa.users.*;
+public class Xoud_bmk_row {
+       public Xoud_bmk_row(int bmk_id, int bmk_sort, String bmk_wiki, String 
bmk_page, String bmk_qarg, String bmk_wtxt, DateAdp bmk_time, int bmk_count) {
+               this.bmk_id = bmk_id; this.bmk_sort = bmk_sort; this.bmk_wiki = 
bmk_wiki; this.bmk_page = bmk_page; this.bmk_qarg = bmk_qarg; this.bmk_wtxt = 
bmk_wtxt; this.bmk_time = bmk_time; this.bmk_count = bmk_count;
+       }
+       public int Bmk_id() {return bmk_id;} private final int bmk_id;
+       public int Bmk_sort() {return bmk_sort;} private final int bmk_sort;
+       public String Bmk_wiki() {return bmk_wiki;} private final String 
bmk_wiki;
+       public String Bmk_page() {return bmk_page;} private final String 
bmk_page;
+       public String Bmk_qarg() {return bmk_qarg;} private final String 
bmk_qarg;
+       public String Bmk_wtxt() {return bmk_wtxt;} private final String 
bmk_wtxt;
+       public DateAdp Bmk_time() {return bmk_time;} private final DateAdp 
bmk_time;
+       public int Bmk_count() {return bmk_count;} private final int bmk_count;
+}
diff --git a/400_xowa/src/gplx/xowa/users/data/Xoud_bmk_tbl.java 
b/400_xowa/src/gplx/xowa/users/data/Xoud_bmk_tbl.java
new file mode 100644
index 0000000..13f028a
--- /dev/null
+++ b/400_xowa/src/gplx/xowa/users/data/Xoud_bmk_tbl.java
@@ -0,0 +1,80 @@
+/*
+XOWA: the XOWA Offline Wiki Application
+Copyright (C) 2012 gnosy...@gmail.com
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package gplx.xowa.users.data; import gplx.*; import gplx.xowa.*; import 
gplx.xowa.users.*;
+import gplx.dbs.*;
+public class Xoud_bmk_tbl {
+       private Db_stmt stmt_select, stmt_insert, stmt_delete;
+       public Db_provider Provider() {return provider;} public Xoud_bmk_tbl 
Provider_(Db_provider v) {this.Rls_all(); provider = v; return this;} private 
Db_provider provider;
+       @gplx.Virtual public void Insert(int sort, String wiki, String page, 
String qarg, String wtxt, DateAdp time, int count) {
+               if (stmt_insert == null) stmt_insert = 
Db_stmt_.new_insert_(provider, Tbl_name, Flds__all);
+               try {
+                       
stmt_insert.Clear().Val_int_(sort).Val_str_(wiki).Val_str_(page).Val_str_(qarg).Val_str_(wtxt).Val_str_(time.XtoStr_fmt_iso_8561()).Val_int_(count).Exec_insert();
+               }
+               catch (Exception exc) {stmt_insert = null; throw Err_.err_(exc, 
"stmt failed");} // must reset stmt, else next call will fail
+       }
+       @gplx.Virtual public void Delete(int id) {
+               if (stmt_delete == null) stmt_delete = 
Db_stmt_.new_delete_(provider, Tbl_name, Fld_bmk_id);
+               try {stmt_delete.Clear().Val_int_(id).Exec_delete();}
+               catch (Exception exc) {stmt_delete = null; throw Err_.err_(exc, 
"stmt failed");} // must reset stmt, else next call will fail
+       }
+       @gplx.Virtual public void Select_all(ListAdp rv) {
+               if (stmt_select == null) stmt_select = 
Db_stmt_.new_select_as_rdr(provider, Db_qry__select_in_tbl.new_(Tbl_name, null, 
Flds__all));
+               try {
+                       Db_rdr rdr = stmt_select.Clear().Exec_select_as_rdr();
+                       while (rdr.Move_next()) {
+                               Xoud_bmk_row row = Make_row(rdr);
+                               rv.Add(row);
+                       }
+                       rdr.Close();
+               }
+               catch (Exception exc) {stmt_select = null; throw Err_.err_(exc, 
"stmt failed");} // must reset stmt, else next call will fail
+       }
+       private Xoud_bmk_row Make_row(Db_rdr rdr) {
+               return new Xoud_bmk_row
+               ( rdr.Read_int(0)
+               , rdr.Read_int(1)
+               , rdr.Read_str(2)
+               , rdr.Read_str(3)
+               , rdr.Read_str(4)
+               , rdr.Read_str(5)
+               , rdr.Read_date_by_str(6)
+               , rdr.Read_int(7)
+               );
+       }
+       public void Rls_all() {
+               if (stmt_select != null) {stmt_select.Rls(); stmt_select = 
null;}
+               if (stmt_insert != null) {stmt_insert.Rls(); stmt_insert = 
null;}
+               if (stmt_delete != null) {stmt_delete.Rls(); stmt_delete = 
null;}
+               provider = null;
+       }
+       public static final String Tbl_name = "cfg_bmk", Fld_bmk_id = "bmk_id", 
Fld_bmk_count = "bmk_count", Fld_bmk_sort = "bmk_sort"
+       , Fld_bmk_time = "bmk_time", Fld_bmk_wiki = "bmk_wiki", Fld_bmk_page = 
"bmk_page", Fld_bmk_qarg = "bmk_qarg", Fld_bmk_wtxt = "bmk_wtxt";
+       public static final String[] Flds__all = new String[] {Fld_bmk_id, 
Fld_bmk_sort, Fld_bmk_wiki, Fld_bmk_page, Fld_bmk_qarg, Fld_bmk_wtxt, 
Fld_bmk_time, Fld_bmk_count};
+       public static final String Tbl_sql = String_.Concat_lines_nl
+       ( "CREATE TABLE cfg_bmk"
+       , "( bmk_id                                     integer                 
NOT NULL        PRIMARY KEY   AUTOINCREMENT"
+       , ", bmk_sort                           integer                 NOT 
NULL"
+       , ", bmk_wiki                           nvarchar(255)   NOT NULL"
+       , ", bmk_page                           nvarchar(255)   NOT NULL"
+       , ", bmk_qarg                           nvarchar(255)   NOT NULL"
+       , ", bmk_wtxt                           nvarchar(255)   NOT NULL"
+       , ", bmk_time                           nvarchar(20)    NOT NULL"
+       , ", bmk_count                          integer                 NOT 
NULL"
+       , ");"
+       );
+}
diff --git a/400_xowa/src/gplx/xowa/users/data/Xoud_data_mgr.java 
b/400_xowa/src/gplx/xowa/users/data/Xoud_data_mgr.java
new file mode 100644
index 0000000..a33dd50
--- /dev/null
+++ b/400_xowa/src/gplx/xowa/users/data/Xoud_data_mgr.java
@@ -0,0 +1,37 @@
+/*
+XOWA: the XOWA Offline Wiki Application
+Copyright (C) 2012 gnosy...@gmail.com
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package gplx.xowa.users.data; import gplx.*; import gplx.xowa.*; import 
gplx.xowa.users.*;
+import gplx.threads.*; import gplx.dbs.*; import gplx.dbs.schemas.updates.*; 
import gplx.dbs.schemas.*;
+public class Xoud_data_mgr {
+//             private Gfdb_db_base user_db = new Gfdb_db_base();
+       public Xoud_data_mgr() {
+       }
+       public Xoud_regy_mgr Regy_mgr() {return regy_mgr;} private 
Xoud_regy_mgr regy_mgr = new Xoud_regy_mgr();
+       public Xoud_history_mgr History_mgr() {return history_mgr;} private 
Xoud_history_mgr history_mgr = new Xoud_history_mgr();
+       public void Init_by_app(Xoa_app app) {
+//                     user_db.Schema().Loader_(Schema_loader_mgr_.Sqlite);
+//                     Init_user_db_changes(user_db.Schema().Updater());
+//                     Db_provider provider = app.User().Db_mgr().Provider();
+//                     user_db.Init(provider);
+//                     history_mgr.History_tbl().Provider_(provider);
+       }
+//             private void Init_user_db_changes(Schema_update_mgr updater) {
+//                     
updater.Add(Schema_update_cmd_.Make_tbl_create(Xoud_regy_tbl.Tbl_name, 
Xoud_regy_tbl.Tbl_sql, Xoud_regy_tbl.Idx_core));
+//                     
updater.Add(Schema_update_cmd_.Make_tbl_create(Xoud_history_tbl.Tbl_name, 
Xoud_history_tbl.Tbl_sql, Xoud_history_tbl.Idx_core));
+//             }
+}
diff --git a/400_xowa/src/gplx/xowa/users/data/Xoud_history_mgr.java 
b/400_xowa/src/gplx/xowa/users/data/Xoud_history_mgr.java
new file mode 100644
index 0000000..1c636f5
--- /dev/null
+++ b/400_xowa/src/gplx/xowa/users/data/Xoud_history_mgr.java
@@ -0,0 +1,52 @@
+/*
+XOWA: the XOWA Offline Wiki Application
+Copyright (C) 2012 gnosy...@gmail.com
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package gplx.xowa.users.data; import gplx.*; import gplx.xowa.*; import 
gplx.xowa.users.*;
+import gplx.threads.*;
+public class Xoud_history_mgr implements GfoInvkAble {
+       public Xoud_history_tbl History_tbl() {return history_tbl;} private 
final Xoud_history_tbl history_tbl = new Xoud_history_tbl();
+       public void Update_async(Gfo_async_mgr async_mgr, Xoa_ttl ttl, Xoa_url 
url) {
+//                     if (Skip_history(ttl)) return;
+//                     async_mgr.Queue(this, Invk_update, "wiki", 
String_.new_utf8_(url.Wiki_bry()), "page", String_.new_utf8_(url.Page_bry()), 
"qarg", String_.new_utf8_(url.Args_all_as_bry()));
+       }
+       private void Update(String wiki, String page, String qarg) {
+               Xoud_history_row row = history_tbl.Select_by_page(wiki, page, 
qarg);
+               DateAdp time = DateAdp_.Now();
+               if (row == null)
+                       history_tbl.Insert(wiki, page, qarg, time, 1);
+               else
+                       history_tbl.Update(wiki, page, qarg, time, 
row.History_count() + 1);
+       }
+       public void Select(ListAdp rv, int top) {
+               history_tbl.Select_by_top(rv, top);
+       }
+       public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) {
+               if              (ctx.Match(k, Invk_update))             
Update(m.ReadStr("wiki"), m.ReadStr("page"), m.ReadStr("qarg"));
+               else    return GfoInvkAble_.Rv_unhandled;
+               return this;
+       }       private static final String Invk_update = "update";
+       public static boolean Skip_history(Xoa_ttl ttl) {
+               byte[] page_db = ttl.Page_db();
+               return  (       ttl.Ns().Id_special()
+                               &&      (       Bry_.Eq(page_db, 
gplx.xowa.users.history.Xou_history_mgr.Ttl_name)      // do not add 
XowaPageHistory to history
+                                       ||      Bry_.Eq(page_db, 
gplx.xowa.specials.xowa.popup_history.Popup_history_page.Ttl_name_bry)
+                                       ||      Bry_.Eq(page_db, 
gplx.xowa.specials.xowa.default_tab.Default_tab_page.Ttl_name_bry)
+                                       ||      Bry_.Eq(page_db, 
Xoud_history_special.Ttl_name)
+                                       )
+                               );
+       }
+}
diff --git a/400_xowa/src/gplx/xowa/users/data/Xoud_history_row.java 
b/400_xowa/src/gplx/xowa/users/data/Xoud_history_row.java
new file mode 100644
index 0000000..7835c2b
--- /dev/null
+++ b/400_xowa/src/gplx/xowa/users/data/Xoud_history_row.java
@@ -0,0 +1,32 @@
+/*
+XOWA: the XOWA Offline Wiki Application
+Copyright (C) 2012 gnosy...@gmail.com
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package gplx.xowa.users.data; import gplx.*; import gplx.xowa.*; import 
gplx.xowa.users.*;
+public class Xoud_history_row {
+       public Xoud_history_row(String history_wiki, String history_page, 
String history_qarg, DateAdp history_time, int history_count) {
+               this.history_wiki = history_wiki;
+               this.history_page = history_page;
+               this.history_qarg = history_qarg;
+               this.history_time = history_time;
+               this.history_count = history_count;
+       }
+       public String History_wiki() {return history_wiki;} private final 
String history_wiki;
+       public String History_page() {return history_page;} private final 
String history_page;
+       public String History_qarg() {return history_qarg;} private final 
String history_qarg;
+       public DateAdp History_time() {return history_time;} private final 
DateAdp history_time;
+       public int History_count() {return history_count;} private final int 
history_count;
+}
diff --git a/400_xowa/src/gplx/xowa/users/data/Xoud_history_special.java 
b/400_xowa/src/gplx/xowa/users/data/Xoud_history_special.java
new file mode 100644
index 0000000..3120b26
--- /dev/null
+++ b/400_xowa/src/gplx/xowa/users/data/Xoud_history_special.java
@@ -0,0 +1,59 @@
+/*
+XOWA: the XOWA Offline Wiki Application
+Copyright (C) 2012 gnosy...@gmail.com
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package gplx.xowa.users.data; import gplx.*; import gplx.xowa.*; import 
gplx.xowa.users.*;
+import gplx.xowa.specials.*;
+public class Xoud_history_special implements Bry_fmtr_arg, Xows_page {
+       private ListAdp rows = ListAdp_.new_();
+       public void Special_gen(Xoa_url calling_url, Xoa_page page, Xow_wiki 
wiki, Xoa_ttl ttl) {
+               Xoa_app app = wiki.App();
+               Xoud_history_mgr mgr = app.User().Data_mgr().History_mgr();
+               mgr.Select(rows, 100);
+               Bry_bfr bfr = app.Utl_bry_bfr_mkr().Get_m001(); 
+               html_grp.Bld_bfr_many(bfr, this);
+               page.Data_raw_(bfr.Mkr_rls().Xto_bry_and_clear());
+       }
+       public void XferAry(Bry_bfr bfr, int idx) {
+               int len = rows.Count();
+               for (int i = 0; i < len; i++) {
+                       Xoud_history_row row = 
(Xoud_history_row)rows.FetchAt(i);
+                       html_itm.Bld_bfr_many(bfr, row.History_wiki(), 
row.History_page(), row.History_count(), 
row.History_time().XtoStr_fmt_yyyy_MM_dd_HH_mm());
+               }
+       }
+       private static Bry_fmtr html_grp = 
Bry_fmtr.new_(String_.Concat_lines_nl_skip_last
+       ( "<table class='sortable'>"
+       , "  <tr>"
+       , "    <th>page</th>"
+       , "    <th>wiki</th>"
+       , "    <th>views</th>"
+       , "    <th>time</th>"
+       , "  </tr>~{itms}"
+       , "</table>"
+       ), "itms"
+       );
+       private static Bry_fmtr html_itm = 
Bry_fmtr.new_(String_.Concat_lines_nl_skip_last
+       ( ""
+       , "  <tr>"
+       , "    <td>[[~{itm_wiki}:~{itm_page}|~{itm_page}]]</td>"
+       , "    <td>~{itm_wiki}</td>"
+       , "    <td>~{itm_count}</td>"
+       , "    <td>~{itm_last}</td>"
+       , "  </tr>"
+       ), "itm_wiki", "itm_page", "itm_count", "itm_last"
+       );
+       public static final byte[] Ttl_name = Bry_.new_ascii_("XowaHistory");
+}
diff --git a/400_xowa/src/gplx/xowa/users/data/Xoud_history_tbl.java 
b/400_xowa/src/gplx/xowa/users/data/Xoud_history_tbl.java
new file mode 100644
index 0000000..683b875
--- /dev/null
+++ b/400_xowa/src/gplx/xowa/users/data/Xoud_history_tbl.java
@@ -0,0 +1,99 @@
+/*
+XOWA: the XOWA Offline Wiki Application
+Copyright (C) 2012 gnosy...@gmail.com
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package gplx.xowa.users.data; import gplx.*; import gplx.xowa.*; import 
gplx.xowa.users.*;
+import gplx.dbs.*; import gplx.dbs.schemas.*; import 
gplx.dbs.schemas.updates.*;
+public class Xoud_history_tbl {
+       private Db_stmt stmt_select_by_page, stmt_select_by_top, stmt_insert, 
stmt_update, stmt_delete;
+       public Db_provider Provider() {return provider;} public 
Xoud_history_tbl Provider_(Db_provider v) {this.Rls_all(); provider = v; return 
this;} private Db_provider provider;
+       @gplx.Virtual public void Insert(String wiki, String page, String qarg, 
DateAdp time, int count) {
+               if (stmt_insert == null) stmt_insert = 
Db_stmt_.new_insert_(provider, Tbl_name, Flds__all);
+               try {
+                       
stmt_insert.Clear().Val_str_(wiki).Val_str_(page).Val_str_(qarg).Val_str_(time.XtoStr_fmt_iso_8561()).Val_int_(count).Exec_insert();
+               }
+               catch (Exception exc) {stmt_insert = null; throw Err_.err_(exc, 
"stmt failed");} // must reset stmt, else next call will fail
+       }
+       @gplx.Virtual public void Update(String wiki, String page, String qarg, 
DateAdp time, int count) {
+               if (stmt_update == null) stmt_update = 
Db_stmt_.new_update_(provider, Tbl_name, String_.Ary(Fld_history_wiki, 
Fld_history_page, Fld_history_qarg), String_.Ary(Fld_history_time, 
Fld_history_count));
+               try {
+                       
stmt_update.Clear().Val_str_(time.XtoStr_fmt_iso_8561()).Val_int_(count).Val_str_(wiki).Val_str_(page).Val_str_(qarg).Exec_update();
+               }
+               catch (Exception exc) {stmt_update = null; throw Err_.err_(exc, 
"stmt failed");} // must reset stmt, else next call will fail
+       }
+       @gplx.Virtual public void Delete(String wiki, String page, String qarg) 
{
+               if (stmt_delete == null) stmt_delete = 
Db_stmt_.new_delete_(provider, Tbl_name, Fld_history_wiki, Fld_history_page, 
Fld_history_qarg);
+               try 
{stmt_delete.Clear().Val_str_(wiki).Val_str_(page).Val_str_(qarg).Exec_delete();}
+               catch (Exception exc) {stmt_delete = null; throw Err_.err_(exc, 
"stmt failed");} // must reset stmt, else next call will fail
+       }
+       @gplx.Virtual public Xoud_history_row Select_by_page(String wiki, 
String page, String qarg) {
+               if (stmt_select_by_page == null) stmt_select_by_page = 
Db_stmt_.new_select_as_rdr(provider, Db_qry__select_in_tbl.new_(Tbl_name, 
String_.Ary(Fld_history_wiki, Fld_history_page, Fld_history_qarg), Flds__all));
+               try {
+                       Db_rdr rdr = 
stmt_select_by_page.Clear().Val_str_(wiki).Val_str_(page).Val_str_(qarg).Exec_select_as_rdr();
+                       Xoud_history_row rv = null;
+                       if (rdr.Move_next())
+                               rv = Make_row(rdr);
+                       rdr.Close();
+                       return rv;
+               }
+               catch (Exception exc) {stmt_select_by_page = null; throw 
Err_.err_(exc, "stmt failed");} // must reset stmt, else next call will fail
+       }
+       @gplx.Virtual public void Select_by_top(ListAdp rv, int count) {
+               Db_qry__select_in_tbl qry = new Db_qry__select_in_tbl(Tbl_name, 
Flds__all, null, null, null, Fld_history_time + " DESC", " LIMIT " + 
Int_.Xto_str(count));
+               stmt_select_by_top = Db_stmt_.new_select_as_rdr(provider, qry);
+               try {
+                       Db_rdr rdr = 
stmt_select_by_top.Clear().Exec_select_as_rdr();
+                       rv.Clear();                             
+                       while (rdr.Move_next()) {
+                               Xoud_history_row row = Make_row(rdr);
+                               rv.Add(row);
+                       }
+                       rdr.Close();
+               }
+               catch (Exception exc) {stmt_select_by_top = null; throw 
Err_.err_(exc, "stmt failed");} // must reset stmt, else next call will fail
+       }
+       private Xoud_history_row Make_row(Db_rdr rdr) {
+               return new Xoud_history_row
+               ( rdr.Read_str(0)
+               , rdr.Read_str(1)
+               , rdr.Read_str(2)
+               , rdr.Read_date_by_str(3)
+               , rdr.Read_int(4)
+               );
+       }
+       public void Rls_all() {
+               if (stmt_select_by_page != null) {stmt_select_by_page.Rls(); 
stmt_select_by_page = null;}
+               if (stmt_select_by_top != null) {stmt_select_by_top.Rls(); 
stmt_select_by_top = null;}
+               if (stmt_insert != null) {stmt_insert.Rls(); stmt_insert = 
null;}
+               if (stmt_update != null) {stmt_update.Rls(); stmt_update = 
null;}
+               if (stmt_delete != null) {stmt_delete.Rls(); stmt_delete = 
null;}
+               provider = null;
+       }
+       public static final String Tbl_name = "user_history", Fld_history_wiki 
= "history_wiki", Fld_history_page = "history_page", Fld_history_qarg = 
"history_qarg"
+       , Fld_history_time = "history_time", Fld_history_count = "history_count"
+       ;
+       private static final String[] Flds__all = new String[] 
{Fld_history_wiki, Fld_history_page, Fld_history_qarg, Fld_history_time, 
Fld_history_count};
+       public static final String Tbl_sql = String_.Concat_lines_nl
+       ( "CREATE TABLE user_history"
+       , "( history_wiki                    nvarchar(255)   NOT NULL"
+       , ", history_page                    nvarchar(255)   NOT NULL"
+       , ", history_qarg                    nvarchar(255)   NOT NULL"
+       , ", history_time                    nvarchar(20)    NOT NULL"
+       , ", history_count                   integer         NOT NULL"
+       , ");"
+       );
+       public static final Db_idx_itm Idx_core = Db_idx_itm.sql_("CREATE 
UNIQUE INDEX user_history_core ON user_history (history_wiki, history_page, 
history_qarg);");
+}
diff --git a/400_xowa/src/gplx/xowa/users/data/Xoud_regy_mgr.java 
b/400_xowa/src/gplx/xowa/users/data/Xoud_regy_mgr.java
new file mode 100644
index 0000000..1e97802
--- /dev/null
+++ b/400_xowa/src/gplx/xowa/users/data/Xoud_regy_mgr.java
@@ -0,0 +1,31 @@
+/*
+XOWA: the XOWA Offline Wiki Application
+Copyright (C) 2012 gnosy...@gmail.com
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package gplx.xowa.users.data; import gplx.*; import gplx.xowa.*; import 
gplx.xowa.users.*;
+public class Xoud_regy_mgr {
+       private Xoud_regy_tbl regy_tbl = new Xoud_regy_tbl();
+       public int Next_id(String tbl) {
+               String grp = "xowa." + tbl, key = "next_id";
+               int next_id = Int_.parse_or_(regy_tbl.Select_val(grp, key), 1); 
// EX: xowa.cfg_history|next_id|1
+               String new_val = Int_.Xto_str(next_id + 1);
+               if (next_id == 1)
+                       regy_tbl.Insert(grp, key, new_val);
+               else
+                       regy_tbl.Update(grp, key, new_val);
+               return next_id;
+       }
+}
diff --git a/400_xowa/src/gplx/xowa/users/data/Xoud_regy_row.java 
b/400_xowa/src/gplx/xowa/users/data/Xoud_regy_row.java
new file mode 100644
index 0000000..1bfcccb
--- /dev/null
+++ b/400_xowa/src/gplx/xowa/users/data/Xoud_regy_row.java
@@ -0,0 +1,24 @@
+/*
+XOWA: the XOWA Offline Wiki Application
+Copyright (C) 2012 gnosy...@gmail.com
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package gplx.xowa.users.data; import gplx.*; import gplx.xowa.*; import 
gplx.xowa.users.*;
+public class Xoud_regy_row {
+       public Xoud_regy_row(String cfg_grp, String cfg_key, String cfg_val) 
{this.cfg_grp = cfg_grp; this.cfg_key = cfg_key; this.cfg_val = cfg_val;}
+       public String Cfg_grp() {return cfg_grp;} private final String cfg_grp;
+       public String Cfg_key() {return cfg_key;} private final String cfg_key;
+       public String Cfg_val() {return cfg_val;} private final String cfg_val;
+}
diff --git a/400_xowa/src/gplx/xowa/users/data/Xoud_regy_tbl.java 
b/400_xowa/src/gplx/xowa/users/data/Xoud_regy_tbl.java
new file mode 100644
index 0000000..dcc3e13
--- /dev/null
+++ b/400_xowa/src/gplx/xowa/users/data/Xoud_regy_tbl.java
@@ -0,0 +1,85 @@
+/*
+XOWA: the XOWA Offline Wiki Application
+Copyright (C) 2012 gnosy...@gmail.com
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package gplx.xowa.users.data; import gplx.*; import gplx.xowa.*; import 
gplx.xowa.users.*;
+import gplx.dbs.*; import gplx.dbs.schemas.*; import 
gplx.dbs.schemas.updates.*;
+public class Xoud_regy_tbl {
+       private Db_stmt stmt_select_grp, stmt_select_key, stmt_insert, 
stmt_update, stmt_delete;
+       public Db_provider Provider() {return provider;} public Xoud_regy_tbl 
Provider_(Db_provider v) {this.Rls_all(); provider = v; return this;} private 
Db_provider provider;
+       @gplx.Virtual public void Insert(String grp, String key, String val) {
+               if (stmt_insert == null) stmt_insert = 
Db_stmt_.new_insert_(provider, Tbl_name, Flds__all);
+               try 
{stmt_insert.Clear().Val_str_(grp).Val_str_(key).Val_str_(val).Exec_insert();}
+               catch (Exception exc) {stmt_insert = null; throw Err_.err_(exc, 
"stmt failed");} // must reset stmt, else next call will fail
+       }
+       @gplx.Virtual public void Update(String grp, String key, String val) {
+               if (stmt_update == null) stmt_update = 
Db_stmt_.new_update_(provider, Tbl_name, String_.Ary(Fld_regy_grp, 
Fld_regy_key), Fld_regy_val);
+               try 
{stmt_update.Clear().Val_str_(grp).Val_str_(key).Val_str_(Fld_regy_val).Exec_update();}
+               catch (Exception exc) {stmt_update = null; throw Err_.err_(exc, 
"stmt failed");} // must reset stmt, else next call will fail
+       }
+       @gplx.Virtual public void Delete(String grp, String key) {
+               if (stmt_delete == null) stmt_delete = 
Db_stmt_.new_delete_(provider, Tbl_name, Fld_regy_grp, Fld_regy_key);
+               try 
{stmt_delete.Clear().Val_str_(grp).Val_str_(key).Exec_delete();}
+               catch (Exception exc) {stmt_delete = null; throw Err_.err_(exc, 
"stmt failed");} // must reset stmt, else next call will fail
+       }
+       @gplx.Virtual public void Select_by_grp(ListAdp rv, String grp) {
+               if (stmt_select_grp == null) stmt_select_grp = 
Db_stmt_.new_select_as_rdr(provider, Db_qry__select_in_tbl.new_(Tbl_name, 
String_.Ary(Fld_regy_grp), Flds__all));
+               try {
+                       Db_rdr rdr = 
stmt_select_grp.Clear().Val_str_(grp).Exec_select_as_rdr();
+                       while (rdr.Move_next()) {
+                               Xoud_regy_row row = Make_row(rdr);
+                               rv.Add(row);
+                       }
+                       rdr.Close();
+               }
+               catch (Exception exc) {stmt_select_grp = null; throw 
Err_.err_(exc, "stmt failed");} // must reset stmt, else next call will fail
+       }
+       @gplx.Virtual public String Select_val(String grp, String key) {
+               if (stmt_select_key == null) stmt_select_key = 
Db_stmt_.new_select_as_rdr(provider, Db_qry__select_in_tbl.new_(Tbl_name, 
String_.Ary(Fld_regy_grp, Fld_regy_key), Flds__all));
+               try {
+                       Db_rdr rdr = 
stmt_select_grp.Clear().Val_str_(grp).Val_str_(key).Exec_select_as_rdr();
+                       String rv = rdr.Read_str(2);
+                       rdr.Close();
+                       return rv;
+               }
+               catch (Exception exc) {stmt_select_grp = null; throw 
Err_.err_(exc, "stmt failed");} // must reset stmt, else next call will fail
+       }
+       private Xoud_regy_row Make_row(Db_rdr rdr) {
+               return new Xoud_regy_row
+               ( rdr.Read_str(0)
+               , rdr.Read_str(1)
+               , rdr.Read_str(2)
+               );
+       }
+       public void Rls_all() {
+               if (stmt_select_grp != null) {stmt_select_grp.Rls(); 
stmt_select_grp = null;}
+               if (stmt_select_key != null) {stmt_select_key.Rls(); 
stmt_select_key = null;}
+               if (stmt_insert != null) {stmt_insert.Rls(); stmt_insert = 
null;}
+               if (stmt_update != null) {stmt_update.Rls(); stmt_update = 
null;}
+               if (stmt_delete != null) {stmt_delete.Rls(); stmt_delete = 
null;}
+               provider = null;
+       }
+       public static final String Tbl_name = "user_regy", Fld_regy_grp = 
"regy_grp", Fld_regy_key = "regy_key", Fld_regy_val = "regy_val";
+       private static final String[] Flds__all = new String[] {Fld_regy_grp, 
Fld_regy_key, Fld_regy_val};
+       public static final Db_idx_itm Idx_core = Db_idx_itm.sql_("CREATE 
UNIQUE INDEX user_regy_core ON user_regy (regy_grp, regy_key);");
+       public static final String Tbl_sql = String_.Concat_lines_nl
+       ( "CREATE TABLE user_regy"
+       , "( regy_grp           nvarchar(255)           NOT NULL           -- 
EX: xowa.schema.site"
+       , ", regy_key           nvarchar(255)           NOT NULL           -- 
EX: next_id"
+       , ", regy_val           nvarchar(255)           NOT NULL           -- 
EX: 1"
+       , ");"
+       );
+}
diff --git a/400_xowa/src/gplx/xowa/users/data/Xoud_site_row.java 
b/400_xowa/src/gplx/xowa/users/data/Xoud_site_row.java
new file mode 100644
index 0000000..033ba6f
--- /dev/null
+++ b/400_xowa/src/gplx/xowa/users/data/Xoud_site_row.java
@@ -0,0 +1,29 @@
+/*
+XOWA: the XOWA Offline Wiki Application
+Copyright (C) 2012 gnosy...@gmail.com
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package gplx.xowa.users.data; import gplx.*; import gplx.xowa.*; import 
gplx.xowa.users.*;
+public class Xoud_site_row {
+       public Xoud_site_row(int site_id, int site_priority, String 
site_domain, String site_name, String site_path, String site_xtn) {
+               this.site_id = site_id; this.site_priority = site_priority; 
this.site_domain = site_domain; this.site_name = site_name; this.site_path = 
site_path; this.site_xtn = site_xtn;
+       }
+       public int Site_id() {return site_id;} private final int site_id;
+       public int Site_priority() {return site_priority;} private final int 
site_priority;
+       public String Site_domain() {return site_domain;} private final String 
site_domain;
+       public String Site_name() {return site_name;} private final String 
site_name;
+       public String Site_path() {return site_path;} private final String 
site_path;
+       public String Site_xtn() {return site_xtn;} private String site_xtn;
+}
diff --git a/400_xowa/src/gplx/xowa/users/data/Xoud_site_tbl.java 
b/400_xowa/src/gplx/xowa/users/data/Xoud_site_tbl.java
new file mode 100644
index 0000000..7721114
--- /dev/null
+++ b/400_xowa/src/gplx/xowa/users/data/Xoud_site_tbl.java
@@ -0,0 +1,76 @@
+/*
+XOWA: the XOWA Offline Wiki Application
+Copyright (C) 2012 gnosy...@gmail.com
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package gplx.xowa.users.data; import gplx.*; import gplx.xowa.*; import 
gplx.xowa.users.*;
+import gplx.dbs.*;
+public class Xoud_site_tbl {
+       private Db_stmt stmt_select, stmt_insert, stmt_delete;
+       public Db_provider Provider() {return provider;} public Xoud_site_tbl 
Provider_(Db_provider v) {this.Rls_all(); provider = v; return this;} private 
Db_provider provider;
+       @gplx.Virtual public void Insert(int priority, String domain, String 
name, String path, String xtn) {
+               if (stmt_insert == null) stmt_insert = 
Db_stmt_.new_insert_(provider, Tbl_name, Flds__all);
+               try {
+                       
stmt_insert.Clear().Val_int_(priority).Val_str_(domain).Val_str_(name).Val_str_(path).Val_str_(xtn).Exec_insert();
+               }
+               catch (Exception exc) {stmt_insert = null; throw Err_.err_(exc, 
"stmt failed");} // must reset stmt, else next call will fail
+       }
+       @gplx.Virtual public void Delete(int id) {
+               if (stmt_delete == null) stmt_delete = 
Db_stmt_.new_delete_(provider, Tbl_name, Fld_site_id);
+               try {stmt_delete.Clear().Val_int_(id).Exec_delete();}
+               catch (Exception exc) {stmt_delete = null; throw Err_.err_(exc, 
"stmt failed");} // must reset stmt, else next call will fail
+       }
+       @gplx.Virtual public void Select_all(ListAdp rv) {
+               if (stmt_select == null) stmt_select = 
Db_stmt_.new_select_as_rdr(provider, Db_qry__select_in_tbl.new_(Tbl_name, null, 
Flds__all));
+               try {
+                       Db_rdr rdr = stmt_select.Clear().Exec_select_as_rdr();
+                       while (rdr.Move_next()) {
+                               Xoud_site_row row = Make_row(rdr);
+                               rv.Add(row);
+                       }
+                       rdr.Close();
+               }
+               catch (Exception exc) {stmt_select = null; throw Err_.err_(exc, 
"stmt failed");} // must reset stmt, else next call will fail
+       }
+       private Xoud_site_row Make_row(Db_rdr rdr) {
+               return new Xoud_site_row
+               ( rdr.Read_int(0)
+               , rdr.Read_int(1)
+               , rdr.Read_str(2)
+               , rdr.Read_str(3)
+               , rdr.Read_str(4)
+               , rdr.Read_str(5)
+               );
+       }
+       public void Rls_all() {
+               if (stmt_select != null) {stmt_select.Rls(); stmt_select = 
null;}
+               if (stmt_insert != null) {stmt_insert.Rls(); stmt_insert = 
null;}
+               if (stmt_delete != null) {stmt_delete.Rls(); stmt_delete = 
null;}
+               provider = null;
+       }
+       public static final String Tbl_name = "cfg_site", Fld_site_id = 
"site_id", Fld_site_priority = "site_priority", Fld_site_domain = "site_domain"
+       , Fld_site_name = "site_name", Fld_site_path = "site_path", 
Fld_site_xtn = "site_xtn";
+       private static final String[] Flds__all = new String[] {Fld_site_id, 
Fld_site_priority, Fld_site_domain, Fld_site_name, Fld_site_path, Fld_site_xtn};
+       public static final String Tbl_sql = String_.Concat_lines_nl
+       ( "CREATE TABLE cfg_site"
+       , "( site_id                            integer                 NOT 
NULL PRIMARY KEY"
+       , ", site_priority                      integer                 NOT 
NULL                                        -- EX: 0=default; 1+ is order if 0 
is unavailable"
+       , ", site_domain                        nvarchar(255)   NOT NULL        
                                -- EX: en.wikipedia.org; NOTE: no protocol 
(https:)"
+       , ", site_name                          nvarchar(255)   NOT NULL        
                                -- EX: English Wikipedia"
+       , ", site_path                          nvarchar(255)   NOT NULL        
                                -- EX: ~{xowa_root}/wiki/en.wikipedia.org/"
+       , ", site_xtn                           text                    NOT 
NULL"
+       , ");"
+       );
+}
diff --git a/400_xowa/src/gplx/xowa/users/history/Xou_history_mgr.java 
b/400_xowa/src/gplx/xowa/users/history/Xou_history_mgr.java
index 604d24b..36a4168 100644
--- a/400_xowa/src/gplx/xowa/users/history/Xou_history_mgr.java
+++ b/400_xowa/src/gplx/xowa/users/history/Xou_history_mgr.java
@@ -56,14 +56,8 @@
                Add(url, ttl, page_ttl);
        }
        public void Add(Xoa_url url, Xoa_ttl ttl, byte[] page_ttl) {
+               if (gplx.xowa.users.data.Xoud_history_mgr.Skip_history(ttl)) 
return;
                if (!load_chk) Load();
-               byte[] page_db = ttl.Page_db();
-               if (    ttl.Ns().Id_special()
-                       &&      (       Bry_.Eq(page_db, 
Xou_history_mgr.Ttl_name)      // do not add XowaPageHistory to history
-                               ||      Bry_.Eq(page_db, 
gplx.xowa.specials.xowa.popup_history.Popup_history_page.Ttl_name_bry)
-                               ||      Bry_.Eq(page_db, 
gplx.xowa.specials.xowa.default_tab.Default_tab_page.Ttl_name_bry)
-                               )
-                       ) return; 
                byte[] key = Xou_history_itm.key_(url.Wiki_bry(), page_ttl);
                Xou_history_itm itm = (Xou_history_itm)itms.Fetch(key);
                if (itm == null) {
diff --git a/400_xowa/src_310_url/gplx/xowa/Xoa_url.java 
b/400_xowa/src_310_url/gplx/xowa/Xoa_url.java
index b34d832..238ec2e 100644
--- a/400_xowa/src_310_url/gplx/xowa/Xoa_url.java
+++ b/400_xowa/src_310_url/gplx/xowa/Xoa_url.java
@@ -62,19 +62,20 @@
                }
                return false;
        }
-       public void Args_fill(OrderedHash trg_args) {
-               int trg_len = trg_args.Count();
-               for (int i = 0; i < trg_len; i++) {
-                       Gfo_url_arg trg_arg = (Gfo_url_arg)trg_args.FetchAt(i);
-                       trg_arg.Val_bry_(null);
-               }
-               int src_len = args.length;
-               for (int i = 0; i < src_len; i++) {
-                       Gfo_url_arg src_arg = args[i];
-                       Gfo_url_arg trg_arg = 
(Gfo_url_arg)trg_args.Fetch(src_arg.Key_bry());
-                       if (trg_arg != null) 
trg_arg.Val_bry_(src_arg.Val_bry());
-               }
-       }
+       // DELETE:not_used; DATE:2014-12-07
+//             public void Args_fill(OrderedHash trg_args) {
+//                     int trg_len = trg_args.Count();
+//                     for (int i = 0; i < trg_len; i++) {
+//                             Gfo_url_arg trg_arg = 
(Gfo_url_arg)trg_args.FetchAt(i);
+//                             trg_arg.Val_bry_(null);
+//                     }
+//                     int src_len = args.length;
+//                     for (int i = 0; i < src_len; i++) {
+//                             Gfo_url_arg src_arg = args[i];
+//                             Gfo_url_arg trg_arg = 
(Gfo_url_arg)trg_args.Fetch(src_arg.Key_bry());
+//                             if (trg_arg != null) 
trg_arg.Val_bry_(src_arg.Val_bry());
+//                     }
+//             }
        public byte[] Args_all_as_bry() {
                int args_len = args.length;
                if (args_len == 0) return Bry_.Empty;
diff --git a/400_xowa/src_310_url/gplx/xowa/Xoa_url_arg_hash.java 
b/400_xowa/src_310_url/gplx/xowa/Xoa_url_arg_hash.java
index 121fd2f..82255d1 100644
--- a/400_xowa/src_310_url/gplx/xowa/Xoa_url_arg_hash.java
+++ b/400_xowa/src_310_url/gplx/xowa/Xoa_url_arg_hash.java
@@ -17,7 +17,7 @@
 */
 package gplx.xowa; import gplx.*;
 public class Xoa_url_arg_hash {
-       OrderedHash hash = OrderedHash_.new_bry_();
+       private OrderedHash hash = OrderedHash_.new_bry_();
        public Gfo_url_arg Get_arg(byte[] key) {return 
(Gfo_url_arg)hash.Fetch(key);}
        public int Get_val_int_or(byte[] key, int or) {
                byte[] val_bry = Get_val_bry_or(key, null); if (val_bry == 
null) return or;             
diff --git 
a/tst/400_xowa/root/file/en.wikipedia.org/fsdb.user/fsdb.atr.00.sqlite3 
b/tst/400_xowa/root/file/en.wikipedia.org/fsdb.user/fsdb.atr.00.sqlite3
index ef8dca8..1b5fbeb 100644
--- a/tst/400_xowa/root/file/en.wikipedia.org/fsdb.user/fsdb.atr.00.sqlite3
+++ b/tst/400_xowa/root/file/en.wikipedia.org/fsdb.user/fsdb.atr.00.sqlite3
Binary files differ
diff --git "a/tst/400_xowa/root/file/en.wikipedia.org/wiki.orig\04300.sqlite3" 
"b/tst/400_xowa/root/file/en.wikipedia.org/wiki.orig\04300.sqlite3"
index 4f402f9..cc7c821 100644
--- "a/tst/400_xowa/root/file/en.wikipedia.org/wiki.orig\04300.sqlite3"
+++ "b/tst/400_xowa/root/file/en.wikipedia.org/wiki.orig\04300.sqlite3"
Binary files differ
diff --git 
a/tst/400_xowa/root/wiki/en.wikipedia.org/en.wikipedia.org.002.sqlite3 
b/tst/400_xowa/root/wiki/en.wikipedia.org/en.wikipedia.org.002.sqlite3
index 344a1a0..6778de1 100644
--- a/tst/400_xowa/root/wiki/en.wikipedia.org/en.wikipedia.org.002.sqlite3
+++ b/tst/400_xowa/root/wiki/en.wikipedia.org/en.wikipedia.org.002.sqlite3
Binary files differ

-- 
To view, visit https://gerrit.wikimedia.org/r/178139
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: I9fbcc79d3e6c2b1d41cd90c0564f37fd1710ccc3
Gerrit-PatchSet: 1
Gerrit-Project: xowa
Gerrit-Branch: master
Gerrit-Owner: Gnosygnu <gnosy...@gmail.com>
Gerrit-Reviewer: Gnosygnu <gnosy...@gmail.com>

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to