main.html 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705
  1. <html>
  2. <head>
  3. <link href="Css/sweetalert2.min.css" rel="stylesheet" />
  4. <style type="text/css">
  5. /************ init world ************/
  6. html, body {
  7. margin: 0;
  8. padding: 0;
  9. width: 100%;
  10. height: 100%;
  11. overflow: hidden;
  12. }
  13. /************ util ************/
  14. .fill-parent {
  15. width: 100% !important;
  16. height: 100% !important;
  17. }
  18. .fill-parent-fixed {
  19. position: fixed;
  20. top: 0;
  21. bottom: 0;
  22. left: 0;
  23. right: 0;
  24. }
  25. .fill-parent-zoom {
  26. max-width: 100%;
  27. max-height: 100%;
  28. }
  29. .scrollable {
  30. overflow: scroll;
  31. }
  32. .scrollable-v {
  33. overflow-y: scroll;
  34. }
  35. .raw-text {
  36. white-space: pre-wrap;
  37. }
  38. /************ objects ************/
  39. .tile-list {
  40. display: flex;
  41. flex-wrap: wrap;
  42. align-content: flex-start;
  43. }
  44. .tile-list .list-view-item {
  45. border: solid 1px gray;
  46. margin-right: 13px;
  47. margin-left: 10px;
  48. margin-top: 5px;
  49. width: 200px;
  50. height: 240px;
  51. text-align: center;
  52. position: relative;
  53. overflow: hidden;
  54. }
  55. .tile-list .list-view-icon {
  56. height: 100%;
  57. width: 100%;
  58. object-fit: cover;
  59. object-position: top;
  60. }
  61. .tile-list .list-view-label {
  62. display: block;
  63. position: absolute;
  64. bottom: 0;
  65. color: rgb(255, 255, 255);
  66. background-color: rgba(0, 0, 0, 0.5);
  67. width: 100%;
  68. white-space: pre-wrap;
  69. }
  70. .progressBar {
  71. background-color: black;
  72. border-radius: 13px; /* (height of inner div) / 2 + padding */
  73. padding: 3px;
  74. }
  75. .progressbar > div {
  76. background-color: orange;
  77. width: 1%; /* Adjust with JavaScript */
  78. height: 20px;
  79. border-radius: 10px;
  80. }
  81. /************ biz ************/
  82. .navRow {
  83. width: 100%;
  84. height: 10%;
  85. border-bottom: solid black 1px;
  86. }
  87. .navCell-left {
  88. float: left;
  89. width: 16%;
  90. height: 100%;
  91. text-align: left;
  92. }
  93. .navCell-center {
  94. width: 65%;
  95. height: 100%;
  96. display: flex;
  97. justify-content: center;
  98. align-items: center;
  99. text-align: center;
  100. }
  101. .navCell-right {
  102. float: right;
  103. width: 19%;
  104. height: 100%;
  105. text-align: right;
  106. }
  107. .contentRow {
  108. width: 100%;
  109. height: 80%;
  110. margin-top: 10px;
  111. margin-bottom: 10px;
  112. overflow-y: scroll;
  113. }
  114. .contentRow::-webkit-scrollbar {
  115. width: 6px;
  116. }
  117. .contentRow::-webkit-scrollbar-thumb {
  118. border-radius: 10px;
  119. -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.5);
  120. }
  121. .bottomRow {
  122. width: 100%;
  123. height: 9%;
  124. border-top: solid black 1px;
  125. }
  126. .bottomCell {
  127. width: 100%;
  128. height: 100%;
  129. }
  130. .main-content {
  131. position: fixed;
  132. top: 0;
  133. left: 0;
  134. right: 0;
  135. bottom: 0;
  136. }
  137. .main-content p {
  138. text-indent: 2em;
  139. font-size: 30px;
  140. }
  141. .main-content-h img {
  142. max-width: 100%;
  143. }
  144. .main-content-v img {
  145. height: 100%;
  146. }
  147. </style>
  148. <script type="text/javascript">
  149. function LnListView(containerSelector) {
  150. var self = this;
  151. var targetSelector = containerSelector;
  152. var itemClickedCallback = function () { };
  153. var dataSource = [];
  154. Object.defineProperty(self, "DataSource", {
  155. get: function () { return dataSource; },
  156. set: function (newValue) {
  157. dataSource = newValue;
  158. Render();
  159. }
  160. });
  161. Object.defineProperty(self, "ItemClicked", {
  162. set: function (newValue) {
  163. itemClickedCallback = newValue;
  164. }
  165. });
  166. function Render() {
  167. var t = $(targetSelector);
  168. for (var i = 0; i < dataSource.length; i++) {
  169. var ditem = dataSource[i];
  170. var licon = $('<img class="list-view-icon" />').attr("src", ditem.icon || "Images/list-item-icon.png");
  171. var llabel = $('<span class="list-view-label"></span>').text(ditem.text);
  172. var litem = $('<a class="list-view-item" href="javascript:void(0);"></a>');
  173. litem.append(licon);
  174. litem.append(llabel);
  175. BuildEvent(litem, ditem);
  176. t.append(litem);
  177. }
  178. };
  179. function BuildEvent(item, data) {
  180. item.click(function () {
  181. itemClickedCallback(self, data);
  182. });
  183. };
  184. };
  185. var pageFrame = {
  186. dummy: null
  187. , nativeApi: eval(LnoidNativeFunctions.GenerateFunctionStubForEval())
  188. , DetectInDebugging: function () {
  189. if (window.location.pathname.indexOf("/android_asset/") !== 0) {
  190. var btn = document.createElement("button");
  191. btn.innerHTML = "ReLoad";
  192. btn.onclick = function () { pageFrame.nativeApi.ReLoad(); };
  193. var sty = btn.style;
  194. sty.display = "block";
  195. sty.position = "fixed";
  196. sty.right = "10px";
  197. sty.bottom = "10px";
  198. sty.zIndex = 9999999999;
  199. document.body.appendChild(btn);
  200. }
  201. }
  202. , exitApp: function () {
  203. swal("正在保存设置...");
  204. swal.showLoading();
  205. var json = JSON.stringify(pageFrame.conf);
  206. pageFrame.nativeApi.WriteSettingAsync(function (ret) {
  207. if (ret.Success) {
  208. swal({ type: 'success', html: '已成功保存设置,正在退出' });
  209. swal.showLoading();
  210. setTimeout(function () {
  211. pageFrame.nativeApi.ExitApp();
  212. }, 1500);
  213. } else {
  214. swal("呃,保存设置出现错误……", ret.Exception.InnerException.Message, 'error');
  215. swal.hideLoading();
  216. }
  217. }, json);
  218. }
  219. , exitAppAsk: function () {
  220. swal({
  221. title: "真的要这样做吗?",
  222. text: "应用程序将会退出!",
  223. type: "warning",
  224. showCancelButton: true,
  225. confirmButtonColor: "#3085d6",
  226. confirmButtonText: "是的,我要退出!",
  227. cancelButtonColor: "#d33",
  228. cancelButtonText: "取消"
  229. }).then(function () { pageFrame.exitApp(); });
  230. }
  231. , initPage: function () {
  232. this.nativeApi.WriteLog("_nativeApi loaded. ua:" + navigator["userAgent"]);
  233. this.DetectInDebugging();
  234. window.__backButton_Pressed = pageFrame.exitAppAsk;
  235. window.__menuButton_Pressed = function () {
  236. swal({
  237. title: '进入配置',
  238. input: 'select',
  239. inputOptions: {
  240. 'W': '排版',
  241. 'ZU': '界面缩放',
  242. 'ZC': '内容缩放'
  243. },
  244. inputPlaceholder: '请选择一项',
  245. showCancelButton: true
  246. }).then(function (result) {
  247. switch (result) {
  248. case "W":
  249. swal({
  250. title: '排版',
  251. input: 'select',
  252. inputOptions: {
  253. 'v': '竖排',
  254. 'h': '横排'
  255. },
  256. inputPlaceholder: '请选择一项',
  257. showCancelButton: true
  258. }).then(function (w) {
  259. pageFrame.conf.textWriteMode = w;
  260. });
  261. break;
  262. case "ZU":
  263. swal({
  264. title: '界面缩放',
  265. input: 'select',
  266. inputOptions: {
  267. '150%': '大',
  268. '100%': '标准',
  269. '75%': '小'
  270. },
  271. inputPlaceholder: '请选择一项',
  272. showCancelButton: true
  273. }).then(function (z) {
  274. document.getElementsByTagName("html")[0].style.zoom =
  275. pageFrame.conf.uiZoom = z;
  276. });
  277. break;
  278. default:
  279. swal({ type: 'success', html: 'You selected: ' + result + ' but not impl!' });
  280. break;
  281. }
  282. });
  283. }
  284. var ret = pageFrame.nativeApi.ReadSetting();
  285. if (ret.Success) {
  286. if (ret.Result) pageFrame.conf = JSON.parse(ret.Result);
  287. if (!pageFrame.conf) pageFrame.conf = { textWriteMode: "v" };
  288. document.getElementsByTagName("html")[0].style.zoom =
  289. pageFrame.conf.uiZoom = pageFrame.conf.uiZoom || "100%";
  290. pageFrame.showMenu();
  291. } else {
  292. swal('呃,读取设置出现错误……', ret.Exception.InnerException.Message, 'error');
  293. }
  294. }
  295. , showPathSetting: function (cb) {
  296. var storages = {};
  297. var result = pageFrame.nativeApi.GetDirs("/storage", true);
  298. if (result.Success) {
  299. var items = result.Result;
  300. for (var i = 0; i < items.length; i++) {
  301. var item = items[i];
  302. if (item === "/storage/emulated") continue;
  303. if (item === "/storage/self") continue;
  304. storages[item + "/LnO"] = "外部存储(" + item.substr(item.lastIndexOf("/") + 1) + ")";
  305. }
  306. }
  307. storages['/sdcard/LnO'] = '内部存储';
  308. swal({
  309. title: '选择数据所在存储器',
  310. input: 'select',
  311. showCancelButton: true,
  312. inputPlaceholder: '选择Lno目录所在存储器',
  313. inputOptions: storages,
  314. inputValidator: function (value) {
  315. return new Promise(function (resolve, reject) {
  316. var ret = pageFrame.nativeApi.PathExist(value);
  317. if (!ret.Success) reject(ret.Exception.InnerException.Message);
  318. else if (!ret.Result) reject("路径不存在");
  319. else resolve(value);
  320. });
  321. }
  322. }).then(function (result) {
  323. pageFrame.conf.dataPath = result;
  324. if (cb) cb();
  325. }, function () { pageFrame.exitAppAsk() });
  326. }
  327. , showMenu: function () {
  328. if (!pageFrame.conf.dataPath) {
  329. pageFrame.showPathSetting(pageFrame.loadPrefixList);
  330. } else {
  331. //TO DO: continue read | favs | new read
  332. pageFrame.loadPrefixList();
  333. }
  334. }
  335. , showLoadingProgressBar: function (title) {
  336. swal({ title: title || "正在读取数据", html: '<div id="progressBar" class="progressBar"><div></div></div>', allowOutsideClick: false });
  337. swal.showLoading();
  338. }
  339. , updateLoadingProgressBar: function (cent) {
  340. $("#progressBar>div").css("width", (cent * 100) + "%");
  341. }
  342. , loadPrefixList: function () {
  343. window.__backButton_Pressed = pageFrame.exitAppAsk;
  344. pageFrame.showLoadingProgressBar();
  345. pageFrame.nativeApi.GetPrefixesAsync(
  346. function (ret) {
  347. if (!ret.Success) {
  348. //TODO: Show Correct ERROR info
  349. swal('呃,读取数据出现错误……', ret.Exception.InnerException.Message, 'error');
  350. swal.hideLoading();
  351. } else {
  352. swal.close();
  353. $(".navCell-center").text("首字母列表\r\n(共 " + ret.Result.length + " 项)");
  354. var container = $('<div id="lvPrefix" class="tile-list fill-parent"></div>');
  355. $(".contentCell").html("").append(container);
  356. var lv = new LnListView("#lvPrefix");
  357. lv.DataSource = Enumerable.From(ret.Result).Select(function (p) {
  358. return {
  359. dummy: null
  360. , icon: "Images/prefix.png"
  361. , prefix: p.Prefix
  362. , text: p.Prefix + "\r\n(共 " + p.SeriesCount + " 项)"
  363. , value: p.Path
  364. };
  365. }).ToArray();
  366. lv.ItemClicked = function (sender, e) {
  367. //TODO: Save/Restore Scroll position
  368. pageFrame.loadSeriesList(e);
  369. }
  370. }
  371. }
  372. , pageFrame.conf.dataPath
  373. , function (progress) {
  374. pageFrame.updateLoadingProgressBar(progress.Current / progress.Total);
  375. }
  376. );
  377. }
  378. , loadSeriesList: function (dataItem) {
  379. window.__backButton_Pressed = pageFrame.loadPrefixList;
  380. pageFrame.showLoadingProgressBar();
  381. pageFrame.nativeApi.GetSeriesAsync(
  382. function (ret) {
  383. if (!ret.Success) {
  384. swal('呃,读取数据出现错误……', ret.Exception.InnerException.Message, 'error');
  385. swal.hideLoading();
  386. } else {
  387. swal.close();
  388. $(".navCell-center").text("首字母:" + dataItem.text);
  389. var container = $('<div id="lvSeries" class="tile-list fill-parent"></div>');
  390. $(".contentCell").html("").append(container);
  391. var lv = new LnListView("#lvSeries");
  392. lv.DataSource = Enumerable.From(ret.Result).Select(function (p) {
  393. return {
  394. dummy: null,
  395. icon: p.CoverData,
  396. text: p.SeriesName + "\r\n(共 " + p.VolumeCount + " 卷)",
  397. value: p.Path
  398. };
  399. }).ToArray();
  400. lv.ItemClicked = function (sender, e) {
  401. e.__parentItem = dataItem;
  402. pageFrame.loadVolumeList(e);
  403. }
  404. }
  405. },
  406. dataItem.value
  407. , function (progress) {
  408. pageFrame.updateLoadingProgressBar(progress.Current / progress.Total);
  409. }
  410. );
  411. }
  412. , loadVolumeList: function (dataItem) {
  413. window.__backButton_Pressed = function () { pageFrame.loadSeriesList(dataItem.__parentItem); };
  414. pageFrame.showLoadingProgressBar();
  415. pageFrame.nativeApi.GetVolumesAsync(
  416. function (ret) {
  417. if (!ret.Success) {
  418. swal('呃,读取数据出现错误……', ret.Exception.InnerException.Message, 'error');
  419. swal.hideLoading();
  420. } else {
  421. swal.close();
  422. $(".navCell-center").text(dataItem.text);
  423. var container = $('<div id="lvVolumes" class="tile-list fill-parent"></div>');
  424. $(".contentCell").html("").append(container);
  425. var lv = new LnListView("#lvVolumes");
  426. lv.DataSource = Enumerable.From(ret.Result).Select(function (p) {
  427. return {
  428. dummy: null
  429. , icon: p.CoverData
  430. , text: p.VolumeName
  431. , value: p.Path
  432. };
  433. }).ToArray();
  434. lv.ItemClicked = function (sender, e) {
  435. e.__parentItem = dataItem;
  436. pageFrame.loadChapterList(e, dataItem);
  437. }
  438. }
  439. }
  440. , dataItem.value
  441. , function (progress) {
  442. pageFrame.updateLoadingProgressBar(progress.Current / progress.Total);
  443. }
  444. );
  445. }
  446. , loadChapterList: function (dataItem) {
  447. window.__backButton_Pressed = function () { pageFrame.loadVolumeList(dataItem.__parentItem); };
  448. pageFrame.showLoadingProgressBar();
  449. pageFrame.nativeApi.GetChaptersAsync(
  450. function (ret) {
  451. if (!ret.Success) {
  452. swal('呃,读取数据出现错误……', ret.Exception.InnerException.Message, 'error');
  453. swal.hideLoading();
  454. } else {
  455. swal.close();
  456. $(".navCell-center").text(dataItem.text + "\r\n(共 " + ret.Result.length + " 章节)");
  457. var container = $('<div id="lvVolumes" class="tile-list fill-parent"></div>');
  458. $(".contentCell").html("").append(container);
  459. var lv = new LnListView("#lvVolumes");
  460. lv.DataSource = Enumerable.From(ret.Result).Select(function (p) {
  461. return {
  462. dummy: null
  463. , icon: p.FirstPicture
  464. , text: p.ChapterName
  465. , value: p.Path
  466. };
  467. }).ToArray();
  468. lv.ItemClicked = function (sender, e) {
  469. e.__parentItem = dataItem;
  470. pageFrame.loadContent(e);
  471. }
  472. }
  473. }
  474. , dataItem.value
  475. , function (progress) {
  476. pageFrame.updateLoadingProgressBar(progress.Current / progress.Total);
  477. }
  478. );
  479. }
  480. , handleText: function (t) {
  481. if (pageFrame.conf.textWriteMode == "v") {
  482. var arrProcessed = [];
  483. var code;
  484. for (var i = 0; i < t.length; i++) {
  485. code = t.charCodeAt(i);
  486. if (code > 32 && code < 127) {
  487. code += 0xFEE0;
  488. }
  489. arrProcessed.push(code);
  490. }
  491. t = String.fromCharCode.apply(window, arrProcessed);
  492. t = t.split("“").join("『");
  493. t = t.split("”").join("』");
  494. t = t.split("(").join("(");
  495. t = t.split(")").join(")");
  496. t = t.split("》").join(">>");
  497. t = t.split("《").join("<<");
  498. }
  499. return t;
  500. }
  501. , loadContent: function (dataItem) {
  502. var container;
  503. window.__backButton_Pressed = function () {
  504. $("#uiContainer").show();
  505. $(container).remove();
  506. window.__backButton_Pressed = function () { pageFrame.loadVolumeList(dataItem.__parentItem.__parentItem); }
  507. };
  508. pageFrame.showLoadingProgressBar();
  509. pageFrame.nativeApi.GetChapterContentAsync(
  510. function (ret) {
  511. if (!ret.Success) {
  512. swal('呃,读取数据出现错误……', ret.Exception.InnerException.Message, 'error');
  513. swal.hideLoading();
  514. } else {
  515. swal.close();
  516. container = $('<div class="main-content"></div>');
  517. container.addClass("main-content-" + (pageFrame.conf.textWriteMode || ""));
  518. var contentArray = Enumerable.From(ret.Result).Select(function (p) {
  519. return p.indexOf("<") !== 0
  520. ? $("<p></p>").html(pageFrame.handleText(p))
  521. : $(p);
  522. }).ToArray();
  523. var contentWrapper = $('<div></div>');
  524. contentWrapper.append(contentArray);
  525. container.append(contentWrapper);
  526. $("#uiContainer").hide();
  527. $(document.body).append(container);
  528. var elContainer = container[0];
  529. var elContentWrapper = contentWrapper[0];
  530. if (pageFrame.conf.textWriteMode == "v") {
  531. elContentWrapper.style.height = elContainer.clientHeight;
  532. elContentWrapper.style.webkitWritingMode = "vertical-rl";
  533. var myScroll = new iScroll(elContainer, { hScrollbar: true, vScrollbar: false });
  534. myScroll.scrollTo(-elContentWrapper.clientWidth, 0);
  535. } else {
  536. var myScroll = new iScroll(elContainer, { hScrollbar: false, vScrollbar: true });
  537. myScroll.scrollTo(0, 200);
  538. }
  539. }
  540. }
  541. , dataItem.value
  542. , function (progress) {
  543. pageFrame.updateLoadingProgressBar(progress.Current / progress.Total);
  544. }
  545. );
  546. }
  547. , showDownloads: function () {
  548. var url7ZAll = "magnet:?xt=urn:btih:f7fbc1ea18c9a3a96bfa116b772e85b75f393800";
  549. var urlZipByPrefix = "magnet:?xt=urn:btih:ed4feb60314b0638c263a20f346cbf888030085e";
  550. swal({
  551. title: '获取数据文件',
  552. input: 'radio',
  553. inputOptions: {
  554. "7z": "7z分卷完整"
  555. , "Zip": "Zip分卷按首字母"
  556. , "Help": "数据文件使用方法"
  557. }, inputValidator: function (result) {
  558. return new Promise(function (resolve, reject) {
  559. if (result) {
  560. resolve();
  561. } else {
  562. reject('请选择一项!');
  563. }
  564. });
  565. }
  566. }).then(function (result) {
  567. if (result === "Help") swal({ title: "数据文件使用方法", text: "请在手机存储或SD卡创建目录「LnO」区分大小写(大小大),然后将下载后的压缩包解压进去,确保里面有单个字母的文件夹。" });
  568. var brac = {
  569. "7z": { title: "7z分卷完整", text: url7ZAll, confirmButtonText: "复制" }
  570. , "Zip": { title: "Zip分卷按首字母", text: urlZipByPrefix, confirmButtonText: "复制" }
  571. };
  572. swal(brac[result]).then(function () {
  573. pageFrame.nativeApi.CopyText(brac[result].text);
  574. });
  575. });
  576. }
  577. };
  578. //-----------------------
  579. </script>
  580. </head>
  581. <body onload="pageFrame.initPage();">
  582. <div id="uiContainer">
  583. <div class="navRow">
  584. <div class="navCell-left">
  585. <a href="javascript:void(0); " onclick="window.__backButton_Pressed();">
  586. <img class="fill-parent-zoom" src="Images/back.png" />
  587. </a>
  588. </div>
  589. <div class="navCell-right">
  590. <a href="javascript:void(0); " onclick="window.__menuButton_Pressed();">
  591. <img class="fill-parent-zoom" src="Images/menu.png" />
  592. </a>
  593. </div>
  594. <div class="navCell-center raw-text">
  595. 主菜单
  596. </div>
  597. </div>
  598. <div class="contentRow">
  599. <div class="contentCell">
  600. </div>
  601. </div>
  602. <div class="bottomRow">
  603. <div class="bottomCell">
  604. 一个蛋疼的人做的蛋疼应用。。。
  605. <input type="button" value="获取数据文件" onclick="pageFrame.showDownloads();" />
  606. </div>
  607. </div>
  608. </div>
  609. <script src="Scripts/jquery-2.2.0.js"></script>
  610. <script src="Scripts/sweetalert2.min.js"></script>
  611. <script src="Scripts/linq.min.js"></script>
  612. <script src="Scripts/iscroll.js"></script>
  613. </body>
  614. </html>