Calibre OPDS (and HTML) PHP Server : web-based light alternative to Calibre content server / Calibre2OPDS to serve ebooks (epub, mobi, pdf, ...) http://blog.slucas.fr/en/oss/calibre-opds-php-server
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

366 lines
11KB

  1. // util.js
  2. // copyright Sébastien Lucas
  3. // https://github.com/seblucas/cops
  4. var templatePage, templateBookDetail, templateMain, currentData, before, filterList;
  5. var cache = new LRUCache(30);
  6. var DEBUG = false;
  7. var isPushStateEnabled = window.history && window.history.pushState && window.history.replaceState &&
  8. // pushState isn't reliable on iOS until 5.
  9. !navigator.userAgent.match(/((iPod|iPhone|iPad).+\bOS\s+[1-4]|WebApps\/.+CFNetwork)/);
  10. function debug_log(text) {
  11. if ( DEBUG ) {
  12. console.log(text);
  13. }
  14. }
  15. function updateCookie (id) {
  16. if($(id).prop('pattern') && !$(id).val().match(new RegExp ($(id).prop('pattern')))) {
  17. return;
  18. }
  19. var name = $(id).attr('id');
  20. var value = $(id).val ();
  21. $.cookie(name, value, { expires: 365 });
  22. }
  23. function updateCookieFromCheckbox (id) {
  24. var name = $(id).attr('id');
  25. if ((/^style/).test (name)) {
  26. name = "style";
  27. }
  28. if ($(id).is(":checked"))
  29. {
  30. if ($(id).is(':radio')) {
  31. $.cookie(name, $(id).val (), { expires: 365 });
  32. } else {
  33. $.cookie(name, '1', { expires: 365 });
  34. }
  35. }
  36. else
  37. {
  38. $.cookie(name, '0', { expires: 365 });
  39. }
  40. }
  41. function elapsed () {
  42. var elapsedTime = new Date () - before;
  43. return "Elapsed : " + elapsedTime;
  44. }
  45. function retourMail(data, textStatus, jqXHR ) {
  46. $("#mailButton :first-child").removeClass ("icon-spinner icon-spin").addClass ("icon-envelope");
  47. alert (data);
  48. }
  49. function sendToMailAddress (component, dataid) {
  50. var email = $.cookie ('email');
  51. if (!$.cookie ('email')) {
  52. email = window.prompt (currentData.const.i18n.customizeEmail, "");
  53. $.cookie ('email', email, { expires: 365 });
  54. }
  55. var url = 'sendtomail.php';
  56. if (currentData.databaseId) {
  57. url = url + '?db=' + currentData.databaseId;
  58. }
  59. $("#mailButton :first-child").removeClass ("icon-envelope").addClass ("icon-spinner icon-spin");
  60. $.ajax ({'url': url, 'type': 'post', 'data': { 'data': dataid, 'email': email }, 'success': retourMail});
  61. }
  62. function strformat () {
  63. var s = arguments[0];
  64. for (var i = 0; i < arguments.length - 1; i++) {
  65. var reg = new RegExp("\\{" + i + "\\}", "gm");
  66. s = s.replace(reg, arguments[i + 1]);
  67. }
  68. return s;
  69. }
  70. function isDefined(x) {
  71. var undefinedVar;
  72. return x !== undefinedVar;
  73. }
  74. function getCurrentOption (option) {
  75. if (!$.cookie (option)) {
  76. if (currentData && currentData.const && currentData.const.config && currentData.const.config [option]) {
  77. return currentData.const.config [option];
  78. }
  79. }
  80. return $.cookie (option);
  81. }
  82. function htmlEscape(str) {
  83. return String(str)
  84. .replace(/&/g, '&amp;')
  85. .replace(/"/g, '&quot;')
  86. .replace(/'/g, '&#39;')
  87. .replace(/</g, '&lt;')
  88. .replace(/>/g, '&gt;');
  89. }
  90. /************************************************
  91. * All functions needed to filter the book list by tags
  92. ************************************************
  93. */
  94. function getTagList () {
  95. var tagList = {};
  96. $(".se").each (function(){
  97. if ($(this).parents (".filtered").length > 0) { return; }
  98. var taglist = $(this).text();
  99. var tagarray = taglist.split (",");
  100. for (var i in tagarray) {
  101. var tag = tagarray [i].replace(/^\s+/g,'').replace(/\s+$/g,'');
  102. tagList [tag] = 1;
  103. }
  104. });
  105. return tagList;
  106. }
  107. function doFilter () {
  108. $(".books").removeClass("filtered");
  109. if (jQuery.isEmptyObject(filterList)) {
  110. updateFilters ();
  111. return;
  112. }
  113. $(".se").each (function(){
  114. var taglist = ", " + $(this).text() + ", ";
  115. var toBeFiltered = false;
  116. for (var filter in filterList) {
  117. var onlyThisTag = filterList [filter];
  118. filter = ', ' + filter + ', ';
  119. var myreg = new RegExp (filter);
  120. if (myreg.test (taglist)) {
  121. if (onlyThisTag === false) {
  122. toBeFiltered = true;
  123. }
  124. } else {
  125. if (onlyThisTag === true) {
  126. toBeFiltered = true;
  127. }
  128. }
  129. }
  130. if (toBeFiltered) { $(this).parents (".books").addClass ("filtered"); }
  131. });
  132. updateFilters ();
  133. }
  134. function updateFilters () {
  135. var tagList = getTagList ();
  136. // If there is already some filters then let's prepare to update the list
  137. $("#filter ul li").each (function () {
  138. var text = $(this).text ();
  139. if (isDefined (tagList [text]) || $(this).attr ('class')) {
  140. tagList [text] = 0;
  141. } else {
  142. tagList [text] = -1;
  143. }
  144. });
  145. // Update the filter -1 to remove, 1 to add, 0 already there
  146. for (var tag in tagList) {
  147. var tagValue = tagList [tag];
  148. if (tagValue === -1) {
  149. $("#filter ul li:contains('" + tag + "')").remove();
  150. }
  151. if (tagValue === 1) {
  152. $("#filter ul").append ("<li>" + tag + "</li>");
  153. }
  154. }
  155. $("#filter ul").append ("<li>_CLEAR_</li>");
  156. // Sort the list alphabetically
  157. $('#filter ul li').sortElements(function(a, b){
  158. return $(a).text() > $(b).text() ? 1 : -1;
  159. });
  160. }
  161. function handleFilterEvents () {
  162. $("#filter ul").on ("click", "li", function(){
  163. var filter = $(this).text ();
  164. if (filter === "_CLEAR_") {
  165. filterList = {};
  166. $("#filter ul li").removeClass ("filter-exclude");
  167. $("#filter ul li").removeClass ("filter-include");
  168. doFilter ();
  169. return;
  170. }
  171. switch ($(this).attr("class")) {
  172. case "filter-include" :
  173. $(this).attr("class", "filter-exclude");
  174. filterList [filter] = false;
  175. break;
  176. case "filter-exclude" :
  177. $(this).removeClass ("filter-exclude");
  178. delete filterList [filter];
  179. break;
  180. default :
  181. $(this).attr("class", "filter-include");
  182. filterList [filter] = true;
  183. break;
  184. }
  185. doFilter ();
  186. });
  187. }
  188. /************************************************
  189. * Functions to handle Ajax navigation
  190. ************************************************
  191. */
  192. function navigateTo (url) {
  193. before = new Date ();
  194. var jsonurl = url.replace ("index", "getJSON");
  195. var cachedData = cache.get (jsonurl);
  196. if (cachedData) {
  197. history.pushState(jsonurl, "", url);
  198. updatePage (cachedData);
  199. } else {
  200. $.getJSON(jsonurl, function(data) {
  201. history.pushState(jsonurl, "", url);
  202. cache.put (jsonurl, data);
  203. updatePage (data);
  204. });
  205. }
  206. }
  207. function updatePage (data) {
  208. var result;
  209. filterList = {};
  210. data ["const"] = currentData ["const"];
  211. if (false && $("section").length && currentData.isPaginated === 0 && data.isPaginated === 0) {
  212. // Partial update (for now disabled)
  213. debug_log ("Partial update");
  214. result = templateMain (data);
  215. $("h1").html (data.title);
  216. $("section").html (result);
  217. } else {
  218. // Full update
  219. result = templatePage (data);
  220. $("body").html (result);
  221. }
  222. document.title = data.title;
  223. currentData = data;
  224. debug_log (elapsed ());
  225. if ($.cookie('toolbar') === '1') { $("#tool").show (); }
  226. if (currentData.containsBook === 1) {
  227. $("#sortForm").show ();
  228. if (getCurrentOption ("html_tag_filter") === "1") {
  229. $("#filter ul").empty ();
  230. updateFilters ();
  231. handleFilterEvents ();
  232. }
  233. } else {
  234. $("#sortForm").hide ();
  235. }
  236. }
  237. function handleLinks () {
  238. $("body").on ("click", "a[href^='index']", link_Clicked);
  239. $("body").on ("submit", "#searchForm", search_Submitted);
  240. $("body").on ("click", "#sort", function(){
  241. $('.books').sortElements(function(a, b){
  242. var test = 1;
  243. if ($("#sortorder").val() === "desc")
  244. {
  245. test = -1;
  246. }
  247. return $(a).find ("." + $("#sortchoice").val()).text() > $(b).find ("." + $("#sortchoice").val()).text() ? test : -test;
  248. });
  249. });
  250. $("body").on ("click", ".headright", function(){
  251. if ($("#tool").is(":hidden")) {
  252. $("#tool").slideDown("slow");
  253. $.cookie('toolbar', '1', { expires: 365 });
  254. } else {
  255. $("#tool").slideUp();
  256. $.removeCookie('toolbar');
  257. }
  258. });
  259. $("body").magnificPopup({
  260. delegate: '.fancycover', // child items selector, by clicking on it popup will open
  261. type: 'image',
  262. gallery:{enabled:true, preload: [0,2]},
  263. disableOn: function() {
  264. if( getCurrentOption ("use_fancyapps") === "1" ) {
  265. return true;
  266. }
  267. return false;
  268. }
  269. });
  270. }
  271. function link_Clicked (event) {
  272. var currentLink = $(this);
  273. if (!isPushStateEnabled ||
  274. currentData.page === "19") {
  275. return;
  276. }
  277. event.preventDefault();
  278. var url = currentLink.attr('href');
  279. if ($(".mfp-ready").length)
  280. {
  281. $.magnificPopup.close();
  282. }
  283. // The bookdetail / about should be displayed in a lightbox
  284. if (getCurrentOption ("use_fancyapps") === "1" &&
  285. (currentLink.hasClass ("fancydetail") || currentLink.hasClass ("fancyabout"))) {
  286. before = new Date ();
  287. var jsonurl = url.replace ("index", "getJSON");
  288. $.getJSON(jsonurl, function(data) {
  289. data ["const"] = currentData ["const"];
  290. var detail = "";
  291. if (data.page === "16") {
  292. detail = data.fullhtml;
  293. } else {
  294. detail = templateBookDetail (data);
  295. }
  296. $.magnificPopup.open({
  297. items: {
  298. src: detail,
  299. type: 'inline'
  300. }
  301. });
  302. debug_log (elapsed ());
  303. });
  304. return;
  305. }
  306. navigateTo (url);
  307. }
  308. function search_Submitted (event) {
  309. if (!isPushStateEnabled ||
  310. currentData.page === "19") {
  311. return;
  312. }
  313. event.preventDefault();
  314. var url = strformat ("index.php?page=9&current={0}&query={1}&db={2}", currentData.page, $("input[name=query]").val (), currentData.databaseId);
  315. navigateTo (url);
  316. }
  317. window.onpopstate = function(event) {
  318. before = new Date ();
  319. var data = cache.get (event.state);
  320. updatePage (data);
  321. };
  322. $(document).keydown(function(e){
  323. if (e.keyCode === 37 && $("#prevLink").length > 0) {
  324. navigateTo ($("#prevLink").attr('href'));
  325. }
  326. if (e.keyCode === 39 && $("#nextLink").length > 0) {
  327. navigateTo ($("#nextLink").attr('href'));
  328. }
  329. });