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
No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.

499 líneas
14KB

  1. // util.js
  2. // copyright Sébastien Lucas
  3. // https://github.com/seblucas/cops
  4. /*jshint curly: true, latedef: true, trailing: true, noarg: true, undef: true, browser: true, jquery: true, unused: true, devel: true, loopfunc: true */
  5. /*global LRUCache, doT, Bloodhound, postRefresh */
  6. var templatePage, templateBookDetail, templateMain, templateSuggestion, currentData, before, filterList;
  7. if (typeof LRUCache != 'undefined') {
  8. var cache = new LRUCache(30);
  9. }
  10. $.ajaxSetup({
  11. cache: false
  12. });
  13. var copsTypeahead = new Bloodhound({
  14. datumTokenizer: Bloodhound.tokenizers.obj.whitespace('title'),
  15. queryTokenizer: Bloodhound.tokenizers.whitespace,
  16. limit: 30,
  17. remote: {
  18. url: 'getJSON.php?page=9&search=1&db=%DB&query=%QUERY',
  19. replace: function (url, query) {
  20. if (currentData.multipleDatabase === 1 && currentData.databaseId === "") {
  21. return url.replace('%QUERY', query).replace('&db=%DB', "");
  22. }
  23. return url.replace('%QUERY', query).replace('%DB', currentData.databaseId);
  24. }
  25. }
  26. });
  27. copsTypeahead.initialize();
  28. var DEBUG = false;
  29. var isPushStateEnabled = window.history && window.history.pushState && window.history.replaceState &&
  30. // pushState isn't reliable on iOS until 5.
  31. !navigator.userAgent.match(/((iPod|iPhone|iPad).+\bOS\s+[1-4]|WebApps\/.+CFNetwork)/);
  32. function debug_log(text) {
  33. if ( DEBUG ) {
  34. console.log(text);
  35. }
  36. }
  37. /*exported updateCookie */
  38. function updateCookie (id) {
  39. if ($(id).prop('pattern') && !$(id).val().match(new RegExp ($(id).prop('pattern')))) {
  40. return;
  41. }
  42. var name = $(id).attr('id');
  43. var value = $(id).val ();
  44. $.cookie(name, value, { expires: 365 });
  45. }
  46. /*exported updateCookieFromCheckbox */
  47. function updateCookieFromCheckbox (id) {
  48. var name = $(id).attr('id');
  49. if ((/^style/).test (name)) {
  50. name = "style";
  51. }
  52. if ($(id).is(":checked"))
  53. {
  54. if ($(id).is(':radio')) {
  55. $.cookie(name, $(id).val (), { expires: 365 });
  56. } else {
  57. $.cookie(name, '1', { expires: 365 });
  58. }
  59. }
  60. else
  61. {
  62. $.cookie(name, '0', { expires: 365 });
  63. }
  64. }
  65. /*exported updateCookieFromCheckboxGroup */
  66. function updateCookieFromCheckboxGroup (id) {
  67. var name = $(id).attr('name');
  68. var idBase = name.replace (/\[\]/, "");
  69. var group = [];
  70. $(':checkbox[name="' + name + '"]:checked').each (function () {
  71. var id = $(this).attr("id");
  72. group.push (id.replace (idBase + "_", ""));
  73. });
  74. $.cookie(idBase, group.join (), { expires: 365 });
  75. }
  76. function elapsed () {
  77. var elapsedTime = new Date () - before;
  78. return "Elapsed : " + elapsedTime;
  79. }
  80. function retourMail(data) {
  81. $("#mailButton :first-child").removeClass ("icon-spinner icon-spin").addClass ("icon-envelope");
  82. alert (data);
  83. }
  84. /*exported sendToMailAddress */
  85. function sendToMailAddress (component, dataid) {
  86. var email = $.cookie ('email');
  87. if (!$.cookie ('email')) {
  88. email = window.prompt (currentData.c.i18n.customizeEmail, "");
  89. if (email === null)
  90. {
  91. return;
  92. }
  93. $.cookie ('email', email, { expires: 365 });
  94. }
  95. var url = 'sendtomail.php';
  96. if (currentData.databaseId) {
  97. url = url + '?db=' + currentData.databaseId;
  98. }
  99. $("#mailButton :first-child").removeClass ("icon-envelope").addClass ("icon-spinner icon-spin");
  100. $.ajax ({'url': url, 'type': 'post', 'data': { 'data': dataid, 'email': email }, 'success': retourMail});
  101. }
  102. function str_format () {
  103. var s = arguments[0];
  104. for (var i = 0; i < arguments.length - 1; i++) {
  105. var reg = new RegExp("\\{" + i + "\\}", "gm");
  106. s = s.replace(reg, arguments[i + 1]);
  107. }
  108. return s;
  109. }
  110. function isDefined(x) {
  111. var undefinedVar;
  112. return x !== undefinedVar;
  113. }
  114. function getCurrentOption (option) {
  115. if (!$.cookie (option)) {
  116. if (currentData && currentData.c && currentData.c.config && currentData.c.config [option]) {
  117. return currentData.c.config [option];
  118. }
  119. }
  120. return $.cookie (option);
  121. }
  122. /*exported htmlspecialchars */
  123. function htmlspecialchars(str) {
  124. return String(str)
  125. .replace(/&/g, '&amp;')
  126. .replace(/"/g, '&quot;')
  127. .replace(/'/g, '&#39;')
  128. .replace(/</g, '&lt;')
  129. .replace(/>/g, '&gt;');
  130. }
  131. /************************************************
  132. * All functions needed to filter the book list by tags
  133. ************************************************
  134. */
  135. function getTagList () {
  136. var tagList = {};
  137. $(".se").each (function(){
  138. if ($(this).parents (".filtered").length > 0) { return; }
  139. var taglist = $(this).text();
  140. var tagarray = taglist.split (",");
  141. for (var i in tagarray) {
  142. var tag = tagarray [i].replace(/^\s+/g,'').replace(/\s+$/g,'');
  143. tagList [tag] = 1;
  144. }
  145. });
  146. return tagList;
  147. }
  148. function updateFilters () {
  149. var tagList = getTagList ();
  150. // If there is already some filters then let's prepare to update the list
  151. $("#filter ul li").each (function () {
  152. var text = $(this).text ();
  153. if (isDefined (tagList [text]) || $(this).attr ('class')) {
  154. tagList [text] = 0;
  155. } else {
  156. tagList [text] = -1;
  157. }
  158. });
  159. // Update the filter -1 to remove, 1 to add, 0 already there
  160. for (var tag in tagList) {
  161. var tagValue = tagList [tag];
  162. if (tagValue === -1) {
  163. $("#filter ul li").filter (function () { return $.text([this]) === tag; }).remove();
  164. }
  165. if (tagValue === 1) {
  166. $("#filter ul").append ("<li>" + tag + "</li>");
  167. }
  168. }
  169. $("#filter ul").append ("<li>_CLEAR_</li>");
  170. // Sort the list alphabetically
  171. $('#filter ul li').sortElements(function(a, b){
  172. return $(a).text() > $(b).text() ? 1 : -1;
  173. });
  174. }
  175. function doFilter () {
  176. $(".books").removeClass("filtered");
  177. if (jQuery.isEmptyObject(filterList)) {
  178. updateFilters ();
  179. return;
  180. }
  181. $(".se").each (function(){
  182. var taglist = ", " + $(this).text() + ", ";
  183. var toBeFiltered = false;
  184. for (var filter in filterList) {
  185. var onlyThisTag = filterList [filter];
  186. filter = ', ' + filter + ', ';
  187. var myreg = new RegExp (filter);
  188. if (myreg.test (taglist)) {
  189. if (onlyThisTag === false) {
  190. toBeFiltered = true;
  191. }
  192. } else {
  193. if (onlyThisTag === true) {
  194. toBeFiltered = true;
  195. }
  196. }
  197. }
  198. if (toBeFiltered) { $(this).parents (".books").addClass ("filtered"); }
  199. });
  200. // Handle the books with no tags
  201. var atLeastOneTagSelected = false;
  202. for (var filter in filterList) {
  203. if (filterList [filter] === true) {
  204. atLeastOneTagSelected = true;
  205. }
  206. }
  207. if (atLeastOneTagSelected) {
  208. $(".books").not (":has(span.se)").addClass ("filtered");
  209. }
  210. updateFilters ();
  211. }
  212. function handleFilterEvents () {
  213. $("#filter ul").on ("click", "li", function(){
  214. var filter = $(this).text ();
  215. if (filter === "_CLEAR_") {
  216. filterList = {};
  217. $("#filter ul li").removeClass ("filter-exclude");
  218. $("#filter ul li").removeClass ("filter-include");
  219. doFilter ();
  220. return;
  221. }
  222. switch ($(this).attr("class")) {
  223. case "filter-include" :
  224. $(this).attr("class", "filter-exclude");
  225. filterList [filter] = false;
  226. break;
  227. case "filter-exclude" :
  228. $(this).removeClass ("filter-exclude");
  229. delete filterList [filter];
  230. break;
  231. default :
  232. $(this).attr("class", "filter-include");
  233. filterList [filter] = true;
  234. break;
  235. }
  236. doFilter ();
  237. });
  238. }
  239. /************************************************
  240. * Functions to handle Ajax navigation
  241. ************************************************
  242. */
  243. var updatePage, navigateTo;
  244. updatePage = function (data) {
  245. var result;
  246. filterList = {};
  247. data.c = currentData.c;
  248. if (false && $("section").length && currentData.isPaginated === 0 && data.isPaginated === 0) {
  249. // Partial update (for now disabled)
  250. debug_log ("Partial update");
  251. result = templateMain (data);
  252. $("h1").html (data.title);
  253. $("section").html (result);
  254. } else {
  255. // Full update
  256. result = templatePage (data);
  257. $("body").html (result);
  258. }
  259. document.title = data.title;
  260. currentData = data;
  261. setTimeout( function() { $("input[name=query]").focus(); }, 500 );
  262. debug_log (elapsed ());
  263. if ($.cookie('toolbar') === '1') { $("#tool").show (); }
  264. if (currentData.containsBook === 1) {
  265. $("#sortForm").show ();
  266. if (getCurrentOption ("html_tag_filter") === "1") {
  267. $("#filter ul").empty ();
  268. updateFilters ();
  269. handleFilterEvents ();
  270. }
  271. } else {
  272. $("#sortForm").hide ();
  273. }
  274. $('input[name=query]').typeahead(
  275. {
  276. hint: true,
  277. minLength : 3
  278. },
  279. {
  280. name: 'search',
  281. displayKey: 'title',
  282. templates: {
  283. suggestion: templateSuggestion
  284. },
  285. source: copsTypeahead.ttAdapter()
  286. });
  287. $('input[name=query]').bind('typeahead:selected', function(obj, datum) {
  288. if (isPushStateEnabled) {
  289. navigateTo (datum.navlink);
  290. } else {
  291. window.location = datum.navlink;
  292. }
  293. });
  294. if(typeof postRefresh == 'function')
  295. { postRefresh(); }
  296. };
  297. navigateTo = function (url) {
  298. $("h1").append (" <i class='icon-spinner icon-spin'></i>");
  299. before = new Date ();
  300. var jsonurl = url.replace ("index", "getJSON");
  301. var cachedData = cache.get (jsonurl);
  302. if (cachedData) {
  303. history.pushState(jsonurl, "", url);
  304. updatePage (cachedData);
  305. } else {
  306. $.getJSON(jsonurl, function(data) {
  307. history.pushState(jsonurl, "", url);
  308. cache.put (jsonurl, data);
  309. updatePage (data);
  310. });
  311. }
  312. };
  313. function link_Clicked (event) {
  314. var currentLink = $(this);
  315. if (!isPushStateEnabled ||
  316. currentData.page === "19") {
  317. return;
  318. }
  319. event.preventDefault();
  320. var url = currentLink.attr('href');
  321. if ($(".mfp-ready").length)
  322. {
  323. $.magnificPopup.close();
  324. }
  325. // The bookdetail / about should be displayed in a lightbox
  326. if (getCurrentOption ("use_fancyapps") === "1" &&
  327. (currentLink.hasClass ("fancydetail") || currentLink.hasClass ("fancyabout"))) {
  328. before = new Date ();
  329. var jsonurl = url.replace ("index", "getJSON");
  330. $.getJSON(jsonurl, function(data) {
  331. data.c = currentData.c;
  332. var detail = "";
  333. if (data.page === "16") {
  334. detail = data.fullhtml;
  335. } else {
  336. detail = templateBookDetail (data);
  337. }
  338. $.magnificPopup.open({
  339. items: {
  340. src: detail,
  341. type: 'inline'
  342. }
  343. });
  344. debug_log (elapsed ());
  345. });
  346. return;
  347. }
  348. navigateTo (url);
  349. }
  350. function search_Submitted (event) {
  351. if (!isPushStateEnabled ||
  352. currentData.page === "19") {
  353. return;
  354. }
  355. event.preventDefault();
  356. var url = str_format ("index.php?page=9&current={0}&query={1}&db={2}", currentData.page, encodeURIComponent ($("input[name=query]").val ()), currentData.databaseId);
  357. navigateTo (url);
  358. }
  359. /*exported handleLinks */
  360. function handleLinks () {
  361. $("body").on ("click", "a[href^='index']", link_Clicked);
  362. $("body").on ("submit", "#searchForm", search_Submitted);
  363. $("body").on ("click", "#sort", function(){
  364. $('.books').sortElements(function(a, b){
  365. var test = 1;
  366. if ($("#sortorder").val() === "desc")
  367. {
  368. test = -1;
  369. }
  370. return $(a).find ("." + $("#sortchoice").val()).text() > $(b).find ("." + $("#sortchoice").val()).text() ? test : -test;
  371. });
  372. });
  373. $("body").on ("click", ".headright", function(){
  374. if ($("#tool").is(":hidden")) {
  375. $("#tool").slideDown("slow");
  376. $("input[name=query]").focus();
  377. $.cookie('toolbar', '1', { expires: 365 });
  378. } else {
  379. $("#tool").slideUp();
  380. $.removeCookie('toolbar');
  381. }
  382. });
  383. $("body").magnificPopup({
  384. delegate: '.fancycover', // child items selector, by clicking on it popup will open
  385. type: 'image',
  386. gallery:{enabled:true, preload: [0,2]},
  387. disableOn: function() {
  388. if( getCurrentOption ("use_fancyapps") === "1" ) {
  389. return true;
  390. }
  391. return false;
  392. }
  393. });
  394. }
  395. window.onpopstate = function(event) {
  396. if (!isDefined (currentData)) {
  397. return;
  398. }
  399. before = new Date ();
  400. var data = cache.get (event.state);
  401. updatePage (data);
  402. };
  403. $(document).keydown(function(e){
  404. if (e.keyCode === 37 && $("#prevLink").length > 0) {
  405. navigateTo ($("#prevLink").attr('href'));
  406. }
  407. if (e.keyCode === 39 && $("#nextLink").length > 0) {
  408. navigateTo ($("#nextLink").attr('href'));
  409. }
  410. });
  411. /*exported initiateAjax */
  412. function initiateAjax (url, theme) {
  413. $.when($.get('templates/' + theme + '/header.html'),
  414. $.get('templates/' + theme + '/footer.html'),
  415. $.get('templates/' + theme + '/bookdetail.html'),
  416. $.get('templates/' + theme + '/main.html'),
  417. $.get('templates/' + theme + '/page.html'),
  418. $.get('templates/' + theme + '/suggestion.html'),
  419. $.getJSON(url)).done(function(header, footer, bookdetail, main, page, suggestion, data){
  420. templateBookDetail = doT.template (bookdetail [0]);
  421. var defMain = {
  422. bookdetail: bookdetail [0]
  423. };
  424. templateMain = doT.template (main [0], undefined, defMain);
  425. var defPage = {
  426. header: header [0],
  427. footer: footer [0],
  428. main : main [0],
  429. bookdetail: bookdetail [0]
  430. };
  431. templatePage = doT.template (page [0], undefined, defPage);
  432. templateSuggestion = doT.template (suggestion [0]);
  433. currentData = data [0];
  434. updatePage (data [0]);
  435. cache.put (url, data [0]);
  436. if (isPushStateEnabled) {
  437. history.replaceState(url, "", window.location);
  438. }
  439. handleLinks ();
  440. });
  441. }