Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
// This file is part of Moodle - http://moodle.org/
2
//
3
// Moodle is free software: you can redistribute it and/or modify
4
// it under the terms of the GNU General Public License as published by
5
// the Free Software Foundation, either version 3 of the License, or
6
// (at your option) any later version.
7
//
8
// Moodle 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 General Public License for more details.
12
//
13
// You should have received a copy of the GNU General Public License
14
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
15
/* eslint-disable no-unused-vars */
16
 
17
/**
18
 * A autosave function for the Atto editor.
19
 *
20
 * @module     moodle-editor_atto-autosave
21
 * @submodule  autosave-base
22
 * @package    editor_atto
23
 * @copyright  2014 Damyon Wiese
24
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
25
 */
26
 
27
var SUCCESS_MESSAGE_TIMEOUT = 5000,
28
    RECOVER_MESSAGE_TIMEOUT = 60000,
29
    LOGNAME_AUTOSAVE = 'moodle-editor_atto-editor-autosave';
30
 
31
function EditorAutosave() {}
32
 
33
EditorAutosave.ATTRS = {
34
    /**
35
     * Enable/Disable auto save for this instance.
36
     *
37
     * @attribute autosaveEnabled
38
     * @type Boolean
39
     * @writeOnce
40
     */
41
    autosaveEnabled: {
42
        value: true,
43
        writeOnce: true
44
    },
45
 
46
    /**
47
     * The time between autosaves (in seconds).
48
     *
49
     * @attribute autosaveFrequency
50
     * @type Number
51
     * @default 60
52
     * @writeOnce
53
     */
54
    autosaveFrequency: {
55
        value: 60,
56
        writeOnce: true
57
    },
58
 
59
    /**
60
     * Unique hash for this page instance. Calculated from $PAGE->url in php.
61
     *
62
     * @attribute pageHash
63
     * @type String
64
     * @writeOnce
65
     */
66
    pageHash: {
67
        value: '',
68
        writeOnce: true
69
    }
70
};
71
 
72
EditorAutosave.prototype = {
73
 
74
    /**
75
     * The text that was auto saved in the last request.
76
     *
77
     * @property lastText
78
     * @type string
79
     */
80
    lastText: "",
81
 
82
    /**
83
     * Autosave instance.
84
     *
85
     * @property autosaveInstance
86
     * @type string
87
     */
88
    autosaveInstance: null,
89
 
90
    /**
91
     * Autosave Timer.
92
     *
93
     * @property autosaveTimer
94
     * @type object
95
     */
96
    autosaveTimer: null,
97
 
98
    /**
99
     * Initialize the autosave process
100
     *
101
     * @method setupAutosave
102
     * @chainable
103
     */
104
    setupAutosave: function() {
105
        var draftid = -1,
106
            form,
107
            optiontype = null,
108
            options = this.get('filepickeroptions'),
109
            params;
110
 
111
        if (!this.get('autosaveEnabled')) {
112
            // Autosave disabled for this instance.
113
            return;
114
        }
115
 
116
        this.autosaveInstance = Y.stamp(this);
117
        for (optiontype in options) {
118
            if (typeof options[optiontype].itemid !== "undefined") {
119
                draftid = options[optiontype].itemid;
120
            }
121
        }
122
 
123
        // First see if there are any saved drafts.
124
        // Make an ajax request.
125
        params = {
126
            contextid: this.get('contextid'),
127
            action: 'resume',
128
            draftid: draftid,
129
            elementid: this.get('elementid'),
130
            pageinstance: this.autosaveInstance,
131
            pagehash: this.get('pageHash')
132
        };
133
 
134
        this.autosaveIo(params, this, {
135
            success: function(response) {
136
                if (response === null) {
137
                    // This can happen when there is nothing to resume from.
138
                    return;
139
                } else if (!response) {
140
                    Y.log('Invalid response received.', 'debug', LOGNAME_AUTOSAVE);
141
                    return;
142
                }
143
 
144
                // Revert untouched editor contents to an empty string.
145
                var emptyContents = [
146
                    // For FF and Chrome.
147
                    '<p></p>',
148
                    '<p><br></p>',
149
                    '<br>',
150
                    '<p dir="rtl" style="text-align: right;"></p>',
151
                    '<p dir="rtl" style="text-align: right;"><br></p>',
152
                    '<p dir="ltr" style="text-align: left;"></p>',
153
                    '<p dir="ltr" style="text-align: left;"><br></p>',
154
                    // For IE 9 and 10.
155
                    '<p>&nbsp;</p>',
156
                    '<p><br>&nbsp;</p>',
157
                    '<p dir="rtl" style="text-align: right;">&nbsp;</p>',
158
                    '<p dir="rtl" style="text-align: right;"><br>&nbsp;</p>',
159
                    '<p dir="ltr" style="text-align: left;">&nbsp;</p>',
160
                    '<p dir="ltr" style="text-align: left;"><br>&nbsp;</p>'
161
                ];
162
                if (emptyContents.includes(response.result)) {
163
                    response.result = '';
164
                }
165
 
166
                if (response.error || typeof response.result === 'undefined') {
167
                    Y.log('Error occurred recovering draft text: ' + response.error, 'debug', LOGNAME_AUTOSAVE);
168
                    this.showMessage(M.util.get_string('errortextrecovery', 'editor_atto'),
169
                            NOTIFY_WARNING, RECOVER_MESSAGE_TIMEOUT);
170
                } else if (response.result !== this.textarea.get('value') &&
171
                        response.result !== '') {
172
                    Y.log('Autosave text found - recover it.', 'debug', LOGNAME_AUTOSAVE);
173
                    this.recoverText(response.result);
174
                }
175
                this._fireSelectionChanged();
176
 
177
            },
178
            failure: function() {
179
                this.showMessage(M.util.get_string('errortextrecovery', 'editor_atto'),
180
                        NOTIFY_WARNING, RECOVER_MESSAGE_TIMEOUT);
181
            }
182
        });
183
 
184
        // Now setup the timer for periodic saves.
185
        var delay = parseInt(this.get('autosaveFrequency'), 10) * 1000;
186
        this.autosaveTimer = Y.later(delay, this, this.saveDraft, false, true);
187
 
188
        // Now setup the listener for form submission.
189
        form = this.textarea.ancestor('form');
190
        if (form) {
191
            this.autosaveIoOnSubmit(form, {
192
                action: 'reset',
193
                contextid: this.get('contextid'),
194
                elementid: this.get('elementid'),
195
                pageinstance: this.autosaveInstance,
196
                pagehash: this.get('pageHash')
197
            });
198
        }
199
        return this;
200
    },
201
 
202
    /**
203
     * Recover a previous version of this text and show a message.
204
     *
205
     * @method recoverText
206
     * @param {String} text
207
     * @chainable
208
     */
209
    recoverText: function(text) {
210
        this.editor.setHTML(text);
211
        this.saveSelection();
212
        this.updateOriginal();
213
        this.lastText = text;
214
 
215
        this.showMessage(M.util.get_string('textrecovered', 'editor_atto'),
216
                NOTIFY_INFO, RECOVER_MESSAGE_TIMEOUT);
217
 
218
        // Fire an event that the editor content has changed.
219
        require(['core_editor/events'], function(editorEvents) {
220
            editorEvents.notifyEditorContentRestored(this.editor.getDOMNode());
221
        }.bind(this));
222
 
223
        return this;
224
    },
225
 
226
    /**
227
     * Save a single draft via ajax.
228
     *
229
     * @method saveDraft
230
     * @chainable
231
     */
232
    saveDraft: function() {
233
        var url, params;
234
 
235
        if (!this.editor.getDOMNode()) {
236
            // Stop autosaving if the editor was removed from the page.
237
            this.autosaveTimer.cancel();
238
            return;
239
        }
240
        // Only copy the text from the div to the textarea if the textarea is not currently visible.
241
        if (!this.editor.get('hidden')) {
242
            this.updateOriginal();
243
        }
244
        var newText = this.textarea.get('value');
245
 
246
        if (newText !== this.lastText) {
247
            Y.log('Autosave text', 'debug', LOGNAME_AUTOSAVE);
248
 
249
            // Make an ajax request.
250
            url = M.cfg.wwwroot + this.get('autosaveAjaxScript');
251
            params = {
252
                sesskey: M.cfg.sesskey,
253
                contextid: this.get('contextid'),
254
                action: 'save',
255
                drafttext: newText,
256
                elementid: this.get('elementid'),
257
                pagehash: this.get('pageHash'),
258
                pageinstance: this.autosaveInstance
259
            };
260
 
261
            // Reusable error handler - must be passed the correct context.
262
            var ajaxErrorFunction = function(response) {
263
                var errorDuration = parseInt(this.get('autosaveFrequency'), 10) * 1000;
264
                Y.log('Error while autosaving text', 'warn', LOGNAME_AUTOSAVE);
265
                Y.log(response, 'warn', LOGNAME_AUTOSAVE);
266
                this.showMessage(M.util.get_string('autosavefailed', 'editor_atto'), NOTIFY_WARNING, errorDuration);
267
            };
268
 
269
            this.autosaveIo(params, this, {
270
                failure: ajaxErrorFunction,
271
                success: function(response) {
272
                    if (response && response.error) {
273
                        Y.soon(Y.bind(ajaxErrorFunction, this, [response]));
274
                    } else {
275
                        // All working.
276
                        this.lastText = newText;
277
                        this.showMessage(M.util.get_string('autosavesucceeded', 'editor_atto'),
278
                                NOTIFY_INFO, SUCCESS_MESSAGE_TIMEOUT);
279
                    }
280
                }
281
            });
282
        }
283
        return this;
284
    }
285
};
286
 
287
Y.Base.mix(Y.M.editor_atto.Editor, [EditorAutosave]);