Skip to content

Commit

Permalink
Extracting sync-mode and editors modules
Browse files Browse the repository at this point in the history
  • Loading branch information
flbulgarelli committed Aug 6, 2020
1 parent 69ee140 commit c913a12
Show file tree
Hide file tree
Showing 7 changed files with 262 additions and 147 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -369,14 +369,14 @@ contents. There are two different approaches:

```javascript
// simplest method - you can register just one
mumuki.submission.registerContentSyncer(() => {
mumuki.editors.registerContentSyncer(() => {
// ... write here your custom component content...
$('#mu-custom-editor-value').val(/* ... */);
});

// alternate method
// you can register many sources
mumuki.CustomEditor.addSource({
mumuki.editors.addCustomSource({
getContent() {
return { name: "solution[content]", value: /* ... */ } ;
}
Expand Down
104 changes: 104 additions & 0 deletions app/assets/javascripts/mumuki_laboratory/application/editors.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/**
* This module allows to read and write the current editor's contents
* regardless if it is an standard editor or a custom editor
*/
mumuki.editors = {
/**
* @type {() => void}
*/
_contentSyncer: null,

/**
* Updates the current editor's content
*
* @param {string} content
*/
setContent(content) {
const $customEditor = $('#mu-custom-editor-value');
if ($customEditor.length) {
$customEditor.val(content);
} else {
mumuki.editor.setContent(content);
}
},


/**
* @returns {EditorProperty[]}
*/
getContents() {
return mumuki.CustomEditor.hasSources ?
mumuki.CustomEditor.getContents() :
this.getStandardEditorContents();
},

/**
* Syncs and returns the content objects of the standard editor form
*
* This content object may include keys like {@code content},
* {@code content_extra} and {@code content_test}
*
* @returns {EditorProperty[]}
*/
getStandardEditorContents() {
this._syncContent();
return $('.new_solution').serializeArray();
},

/**
* Answers a submission object with a key for each of the current
* editor sources.
*
* This method will use CustomEditor's sources if availble, or
* standard editor's content sources otherwise
*
* @returns {Submission}
*/
getSubmission() {
let content = {};
let contents = this.getContents();
contents.forEach((it) => {
content[it.name] = it.value;
});
return content;
},

/**
* Copies current solution from it native rendering components
* to the appropriate submission form elements.
*
* Both editors and runners with a custom editor that don't register a source should
* register its own syncer function in order to {@link syncContent} work properly.
*
* @see registerContentSyncer
* @see CustomEditor#addSource
*/
_syncContent() {
if (this._contentSyncer) {
this._contentSyncer();
}
},

/**
* Sets a content syncer, that will be used by {@link _syncContent}
* in ordet to dump solution into the submission form fields.
*
* Each editor should have its own syncer registered - otherwise previous or none may be used
* causing unpredicatble behaviours - or cleared by passing {@code null}.
*
* As a particular case, runners with custom editors that don't add sources using {@link CustomEditor#addSource}
* should set the {@code #mu-custom-editor-value} value within its syncer.
*
* @param {() => void} [syncer] the syncer, or null, if no sync'ing is needed
*/
registerContentSyncer(syncer = null) {
this._contentSyncer = syncer;
},

/**
* @param {CustomEditorSource} source
*/
addCustomSource(source) {
mumuki.CustomEditor.addSource(source)
}
};
74 changes: 11 additions & 63 deletions app/assets/javascripts/mumuki_laboratory/application/progress.js
Original file line number Diff line number Diff line change
@@ -1,48 +1,4 @@
mumuki.progress = (() => {

class LocalSyncMode {
syncProgress() {
$('.progress-list-item').each((_, it) => {
this._updateProgressListItemClass($(it))
});
}

syncExerciseEditorValue() {
const lastSubmission = mumuki.SubmissionsStore.getLastSubmission(mumuki.currentExerciseId);
if (lastSubmission) {
/** @todo reify editors module */
/** @todo extract core module */
const content = mumuki.SubmissionsStore.submissionSolutionContent(lastSubmission.submission);
const $customEditor = $('#mu-custom-editor-value');
if ($customEditor.length) {
$customEditor.val(content);
} else {
mumuki.editor.setContent(content);
}
}
}

/**
* @param {JQuery} $anchor
*/
_updateProgressListItemClass($anchor) {
const exerciseId = $anchor.data('mu-exercise-id');
const status = mumuki.SubmissionsStore.getLastSubmissionStatus(exerciseId);
$anchor.attr('class', mumuki.renderers.progressListItemClassForStatus(status, exerciseId == mumuki.currentExerciseId));
}

}

class ServerSyncMode {
syncProgress() {
// nothing
}

syncExerciseEditorValue() {
// nothing
}
}

/**
* Updates the current exercise progress indicator
*
Expand All @@ -53,29 +9,21 @@ mumuki.progress = (() => {
if(data.guide_finished_by_solution) $('#guide-done').modal();
};


/** Selects the most appropriate sync mode */
function _selectSyncMode() {
if (mumuki.incognitoMode) {
mumuki.progress._syncMode = new LocalSyncMode();
} else {
mumuki.progress._syncMode = new ServerSyncMode();
}
/**
* Update all links in the progress bar with the given function
*
* @param {(anchor: JQuery) => string} f
*/
function updateWholeProgressBar(f) {
$('.progress-list-item').each((_, it) => {
const $anchor = $(it);
$anchor.attr('class', f($anchor))
});
}

mumuki.load(() => {
/** @todo move to another module & load sync mode lazily */
mumuki.progress._selectSyncMode();
mumuki.progress._syncMode.syncProgress();
mumuki.progress._syncMode.syncExerciseEditorValue();
})

return {
updateProgressBarAndShowModal,

/** @type {LocalSyncMode|ServerSyncMode} */
_syncMode: null,
_selectSyncMode
updateWholeProgressBar
};
})();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,81 +56,6 @@ mumuki.submission = (() => {
}
}

// ============
// Content Sync
// ============

/**
* Syncs and returns the content objects of the standard editor form
*
* This content object may include keys like {@code content},
* {@code content_extra} and {@code content_test}
*
* @returns {EditorProperty[]}
*/
function getStandardEditorContents() {
mumuki.submission._syncContent();
return $('.new_solution').serializeArray();
}

/**
* Answers a content object with a key for each of the current
* editor sources.
*
* This method will use CustomEditor's sources if availble, or
* standard editor's content sources otherwise
*
* @returns {Submission}
*/
function getContent() {
let content = {};
let contents;

if (mumuki.CustomEditor.hasSources) {
contents = mumuki.CustomEditor.getContents();
} else {
contents = mumuki.submission.getStandardEditorContents();
}

contents.forEach((it) => {
content[it.name] = it.value;
});

// @ts-ignore
return content;
}

/**
* Copies current solution from it native rendering components
* to the appropriate submission form elements.
*
* Both editors and runners with a custom editor that don't register a source should
* register its own syncer function in order to {@link syncContent} work properly.
*
* @see registerContentSyncer
* @see CustomEditor#addSource
*/
function _syncContent() {
if (mumuki.submission._contentSyncer) {
mumuki.submission._contentSyncer();
}
}

/**
* Sets a content syncer, that will be used by {@link _syncContent}
* in ordet to dump solution into the submission form fields.
*
* Each editor should have its own syncer registered - otherwise previous or none may be used
* causing unpredicatble behaviours - or cleared by passing {@code null}.
*
* As a particular case, runners with custom editors that don't add sources using {@link CustomEditor#addSource}
* should set the {@code #mu-custom-editor-value} value within its syncer.
*
* @param {() => void} [syncer] the syncer, or null, if no sync'ing is needed
*/
function registerContentSyncer(syncer = null) {
mumuki.submission._contentSyncer = syncer;
}

// ==========
// Processing
Expand Down Expand Up @@ -222,8 +147,7 @@ mumuki.submission = (() => {
mumuki.submission._selectSolutionProcessor(submitButton, $submissionsResults);

submitButton.start(() => {
var solution = mumuki.submission.getContent();
mumuki.submission.processSolution(solution);
mumuki.submission.processSolution(mumuki.editors.getSubmission());
});

submitButton.checkAttemptsLeft();
Expand All @@ -249,11 +173,6 @@ mumuki.submission = (() => {
_registerSolutionProcessor,
_selectSolutionProcessor,

_syncContent,
registerContentSyncer,
getStandardEditorContents,
getContent,

animateTimeoutError,
SubmitButton,
};
Expand Down
75 changes: 75 additions & 0 deletions app/assets/javascripts/mumuki_laboratory/application/sync-mode.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
mumuki.syncMode = (() => {

/**
* Syncs progress and solutions
* from local storage
*/
class ClientSyncMode {
syncProgress() {
mumuki.progress.updateWholeProgressBar($anchor => this._getProgressListItemClass($anchor));
}

syncEditorContent() {
const lastSubmission = mumuki.SubmissionsStore.getLastSubmission(mumuki.currentExerciseId);
if (lastSubmission) {
/** @todo extract core module */
const content = mumuki.SubmissionsStore.submissionSolutionContent(lastSubmission.submission);
mumuki.editors.setContent(content);
}
}

/**
* @param {JQuery} $anchor
*/
_getProgressListItemClass($anchor) {
const exerciseId = $anchor.data('mu-exercise-id');
const status = mumuki.SubmissionsStore.getLastSubmissionStatus(exerciseId);
return mumuki.renderers.progressListItemClassForStatus(status, exerciseId == mumuki.currentExerciseId);
}

}

/**
* Syncs progress and solutions
* from server.
*
* This class does actually nothing
* since that behaviour is actually the default one, son no additional actions
* are nedeed.
*/
class ServerSyncMode {
syncProgress() {
// nothing
}

syncEditorContent() {
// nothing
}
}


/** Selects the most appropriate sync mode */
function _selectSyncMode() {
if (mumuki.incognitoMode) {
mumuki.syncMode._current = new ClientSyncMode();
} else {
mumuki.syncMode._current = new ServerSyncMode();
}
}

mumuki.load(() => {
mumuki.syncMode._selectSyncMode();
mumuki.syncMode._current.syncProgress();
mumuki.syncMode._current.syncEditorContent();
})

return {
ServerSyncMode,
ClientSyncMode,

_selectSyncMode,

/** @type {ClientSyncMode|ServerSyncMode}*/
_current: null
}
})();
Loading

0 comments on commit c913a12

Please sign in to comment.