PageRenderTime 53ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/wp-content/plugins/simple-history/js/scripts.js

https://bitbucket.org/handgran/teikei_site
JavaScript | 774 lines | 461 code | 243 blank | 70 comment | 33 complexity | 016aeef61d89e1814758be94691da2e9 MD5 | raw file
Possible License(s): GPL-2.0, Apache-2.0, MIT
  1. /*jshint multistr: true */
  2. var simple_history = (function($) {
  3. var api_base_url = window.ajaxurl;
  4. // Plugins may have modified the Ajax URL so it already contains query params
  5. if (api_base_url.indexOf("?") >= 0) {
  6. api_base_url += "&";
  7. } else {
  8. api_base_url += "?";
  9. }
  10. api_base_url += "action=simple_history_api";
  11. var debug = function(what) {
  12. if (typeof what == "object") {
  13. var newWhat = "";
  14. _.each(what, function(val, key) {
  15. newWhat += key + ": " + val + "\n";
  16. });
  17. what = newWhat;
  18. }
  19. $(".SimpleHistoryLogitems__debug").append("<br>" + what);
  20. };
  21. var LogRowsCollection = Backbone.Collection.extend({
  22. initialize: function(models, options) {
  23. this.mainView = options.mainView;
  24. $(document).trigger("SimpleHistory:logRowsCollectionInitialize");
  25. },
  26. reload: function() {
  27. this.trigger("reload");
  28. $(document).trigger("SimpleHistory:logRowsCollectionReload");
  29. var pager_size = this.mainView.$el.data("pagerSize");
  30. this.url = api_base_url + "&type=overview&format=html";
  31. this.url += "&posts_per_page=" + pager_size;
  32. // Reset some vars
  33. this.api_args = null;
  34. this.max_id = null;
  35. this.min_id = null;
  36. this.pages_count = null;
  37. this.total_row_count = null;
  38. this.page_rows_from = null;
  39. this.page_rows_to = null;
  40. this.max_id_first_page = null;
  41. // Get first page
  42. // We don't have max_id yet
  43. var that = this;
  44. var url_data = {
  45. paged: 1,
  46. // here we want to append custom args/data for filters
  47. };
  48. // trigger so plugins can modify url parameters
  49. this.trigger("before_fetch", this, url_data);
  50. this.fetch({
  51. reset: true,
  52. data: url_data,
  53. // called on 404 and similar
  54. error: function(collection, response, options) {
  55. collection.trigger("reloadError", [ response, options ]);
  56. $(document).trigger("SimpleHistory:logRowsCollectionReloadError", [ response, options ]);
  57. },
  58. success: function(collection, response, options) {
  59. collection.trigger("reloadDone", [ response, options ]);
  60. $(document).trigger("SimpleHistory:logRowsCollectionReloadDone", [ response, options ]);
  61. }
  62. });
  63. },
  64. /*
  65. * Parse ajax response to make it fit to format used by backbone
  66. */
  67. parse: function(resp, xhr) {
  68. if (!resp || !resp.data) {
  69. alert("Error in response, could not parse");
  70. return;
  71. }
  72. this.api_args = resp.data.api_args;
  73. this.max_id = resp.data.max_id;
  74. this.min_id = resp.data.min_id;
  75. this.pages_count = resp.data.pages_count;
  76. this.total_row_count = resp.data.total_row_count;
  77. this.page_rows_from = resp.data.page_rows_from;
  78. this.page_rows_to = resp.data.page_rows_to;
  79. // Store first max_id found, since that's the max id we use for
  80. // all subsequent paginations
  81. if ( ! this.max_id_first_page ) {
  82. this.max_id_first_page = this.max_id;
  83. $(document).trigger("SimpleHistory:logRowsCollectionFirstLoad");
  84. $(".SimpleHistory__waitingForFirstLoad").addClass("SimpleHistory__waitingForFirstLoad--isLoaded");
  85. // Add class to body to so we can catch loaded log everywhere in CSS
  86. $("body").addClass("SimpleHistory--isLoaded");
  87. }
  88. var arrRows = [];
  89. _.each(resp.data.log_rows, function(row) {
  90. arrRows.push({
  91. html: row
  92. });
  93. });
  94. return arrRows;
  95. }
  96. });
  97. var OccasionsLogRowsCollection = Backbone.Collection.extend({
  98. initialize: function(models, options) {
  99. this.url = api_base_url + "&type=occasions&format=html";
  100. this.fetch({
  101. reset: true,
  102. data: {
  103. logRowID: options.logRowID,
  104. occasionsID: options.occasionsID,
  105. occasionsCount: options.occasionsCount,
  106. occasionsCountMaxReturn: options.occasionsCountMaxReturn
  107. }
  108. });
  109. },
  110. parse: function(resp, xhr) {
  111. this.api_args = resp.data.api_args;
  112. this.max_id = resp.data.max_id;
  113. this.min_id = resp.data.min_id;
  114. this.pages_count = resp.data.pages_count;
  115. this.total_row_count = resp.data.total_row_count;
  116. this.page_rows_from = resp.data.page_rows_from;
  117. this.page_rows_to = resp.data.page_rows_to;
  118. var arrRows = [];
  119. _.each(resp.data.log_rows, function(row) {
  120. arrRows.push({
  121. html: row
  122. });
  123. });
  124. return arrRows;
  125. }
  126. });
  127. var OccasionsView = Backbone.View.extend({
  128. initialize: function() {
  129. var logRowID = this.attributes.logRow.data("rowId");
  130. var occasionsCount = this.attributes.logRow.data("occasionsCount");
  131. var occasionsCountMaxReturn = 15;
  132. this.occasionsCountMaxReturn = occasionsCountMaxReturn;
  133. var occasionsID = this.attributes.logRow.data("occasionsId");
  134. this.attributes.logRow.addClass("SimpleHistoryLogitem--occasionsOpening");
  135. this.logRows = new OccasionsLogRowsCollection([], {
  136. logRow: this.attributes.logRow,
  137. logRowID: logRowID,
  138. occasionsID: occasionsID,
  139. occasionsCount: occasionsCount,
  140. occasionsCountMaxReturn: occasionsCountMaxReturn
  141. });
  142. this.logRows.on("reset", this.render, this);
  143. // Trigger event for plugins
  144. this.logRows.on("reset", function() {
  145. $(document).trigger("SimpleHistory:logRowsCollectionOccasionsLoaded");
  146. }, this);
  147. },
  148. render: function() {
  149. var $html = $([]);
  150. this.logRows.each(function(model) {
  151. var $li = $(model.get("html"));
  152. $li.addClass("SimpleHistoryLogitem--occasion");
  153. $html = $html.add($li);
  154. });
  155. // If occasionsCount is more than occasionsCountMaxReturn then show a message
  156. var occasionsCount = this.attributes.logRow.data("occasionsCount");
  157. if (occasionsCount > this.occasionsCountMaxReturn) {
  158. var templateTooMany = wp.template("simple-history-occasions-too-many");
  159. $html = $html.add(templateTooMany({ occasionsCount: occasionsCount, occasionsCountMaxReturn: this.occasionsCountMaxReturn }));
  160. }
  161. this.$el.html($html);
  162. this.attributes.logRow.removeClass("SimpleHistoryLogitem--occasionsOpening").addClass("SimpleHistoryLogitem--occasionsOpened");
  163. this.$el.addClass("haveOccasionsAdded");
  164. }
  165. });
  166. var DetailsModel = Backbone.Model.extend({
  167. url: api_base_url + "&type=single&format=html"
  168. });
  169. /**
  170. * DetailsView is a modal popup thingie with all info about a LogRow
  171. */
  172. var DetailsView = Backbone.View.extend({
  173. initialize: function(attributes) {
  174. this.model.fetch({
  175. data: {
  176. id: this.model.get("id")
  177. }
  178. });
  179. this.template = $("#tmpl-simple-history-logitems-modal").html();
  180. this.show();
  181. this.listenTo(this.model, "change", this.render);
  182. // also close on esc
  183. var view = this;
  184. $(document).on("keydown.simplehistory.modal", function(e) {
  185. if (e.keyCode == 27) {
  186. view.close();
  187. }
  188. });
  189. },
  190. events: {
  191. "click .SimpleHistory-modal__background": "close",
  192. "click .SimpleHistory-modal__contentClose": "close"
  193. },
  194. show: function() {
  195. var $modalEl = $(".SimpleHistory-modal");
  196. if (!$modalEl.length) {
  197. $modalEl = $(this.template);
  198. $modalEl.appendTo("body");
  199. }
  200. this.setElement($modalEl);
  201. var $modalContentEl = $modalEl.find(".SimpleHistory-modal__content");
  202. $modalContentEl.addClass("SimpleHistory-modal__content--enter");
  203. // Force repaint before adding active class
  204. var offsetHeight = $modalContentEl.get(0).offsetHeight;
  205. $modalContentEl.addClass("SimpleHistory-modal__content--enter-active");
  206. },
  207. close: function() {
  208. var $modalContentEl = this.$el.find(".SimpleHistory-modal__content");
  209. $modalContentEl.addClass("SimpleHistory-modal__content--leave");
  210. // Force repaint before adding active class
  211. var offsetHeight = $modalContentEl.get(0).offsetHeight;
  212. $modalContentEl.addClass("SimpleHistory-modal__content--leave-active");
  213. this.$el.addClass("SimpleHistory-modal__leave-active");
  214. // Cleanup
  215. var view = this;
  216. setTimeout(function() {
  217. view.$el.remove();
  218. $(document).off("keyup.simplehistory.modal");
  219. view.remove();
  220. Backbone.history.navigate("overview");
  221. }, 400);
  222. },
  223. render: function() {
  224. var $modalContentInnerEl = this.$el.find(".SimpleHistory-modal__contentInner");
  225. var logRowLI = this.model.get("data").log_rows[0];
  226. $modalContentInnerEl.html(logRowLI);
  227. }
  228. });
  229. var RowsView = Backbone.View.extend({
  230. initialize: function() {
  231. this.collection.on("reset", this.render, this);
  232. this.collection.on("reload", this.onReload, this);
  233. this.collection.on("reloadDone", this.onReloadDone, this);
  234. this.collection.on("reloadError", this.onReloadError, this);
  235. // Trigger event for plugins
  236. this.collection.on("reset", function() {
  237. $(document).trigger("SimpleHistory:logLoaded");
  238. }, this);
  239. },
  240. onReloadError: function(args) {
  241. // If we get an error while loading the log it can be because the AJAX response
  242. // is returning parts of HTML because of plugins giving errors
  243. // not our fault, but instead of just ajax spinning forever we can
  244. // tell the user about this perhaps and they can do something about it
  245. // or else they will uninstall the plugin (worst) or post in support forum (better)
  246. // or we can help them solve it here (best)
  247. var response = args[0];
  248. if ( response && response.responseText ) {
  249. // console.log( response.responseText );
  250. $("html").removeClass("SimpleHistory-isLoadingPage");
  251. $(".SimpleHistory__waitingForFirstLoad").addClass("SimpleHistory__waitingForFirstLoad--isLoaded");
  252. var $mainViewElm = this.collection.mainView.$el;
  253. $mainViewElm.addClass("SimpleHistory--ajaxHasErrors");
  254. var noHitsClass = "SimpleHistoryLogitems__ajaxError";
  255. // Remove maybe previous div with message
  256. $mainViewElm.find("." + noHitsClass).remove();
  257. // Add div with message
  258. var $noHitsElm = $("<div />")
  259. .html( "<div class='SimpleHistoryLogitems__ajaxError__infoMessage'>" + simple_history_script_vars.ajaxLoadError + "</div>" + response.responseText )
  260. .addClass(noHitsClass)
  261. .appendTo( $mainViewElm.find(".SimpleHistoryLogitems__above") )
  262. ;
  263. }
  264. },
  265. onReload: function() {
  266. $(document).trigger("SimpleHistory:logReloadStart");
  267. $("html").addClass("SimpleHistory-isLoadingPage");
  268. },
  269. onReloadDone: function() {
  270. $("html").removeClass("SimpleHistory-isLoadingPage");
  271. var $mainViewElm = this.collection.mainView.$el;
  272. // Remove maybe previous div with ajax error message
  273. $mainViewElm.find(".SimpleHistoryLogitems__ajaxError").remove();
  274. // Add message if no hits
  275. $mainViewElm.removeClass("SimpleHistory--hasNoHits");
  276. if (! this.collection.length ) {
  277. $mainViewElm.addClass("SimpleHistory--hasNoHits");
  278. var noHitsClass = "SimpleHistoryLogitems__noHits";
  279. // Remove maybe previous div with message
  280. $mainViewElm.find("." + noHitsClass).remove();
  281. // Add div with message
  282. var $noHitsElm = $("<div />")
  283. .html( simple_history_script_vars.logNoHits )
  284. .addClass(noHitsClass)
  285. .appendTo( $mainViewElm.find(".SimpleHistoryLogitems__above") )
  286. ;
  287. } // add msg
  288. },
  289. events: {
  290. "click .SimpleHistoryLogitem__occasions a": "showOccasions",
  291. "click .SimpleHistoryLogitem__permalink": "permalink"
  292. },
  293. permalink: function(e) {
  294. // If cmd is pressed then don't show modal because then user wants
  295. // to open modal in new window/tab
  296. if (e.metaKey) {
  297. return true;
  298. }
  299. e.preventDefault();
  300. var $target = $(e.target);
  301. var $logRow = $target.closest(".SimpleHistoryLogitem");
  302. var logRowID = $logRow.data("rowId");
  303. Backbone.history.navigate("item/" + logRowID, { trigger: true });
  304. },
  305. showOccasions: function(e) {
  306. e.preventDefault();
  307. var $target = $(e.target);
  308. var $logRow = $target.closest(".SimpleHistoryLogitem");
  309. var $occasionsElm = $("<li class='SimpleHistoryLogitem__occasionsItemsWrap'><ul class='SimpleHistoryLogitem__occasionsItems'/></li>");
  310. $logRow.after($occasionsElm);
  311. this.occasionsView = new OccasionsView({
  312. el: $occasionsElm.find(".SimpleHistoryLogitem__occasionsItems"),
  313. attributes: {
  314. logRow: $logRow
  315. }
  316. });
  317. },
  318. render: function() {
  319. var html = "";
  320. this.collection.each(function(model) {
  321. html += model.get("html");
  322. });
  323. this.$el.html( html );
  324. // Rendering of log rows items is done
  325. this.trigger("renderDone");
  326. }
  327. });
  328. var PaginationView = Backbone.View.extend({
  329. initialize: function() {
  330. $(document).keydown({ view: this }, this.keyboardNav);
  331. this.collection.on("reset", this.render, this);
  332. },
  333. events: {
  334. "click .SimpleHistoryPaginationLink": "navigateArrow",
  335. "keyup .SimpleHistoryPaginationCurrentPage": "navigateToPage",
  336. "keydown": "keydown"
  337. },
  338. keyboardNav: function(e) {
  339. // if modal with details is open then don't nav away
  340. if ($(".SimpleHistory-modal").length) {
  341. return;
  342. }
  343. // Only go on if on own page
  344. if (!$(".dashboard_page_simple_history_page").length) {
  345. return;
  346. }
  347. // Don't nav away if a text input (like the search box) is selected
  348. var $target = $(e.target);
  349. if ($target.is("input")) {
  350. return;
  351. }
  352. var paged;
  353. if (e.keyCode == 37) {
  354. // prev page
  355. paged = +e.data.view.collection.api_args.paged - 1;
  356. } else if (e.keyCode == 39) {
  357. // next page
  358. paged = +e.data.view.collection.api_args.paged + 1;
  359. }
  360. if (paged) {
  361. e.data.view.fetchPage(paged);
  362. }
  363. },
  364. navigateToPage: function(e) {
  365. // keycode 13 = enter
  366. if (e.keyCode == 13) {
  367. var $target = $(e.target);
  368. var paged = parseInt( $target.val() );
  369. // We must go to a page more than zero and max total_row_count
  370. if (paged < 1) {
  371. paged = 1;
  372. }
  373. if ( paged > this.collection.pages_count ) {
  374. paged = this.collection.pages_count;
  375. }
  376. this.fetchPage(paged);
  377. }
  378. },
  379. navigateArrow: function(e) {
  380. e.preventDefault();
  381. var $target = $(e.target);
  382. // if link has class disabled then don't nav away
  383. if ($target.is(".disabled")) {
  384. return;
  385. }
  386. // direction = first|prev|next|last
  387. var direction = $target.data("direction");
  388. var paged;
  389. switch (direction) {
  390. case "first":
  391. paged = 1;
  392. break;
  393. case "last":
  394. paged = this.collection.pages_count;
  395. break;
  396. case "prev":
  397. paged = +this.collection.api_args.paged - 1;
  398. break;
  399. case "next":
  400. paged = +this.collection.api_args.paged + 1;
  401. break;
  402. }
  403. this.fetchPage(paged);
  404. },
  405. /**
  406. * Fetch a page from the server
  407. * Calls collection.fetch with the page we want to view as argument
  408. */
  409. fetchPage: function(paged) {
  410. $(document).trigger("SimpleHistory:logReloadStart");
  411. $("html").addClass("SimpleHistory-isLoadingPage");
  412. var url_data = {
  413. paged: paged,
  414. max_id_first_page: this.collection.max_id_first_page
  415. };
  416. this.collection.trigger("before_fetch", this.collection, url_data);
  417. // nav = fetch collection items again
  418. this.collection.fetch({
  419. reset: true,
  420. data: url_data,
  421. success: function() {
  422. $("html").removeClass("SimpleHistory-isLoadingPage");
  423. }
  424. });
  425. // Scroll to top of el
  426. $("html, body").animate({
  427. scrollTop: this.attributes.mainView.$el.offset().top - 85
  428. }, 350);
  429. },
  430. render: function() {
  431. var compiled = wp.template("simple-history-logitems-pagination");
  432. this.$el.html( compiled({
  433. min_id: this.collection.min_id,
  434. max_id: this.collection.max_id,
  435. pages_count: this.collection.pages_count,
  436. total_row_count: this.collection.total_row_count,
  437. page_rows_from: this.collection.page_rows_from,
  438. page_rows_to: this.collection.page_rows_to,
  439. api_args: this.collection.api_args,
  440. strings: simple_history_script_vars.pagination
  441. }) );
  442. }
  443. });
  444. var MainView = Backbone.View.extend({
  445. el: ".SimpleHistoryGui",
  446. initialize: function() {
  447. this.addNeededElements();
  448. },
  449. manualInitialize: function() {
  450. // Don't try to init if our element does not exist
  451. if (!this.$el.length) {
  452. return;
  453. }
  454. this.logRouter = new LogRouter();
  455. Backbone.history.start();
  456. this.logRowsCollection = new LogRowsCollection([], {
  457. mainView: this,
  458. });
  459. this.rowsView = new RowsView({
  460. el: this.$el.find(".SimpleHistoryLogitems"),
  461. collection: this.logRowsCollection
  462. });
  463. $(document).trigger("SimpleHistory:mainViewInitBeforeLoadRows");
  464. // Load log first time
  465. this.logRowsCollection.reload();
  466. $(document).trigger("SimpleHistory:mainViewInitAfterLoadRows");
  467. this.paginationView = new PaginationView({
  468. el: this.$el.find(".SimpleHistoryLogitems__pagination"),
  469. collection: this.logRowsCollection,
  470. attributes: {
  471. mainView: this
  472. }
  473. });
  474. $(document).trigger("SimpleHistory:init");
  475. },
  476. /**
  477. * Add the elements needed for the GUI
  478. */
  479. addNeededElements: function() {
  480. var template = $("#tmpl-simple-history-base").html();
  481. this.$el.html( template );
  482. },
  483. });
  484. var LogRouter = Backbone.Router.extend({
  485. routes: {
  486. "item/:number": "item",
  487. '*default': 'default'
  488. },
  489. item: function(logRowID) {
  490. var detailsModel = new DetailsModel({
  491. id: logRowID
  492. });
  493. var detailsView = new DetailsView({
  494. model: detailsModel
  495. });
  496. },
  497. default: function() {
  498. return false;
  499. }
  500. });
  501. var mainView = new MainView();
  502. // Init MainView on domReady
  503. // This is to make sure dropins and plugins have been loaded
  504. $(document).ready(function() {
  505. mainView.manualInitialize();
  506. });
  507. return mainView;
  508. })(jQuery);
  509. /**
  510. * When the clear-log-button in admin is clicked then check that users really wants to clear the log
  511. */
  512. jQuery(".js-SimpleHistory-Settings-ClearLog").on("click", function(e) {
  513. if (confirm(simple_history_script_vars.settingsConfirmClearLog)) {
  514. return;
  515. } else {
  516. e.preventDefault();
  517. }
  518. });
  519. /**
  520. * Add support for TimeAgo
  521. */
  522. (function($) {
  523. var $document = $(document);
  524. // called on first load + on pagination
  525. $document.on("SimpleHistory:logLoaded", addTimeAgo);
  526. // when log is loaded and when occassions are loaded
  527. $document.on("SimpleHistory:logRowsCollectionOccasionsLoaded", addTimeAgo);
  528. function addTimeAgo() {
  529. $(".SimpleHistoryLogitem__when time").timeago();
  530. }
  531. })(jQuery);