Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

issue/2714 Decoupled menu, page, article, block and component #2716

Merged
merged 21 commits into from
Apr 20, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
81cacf1
issue/2714 Decoupled menu, page, article, block and component
oliverfoster Apr 2, 2020
64274b7
Typo
oliverfoster Apr 2, 2020
2de000f
Recommendations
oliverfoster Apr 4, 2020
1942b1a
issue/2714: Added Adapt.log.warnOnce
oliverfoster Apr 6, 2020
347f520
Linting fixes
oliverfoster Apr 6, 2020
96bda87
issue/2714 Added fallback model/view name discovery
oliverfoster Apr 6, 2020
ebdabc5
issue/2645 Added manifest creation and loading
oliverfoster Apr 6, 2020
9a20e9b
issue/2714 Decoupled menu, page, article, block and component
oliverfoster Apr 2, 2020
53264e5
Typo
oliverfoster Apr 2, 2020
76cdf72
Recommendations
oliverfoster Apr 4, 2020
818dd05
issue/2714: Added Adapt.log.warnOnce
oliverfoster Apr 6, 2020
2bb469b
Linting fixes
oliverfoster Apr 6, 2020
b4abd4b
issue/2714 Added fallback model/view name discovery
oliverfoster Apr 6, 2020
be03dec
Merge branch 'issue/2714' of https://github.com/adaptlearning/adapt_f…
oliverfoster Apr 7, 2020
a861913
issue/2645 Added manifest creation and loading
oliverfoster Apr 6, 2020
c1130ae
Merge branch 'issue/2645' of https://github.com/adaptlearning/adapt_f…
oliverfoster Apr 7, 2020
d6dfe8d
Recommendations
olivermartinfoster Apr 14, 2020
4dd0ba8
Improved babel source mapping
olivermartinfoster Apr 14, 2020
3c7b656
Readded sourceType to babel config
olivermartinfoster Apr 14, 2020
cb1ee71
issue/2645 Added manifest creation and loading
olivermartinfoster Apr 14, 2020
cf924ca
isTypeGroup warning simplified
olivermartinfoster Apr 14, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 45 additions & 16 deletions grunt/config/babel.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,51 @@
module.exports = {
options: {
sourceMap: true,
inputSourceMap: true,
sourceType: 'script',
minified: true,
comments: false,
presets: [
[
'@babel/preset-env',
{
"targets": {
"ie": "11"
compile: {
options: {
inputSourceMap: false,
sourceType: 'script',
minified: true,
comments: false,
presets: [
[
'@babel/preset-env',
{
"targets": {
"ie": "11"
}
}
}
]
]
]
},
files: [{
"expand": true,
"cwd": "<%= tempdir %>",
"src": [
"adapt.min.js"
],
"dest": "<%= outputdir %>adapt/js/",
"ext": ".min.js"
}]
},
dist: {
dev: {
options: {
sourceMap: true,
inputSourceMap: true,
sourceType: 'script',
retainLines: true,
minified: false,
compact: false,
comments: true,
presets: [
[
'@babel/preset-env',
{
"targets": {
"ie": "11"
}
}
]
]
},
files: [{
"expand": true,
"cwd": "<%= tempdir %>",
Expand All @@ -27,4 +56,4 @@ module.exports = {
"ext": ".min.js"
}]
}
};
};
4 changes: 2 additions & 2 deletions grunt/config/watch.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ module.exports = {
tasks: ['handlebars', 'javascript:dev']
},
courseJson: {
files: ['<%= sourcedir %>course/**/*.<%= jsonext %>'],
tasks: ['jsonlint', 'check-json', 'newer:copy:courseJson', 'schema-defaults']
files: ['<%= sourcedir %>course/**/*.<%= jsonext %>', '<%= outputdir %>course/*/language_data_manifest.js'],
tasks: ['language-data-manifests', 'jsonlint', 'check-json', 'newer:copy:courseJson', 'schema-defaults']
},
courseAssets: {
files: ['<%= sourcedir %>course/<%=languages%>/*', '!<%= sourcedir %>course/<%=languages%>/*.<%= jsonext %>'],
Expand Down
3 changes: 2 additions & 1 deletion grunt/tasks/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@ module.exports = function(grunt) {
'build-config',
'copy',
'schema-defaults',
'language-data-manifests',
'handlebars',
'tracking-insert',
'javascript:compile',
'babel',
'babel:compile',
'clean:dist',
'less:compile',
'replace',
Expand Down
3 changes: 2 additions & 1 deletion grunt/tasks/dev.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@ module.exports = function(grunt) {
'build-config',
'copy',
'schema-defaults',
'language-data-manifests',
'handlebars',
'tracking-insert',
'javascript:dev',
'babel',
'babel:dev',
'less:dev',
'replace',
'scripts:adaptpostbuild',
Expand Down
3 changes: 2 additions & 1 deletion grunt/tasks/diff.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@ module.exports = function(grunt) {
'build-config',
'copy',
'schema-defaults',
'language-data-manifests',
'newer:handlebars:compile',
'tracking-insert',
'newer:javascript:dev',
'babel',
'babel:dev',
'newer:less:dev',
'replace',
'scripts:adaptpostbuild',
Expand Down
7 changes: 7 additions & 0 deletions grunt/tasks/language-data-manifests.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module.exports = function(grunt) {
const Helpers = require('../helpers')(grunt);
grunt.registerTask('language-data-manifests', 'Creates a manifest for each set of language data files', function() {
const languages = Helpers.getFramework({ useOutputData: true }).getData().languages;
languages.forEach(language => language.saveManifest());
});
};
3 changes: 2 additions & 1 deletion grunt/tasks/server-build.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@ module.exports = function(grunt) {
'_log-vars',
'build-config',
'copy',
'language-data-manifests',
'less:' + requireMode,
'handlebars',
'javascript:' + requireMode,
'babel',
'babel:' + requireMode,
'replace',
'scripts:adaptpostbuild',
'clean:temp'
Expand Down
177 changes: 140 additions & 37 deletions src/core/js/adapt.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ define([
initialize() {
this.loadScript = window.__loadScript;
this.location = {};
this.componentStore = {};
this.store = {};
this.setupWait();
}

Expand All @@ -28,6 +28,14 @@ define([
};
}

/**
* @deprecated since v6.0.0 - please use `Adapt.store` instead
*/
get componentStore() {
this.log && this.log.deprecated('Adapt.componentStore, please use Adapt.store instead');
return this.store;
}

init() {
this.addDirection();
this.disableAnimation();
Expand Down Expand Up @@ -148,39 +156,149 @@ define([
scrollTo() {}

/**
* Used to register components with the Adapt 'component store'
* @param {string} name The name of the component to be registered
* Used to register models and views with `Adapt.store`
* @param {string|Array} name The name(s) of the model/view to be registered
* @param {object} object Object containing properties `model` and `view` or (legacy) an object representing the view
*/
register(name, object) {
if (this.componentStore[name]) {
throw Error('The component "' + name + '" already exists in your project');
if (name instanceof Array) {
// if an array is passed, iterate by recursive call
name.forEach(name => this.register(name, object));
return object;
}

if (name.split(' ').length > 1) {
// if name with spaces is passed, split and pass as array
this.register(name.split(' '), object);
return object;
}

if ((!object.view && !object.model) || object instanceof Backbone.View) {
this.log && this.log.deprecated('View-only registrations are no longer supported');
object = { view: object };
}

if (object.view && !object.view.template) {
object.view.template = name;
}

const isModelSetAndInvalid = (object.model &&
!(object.model.prototype instanceof Backbone.Model) &&
!(object.model instanceof Function));
if (isModelSetAndInvalid) {
throw new Error('The registered model is not a Backbone.Model or Function');
}

if (object.view) {
// use view+model object
if (!object.view.template) object.view.template = name;
} else {
// use view object
if (!object.template) object.template = name;
const isViewSetAndInvalid = (object.view &&
!(object.view.prototype instanceof Backbone.View) &&
!(object.view instanceof Function));
if (isViewSetAndInvalid) {
throw new Error('The registered view is not a Backbone.View or Function');
}

this.componentStore[name] = object;
this.store[name] = Object.assign({}, this.store[name], object);

return object;
}

/**
* Fetches a component view class from the componentStore. For a usage example, see either HotGraphic or Narrative
* @param {string} name The name of the componentView you want to fetch e.g. `"hotgraphic"`
* @returns {ComponentView} Reference to the view class
* Parses a view class name.
* @param {string|Backbone.Model|Backbone.View|object} nameModelViewOrData The name of the view class you want to fetch e.g. `"hotgraphic"` or its model or its json data
*/
getViewName(nameModelViewOrData) {
if (typeof nameModelViewOrData === 'string') {
return nameModelViewOrData;
}
if (nameModelViewOrData instanceof Backbone.Model) {
nameModelViewOrData = nameModelViewOrData.toJSON();
}
if (nameModelViewOrData instanceof Backbone.View) {
let foundName;
_.find(this.store, (entry, name) => {
if (!entry || !entry.view) return;
if (!(nameModelViewOrData instanceof entry.view)) return;
foundName = name;
return true;
});
return foundName;
}
if (nameModelViewOrData instanceof Object) {
const names = [
typeof nameModelViewOrData._view === 'string' && nameModelViewOrData._view,
typeof nameModelViewOrData._component === 'string' && nameModelViewOrData._component,
typeof nameModelViewOrData._type === 'string' && nameModelViewOrData._type
].filter(Boolean);
if (names.length) {
// find first fitting view name
const name = names.find(name => this.store[name] && this.store[name].view);
return name || names.pop(); // return last available if none found
}
}
throw new Error('Cannot derive view class name from input');
}

/**
* Fetches a view class from the store. For a usage example, see either HotGraphic or Narrative
* @param {string|Backbone.Model|Backbone.View|object} nameModelViewOrData The name of the view class you want to fetch e.g. `"hotgraphic"` or its model or its json data
* @returns {Backbone.View} Reference to the view class
*/
getViewClass(nameModelViewOrData) {
const name = this.getViewName(nameModelViewOrData);
const object = this.store[name];
if (!object) {
this.log.warnOnce(`A view for '${name}' isn't registered in your project`);
return;
}
const isBackboneView = (object.view && object.view.prototype instanceof Backbone.View);
if (!isBackboneView && object.view instanceof Function) {
return object.view();
}
return object.view;
}

/**
* Parses a model class name.
* @param {string|Backbone.Model|object} name The name of the model you want to fetch e.g. `"hotgraphic"`, the model to process or its json data
*/
getModelName(nameModelOrData) {
if (typeof nameModelOrData === 'string') {
return nameModelOrData;
}
if (nameModelOrData instanceof Backbone.Model) {
nameModelOrData = nameModelOrData.toJSON();
}
if (nameModelOrData instanceof Object) {
const names = [
typeof nameModelOrData._model === 'string' && nameModelOrData._model,
typeof nameModelOrData._component === 'string' && nameModelOrData._component,
typeof nameModelOrData._type === 'string' && nameModelOrData._type
].filter(Boolean);
if (names.length) {
// find first fitting model name
const name = names.find(name => this.store[name] && this.store[name].model);
return name || names.pop(); // return last available if none found
}
}
throw new Error('Cannot derive model class name from input');
}

/**
* Fetches a model class from the store. For a usage example, see either HotGraphic or Narrative
* @param {string|Backbone.Model|object} name The name of the model you want to fetch e.g. `"hotgraphic"` or its json data
* @returns {Backbone.Model} Reference to the view class
*/
getViewClass(name) {
const object = this.componentStore[name];
getModelClass(nameModelOrData) {
const name = this.getModelName(nameModelOrData);
const object = this.store[name];
if (!object) {
throw Error('The component "' + name + '" doesn\'t exist in your project');
this.log.warnOnce(`A model for '${name}' isn't registered in your project`);
return;
}
const isBackboneModel = (object.model && object.model.prototype instanceof Backbone.Model);
if (!isBackboneModel && object.model instanceof Function) {
return object.model();
}
return object.view || object;
return object.model;
}

/**
Expand Down Expand Up @@ -242,28 +360,13 @@ define([
* Trickle uses this function to determine where it should scrollTo after it unlocks
*/
parseRelativeString(relativeString) {
if (relativeString[0] === '@') {
relativeString = relativeString.substr(1);
}

let type = relativeString.match(/(component|block|article|page|menu)/);
if (!type) {
this.log.error('Adapt.parseRelativeString() could not match relative type', relativeString);
return;
}
type = type[0];

const offset = parseInt(relativeString.substr(type.length).trim() || 0);
if (isNaN(offset)) {
this.log.error('Adapt.parseRelativeString() could not parse relative offset', relativeString);
return;
}

const splitIndex = relativeString.search(/[ +\-\d]{1}/);
const type = relativeString.slice(0, splitIndex).replace(/^@/, '');
const offset = parseInt(relativeString.slice(splitIndex).trim() || 0);
return {
type: type,
offset: offset
};

}

addDirection() {
Expand Down
1 change: 1 addition & 0 deletions src/core/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ require([
'core/js/notify',
'core/js/router',
'core/js/models/lockingModel',
'core/js/mpabc',
'core/js/helpers',
'core/js/scrolling',
'core/js/headings',
Expand Down
6 changes: 0 additions & 6 deletions src/core/js/collections/adaptCollection.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,7 @@ define([
class AdaptCollection extends Backbone.Collection {

initialize(models, options) {
this.url = options.url;
this.once('reset', this.loadedData, this);
if (!this.url) return;
this.fetch({
reset: true,
error: () => console.error('ERROR: unable to load file ' + this.url)
});
}

loadedData() {
Expand Down
Loading