PageRenderTime 121ms CodeModel.GetById 31ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/booktype/apps/edit/static/edit/js/booktype/publish.js

https://gitlab.com/wilane/Booktype
JavaScript | 628 lines | 456 code | 133 blank | 39 comment | 34 complexity | a12c1d524105f3be1c1c99e83472857e MD5 | raw file
  1. /*
  2. This file is part of Booktype.
  3. Copyright (c) 2013 Aleksandar Erkalovic <aleksandar.erkalovic@sourcefabric.org>
  4. Booktype is free software: you can redistribute it and/or modify
  5. it under the terms of the GNU Affero General Public License as published by
  6. the Free Software Foundation, either version 3 of the License, or
  7. (at your option) any later version.
  8. Booktype is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU Affero General Public License for more details.
  12. You should have received a copy of the GNU Affero General Public License
  13. along with Booktype. If not, see <http://www.gnu.org/licenses/>.
  14. */
  15. (function (win, jquery, _) {
  16. 'use strict';
  17. jquery.namespace('win.booktype.editor.publish');
  18. win.booktype.editor.publish = (function () {
  19. var removeTaskId = null;
  20. var settingsDialogFormat = null;
  21. var ExportModel = Backbone.Model.extend({
  22. defaults: {
  23. name: 'Export',
  24. taskID: '',
  25. files: {},
  26. comments: [],
  27. username: '',
  28. created: '',
  29. published: '',
  30. exported: ''
  31. }
  32. });
  33. var ExportView = Backbone.View.extend({
  34. tagName: 'div',
  35. className: 'export-item',
  36. events: {
  37. 'click .dropdown-menu a.comment-on-export': 'doOpenComment',
  38. 'click .dropdown-menu a.publish-export': 'doPublishExport',
  39. 'click .dropdown-menu a.remove-export': 'doRemoveExport',
  40. 'click .send-comment': 'doPostComment',
  41. 'blur .export-name': 'doChangeName'
  42. },
  43. enableComments: function (state) {
  44. if (state) {
  45. this.$el.find('.send-comment').attr('aria-disabled', true).removeClass('disabled');
  46. } else {
  47. this.$el.find('.send-comment').attr('aria-disabled', true).addClass('disabled');
  48. }
  49. },
  50. doChangeName: function () {
  51. var $this = this;
  52. win.booktype.sendToCurrentBook({'command': 'rename_export_name',
  53. 'task_id': $this.model.get('taskID'),
  54. 'name': $this.$el.find('.export-name').text()},
  55. function () {
  56. });
  57. },
  58. doRemoveExport: function () {
  59. removeTaskId = this.model.get('taskID');
  60. jquery('#removeExportDialog').modal('show');
  61. },
  62. doPublishExport: function () {
  63. jquery('#publishDialog').modal('show');
  64. },
  65. doOpenComment: function () {
  66. this.$el.find('.export-comments .expand').closest('.export-comments').toggleClass('show');
  67. },
  68. doPostComment: function () {
  69. var $this = this;
  70. var content = $this.$el.find('textarea').val();
  71. win.booktype.ui.notify('Posting comment.');
  72. $this.enableComments(false);
  73. win.booktype.sendToCurrentBook({'command': 'post_export_comment',
  74. 'task_id': $this.model.get('taskID'),
  75. 'book_id': win.booktype.currentBookID,
  76. 'content': content},
  77. function (data) {
  78. var comments = $this.model.get('comments');
  79. comments.push(data.comment);
  80. $this.model.set('comments', comments);
  81. $this.renderComments();
  82. $this.$el.find('textarea').val('');
  83. win.booktype.ui.notify();
  84. $this.enableComments(true);
  85. });
  86. },
  87. initialize: function () {
  88. this.template = _.template(jquery('#templatePublishLine').html());
  89. this.commentTemplate = _.template(jquery('#templatePublishComments').html());
  90. },
  91. renderComments: function () {
  92. var $comment = this.commentTemplate({'comments': this.model.get('comments')});
  93. this.$el.find('.export-comments .notification-list').html($comment);
  94. var num = this.model.get('comments').length;
  95. this.$el.find('.export-comments .badge').html(num);
  96. },
  97. remove: function () {
  98. var $this = this;
  99. this.$el.effect('clip', {}, 500, function () {
  100. $this.$el.remove();
  101. });
  102. },
  103. render: function () {
  104. this.$el.html(this.template(this.model.toJSON()));
  105. this.renderComments();
  106. return this;
  107. },
  108. destoryView: function () {
  109. this.undelegateEvents();
  110. this.$el.removeData().unbind();
  111. this.remove();
  112. Backbone.View.prototype.remove.call(this);
  113. }
  114. });
  115. var ExportListView = Backbone.View.extend({
  116. el: 'div.publish_history',
  117. initialize: function () {
  118. this.template = _.template(jquery('#templatePublishHistory').html());
  119. this.render();
  120. this.exportViewsList = {};
  121. },
  122. showProgress: function () {
  123. var $t = _.template(jquery('#templatePublishProgress').html());
  124. this.$el.find('.empty-message').remove();
  125. this.$el.prepend($t());
  126. },
  127. checkForEmpty: function () {
  128. if (this.collection.length === 0) {
  129. this.$el.append('<div class="empty-message">' + win.booktype._('export_list_empty', 'Nothing has been exported.') + '</div>');
  130. }
  131. },
  132. removeExport: function (taskID) {
  133. var view = this.exportViewsList[taskID];
  134. var elem = exportCollection.findWhere({'taskID': taskID});
  135. exportCollection.remove(elem);
  136. if (view) {
  137. view.remove();
  138. }
  139. this.checkForEmpty();
  140. },
  141. render: function () {
  142. var $this = this;
  143. $this.$el.empty();
  144. this.collection.each(function (expo) {
  145. var expoView = new ExportView({model: expo });
  146. expoView.render();
  147. // Not really needed
  148. expoView.$el.attr('data-task-id', expo.get('taskID'));
  149. this.exportViewsList[expo.get('taskID')] = expoView;
  150. $this.$el.append(expoView.$el);
  151. }, this);
  152. $this.checkForEmpty();
  153. $this.$el.find('.export-expand').click(function () {
  154. $(this).closest('.export-item').toggleClass('detail-view');
  155. });
  156. $this.$el.find('.export-comments .expand').click(function () {
  157. $(this).closest('.export-comments').toggleClass('show');
  158. });
  159. return this;
  160. },
  161. renderNew: function (expo) {
  162. var $this = this;
  163. var expoView = new ExportView({model: expo});
  164. expoView.render();
  165. expoView.$el.attr('data-task-id', expo.get('taskID'));
  166. this.exportViewsList[expo.get('taskID')] = expoView;
  167. $this.$el.find('.alert').remove();
  168. $this.$el.find('.empty-message').remove();
  169. $this.$el.prepend(expoView.$el);
  170. expoView.$el.find('.export-expand').click(function () {
  171. $(this).closest('.export-item').toggleClass('detail-view');
  172. });
  173. expoView.$el.find('.export-comments .expand').click(function () {
  174. $(this).closest('.export-comments').toggleClass('show');
  175. });
  176. return this;
  177. },
  178. destoryView: function () {
  179. _.each(this.exportViewsList, function (el) {
  180. if (el.destroyView) {
  181. el.destroyView();
  182. }
  183. });
  184. this.undelegateEvents();
  185. this.$el.removeData().unbind();
  186. this.remove();
  187. Backbone.View.prototype.remove.call(this);
  188. }
  189. });
  190. var getFormData = function ($dialog) {
  191. var settingsData = $dialog.find('form').serializeArray();
  192. $dialog.find('input[type=checkbox]:not(:checked)').each(function (idx, val) {
  193. settingsData.push({'name': jquery(val).attr('name'), 'value': 'off'});
  194. });
  195. return settingsData;
  196. };
  197. // A List of People
  198. var ExportCollection = Backbone.Collection.extend({
  199. model: ExportModel
  200. });
  201. var FilterView = Backbone.View.extend({
  202. el: '#publish-filter .publish-filter-container',
  203. events: {
  204. 'click input[type=checkbox]': 'doCheckbox',
  205. 'click button.btn-publish': 'doPublish',
  206. 'click a.modal-open': 'doSettings'
  207. },
  208. initialize: function () {
  209. this.template = _.template(jquery('#templatePublishFilter').html());
  210. this.render();
  211. this.enableCheckboxes(true);
  212. this.enablePublish(false);
  213. },
  214. render: function () {
  215. this.$el.html(this.template({}));
  216. },
  217. enableCheckboxes: function (state) {
  218. if (state) {
  219. this.$el.find('input[type=checkbox]').removeAttr('disabled');
  220. } else {
  221. this.$el.find('input[type=checkbox]').prop('disabled', true);
  222. }
  223. },
  224. enablePublish: function (state) {
  225. if (state) {
  226. this.$el.find('button.btn-publish').attr('aria-disabled', true).removeClass('disabled');
  227. } else {
  228. this.$el.find('button.btn-publish').attr('aria-disabled', true).addClass('disabled');
  229. }
  230. },
  231. doSettings: function (evt) {
  232. var name = jquery(evt.target).parent().find('input').attr('name');
  233. if (_.isUndefined(name) || name === '') {
  234. return;
  235. }
  236. settingsDialogFormat = name;
  237. win.booktype.sendToCurrentBook({'command': 'export_settings_get',
  238. 'book_id': win.booktype.currentBookID,
  239. 'format': name},
  240. function (data) {
  241. var $dialog = jquery('#settings-' + settingsDialogFormat);
  242. var $form = $dialog.find('form');
  243. if (_.indexOf(['screenpdf', 'epub', 'mobi', 'xhtml'], settingsDialogFormat) !== -1) {
  244. $form.find('select[name=cover_image] option').slice(2).remove();
  245. _.each(data.covers, function (x, i) {
  246. var $a = jquery('<option value="' + i + '">' + x + '</option>');
  247. $form.find('select[name=cover_image]').append($a);
  248. });
  249. }
  250. _.each(data.settings, function (x) {
  251. var $field = jquery($form.get(0).elements[x.name]);
  252. if ($field.attr('type') === 'checkbox') {
  253. if (x.value === 'on') {
  254. $field.prop('checked', true);
  255. } else {
  256. $field.prop('checked', false);
  257. }
  258. } else {
  259. $field.val(x.value);
  260. }
  261. });
  262. $dialog.find('.steps').find('a').first().click();
  263. $dialog.modal('show');
  264. });
  265. return false;
  266. },
  267. doPublish: function () {
  268. var $this = this;
  269. win.booktype.ui.notify(win.booktype._('exporting_book', 'Exporting book'));
  270. showProgress();
  271. this.enablePublish(false);
  272. this.enableCheckboxes(false);
  273. var formats = jquery.map(this.$el.find('input[type=checkbox]:checked'), function (v) { return jquery(v).attr('name'); });
  274. win.booktype.sendToCurrentBook({'command': 'publish_book',
  275. 'book_id': win.booktype.currentBookID,
  276. 'formats': formats},
  277. function (data) {
  278. if (data.result === false) {
  279. console.log('Some kind of error during the publishing');
  280. if (data.reason) {
  281. // info popup
  282. var renderedModalInfoPopup = _.template(jquery('#modalInfoPopup').html())({
  283. 'header': win.booktype._('publish_error', 'Publish error'),
  284. 'body': data.reason
  285. });
  286. var modalInfoPopup = jquery(jquery.trim(renderedModalInfoPopup));
  287. modalInfoPopup.modal('show');
  288. $this.enablePublish(true);
  289. $this.enableCheckboxes(true);
  290. jquery('#content .publish_history .alert-warning').remove();
  291. }
  292. }
  293. win.booktype.ui.notify();
  294. });
  295. },
  296. doCheckbox: function () {
  297. if (this.$el.find('input[type=checkbox]:checked').length === 0) {
  298. this.enablePublish(false);
  299. } else {
  300. this.enablePublish(true);
  301. }
  302. },
  303. destoryView: function () {
  304. this.undelegateEvents();
  305. this.$el.removeData().unbind();
  306. this.remove();
  307. Backbone.View.prototype.remove.call(this);
  308. }
  309. });
  310. var PublishRouter = Backbone.Router.extend({
  311. routes: {
  312. 'publish': 'publish'
  313. },
  314. publish: function () {
  315. var activePanel = win.booktype.editor.getActivePanel();
  316. activePanel.hide(function () {
  317. win.booktype.editor.data.activePanel = win.booktype.editor.data.panels['publish'];
  318. win.booktype.editor.data.activePanel.show();
  319. });
  320. }
  321. });
  322. var router = new PublishRouter();
  323. var filterView = null;
  324. var exportList = null;
  325. var exportCollection = null;
  326. var showProgress = function () {
  327. exportList.showProgress();
  328. };
  329. var dialogShown = function () {
  330. };
  331. /*******************************************************************************/
  332. /* SHOW */
  333. /*******************************************************************************/
  334. var _show = function () {
  335. // set toolbar
  336. jquery('DIV.contentHeader').html(jquery('#templatePublishHeader').html());
  337. jquery('#content').html(jquery('#templatePublishContent').html());
  338. // Initialize everything
  339. exportCollection = new ExportCollection();
  340. filterView = new FilterView();
  341. exportList = new ExportListView({collection: exportCollection});
  342. // Show tooltips
  343. jquery('DIV.contentHeader [rel=tooltip]').tooltip({container: 'body'});
  344. // Remove exported file
  345. jquery('#removeExportDialog .btn-primary').on('click', function () {
  346. win.booktype.sendToCurrentBook({'command': 'remove_export',
  347. 'task_id': removeTaskId},
  348. function (data) {
  349. if (data.result === true) {
  350. exportList.removeExport(removeTaskId);
  351. }
  352. jquery('#removeExportDialog').modal('hide');
  353. });
  354. });
  355. window.scrollTo(0, 0);
  356. jquery('div.settings-dialog').on('show.bs.modal', dialogShown);
  357. jquery('.wizard-settings').steps({
  358. startIndex: 0,
  359. headerTag: 'h3',
  360. bodyTag: 'section',
  361. transitionEffect: 'slideLeft',
  362. autoFocus: true,
  363. enableAllSteps: true,
  364. enableFinishButton: true,
  365. showFinishButtonAlways: true,
  366. onFinished: function () {
  367. var $dialog = jquery('#settings-' + settingsDialogFormat);
  368. var settingsData = getFormData($dialog);
  369. win.booktype.sendToCurrentBook({'command': 'export_settings_set',
  370. 'format': settingsDialogFormat,
  371. 'data': settingsData},
  372. function () {
  373. $dialog.modal('hide');
  374. });
  375. },
  376. labels: {
  377. finish: win.booktype._('export_save', 'Save')
  378. }
  379. });
  380. win.booktype.sendToCurrentBook({'command': 'get_export_list',
  381. 'book_id': win.booktype.currentBookID},
  382. function (data) {
  383. _.each(data.exports, function (elem) {
  384. if (!exportCollection) {
  385. return;
  386. }
  387. exportCollection.add(new ExportModel({name: elem.name,
  388. taskID: elem['task_id'],
  389. files: elem.files,
  390. comments: elem.comments,
  391. username: elem.username,
  392. created: elem.created})); //, {at: 0});
  393. });
  394. exportList.render();
  395. });
  396. // Trigger events
  397. jquery(document).trigger('booktype-ui-panel-active', ['publish', this]);
  398. };
  399. /*******************************************************************************/
  400. /* HIDE */
  401. /*******************************************************************************/
  402. var _hide = function (callback) {
  403. // Destroy tooltip
  404. jquery('DIV.contentHeader [rel=tooltip]').tooltip('destroy');
  405. filterView.destoryView();
  406. exportList.destoryView();
  407. // Clear content
  408. jquery('#content').empty();
  409. jquery('DIV.contentHeader').empty();
  410. if (!_.isUndefined(callback)) {
  411. callback();
  412. }
  413. };
  414. /*******************************************************************************/
  415. /* INIT */
  416. /*******************************************************************************/
  417. var _init = function () {
  418. jquery('#button-publish').on('click', function () { Backbone.history.navigate('publish', true); });
  419. win.booktype.subscribeToChannel('/booktype/book/' + win.booktype.currentBookID + '/' + win.booktype.currentVersion + '/',
  420. function (message) {
  421. var funcs = {
  422. 'remove_export': function () {
  423. if (exportList) {
  424. exportList.removeExport(message['task_id']);
  425. }
  426. },
  427. 'new_export_comment': function () {
  428. if (!exportCollection) {
  429. return;
  430. }
  431. var elem = exportCollection.findWhere({'taskID': message['task_id']});
  432. var comments = elem.get('comments');
  433. comments.push({'username': message.username,
  434. 'email': message.email,
  435. 'created': message.created,
  436. 'content': message.content});
  437. elem.set('comments', comments);
  438. if (exportList) {
  439. exportList.exportViewsList[message['task_id']].renderComments();
  440. }
  441. },
  442. 'rename_export_name': function () {
  443. if (!exportCollection) {
  444. return;
  445. }
  446. var elem = exportCollection.findWhere({'taskID': message['task_id']});
  447. elem.set('name', message.name);
  448. if (exportList) {
  449. exportList.exportViewsList[message['task_id']].render();
  450. }
  451. },
  452. 'book_published': function () {
  453. if (message.state === 'SUCCESS') {
  454. jquery('#content .publish_history .publishing-message').fadeOut().remove();
  455. var newExport = new ExportModel({name: message.name,
  456. taskID: message['task_id'],
  457. files: message.files,
  458. comments: message.comments,
  459. username: message.username,
  460. created: message.created});
  461. if (exportCollection) {
  462. exportCollection.add(newExport, {at: 0});
  463. }
  464. if (exportList) {
  465. exportList.renderNew(newExport);
  466. }
  467. if (filterView) {
  468. filterView.enableCheckboxes(true);
  469. if (jquery('#content input[type=checkbox]:checked').length !== 0) {
  470. filterView.enablePublish(true);
  471. }
  472. }
  473. }
  474. }
  475. };
  476. if (funcs[message.command]) {
  477. funcs[message.command]();
  478. }
  479. }
  480. );
  481. };
  482. /*******************************************************************************/
  483. /* EXPORT */
  484. /*******************************************************************************/
  485. return {
  486. 'init': _init,
  487. 'show': _show,
  488. 'hide': _hide,
  489. 'name': 'publish'
  490. };
  491. })();
  492. })(window, jQuery, _);