reader.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427
  1. import _ from 'lodash';
  2. import * as utils from '../../share/utils';
  3. import googleFonts from './fonts/fonts.json';
  4. const minuteMs = 60*1000;//количество ms в минуте
  5. const hourMs = 60*minuteMs;//количество ms в часе
  6. const dayMs = 24*hourMs;//количество ms в сутках
  7. const readerActions = {
  8. 'loader': 'На страницу загрузки',
  9. 'loadFile': 'Загрузить файл с диска',
  10. 'loadBuffer': 'Загрузить из буфера обмена',
  11. 'help': 'Вызвать cправку',
  12. 'settings': 'Настроить',
  13. 'undoAction': 'Действие назад',
  14. 'redoAction': 'Действие вперед',
  15. 'fullScreen': 'На весь экран',
  16. 'scrolling': 'Плавный скроллинг',
  17. 'stopScrolling': '',
  18. 'setPosition': 'Установить позицию',
  19. 'search': 'Найти в тексте',
  20. 'copyText': 'Скопировать текст со страницы',
  21. 'convOptions': 'Настроить конвертирование',
  22. 'refresh': 'Принудительно обновить книгу',
  23. 'nightMode': 'Ночной режим',
  24. 'clickControl': 'Управление кликом',
  25. 'offlineMode': 'Автономный режим (без интернета)',
  26. 'contents': 'Оглавление/закладки',
  27. 'libs': 'Сетевая библиотека',
  28. 'recentBooks': 'Показать загруженные',
  29. 'switchToolbar': 'Показать/скрыть панель управления',
  30. 'donate': '',
  31. 'bookBegin': 'В начало книги',
  32. 'bookEnd': 'В конец книги',
  33. 'pageBack': 'Страницу назад',
  34. 'pageForward': 'Страницу вперед',
  35. 'lineBack': 'Строчку назад',
  36. 'lineForward': 'Строчку вперед',
  37. 'incFontSize': 'Увеличить размер шрифта',
  38. 'decFontSize': 'Уменьшить размер шрифта',
  39. 'scrollingSpeedUp': 'Увеличить скорость скроллинга',
  40. 'scrollingSpeedDown': 'Уменьшить скорость скроллинга',
  41. };
  42. //readerActions[name]
  43. const toolButtons = [
  44. {name: 'loadFile', show: true},
  45. {name: 'loadBuffer', show: true},
  46. {name: 'help', show: true},
  47. {name: 'undoAction', show: true},
  48. {name: 'redoAction', show: true},
  49. {name: 'fullScreen', show: true},
  50. {name: 'scrolling', show: true},
  51. {name: 'setPosition', show: true},
  52. {name: 'search', show: true},
  53. {name: 'copyText', show: true},
  54. {name: 'convOptions', show: true},
  55. {name: 'refresh', show: true},
  56. {name: 'contents', show: true},
  57. {name: 'libs', show: true},
  58. {name: 'recentBooks', show: true},
  59. {name: 'nightMode', show: true},
  60. {name: 'clickControl', show: true},
  61. {name: 'offlineMode', show: true},
  62. ];
  63. //readerActions[name]
  64. const hotKeys = [
  65. {name: 'loader', codes: ['Escape']},
  66. {name: 'loadFile', codes: ['F3']},
  67. {name: 'loadBuffer', codes: ['F4']},
  68. {name: 'help', codes: ['F1', 'H']},
  69. {name: 'settings', codes: ['S']},
  70. {name: 'undoAction', codes: ['Ctrl+BracketLeft']},
  71. {name: 'redoAction', codes: ['Ctrl+BracketRight']},
  72. {name: 'fullScreen', codes: ['Enter', 'Backquote', 'F']},
  73. {name: 'scrolling', codes: ['Z']},
  74. {name: 'setPosition', codes: ['P']},
  75. {name: 'search', codes: ['Ctrl+F']},
  76. {name: 'copyText', codes: ['Ctrl+Space']},
  77. {name: 'convOptions', codes: ['Ctrl+M']},
  78. {name: 'refresh', codes: ['R']},
  79. {name: 'contents', codes: ['C']},
  80. {name: 'libs', codes: ['L']},
  81. {name: 'recentBooks', codes: ['X']},
  82. {name: 'nightMode', codes: ['Equal']},
  83. {name: 'clickControl', codes: ['Ctrl+B']},
  84. {name: 'offlineMode', codes: ['O']},
  85. {name: 'switchToolbar', codes: ['Tab', 'Q']},
  86. {name: 'bookBegin', codes: ['Home']},
  87. {name: 'bookEnd', codes: ['End']},
  88. {name: 'pageBack', codes: ['PageUp', 'ArrowLeft', 'Backspace', 'Shift+Space']},
  89. {name: 'pageForward', codes: ['PageDown', 'ArrowRight', 'Space']},
  90. {name: 'lineBack', codes: ['ArrowUp']},
  91. {name: 'lineForward', codes: ['ArrowDown']},
  92. {name: 'incFontSize', codes: ['A']},
  93. {name: 'decFontSize', codes: ['Shift+A']},
  94. {name: 'scrollingSpeedUp', codes: ['Shift+ArrowDown']},
  95. {name: 'scrollingSpeedDown', codes: ['Shift+ArrowUp']},
  96. ];
  97. const fonts = [
  98. {name: 'ReaderDefault', label: 'По-умолчанию', fontVertShift: 0},
  99. {name: 'GEO_1', label: 'BPG Arial', fontVertShift: 10},
  100. {name: 'Arimo', fontVertShift: 0},
  101. {name: 'Avrile', fontVertShift: -10},
  102. {name: 'OpenSans', fontVertShift: -5},
  103. {name: 'Roboto', fontVertShift: 0},
  104. {name: 'Rubik', fontVertShift: 0},
  105. ];
  106. //webFonts: [{css: 'https://fonts.googleapis.com/css?family=Alegreya', name: 'Alegreya', fontVertShift: 0}, ...],
  107. const webFonts = [];
  108. for (const family of googleFonts) {
  109. webFonts.push({
  110. css: `https://fonts.googleapis.com/css?family=${family.replace(/\s/g, '+')}`,
  111. name: family,
  112. fontVertShift: 0,
  113. });
  114. }
  115. //----------------------------------------------------------------------------------------------------------
  116. const settingDefaults = {
  117. textColor: '#000000',
  118. backgroundColor: '#ebe2c9',
  119. wallpaper: '',
  120. wallpaperIgnoreStatusBar: false,
  121. fontStyle: '',// 'italic'
  122. fontWeight: '',// 'bold'
  123. fontSize: 20,// px
  124. fontName: 'ReaderDefault',
  125. webFontName: '',
  126. fontVertShift: 0,
  127. textVertShift: 0,
  128. lineInterval: 3,// px, межстрочный интервал
  129. textAlignJustify: true,// выравнивание по ширине
  130. p: 25,// px, отступ параграфа
  131. indentLR: 15,// px, отступ всего текста слева и справа
  132. indentTB: 0,// px, отступ всего текста сверху и снизу
  133. wordWrap: true,//перенос по слогам
  134. keepLastToFirst: false,// перенос последней строки в первую при листании
  135. dualPageMode: false,
  136. dualIndentLR: 10,// px, отступ слева и справа внутри страницы в двухстраничном режиме
  137. dualDivWidth: 2,// px, ширина разделителя
  138. dualDivHeight: 100,// процент, высота разделителя
  139. dualDivColorAsText: true,//цвет как у текста
  140. dualDivColor: '#000000',
  141. dualDivColorAlpha: 0.7,// прозрачность разделителя
  142. dualDivStrokeFill: 1,// px, заполнение пунктира
  143. dualDivStrokeGap: 1,// px, промежуток пунктира
  144. dualDivShadowWidth: 0,// px, ширина тени
  145. showStatusBar: true,
  146. statusBarTop: false,// top, bottom
  147. statusBarHeight: 19,// px
  148. statusBarColorAsText: true,//цвет как у текста
  149. statusBarColor: '#000000',
  150. statusBarColorAlpha: 0.4,
  151. statusBarClickOpen: true,
  152. nightMode: false, //ночной режим
  153. dayColorSets: {},
  154. nightColorSets: {},
  155. scrollingDelay: 3000,// замедление, ms
  156. scrollingType: 'ease-in-out', //linear, ease, ease-in, ease-out, ease-in-out
  157. pageChangeAnimation: 'blink',// '' - нет, downShift, rightShift, thaw - протаивание, blink - мерцание, rotate - вращение, flip - листание
  158. pageChangeAnimationSpeed: 80, //0-100%
  159. allowUrlParamBookPos: false,
  160. copyFullText: false,
  161. showClickMapPage: true,
  162. clickControl: true,
  163. cutEmptyParagraphs: false,
  164. addEmptyParagraphs: 0,
  165. blinkCachedLoad: true,
  166. showImages: true,
  167. showInlineImagesInCenter: true,
  168. compactTextPerc: 0,
  169. imageHeightLines: 100,
  170. imageFitWidth: true,
  171. enableSitesFilter: true,
  172. splitToPara: false,
  173. djvuQuality: 20,
  174. pdfAsText: true,
  175. pdfQuality: 20,
  176. showServerStorageMessages: true,
  177. showWhatsNewDialog: true,
  178. showDonationDialog: true,
  179. showNeedUpdateNotify: true,
  180. fontShifts: {},
  181. showToolButton: {},
  182. toolBarHideOnScroll: false,
  183. toolBarMultiLine: true,
  184. userHotKeys: {},
  185. userWallpapers: [],
  186. recentShowSameBook: false,
  187. recentSortMethod: '',
  188. //Book Update Checker
  189. bucEnabled: true, // общее включение/выключение проверки обновлений
  190. bucSizeDiff: 1, // разница в размерах файла, при которой показывать наличие обновления
  191. bucSetOnNew: true, // автоматически включать проверку обновлений для вновь загружаемых файлов
  192. bucCancelEnabled: true, // вкл/выкл отмену проверки книг через bucCancelDays
  193. bucCancelDays: 90, // количество дней, через которое отменяется проверка книги, при условии отсутствия обновлений за это время
  194. };
  195. for (const font of fonts)
  196. settingDefaults.fontShifts[font.name] = font.fontVertShift;
  197. for (const font of webFonts)
  198. settingDefaults.fontShifts[font.name] = font.fontVertShift;
  199. for (const button of toolButtons)
  200. settingDefaults.showToolButton[button.name] = button.show;
  201. for (const hotKey of hotKeys)
  202. settingDefaults.userHotKeys[hotKey.name] = hotKey.codes;
  203. const diffExclude = [];
  204. for (const hotKey of hotKeys)
  205. diffExclude.push(`userHotKeys/${hotKey.name}`);
  206. diffExclude.push('userWallpapers');
  207. diffExclude.push('dayColorSets');
  208. diffExclude.push('nightColorSets');
  209. function addDefaultsToSettings(settings) {
  210. const diff = utils.getObjDiff(settings, settingDefaults, {exclude: diffExclude});
  211. if (!utils.isEmptyObjDiffDeep(diff, {isApplyChange: false})) {
  212. return utils.applyObjDiff(settings, diff, {isApplyChange: false});
  213. }
  214. return false;
  215. }
  216. const colorSetsList = [
  217. 'textColor',
  218. 'backgroundColor',
  219. 'wallpaper',
  220. 'statusBarColorAsText',
  221. 'statusBarColor',
  222. 'statusBarColorAlpha',
  223. 'dualDivColorAsText',
  224. 'dualDivColor',
  225. 'dualDivColorAlpha',
  226. ];
  227. function saveColorSets(nightMode, settings) {
  228. const target = (nightMode ? settings.nightColorSets : settings.dayColorSets);
  229. for (const prop of colorSetsList) {
  230. target[prop] = settings[prop];
  231. }
  232. }
  233. function restoreColorSets(nightMode, settings) {
  234. const source = (nightMode ? settings.nightColorSets : settings.dayColorSets);
  235. for (const prop of colorSetsList) {
  236. if (utils.hasProp(source, prop))
  237. settings[prop] = source[prop];
  238. }
  239. }
  240. function getLibsDefaults(mode = 'reader') {
  241. const result = {
  242. startLink: '',
  243. comment: '',
  244. closeAfterSubmit: false,
  245. openInFrameOnEnter: false,
  246. openInFrameOnAdd: false,
  247. helpShowed: false,
  248. mode,
  249. groups: [
  250. {r: 'http://samlib.ru', s: 'http://samlib.ru', list: [
  251. {l: 'http://samlib.ru', c: 'Журнал "Самиздат"'},
  252. ]},
  253. {r: 'http://lib.ru', s: 'http://lib.ru', list: [
  254. {l: 'http://lib.ru', c: 'Библиотека Максима Мошкова'},
  255. ]},
  256. {r: 'https://aldebaran.ru', s: 'https://aldebaran.ru', list: [
  257. {l: 'https://aldebaran.ru', c: 'АЛЬДЕБАРАН | Электронная библиотека книг'},
  258. ]},
  259. ],
  260. };
  261. if (mode === 'liberama') {
  262. result.groups.unshift(
  263. {r: 'http://fantasy-worlds.org', s: 'http://fantasy-worlds.org', list: [
  264. {l: 'http://fantasy-worlds.org', c: 'Миры Фэнтези'},
  265. ]}
  266. );
  267. result.groups.unshift(
  268. {r: 'http://flibusta.is', s: 'http://flibusta.is', list: [
  269. {l: 'http://flibusta.is', c: 'Флибуста | Книжное братство'},
  270. ]}
  271. );
  272. } else if (mode === 'omnireader') {
  273. result.groups.unshift(
  274. {r: 'https://lib.omnireader.ru', s: 'https://lib.omnireader.ru', list: [
  275. {l: 'https://lib.omnireader.ru', c: 'Общественное достояние'},
  276. ]}
  277. );
  278. }
  279. result.startLink = result.groups[0].r;
  280. result.comment = result.groups[0].c;
  281. return result;
  282. }
  283. // initial state
  284. const state = {
  285. toolBarActive: true,
  286. offlineModeActive: false,
  287. serverSyncEnabled: false,
  288. serverStorageKey: '',
  289. profiles: {},
  290. profilesRev: 0,
  291. allowProfilesSave: false,//подстраховка для разработки
  292. whatsNewContentHash: '',
  293. donationNextPopup: Date.now() + dayMs*30,
  294. currentProfile: '',
  295. settings: _.cloneDeep(settingDefaults),
  296. settingsRev: {},
  297. libs: {},
  298. libsRev: 0,
  299. };
  300. // getters
  301. const getters = {};
  302. // actions
  303. const actions = {};
  304. // mutations
  305. const mutations = {
  306. setToolBarActive(state, value) {
  307. state.toolBarActive = value;
  308. },
  309. setOfflineModeActive(state, value) {
  310. state.offlineModeActive = value;
  311. },
  312. setServerSyncEnabled(state, value) {
  313. state.serverSyncEnabled = value;
  314. },
  315. setServerStorageKey(state, value) {
  316. state.serverStorageKey = value;
  317. },
  318. setProfiles(state, value) {
  319. state.profiles = value;
  320. },
  321. setProfilesRev(state, value) {
  322. state.profilesRev = value;
  323. },
  324. setAllowProfilesSave(state, value) {
  325. state.allowProfilesSave = value;
  326. },
  327. setWhatsNewContentHash(state, value) {
  328. state.whatsNewContentHash = value;
  329. },
  330. setDonationNextPopup(state, value) {
  331. state.donationNextPopup = value;
  332. },
  333. setCurrentProfile(state, value) {
  334. state.currentProfile = value;
  335. },
  336. setSettings(state, value) {
  337. let newSettings = Object.assign({}, state.settings, value);
  338. //при смене профиля подгружаются старые настройки, могут отсутствовать атрибуты
  339. //поэтому:
  340. const added = addDefaultsToSettings(newSettings);
  341. if (added)
  342. newSettings = added;
  343. state.settings = newSettings;
  344. },
  345. nightModeToggle(state) {
  346. //переключение режима день-ночь
  347. const newSettings = Object.assign({}, state.settings);
  348. saveColorSets(newSettings.nightMode, newSettings);
  349. newSettings.nightMode = !newSettings.nightMode;
  350. if (newSettings.nightMode && !utils.hasProp(newSettings.nightColorSets, 'textColor')) {
  351. // Ночной режим активирован впервые. Цвета заданы по умолчанию.
  352. newSettings.nightColorSets = {textColor: '#778a9e', backgroundColor: '#363131'};
  353. }
  354. restoreColorSets(newSettings.nightMode, newSettings);
  355. state.settings = newSettings;
  356. },
  357. setSettingsRev(state, value) {
  358. state.settingsRev = Object.assign({}, state.settingsRev, value);
  359. },
  360. setLibs(state, value) {
  361. state.libs = value;
  362. },
  363. setLibsRev(state, value) {
  364. state.libsRev = value;
  365. },
  366. };
  367. export default {
  368. minuteMs,
  369. hourMs,
  370. dayMs,
  371. readerActions,
  372. toolButtons,
  373. hotKeys,
  374. fonts,
  375. webFonts,
  376. settingDefaults,
  377. addDefaultsToSettings,
  378. getLibsDefaults,
  379. namespaced: true,
  380. state,
  381. getters,
  382. actions,
  383. mutations
  384. };