Laverna/laverna

View on GitHub
app/scripts/apps/notes/form/controller.js

Summary

Maintainability
A
1 hr
Test Coverage
/**
 * Copyright (C) 2015 Laverna project Authors.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */
/* global define, requirejs */
define([
    'underscore',
    'q',
    'jquery',
    'backbone.radio',
    'marionette',
    'i18next',
    'apps/notes/form/views/formView',
    'apps/notes/form/views/notebooks'
], function (_, Q, $, Radio, Marionette, i18n, View, NotebooksView) {
    'use strict';

    /**
     * Note form controller.
     *
     * Triggers the following events:
     * 1. channel: notesForm, event: stop
     *
     * Listens to the following events:
     * 1. channel: notes, event: save:after
     *
     * requests:
     * 1. channel: notes, request: save
     *    to save the changes.
     */
    var Controller = Marionette.Object.extend({

        initialize: function(options) {
            this.options = options;

            // Saves data before you change anything, in case you cancel editing
            this.dataBeforeChange = null;
            // Data should be deleted if user wants to cancel editing
            this.deleteData = false;

            _.bindAll(this, 'show', 'redirect');

            // Fetch everything
            Q.all([
                Radio.request('notes', 'get:model:full', options),
                Radio.request('notebooks', 'get:all', _.pick(options, 'profile'))
            ])
            .spread(this.show)
            .catch(function() {
                console.error('Editor error', arguments);
            });

            // Events
            this.listenTo(Radio.channel('notes'), 'update:model', this.redirect);
            this.listenTo(Radio.channel('Confirm'), 'confirm', this.redirect);
            this.listenTo(Radio.channel('Confirm'), 'cancel', this.onConfirmCancel);
        },

        onDestroy: function() {
            this.stopListening();
            this.view.trigger('destroy');
        },

        show: function(note, notebooks) {
            var notebooksView;
            note = note[0];

            // Set document title
            Radio.request('global', 'set:title', note.get('title'));

            // Use behaviours that are appropriate for a device.
            if (Radio.request('global', 'platform') === 'mobile') {
                delete View.prototype.behaviors.Desktop;
            }
            else {
                delete View.prototype.behaviors.Mobile;
            }

            this.view = new View({
                model     : note,
                profile   : note.profileId
            });

            // Show the view and trigger an event
            Radio.request('global', 'region:show', 'content', this.view);
            this.view.trigger('rendered');

            /*
             * Resolve the notebook ID.
             * If the current note doesn't have a notebook attached,
             * try to use one from the filter if it specifies a notebook.
             */
            var activeId = note.get('notebookId');
            if (activeId === '0' && this.options.filter === 'notebook') {
                activeId = this.options.query;
            }

            // Show notebooks selector
            notebooksView = new NotebooksView({
                collection : notebooks,
                activeId   : activeId
            });
            this.view.notebooks.show(notebooksView);

            // Listen to view events
            this.listenTo(this.view, 'save', this.save);
            this.listenTo(this.view, 'cancel', this.showConfirm);

            // Get data before any change is made
            // so that it can be reset when you cancel editing
            var self = this;
            this.getContent()
            .then(function(data) {
                self.dataBeforeChange = data;
            })
            .fail(function(e) {
                console.error('Error getting data on start', e);
            });
        },

        save: function() {
            var self = this;

            return this.getContent()
            .then(function(data) {
                if (data.title === '') {
                    var title = i18n.t('Untitled');
                    data.title = title;
                    Radio.request('global', 'set:title', title);
                }

                return Radio.request('notes', 'save', self.view.model, data, self.view.options.saveTags);
            })
            .fail(function(e) {
                console.error('Error', e);
            });
        },

        getContent: function() {
            var self = this;

            return Radio.request('editor', 'get:data')
            .then(function(data) {
                return _.extend(data, {
                    title      : self.view.ui.title.val().trim(),
                    notebookId : self.view.notebooks.currentView.ui.notebookId.val().trim(),
                });
            });
        },

        /**
         * Warn a user that they have made some changes.
         */
        showConfirm: function() {
            var self = this;

            return this.getContent()
            .then(function(data) {
                // Redirect if data wasn't changed
                if (_.isEqual(self.dataBeforeChange, data)) {
                    return self.redirect();
                }

                // User perhaps wants to cancel editing,
                // if not, deleteData will be set false again in onConfirmCancel
                self.deleteData = true;
                Radio.request('Confirm', 'start', $.t('You have unsaved changes'));
            })
            .fail(function(e) {
                console.error('form ShowConfirm', e);
            });
        },

        // Called when the cancel dialog was accepted
        redirect: function() {
            if (!this.view.getOption('redirect')) {
                return;
            }

            // Stop the module and navigate back
            if(this.deleteData){
                this.deleteData = false;
                if (this.options.method === 'add') {
                    // Delete the note if editing of a new note was canceled
                    var self = this;
                    requirejs(['apps/notes/remove/controller'], function(Controller) {
                        new Controller(_.extend({},
                                {id: self.view.model.id, deleteDirect: true}));
                    });
                }
                else if (this.options.method === 'edit') {
                    // Save the note without any change
                    // if editing of an existing note was canceled
                    Radio.request('notes', 'save', this.view.model, this.dataBeforeChange);
                }
            }

            Radio.trigger('notesForm', 'stop');
            Radio.request('uri', 'back');
        },

        // Called when the cancel dialog was canceled
        onConfirmCancel: function() {
            // Rebind keybindings again because TW bootstrap modal overrites ESC.
            this.view.trigger('bind:keys');
            this.view.options.isClosed = false;

            if (this.view.options.focus !== 'editor') {
                return this.view.ui[this.view.options.focus].focus();
            }
            Radio.trigger('editor', 'focus');
        }

    });

    return Controller;
});