├── .editorconfig ├── .gitignore ├── README.md ├── angular.json ├── e2e ├── protractor.conf.js ├── src │ ├── app.e2e-spec.ts │ └── app.po.ts └── tsconfig.e2e.json ├── firebase ├── README.md ├── backup │ ├── bricks │ │ ├── 08x0CLQKs5JIy6VGD7yO │ │ │ ├── 08x0CLQKs5JIy6VGD7yO.json │ │ │ └── questions │ │ │ │ ├── 3lHQylhqWBRDjjIGFyn6 │ │ │ │ └── 3lHQylhqWBRDjjIGFyn6.json │ │ │ │ ├── AGwtMpjyH6qYA6l3sgSR │ │ │ │ └── AGwtMpjyH6qYA6l3sgSR.json │ │ │ │ ├── CtWZD5GLk3IB1XwiIljT │ │ │ │ └── CtWZD5GLk3IB1XwiIljT.json │ │ │ │ ├── Ol2D3htXRCe1bDduHlj0 │ │ │ │ └── Ol2D3htXRCe1bDduHlj0.json │ │ │ │ ├── RkFIAG9wU11UY37mso4J │ │ │ │ └── RkFIAG9wU11UY37mso4J.json │ │ │ │ ├── hUhmHmJlKCdJEmXw63d2 │ │ │ │ └── hUhmHmJlKCdJEmXw63d2.json │ │ │ │ ├── hYdrOkTHbl09G8OAPyFl │ │ │ │ └── hYdrOkTHbl09G8OAPyFl.json │ │ │ │ ├── jISSuP35vHeYfvkbKKo0 │ │ │ │ └── jISSuP35vHeYfvkbKKo0.json │ │ │ │ ├── kQUUj9kkap2vttW4ZKeY │ │ │ │ └── kQUUj9kkap2vttW4ZKeY.json │ │ │ │ └── lvlr7NW1Zdy1Su1yO2kj │ │ │ │ └── lvlr7NW1Zdy1Su1yO2kj.json │ │ ├── 20min1 │ │ │ └── 20min1.json │ │ ├── 20min2 │ │ │ └── 20min2.json │ │ ├── 20min3 │ │ │ └── 20min3.json │ │ ├── 40min1 │ │ │ └── 40min1.json │ │ ├── 40min2 │ │ │ └── 40min2.json │ │ ├── Arrow │ │ │ ├── Arrow.json │ │ │ └── questions │ │ │ │ └── Arrow │ │ │ │ └── Arrow.json │ │ ├── HorizontalShuffle │ │ │ ├── HorizontalShuffle.json │ │ │ └── questions │ │ │ │ └── HorizontalShuffle │ │ │ │ └── HorizontalShuffle.json │ │ ├── MultipleChoice │ │ │ ├── MultipleChoice.json │ │ │ └── questions │ │ │ │ └── MultipleChoice │ │ │ │ └── MultipleChoice.json │ │ ├── Order │ │ │ ├── Order.json │ │ │ └── questions │ │ │ │ └── Order │ │ │ │ └── Order.json │ │ ├── ShortAnswer │ │ │ ├── ShortAnswer.json │ │ │ └── questions │ │ │ │ └── ShortAnswer │ │ │ │ └── ShortAnswer.json │ │ ├── SingleChoice │ │ │ ├── SingleChoice.json │ │ │ └── questions │ │ │ │ └── SingleChoice │ │ │ │ └── SingleChoice.json │ │ ├── Sort │ │ │ ├── Sort.json │ │ │ └── questions │ │ │ │ └── sort │ │ │ │ └── sort.json │ │ ├── TextHighlighting │ │ │ ├── TextHighlighting.json │ │ │ └── questions │ │ │ │ └── TextHighlighting │ │ │ │ └── TextHighlighting.json │ │ ├── lh0pzfSRgVBSZ8UBaDJb │ │ │ ├── lh0pzfSRgVBSZ8UBaDJb.json │ │ │ └── questions │ │ │ │ └── tN4g9IyPzC3SdM2cAfGB │ │ │ │ └── tN4g9IyPzC3SdM2cAfGB.json │ │ ├── milton │ │ │ ├── milton.json │ │ │ └── questions │ │ │ │ ├── 1 │ │ │ │ └── 1.json │ │ │ │ ├── 2 │ │ │ │ └── 2.json │ │ │ │ ├── 3 │ │ │ │ └── 3.json │ │ │ │ ├── 4 │ │ │ │ └── 4.json │ │ │ │ ├── 5 │ │ │ │ └── 5.json │ │ │ │ ├── 6 │ │ │ │ └── 6.json │ │ │ │ ├── 7 │ │ │ │ └── 7.json │ │ │ │ ├── 8 │ │ │ │ └── 8.json │ │ │ │ ├── 9 │ │ │ │ └── 9.json │ │ │ │ ├── 10 │ │ │ │ └── 10.json │ │ │ │ ├── 11 │ │ │ │ └── 11.json │ │ │ │ ├── 12 │ │ │ │ └── 12.json │ │ │ │ ├── 13 │ │ │ │ └── 13.json │ │ │ │ └── 14 │ │ │ │ └── 14.json │ │ └── uYVMc4KbjLaBsBo9V7Wq │ │ │ └── uYVMc4KbjLaBsBo9V7Wq.json │ ├── classes │ │ └── Z7vl6btzFHhPede4yZSi │ │ │ ├── Z7vl6btzFHhPede4yZSi.json │ │ │ ├── pallets │ │ │ └── f0wIFtZ8CDCjvhD4SnFe │ │ │ │ └── f0wIFtZ8CDCjvhD4SnFe.json │ │ │ └── students │ │ │ └── 0k5CTSiIHCIIxuzXALo7 │ │ │ └── 0k5CTSiIHCIIxuzXALo7.json │ ├── pallets │ │ ├── bsjsJllNgYkos0w3Wrtv │ │ │ ├── bricks │ │ │ │ ├── 6FD2XgVyNJ161KQFBpVo │ │ │ │ │ └── 6FD2XgVyNJ161KQFBpVo.json │ │ │ │ ├── b8nLILCRvFXMCUmcu4NF │ │ │ │ │ └── b8nLILCRvFXMCUmcu4NF.json │ │ │ │ ├── milton │ │ │ │ │ └── milton.json │ │ │ │ └── uAbpscbGDGoVleXCNtQH │ │ │ │ │ └── uAbpscbGDGoVleXCNtQH.json │ │ │ └── bsjsJllNgYkos0w3Wrtv.json │ │ ├── demopallet │ │ │ ├── bricks │ │ │ │ ├── 20min1 │ │ │ │ │ └── 20min1.json │ │ │ │ ├── 20min2 │ │ │ │ │ └── 20min2.json │ │ │ │ ├── 20min3 │ │ │ │ │ └── 20min3.json │ │ │ │ ├── 40min1 │ │ │ │ │ └── 40min1.json │ │ │ │ ├── 40min2 │ │ │ │ │ └── 40min2.json │ │ │ │ └── milton │ │ │ │ │ └── milton.json │ │ │ └── demopallet.json │ │ └── testpallet1 │ │ │ ├── bricks │ │ │ ├── Arrow │ │ │ │ └── Arrow.json │ │ │ ├── HorizontalShuffle │ │ │ │ └── HorizontalShuffle.json │ │ │ ├── MultipleChoice │ │ │ │ └── MultipleChoice.json │ │ │ ├── Order │ │ │ │ └── Order.json │ │ │ ├── ShortAnswer │ │ │ │ └── ShortAnswer.json │ │ │ ├── SingleChoice │ │ │ │ └── SingleChoice.json │ │ │ ├── Sort │ │ │ │ └── Sort.json │ │ │ └── TextHighlighting │ │ │ │ └── TextHighlighting.json │ │ │ └── testpallet1.json │ └── teachers │ │ └── pKOncFi85zWuDaOXbWnCW0ha9YN2 │ │ ├── classes │ │ └── OWtS1woe6V52ICQZrKei │ │ │ └── OWtS1woe6V52ICQZrKei.json │ │ └── pKOncFi85zWuDaOXbWnCW0ha9YN2.json └── firebase-functions │ ├── .firebaserc │ ├── firebase.json │ └── functions │ ├── .eslintrc.json │ ├── index.js │ ├── package-lock.json │ └── package.json ├── package-lock.json ├── package.json ├── server.js ├── src ├── app │ ├── app.component.html │ ├── app.component.ts │ ├── app.module.ts │ ├── app.routing.ts │ ├── auth │ │ ├── auth.module.ts │ │ └── auth.service.ts │ ├── build │ │ ├── brick │ │ │ ├── brick.component.html │ │ │ ├── brick.component.scss │ │ │ ├── brick.component.ts │ │ │ ├── brick.routing.ts │ │ │ ├── brick.service.ts │ │ │ ├── brickTime.pipe.ts │ │ │ ├── brickbuild.module.ts │ │ │ ├── comp │ │ │ │ ├── comp.component.ts │ │ │ │ ├── comp.module.ts │ │ │ │ ├── comp_arrow.component.ts │ │ │ │ ├── comp_horizontal_shuffle.component.ts │ │ │ │ ├── comp_index.ts │ │ │ │ ├── comp_multiple_choice.component.ts │ │ │ │ ├── comp_order.component.ts │ │ │ │ ├── comp_reverse.pipe.ts │ │ │ │ ├── comp_short_answer.component.ts │ │ │ │ ├── comp_single_choice.component.ts │ │ │ │ ├── comp_sort.component.ts │ │ │ │ ├── comp_text.component.ts │ │ │ │ ├── comp_text_highlighting.component.ts │ │ │ │ └── highlight.directive.ts │ │ │ ├── ending.component.html │ │ │ ├── ending.component.ts │ │ │ ├── introduction.component.html │ │ │ ├── introduction.component.scss │ │ │ ├── introduction.component.ts │ │ │ ├── live.component.html │ │ │ ├── live.component.scss │ │ │ ├── live.component.ts │ │ │ ├── question.component.ts │ │ │ ├── review.component.html │ │ │ ├── review.component.ts │ │ │ ├── summary.component.html │ │ │ ├── summary.component.scss │ │ │ ├── summary.component.ts │ │ │ ├── timer.component.ts │ │ │ └── timer.service.ts │ │ ├── build.component.spec.ts │ │ ├── build.component.ts │ │ ├── build.module.ts │ │ └── build.routing.ts │ ├── database │ │ ├── database.module.ts │ │ ├── database.service.spec.ts │ │ └── database.service.ts │ ├── login │ │ ├── login.component.scss │ │ ├── login.component.ts │ │ ├── login.module.ts │ │ ├── sign-in.component.scss │ │ ├── sign-in.component.ts │ │ └── user-type.component.ts │ ├── manage │ │ ├── dashboard │ │ │ ├── dashboard.component.html │ │ │ ├── dashboard.component.scss │ │ │ └── dashboard.component.ts │ │ ├── manage.component.spec.ts │ │ ├── manage.component.ts │ │ ├── manage.module.ts │ │ └── manage.routing.ts │ ├── material.module.ts │ ├── navigation │ │ ├── navigation.component.html │ │ ├── navigation.component.scss │ │ ├── navigation.component.ts │ │ └── navigation.module.ts │ ├── play │ │ ├── brick │ │ │ ├── brick.component.html │ │ │ ├── brick.component.scss │ │ │ ├── brick.component.ts │ │ │ ├── brick.module.ts │ │ │ ├── brick.routing.ts │ │ │ ├── brick.service.ts │ │ │ ├── brickTime.pipe.ts │ │ │ ├── comp │ │ │ │ ├── comp.component.ts │ │ │ │ ├── comp.module.ts │ │ │ │ ├── comp_arrow.component.ts │ │ │ │ ├── comp_horizontal_shuffle.component.ts │ │ │ │ ├── comp_index.ts │ │ │ │ ├── comp_multiple_choice.component.ts │ │ │ │ ├── comp_order.component.ts │ │ │ │ ├── comp_poem.component.ts │ │ │ │ ├── comp_reveal.component.ts │ │ │ │ ├── comp_short_answer.component.ts │ │ │ │ ├── comp_single_choice.component.ts │ │ │ │ ├── comp_sort.component.ts │ │ │ │ ├── comp_text.component.ts │ │ │ │ ├── comp_text_highlighting.component.ts │ │ │ │ └── highlight.directive.ts │ │ │ ├── ending.component.html │ │ │ ├── ending.component.ts │ │ │ ├── introduction.component.html │ │ │ ├── introduction.component.scss │ │ │ ├── introduction.component.ts │ │ │ ├── live.component.html │ │ │ ├── live.component.scss │ │ │ ├── live.component.ts │ │ │ ├── question.component.ts │ │ │ ├── review.component.html │ │ │ ├── review.component.ts │ │ │ ├── summary.component.html │ │ │ ├── summary.component.scss │ │ │ ├── summary.component.ts │ │ │ ├── timer.component.ts │ │ │ └── timer.service.ts │ │ ├── dashboard │ │ │ ├── dashboard.component.html │ │ │ ├── dashboard.component.scss │ │ │ └── dashboard.component.ts │ │ ├── pallet │ │ │ ├── pallet.component.html │ │ │ ├── pallet.component.scss │ │ │ └── pallet.component.ts │ │ ├── play.component.ts │ │ ├── play.module.ts │ │ └── play.routing.ts │ └── schema.ts ├── assets │ ├── lflogo-English.png │ ├── lflogo-Maths.png │ ├── lflogo-Mixed.png │ ├── lflogo-White.png │ ├── lflogo.png │ └── milton-q1-poem.png ├── browserslist ├── colors.scss ├── environments │ ├── environment.prod.ts │ └── environment.ts ├── favicon.ico ├── index.html ├── karma.conf.js ├── main.ts ├── palette.scss ├── polyfills.ts ├── styles.scss ├── test.ts ├── tsconfig.app.json ├── tsconfig.spec.json └── tslint.json ├── tsconfig.json └── tslint.json /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 4 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | .env 4 | firebase/*.json 5 | firebase/backup/brickattempts 6 | firebase/backup/teachers 7 | firebase/classes 8 | firebase/students 9 | firebase/firebase-functions/functions/node_modules 10 | 11 | # compiled output 12 | /dist 13 | /tmp 14 | /out-tsc 15 | 16 | # dependencies 17 | /node_modules 18 | 19 | # IDEs and editors 20 | /.idea 21 | .project 22 | .classpath 23 | .c9/ 24 | *.launch 25 | .settings/ 26 | *.sublime-workspace 27 | 28 | # IDE - VSCode 29 | /.vscode 30 | 31 | # misc 32 | /.sass-cache 33 | /connect.lock 34 | /coverage 35 | /libpeerconnection.log 36 | npm-debug.log 37 | yarn-error.log 38 | testem.log 39 | /typings 40 | 41 | # System Files 42 | .DS_Store 43 | Thumbs.db 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Learning Fortress Frontend 2 | 3 | This is a simple VLE project running using a firebase backend. It is designed as a blended learning tool. 4 | 5 | ## Basic Installation 6 | 1. Make sure you have git, node and npm. We use Visual Studio to edit our code. 7 | 2. Clone and install 8 | ```bash 9 | $ git clone git@github.com:Scholar-6/learning-fortress-frontend.git 10 | $ npm install 11 | ``` 12 | 3. Run `ng run debug` for a dev server that reloads on changes to files. Navigate to `http://localhost:4200/`. 13 | 14 | 4. That's it. It is preconfigured to connect to a dummy firebase db in the google cloud. (This database gets restored every so often) 15 | 16 | Further information on Firebase stuff can be found in the [firebase/README.md](./firebase/README.md) 17 | 18 | ## Code scaffolding 19 | 20 | Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`. 21 | 22 | ## Build 23 | 24 | Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. 25 | 26 | ## Running unit tests 27 | (More work required on developing tests) 28 | Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). 29 | Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/). 30 | 31 | ## Further help 32 | 33 | This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 6.0.8. 34 | 35 | To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md). 36 | -------------------------------------------------------------------------------- /e2e/protractor.conf.js: -------------------------------------------------------------------------------- 1 | // Protractor configuration file, see link for more information 2 | // https://github.com/angular/protractor/blob/master/lib/config.ts 3 | 4 | const { SpecReporter } = require('jasmine-spec-reporter'); 5 | 6 | exports.config = { 7 | allScriptsTimeout: 11000, 8 | specs: [ 9 | './src/**/*.e2e-spec.ts' 10 | ], 11 | capabilities: { 12 | 'browserName': 'chrome' 13 | }, 14 | directConnect: true, 15 | baseUrl: 'http://localhost:4200/', 16 | framework: 'jasmine', 17 | jasmineNodeOpts: { 18 | showColors: true, 19 | defaultTimeoutInterval: 30000, 20 | print: function() {} 21 | }, 22 | onPrepare() { 23 | require('ts-node').register({ 24 | project: require('path').join(__dirname, './tsconfig.e2e.json') 25 | }); 26 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); 27 | } 28 | }; -------------------------------------------------------------------------------- /e2e/src/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { AppPage } from './app.po'; 2 | 3 | describe('workspace-project App', () => { 4 | let page: AppPage; 5 | 6 | beforeEach(() => { 7 | page = new AppPage(); 8 | }); 9 | 10 | it('should display welcome message', () => { 11 | page.navigateTo(); 12 | expect(page.getParagraphText()).toEqual('Welcome to learning-fortress-frontend!'); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /e2e/src/app.po.ts: -------------------------------------------------------------------------------- 1 | import { browser, by, element } from 'protractor'; 2 | 3 | export class AppPage { 4 | navigateTo() { 5 | return browser.get('/'); 6 | } 7 | 8 | getParagraphText() { 9 | return element(by.css('app-root h1')).getText(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /e2e/tsconfig.e2e.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/app", 5 | "module": "commonjs", 6 | "target": "es5", 7 | "types": [ 8 | "jasmine", 9 | "jasminewd2", 10 | "node" 11 | ] 12 | } 13 | } -------------------------------------------------------------------------------- /firebase/README.md: -------------------------------------------------------------------------------- 1 | 2 | As we are using the Firestore db we need to set some security rules in the backend 3 | 4 | https://firebase.google.com/docs/firestore/security/get-started 5 | 6 | ```bash 7 | // Allow read/write access on all documents to any user signed in to the application 8 | service cloud.firestore { 9 | match /databases/{database}/documents { 10 | match /{document=**} { 11 | allow read, write: if request.auth.uid != null; 12 | } 13 | } 14 | } 15 | ``` 16 | 17 | We also have a cloud script if we want to lock down new users from accessing the site. We have opened this up for the moment. 18 | 19 | ## Firebase User blocking signup 20 | This is apparently the only way to block new users with firebase UI at the moment 21 | https://github.com/firebase/firebaseui-web/issues/99 22 | The user can still signup but they cannot access the app until their account has been enabled in the firebase console. 23 | 24 | You need to create a new directory locally, then use the code below and the cloud functions starter guide to implement this from the local machine. https://firebase.google.com/docs/functions/get-started?authuser=0 25 | Make sure to uncomment the relevant function in firebase/firebase-functions/functions/index.js 26 | 27 | ```bash 28 | $ cd firebase/firebase-functions/functions 29 | $ npm install -g firebase-tools 30 | $ npm install 31 | $ firebase login 32 | $ firebase deploy --only functions 33 | ``` 34 | 35 | We also need to redirect new users so they don't auto login after signup 36 | ```javascript 37 | signInSuccess(event: FirebaseUISignInSuccessWithAuthResult) { 38 | if (event.authResult.additionalUserInfo.isNewUser) { 39 | this.afAuth.auth.signOut(); 40 | return true; 41 | } 42 | console.log(`signed in as ${event.authResult.user.displayName} who is${event.authResult.additionalUserInfo.isNewUser?"":" not"} a new user.`); 43 | return true; 44 | } 45 | ``` 46 | -------------------------------------------------------------------------------- /firebase/backup/bricks/08x0CLQKs5JIy6VGD7yO/08x0CLQKs5JIy6VGD7yO.json: -------------------------------------------------------------------------------- 1 | {"totalUsers":{"value":3126,"type":"number"},"highScore":{"value":96.3,"type":"number"},"prep":{"value":"Spend up to five minutes reviewing the dramatis personae (presented in the page preceding the play) or at https://www.bartleby.com/70/4201.html","type":"string"},"creator":{"value":"Josephelvet","type":"string"},"avgScore":{"value":62.3,"type":"number"},"brief":{"value":"Exploring friendships, partnerships and relationships in the play and the extent to which individuals are connected to or isolated from those around them.","type":"string"},"title":{"value":"Test Brick","type":"string"},"subject":{"value":"English Literature","type":"string"},"creationDate":{"value":"2018-08-02T23:00:00.000Z","type":"timestamp"},"type":{"value":1,"type":"number"},"summary":{"value":"Lolwut","type":"string"},"pallet":{"value":{"_referencePath":{"segments":["pallets","bsjsJllNgYkos0w3Wrtv"],"projectId":"learning-fortress","databaseId":"(default)"}},"type":"documentReference"}} -------------------------------------------------------------------------------- /firebase/backup/bricks/08x0CLQKs5JIy6VGD7yO/questions/3lHQylhqWBRDjjIGFyn6/3lHQylhqWBRDjjIGFyn6.json: -------------------------------------------------------------------------------- 1 | {"components":{"value":[{"value":{"data":{"value":{"text":{"value":"The following list contains four of Hamlet’s true attributes but also an attribute which is not strictly true. Identify the odd one out –","type":"string"}},"type":"object"},"name":{"value":"Text","type":"string"}},"type":"object"},{"value":{"data":{"value":{"reveals":{"value":[{"value":"an affinity for actors, perhaps, but he’s not one of the ‘players’","type":"string"},{"value":"certainly not married to Ophelia – even if Gertrude likes the idea","type":"string"},{"value":"who likes to get all the attention and might be a teeny bit spoiled","type":"string"},{"value":"“the expectation and rose of the fair state” as Ophelia calls him","type":"string"},{"value":"intellectually lively – his alma mater is the University of Wittenberg","type":"string"}],"type":"array"},"choices":{"value":[{"value":"a stage actor","type":"string"},{"value":"a bachelor","type":"string"},{"value":"an only child","type":"string"},{"value":"a royal prince","type":"string"},{"value":"a scholar","type":"string"}],"type":"array"}},"type":"object"},"name":{"value":"SingleChoice","type":"string"}},"type":"object"}],"type":"array"},"number":{"value":1,"type":"number"}} -------------------------------------------------------------------------------- /firebase/backup/bricks/08x0CLQKs5JIy6VGD7yO/questions/AGwtMpjyH6qYA6l3sgSR/AGwtMpjyH6qYA6l3sgSR.json: -------------------------------------------------------------------------------- 1 | {"number":{"value":9,"type":"number"},"components":{"value":[{"value":{"data":{"value":{"text":{"value":"The following passage is a jumbled version of lines spoken by Hamlet at the end of Act I. Touch / click and move each line to recreate the text in its correct order:","type":"string"}},"type":"object"},"name":{"value":"Text","type":"string"}},"type":"object"},{"value":{"data":{"value":{"reveal":{"value":"The flourish of a rhyming couplet will often round off the last speech of an important scene.","type":"string"},"choices":{"value":[{"value":"So, gentlemen,","type":"string"},{"value":"With all my love I do commend me to you:","type":"string"},{"value":"And what so poor a man as Hamlet is","type":"string"},{"value":"May do, to express his love and friending to you,","type":"string"},{"value":"God willing, shall not lack. Let us go in together;","type":"string"},{"value":"The time is out of joint: O cursed spite,","type":"string"},{"value":"That ever I was born to set it right!","type":"string"},{"value":"And still your fingers on your lips, I pray.","type":"string"}],"type":"array"}},"type":"object"},"name":{"value":"Order","type":"string"}},"type":"object"}],"type":"array"}} -------------------------------------------------------------------------------- /firebase/backup/bricks/08x0CLQKs5JIy6VGD7yO/questions/CtWZD5GLk3IB1XwiIljT/CtWZD5GLk3IB1XwiIljT.json: -------------------------------------------------------------------------------- 1 | {"components":{"value":[{"value":{"data":{"value":{"reveal":{"value":"The only explicitly married man in the play","type":"string"},"text":{"value":"Uxoricide (from the Latin uxor, wife, and caedere, to kill) is the act of killing one’s wife, or a man who kills his wife. In Hamlet, who is this man?","type":"string"},"entries":{"value":[{"value":{"name":{"value":"Answer","type":"string"},"answer":{"value":"Claudius","type":"string"}},"type":"object"}],"type":"array"}},"type":"object"},"name":{"value":"ShortAnswer","type":"string"}},"type":"object"}],"type":"array"},"number":{"value":5,"type":"number"}} -------------------------------------------------------------------------------- /firebase/backup/bricks/08x0CLQKs5JIy6VGD7yO/questions/Ol2D3htXRCe1bDduHlj0/Ol2D3htXRCe1bDduHlj0.json: -------------------------------------------------------------------------------- 1 | {"components":{"value":[{"value":{"data":{"value":{"text":{"value":"By touch or cursor, sort the following words to render an accurate quotation from Act I:","type":"string"}},"type":"object"},"name":{"value":"Text","type":"string"}},"type":"object"},{"value":{"data":{"value":{"choices":{"value":[{"value":"O","type":"string"},{"value":"cursed","type":"string"},{"value":"spite","type":"string"},{"value":"that","type":"string"},{"value":"ever","type":"string"},{"value":"I","type":"string"},{"value":"was","type":"string"},{"value":"born","type":"string"},{"value":"to","type":"string"},{"value":"set","type":"string"},{"value":"it","type":"string"},{"value":"right","type":"string"}],"type":"array"},"reveal":{"value":"The apostrophe “O” often begins an oath or passionate outburst.","type":"string"}},"type":"object"},"name":{"value":"HorizontalShuffle","type":"string"}},"type":"object"}],"type":"array"},"number":{"value":8,"type":"number"}} -------------------------------------------------------------------------------- /firebase/backup/bricks/08x0CLQKs5JIy6VGD7yO/questions/RkFIAG9wU11UY37mso4J/RkFIAG9wU11UY37mso4J.json: -------------------------------------------------------------------------------- 1 | {"components":{"value":[{"value":{"data":{"value":{"text":{"value":"Read the following passage, from a New York Times review of a recent production of the play, and identify three words within it which characterise specific types of speech in performance:","type":"string"}},"type":"object"},"name":{"value":"Text","type":"string"}},"type":"object"},{"value":{"data":{"value":{"text":{"value":"This Hamlet seldom seems to relate to anyone else onstage. In the big dialogue scenes, you’re conscious of Mr. Cumberbatch riding Shakespeare’s rushing words like a surfboard, as if saving his interior energy for the monologues. In those, he is superb, meticulously tracing lines of thought into revelations that stun, elate, exasperate and sadden him. There’s not a single soliloquy that doesn’t shed fresh insight into how Hamlet thinks. And Ms. Turner stages them beautifully, presenting many in the middle of the action, with Mr. Cumberbatch stepping away as the rest of the cast freezes in tableaus. The effect is of a man separated from reality by his own self-fascinated mind. Hamlet has never seemed so alone, which gives him an added poignancy. This production would benefit greatly, though, if it allowed him to play well with others, too.","type":"string"},"reveal":{"value":"The Greek word logos (word, reckoning or thought) and the Latin word solus (alone) are relevant to the etymology of these terms.","type":"string"},"words":{"value":[{"value":13,"type":"number"},{"value":35,"type":"number"},{"value":59,"type":"number"}],"type":"array"}},"type":"object"},"name":{"value":"TextHighlighting","type":"string"}},"type":"object"}],"type":"array"},"number":{"value":10,"type":"number"}} -------------------------------------------------------------------------------- /firebase/backup/bricks/08x0CLQKs5JIy6VGD7yO/questions/hUhmHmJlKCdJEmXw63d2/hUhmHmJlKCdJEmXw63d2.json: -------------------------------------------------------------------------------- 1 | {"components":{"value":[{"value":{"data":{"value":{"text":{"value":"Divide the following into those who die before the play begins (indicate with a 1), those who die offstage (indicate with a 2), those who die onstage (indicate with a 3):","type":"string"}},"type":"object"},"name":{"value":"Text","type":"string"}},"type":"object"},{"value":{"data":{"value":{"reveals":{"value":{"Yorick":{"value":"all that remains is a skull","type":"string"},"Gertrude":{"value":"accidentally poisoned in the final scene","type":"string"},"Claudius":{"value":"Hamlet finally achieves his aim","type":"string"},"Old Hamlet":{"value":"he’s only a ghost in the play itself","type":"string"},"Polonius":{"value":"behind the arras but still on stage","type":"string"},"Rosencrantz and Guildenstern":{"value":"presumably executed by the English king","type":"string"},"Old Fortinbras":{"value":"slain by Hamlet’s father","type":"string"},"Ophelia":{"value":"she is found drowned","type":"string"}},"type":"object"},"choices":{"value":{"Ophelia":{"value":1,"type":"number"},"Yorick":{"value":0,"type":"number"},"Gertrude":{"value":2,"type":"number"},"Claudius":{"value":2,"type":"number"},"Old Hamlet":{"value":0,"type":"number"},"Polonius":{"value":2,"type":"number"},"Rosencrantz and Guildenstern":{"value":1,"type":"number"},"Old Fortinbras":{"value":0,"type":"number"}},"type":"object"},"categories":{"value":[{"value":"Before Play Begins","type":"string"},{"value":"Offstage","type":"string"},{"value":"Onstage","type":"string"}],"type":"array"}},"type":"object"},"name":{"value":"Sort","type":"string"}},"type":"object"}],"type":"array"},"number":{"value":4,"type":"number"}} -------------------------------------------------------------------------------- /firebase/backup/bricks/08x0CLQKs5JIy6VGD7yO/questions/hYdrOkTHbl09G8OAPyFl/hYdrOkTHbl09G8OAPyFl.json: -------------------------------------------------------------------------------- 1 | {"components":{"value":[{"value":{"data":{"value":{"text":{"value":"Rank the following in terms of their status in terms of authority and social position, with highest at 1 and lowest at 8.","type":"string"}},"type":"object"},"name":{"value":"Text","type":"string"}},"type":"object"},{"value":{"data":{"value":{"reveals":{"value":[{"value":"even a villainous king is top of the pile","type":"string"},{"value":"retains high status as queen by marrying Claudius","type":"string"},{"value":"still a royal prince, even if he hasn’t succeeded his father","type":"string"},{"value":"Claudius’s trusted advisor – a minister of state","type":"string"},{"value":"son of a senior minister, so an influential courtier","type":"string"},{"value":"daughter of a senior minister, a respected presence","type":"string"},{"value":"a junior courtier but familiar with the Prince","type":"string"},{"value":"itinerant performers have no status at all in court","type":"string"}],"type":"array"},"choices":{"value":[{"value":"Claudius","type":"string"},{"value":"Gertrude","type":"string"},{"value":"Hamlet","type":"string"},{"value":"Polonius","type":"string"},{"value":"Laertes","type":"string"},{"value":"Ophelia","type":"string"},{"value":"Osric","type":"string"},{"value":"Actors","type":"string"}],"type":"array"}},"type":"object"},"name":{"value":"Order","type":"string"}},"type":"object"}],"type":"array"},"number":{"value":3,"type":"number"}} -------------------------------------------------------------------------------- /firebase/backup/bricks/08x0CLQKs5JIy6VGD7yO/questions/jISSuP35vHeYfvkbKKo0/jISSuP35vHeYfvkbKKo0.json: -------------------------------------------------------------------------------- 1 | {"components":{"value":[{"value":{"data":{"value":{"text":{"value":"Which of the following characters form natural pairs? Move the names on the right to sit alongside the characters on the left with whom they are most closely associated.","type":"string"}},"type":"object"},"name":{"value":"Text","type":"string"}},"type":"object"},{"value":{"data":{"value":{"categories":{"value":[{"value":{"choices":{"value":[{"value":"Rosencrantz","type":"string"},{"value":"Voltemand","type":"string"},{"value":"Horatio","type":"string"},{"value":"Marcellus","type":"string"}],"type":"array"}},"type":"object"},{"value":{"choices":{"value":[{"value":"Guildenstern","type":"string"},{"value":"Cornelius","type":"string"},{"value":"Hamlet","type":"string"},{"value":"Barnardo","type":"string"}],"type":"array"}},"type":"object"}],"type":"array"},"reveals":{"value":[{"value":"Hamlet’s old uni pals","type":"string"},{"value":"sentries at Elsinore","type":"string"},{"value":"loyal, if unequal, friends","type":"string"},{"value":"Danish ambassadors","type":"string"}],"type":"array"}},"type":"object"},"name":{"value":"Arrow","type":"string"}},"type":"object"}],"type":"array"},"number":{"value":7,"type":"number"}} -------------------------------------------------------------------------------- /firebase/backup/bricks/08x0CLQKs5JIy6VGD7yO/questions/kQUUj9kkap2vttW4ZKeY/kQUUj9kkap2vttW4ZKeY.json: -------------------------------------------------------------------------------- 1 | {"components":{"value":[{"value":{"data":{"value":{"text":{"value":"Fortinbras is like Hamlet in some ways but unlike him in others. Identify two characteristics in which they are similar –","type":"string"}},"type":"object"},"name":{"value":"Text","type":"string"}},"type":"object"},{"value":{"data":{"value":{"choices":{"value":[{"value":"throne passes from his father to his uncle","type":"string"},{"value":"intention to avenge his father","type":"string"},{"value":"soldierly","type":"string"},{"value":"funny","type":"string"},{"value":"focussed","type":"string"},{"value":"natural leaders","type":"string"}],"type":"array"},"correctAnswers":{"value":2,"type":"number"},"reveals":{"value":[{"value":"both their fathers were kings, yet neither sons succeeds to the throne","type":"string"},{"value":"Fortinbras’ father was defeated by Old Hamlet – both sons seek redress","type":"string"},{"value":"Hamlet can fence but has never led an army","type":"string"},{"value":"Fortinbras is serious and determined, Hamlet can be witty and playful","type":"string"},{"value":"Hamlet prevaricates and delays; Fortinbras gets on with the job","type":"string"},{"value":"Hamlet only seems to have one close friend and follower","type":"string"}],"type":"array"}},"type":"object"},"name":{"value":"MultipleChoice","type":"string"}},"type":"object"}],"type":"array"},"number":{"value":2,"type":"number"}} -------------------------------------------------------------------------------- /firebase/backup/bricks/08x0CLQKs5JIy6VGD7yO/questions/lvlr7NW1Zdy1Su1yO2kj/lvlr7NW1Zdy1Su1yO2kj.json: -------------------------------------------------------------------------------- 1 | {"components":{"value":[{"value":{"data":{"value":{"reveal":{"value":"the same two characters who struggle in the grave of Ophelia","type":"string"},"text":{"value":"The above list (Question 4) of deaths omits two important characters who also die. Write their names","type":"string"},"entries":{"value":[{"value":{"name":{"value":"Answer","type":"string"},"answer":{"value":"Hamlet","type":"string"}},"type":"object"},{"value":{"name":{"value":"Answer","type":"string"},"answer":{"value":"Horatio","type":"string"}},"type":"object"}],"type":"array"}},"type":"object"},"name":{"value":"ShortAnswer","type":"string"}},"type":"object"}],"type":"array"},"number":{"value":6,"type":"number"}} -------------------------------------------------------------------------------- /firebase/backup/bricks/20min1/20min1.json: -------------------------------------------------------------------------------- 1 | {"summary":{"value":"","type":"string"},"pallet":{"value":{"_referencePath":{"segments":["pallets","demopallet"],"projectId":"learning-fortress","databaseId":"(default)"}},"type":"documentReference"},"totalUsers":{"value":0,"type":"number"},"highScore":{"value":0,"type":"number"},"prep":{"value":"","type":"string"},"creator":{"value":"","type":"string"},"avgScore":{"value":0,"type":"number"},"brief":{"value":"","type":"string"},"title":{"value":"20 Minute Brick 1 Pending","type":"string"},"subject":{"value":"","type":"string"},"creationDate":{"value":"2018-09-09T09:00:00.000Z","type":"timestamp"},"type":{"value":"1","type":"string"}} -------------------------------------------------------------------------------- /firebase/backup/bricks/20min2/20min2.json: -------------------------------------------------------------------------------- 1 | {"creationDate":{"value":"2018-09-09T09:00:00.000Z","type":"timestamp"},"type":{"value":1,"type":"number"},"summary":{"value":"","type":"string"},"pallet":{"value":{"_referencePath":{"segments":["pallets","demopallet"],"projectId":"learning-fortress","databaseId":"(default)"}},"type":"documentReference"},"totalUsers":{"value":0,"type":"number"},"highScore":{"value":0,"type":"number"},"prep":{"value":"","type":"string"},"creator":{"value":"","type":"string"},"avgScore":{"value":0,"type":"number"},"brief":{"value":"","type":"string"},"title":{"value":"20 Minute Brick 2 Pending","type":"string"},"subject":{"value":"","type":"string"}} -------------------------------------------------------------------------------- /firebase/backup/bricks/20min3/20min3.json: -------------------------------------------------------------------------------- 1 | {"creationDate":{"value":"2018-09-09T09:00:00.000Z","type":"timestamp"},"type":{"value":1,"type":"number"},"summary":{"value":"","type":"string"},"pallet":{"value":{"_referencePath":{"segments":["pallets","demopallet"],"projectId":"learning-fortress","databaseId":"(default)"}},"type":"documentReference"},"totalUsers":{"value":0,"type":"number"},"highScore":{"value":0,"type":"number"},"prep":{"value":"","type":"string"},"creator":{"value":"","type":"string"},"avgScore":{"value":0,"type":"number"},"brief":{"value":"","type":"string"},"title":{"value":"20 Minute Brick 3 Pending","type":"string"},"subject":{"value":"","type":"string"}} -------------------------------------------------------------------------------- /firebase/backup/bricks/40min1/40min1.json: -------------------------------------------------------------------------------- 1 | {"brief":{"value":"","type":"string"},"title":{"value":"40 Minute Brick 1 Pending","type":"string"},"subject":{"value":"","type":"string"},"creationDate":{"value":"2018-09-09T09:00:00.000Z","type":"timestamp"},"type":{"value":2,"type":"number"},"summary":{"value":"","type":"string"},"pallet":{"value":{"_referencePath":{"segments":["pallets","demopallet"],"projectId":"learning-fortress","databaseId":"(default)"}},"type":"documentReference"},"totalUsers":{"value":0,"type":"number"},"highScore":{"value":0,"type":"number"},"prep":{"value":"","type":"string"},"creator":{"value":"","type":"string"},"avgScore":{"value":0,"type":"number"}} -------------------------------------------------------------------------------- /firebase/backup/bricks/40min2/40min2.json: -------------------------------------------------------------------------------- 1 | {"totalUsers":{"value":0,"type":"number"},"highScore":{"value":0,"type":"number"},"prep":{"value":"","type":"string"},"creator":{"value":"","type":"string"},"avgScore":{"value":0,"type":"number"},"brief":{"value":"","type":"string"},"title":{"value":"Maths Brick Pending","type":"string"},"subject":{"value":"","type":"string"},"creationDate":{"value":"2018-09-09T09:00:00.000Z","type":"timestamp"},"type":{"value":2,"type":"number"},"summary":{"value":"","type":"string"},"pallet":{"value":{"_referencePath":{"segments":["pallets","demopallet"],"projectId":"learning-fortress","databaseId":"(default)"}},"type":"documentReference"}} -------------------------------------------------------------------------------- /firebase/backup/bricks/Arrow/Arrow.json: -------------------------------------------------------------------------------- 1 | {"type":{"value":1,"type":"number"},"summary":{"value":"This is a summary","type":"string"},"pallet":{"value":{"_referencePath":{"segments":["pallets","testpallet1"],"projectId":"learning-fortress","databaseId":"(default)"}},"type":"documentReference"},"totalUsers":{"value":0,"type":"number"},"highScore":{"value":0,"type":"number"},"prep":{"value":"","type":"string"},"creator":{"value":"","type":"string"},"avgScore":{"value":0,"type":"number"},"brief":{"value":"This is the arrow question","type":"string"},"title":{"value":"1. Arrow","type":"string"},"subject":{"value":"","type":"string"},"creationDate":{"value":"2018-09-03T09:00:00.000Z","type":"timestamp"}} -------------------------------------------------------------------------------- /firebase/backup/bricks/Arrow/questions/Arrow/Arrow.json: -------------------------------------------------------------------------------- 1 | {"components":{"value":[{"value":{"name":{"value":"Text","type":"string"},"data":{"value":{"text":{"value":"Arrow Question
this is a second line","type":"string"}},"type":"object"}},"type":"object"},{"value":{"data":{"value":{"categories":{"value":[{"value":{"choices":{"value":[{"value":"choice 1","type":"string"},{"value":"choice 2","type":"string"},{"value":"Choice 3","type":"string"}],"type":"array"}},"type":"object"},{"value":{"choices":{"value":[{"value":"choice 1","type":"string"},{"value":"choice 2","type":"string"},{"value":"choice 3","type":"string"}],"type":"array"}},"type":"object"}],"type":"array"},"reveals":{"value":[{"value":"This is a reveal","type":"string"},{"value":"This is another reveal","type":"string"}],"type":"array"}},"type":"object"},"name":{"value":"Arrow","type":"string"}},"type":"object"}],"type":"array"},"number":{"value":1,"type":"number"}} -------------------------------------------------------------------------------- /firebase/backup/bricks/HorizontalShuffle/HorizontalShuffle.json: -------------------------------------------------------------------------------- 1 | {"totalUsers":{"value":0,"type":"number"},"highScore":{"value":0,"type":"number"},"prep":{"value":"","type":"string"},"creator":{"value":"","type":"string"},"avgScore":{"value":0,"type":"number"},"brief":{"value":"HorizontalShuffle Brief","type":"string"},"title":{"value":"2. HorizontalShuffle","type":"string"},"subject":{"value":"","type":"string"},"creationDate":{"value":"2018-09-03T09:00:00.000Z","type":"timestamp"},"type":{"value":1,"type":"number"},"summary":{"value":"","type":"string"},"pallet":{"value":{"_referencePath":{"segments":["pallets","testpallet1"],"projectId":"learning-fortress","databaseId":"(default)"}},"type":"documentReference"}} -------------------------------------------------------------------------------- /firebase/backup/bricks/HorizontalShuffle/questions/HorizontalShuffle/HorizontalShuffle.json: -------------------------------------------------------------------------------- 1 | {"components":{"value":[{"value":{"data":{"value":{"text":{"value":"This is some text","type":"string"}},"type":"object"},"name":{"value":"Text","type":"string"}},"type":"object"},{"value":{"data":{"value":{"reveal":{"value":"This is a reveal","type":"string"},"choices":{"value":[{"value":"choice 1","type":"string"},{"value":"choice 2","type":"string"},{"value":"choice 3","type":"string"}],"type":"array"}},"type":"object"},"name":{"value":"HorizontalShuffle","type":"string"}},"type":"object"}],"type":"array"},"number":{"value":1,"type":"number"}} -------------------------------------------------------------------------------- /firebase/backup/bricks/MultipleChoice/MultipleChoice.json: -------------------------------------------------------------------------------- 1 | {"subject":{"value":"","type":"string"},"creationDate":{"value":"2018-09-03T09:00:00.000Z","type":"timestamp"},"type":{"value":1,"type":"number"},"summary":{"value":"","type":"string"},"pallet":{"value":{"_referencePath":{"segments":["pallets","testpallet1"],"projectId":"learning-fortress","databaseId":"(default)"}},"type":"documentReference"},"totalUsers":{"value":0,"type":"number"},"highScore":{"value":0,"type":"number"},"prep":{"value":"","type":"string"},"creator":{"value":"","type":"string"},"avgScore":{"value":0,"type":"number"},"brief":{"value":"MultipleChoice Brief","type":"string"},"title":{"value":"3. MultipleChoice","type":"string"}} -------------------------------------------------------------------------------- /firebase/backup/bricks/MultipleChoice/questions/MultipleChoice/MultipleChoice.json: -------------------------------------------------------------------------------- 1 | {"number":{"value":1,"type":"number"},"components":{"value":[{"value":{"data":{"value":{"name":{"value":"This is a MultipleChoice Question","type":"string"}},"type":"object"},"name":{"value":"Text","type":"string"}},"type":"object"},{"value":{"name":{"value":"MultipleChoice","type":"string"},"data":{"value":{"choices":{"value":[{"value":"choice 1","type":"string"},{"value":"choice 2","type":"string"},{"value":"choice 3","type":"string"}],"type":"array"},"correctAnswers":{"value":2,"type":"number"},"reveals":{"value":[{"value":"reveal 1","type":"string"},{"value":"reveal 2","type":"string"}],"type":"array"}},"type":"object"}},"type":"object"}],"type":"array"}} -------------------------------------------------------------------------------- /firebase/backup/bricks/Order/Order.json: -------------------------------------------------------------------------------- 1 | {"prep":{"value":"","type":"string"},"creator":{"value":"","type":"string"},"avgScore":{"value":0,"type":"number"},"brief":{"value":"Order Brief","type":"string"},"title":{"value":"4. Order","type":"string"},"subject":{"value":"","type":"string"},"creationDate":{"value":"2018-09-03T09:00:00.000Z","type":"timestamp"},"type":{"value":1,"type":"number"},"summary":{"value":"","type":"string"},"pallet":{"value":{"_referencePath":{"segments":["pallets","testpallet1"],"projectId":"learning-fortress","databaseId":"(default)"}},"type":"documentReference"},"totalUsers":{"value":0,"type":"number"},"highScore":{"value":0,"type":"number"}} -------------------------------------------------------------------------------- /firebase/backup/bricks/Order/questions/Order/Order.json: -------------------------------------------------------------------------------- 1 | {"number":{"value":1,"type":"number"},"components":{"value":[{"value":{"data":{"value":{"text":{"value":"This is an Order question","type":"string"}},"type":"object"},"name":{"value":"Text","type":"string"}},"type":"object"},{"value":{"data":{"value":{"reveals":{"value":[{"value":"reveal 1","type":"string"},{"value":"reveal 2","type":"string"}],"type":"array"},"choices":{"value":[{"value":"choice 1","type":"string"},{"value":"choice 2","type":"string"}],"type":"array"}},"type":"object"},"name":{"value":"Order","type":"string"}},"type":"object"}],"type":"array"}} -------------------------------------------------------------------------------- /firebase/backup/bricks/ShortAnswer/ShortAnswer.json: -------------------------------------------------------------------------------- 1 | {"summary":{"value":"","type":"string"},"pallet":{"value":{"_referencePath":{"segments":["pallets","testpallet1"],"projectId":"learning-fortress","databaseId":"(default)"}},"type":"documentReference"},"totalUsers":{"value":0,"type":"number"},"highScore":{"value":0,"type":"number"},"prep":{"value":"","type":"string"},"creator":{"value":"","type":"string"},"avgScore":{"value":0,"type":"number"},"brief":{"value":"ShortAnswer Brief","type":"string"},"title":{"value":"5. ShortAnswer","type":"string"},"subject":{"value":"","type":"string"},"creationDate":{"value":"2018-09-03T09:00:00.000Z","type":"timestamp"},"type":{"value":1,"type":"number"}} -------------------------------------------------------------------------------- /firebase/backup/bricks/ShortAnswer/questions/ShortAnswer/ShortAnswer.json: -------------------------------------------------------------------------------- 1 | {"components":{"value":[{"value":{"data":{"value":{"text":{"value":"This is a ShortAnswer questions","type":"string"},"entries":{"value":[{"value":{"name":{"value":"Type 'red'","type":"string"},"answer":{"value":"red","type":"string"}},"type":"object"},{"value":{"name":{"value":"Type 'green'","type":"string"},"answer":{"value":"green","type":"string"}},"type":"object"}],"type":"array"},"reveal":{"value":"This is a reveal","type":"string"}},"type":"object"},"name":{"value":"ShortAnswer","type":"string"}},"type":"object"}],"type":"array"},"number":{"value":1,"type":"number"}} -------------------------------------------------------------------------------- /firebase/backup/bricks/SingleChoice/SingleChoice.json: -------------------------------------------------------------------------------- 1 | {"type":{"value":1,"type":"number"},"summary":{"value":"","type":"string"},"pallet":{"value":{"_referencePath":{"segments":["pallets","testpallet1"],"projectId":"learning-fortress","databaseId":"(default)"}},"type":"documentReference"},"totalUsers":{"value":0,"type":"number"},"highScore":{"value":0,"type":"number"},"prep":{"value":"","type":"string"},"creator":{"value":"","type":"string"},"avgScore":{"value":0,"type":"number"},"brief":{"value":"SingleChoice Brief","type":"string"},"title":{"value":"6. SingleChoice","type":"string"},"subject":{"value":"","type":"string"},"creationDate":{"value":"2018-09-02T09:00:00.000Z","type":"timestamp"}} -------------------------------------------------------------------------------- /firebase/backup/bricks/SingleChoice/questions/SingleChoice/SingleChoice.json: -------------------------------------------------------------------------------- 1 | {"components":{"value":[{"value":{"name":{"value":"Text","type":"string"},"data":{"value":{"text":{"value":"This text should appear in the question","type":"string"}},"type":"object"}},"type":"object"},{"value":{"data":{"value":{"choices":{"value":[{"value":"choice 1","type":"string"},{"value":"choice 2","type":"string"},{"value":"choice 3","type":"string"}],"type":"array"},"reveals":{"value":[{"value":"reveal 1","type":"string"}],"type":"array"}},"type":"object"},"name":{"value":"SingleChoice","type":"string"}},"type":"object"}],"type":"array"},"number":{"value":1,"type":"number"}} -------------------------------------------------------------------------------- /firebase/backup/bricks/Sort/Sort.json: -------------------------------------------------------------------------------- 1 | {"totalUsers":{"value":0,"type":"number"},"highScore":{"value":0,"type":"number"},"prep":{"value":"","type":"string"},"creator":{"value":"","type":"string"},"avgScore":{"value":0,"type":"number"},"brief":{"value":"Sort Brief","type":"string"},"title":{"value":"7. Sort","type":"string"},"subject":{"value":"","type":"string"},"creationDate":{"value":"2018-09-03T09:00:00.000Z","type":"timestamp"},"type":{"value":1,"type":"number"},"summary":{"value":"","type":"string"},"pallet":{"value":{"_referencePath":{"segments":["pallets","testpallet1"],"projectId":"learning-fortress","databaseId":"(default)"}},"type":"documentReference"}} -------------------------------------------------------------------------------- /firebase/backup/bricks/Sort/questions/sort/sort.json: -------------------------------------------------------------------------------- 1 | {"components":{"value":[{"value":{"data":{"value":{"text":{"value":"This is a Sort question","type":"string"}},"type":"object"},"name":{"value":"Text","type":"string"}},"type":"object"},{"value":{"data":{"value":{"choices":{"value":{"This is another choice":{"value":"1","type":"string"},"This is a choice":{"value":"0","type":"string"}},"type":"object"},"categories":{"value":[{"value":"Category 1","type":"string"},{"value":"Category 2","type":"string"}],"type":"array"},"reveals":{"value":{"This is a choice":{"value":"this is a reveal","type":"string"},"This is another choice":{"value":"this is another reveal","type":"string"}},"type":"object"}},"type":"object"},"name":{"value":"Sort","type":"string"}},"type":"object"}],"type":"array"},"number":{"value":1,"type":"number"}} -------------------------------------------------------------------------------- /firebase/backup/bricks/TextHighlighting/TextHighlighting.json: -------------------------------------------------------------------------------- 1 | {"brief":{"value":"TextHighlighting Brief","type":"string"},"title":{"value":"8. TextHighlighting","type":"string"},"subject":{"value":"","type":"string"},"creationDate":{"value":"2018-09-03T09:00:00.000Z","type":"timestamp"},"type":{"value":1,"type":"number"},"summary":{"value":"","type":"string"},"pallet":{"value":{"_referencePath":{"segments":["pallets","testpallet1"],"projectId":"learning-fortress","databaseId":"(default)"}},"type":"documentReference"},"totalUsers":{"value":0,"type":"number"},"highScore":{"value":0,"type":"number"},"prep":{"value":"","type":"string"},"creator":{"value":"","type":"string"},"avgScore":{"value":0,"type":"number"}} -------------------------------------------------------------------------------- /firebase/backup/bricks/TextHighlighting/questions/TextHighlighting/TextHighlighting.json: -------------------------------------------------------------------------------- 1 | {"number":{"value":1,"type":"number"},"components":{"value":[{"value":{"data":{"value":{"text":{"value":"This is a TextHighlighting question","type":"string"}},"type":"object"},"name":{"value":"Text","type":"string"}},"type":"object"},{"value":{"data":{"value":{"reveal":{"value":"This is a reveal","type":"string"},"words":{"value":[{"value":0,"type":"number"},{"value":2,"type":"number"}],"type":"array"},"text":{"value":"Select the first and third words of this sentence.","type":"string"}},"type":"object"},"name":{"value":"TextHighlighting","type":"string"}},"type":"object"}],"type":"array"}} -------------------------------------------------------------------------------- /firebase/backup/bricks/lh0pzfSRgVBSZ8UBaDJb/lh0pzfSRgVBSZ8UBaDJb.json: -------------------------------------------------------------------------------- 1 | {"totalUsers":{"value":3126,"type":"number"},"highScore":{"value":"96.3","type":"string"},"prep":{"value":"Spend up to five minutes reviewing the dramatis personae (presented in the page preceding the play) or at https://www.bartleby.com/70/4201.html","type":"string"},"creator":{"value":"Josephelvet","type":"string"},"avgScore":{"value":62.3,"type":"number"},"brief":{"value":"Exploring friendships, partnerships and relationships in the play and the extent to which individuals are connected to or isolated from those around them. !","type":"string"},"title":{"value":"Isolation and Companionship","type":"string"},"subject":{"value":"English Literature","type":"string"},"creationDate":{"value":"2018-05-10T23:00:00.000Z","type":"timestamp"},"type":{"value":2,"type":"number"},"summary":{"value":"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla elementum ex augue, eget ullamcorper velit euismod et. Vivamus gravida lectus vitae rhoncus faucibus. Etiam pellentesque lectus ut orci interdum congue. Curabitur neque libero, ullamcorper sed ultrices ac, placerat eu nunc. Proin luctus porttitor sem, sit amet facilisis justo sodales bibendum. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse eget dolor ac ipsum dictum fermentum. Donec euismod diam id leo iaculis scelerisque non quis turpis. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Phasellus lobortis volutpat libero ut scelerisque. Donec enim urna, condimentum eget mattis quis, convallis eget lectus. Sed vehicula mauris et nisi tristique, ut pretium quam accumsan. Sed posuere tincidunt enim bibendum varius. Phasellus risus odio, iaculis ac laoreet a, consequat ut ante. Aliquam vel odio lobortis, lacinia nunc rhoncus, porttitor justo. Suspendisse potenti. Integer quis lacus nisl. Morbi ut neque bibendum, bibendum magna porta, fringilla nulla. Duis ullamcorper augue posuere enim fringilla, in egestas ligula ultrices. Suspendisse a odio non enim maximus consectetur. Vivamus faucibus porta neque blandit ultricies. Mauris tincidunt orci a ipsum molestie, quis ultricies metus condimentum. Fusce sem massa, auctor a accumsan sed, accumsan non quam. Praesent malesuada vel quam at vehicula. Proin pharetra arcu eget nisl imperdiet convallis. Donec elementum massa non eros aliquam bibendum. Duis quis condimentum urna. Quisque rhoncus, nunc pharetra sagittis ultricies, sapien sapien fringilla risus, at tincidunt lectus nisi eget purus. Phasellus luctus pellentesque sapien sed fermentum. Phasellus accumsan eget est eu gravida. Curabitur.","type":"string"},"pallet":{"value":{"_referencePath":{"segments":["pallets","bsjsJllNgYkos0w3Wrtv"],"projectId":"learning-fortress","databaseId":"(default)"}},"type":"documentReference"}} -------------------------------------------------------------------------------- /firebase/backup/bricks/lh0pzfSRgVBSZ8UBaDJb/questions/tN4g9IyPzC3SdM2cAfGB/tN4g9IyPzC3SdM2cAfGB.json: -------------------------------------------------------------------------------- 1 | {"components":{"value":[{"value":{"data":{"value":{"text":{"value":"The following list contains four of Hamlet’s true attributes but also an attribute which is not strictly true. Identify the odd one out – ","type":"string"}},"type":"object"},"name":{"value":"Text","type":"string"}},"type":"object"},{"value":{"data":{"value":{"choices":{"value":[{"value":"an actor","type":"string"},{"value":"a bachelor","type":"string"},{"value":"an only child","type":"string"},{"value":"a royal prince","type":"string"},{"value":"a scholar","type":"string"}],"type":"array"},"reveals":{"value":[{"value":"an affinity for actors, perhaps, but he’s not one of the ‘players’","type":"string"},{"value":"certainly not married to Ophelia – even if Gertrude likes the idea","type":"string"},{"value":"who likes to get all the attention and might be a teeny bit spoiled","type":"string"},{"value":"“the expectation and rose of the fair state” as Ophelia calls him","type":"string"},{"value":"intellectually lively – his alma mater is the University of Wittenberg","type":"string"}],"type":"array"}},"type":"object"},"name":{"value":"SingleChoice","type":"string"}},"type":"object"}],"type":"array"},"number":{"value":1,"type":"number"}} -------------------------------------------------------------------------------- /firebase/backup/bricks/milton/questions/1/1.json: -------------------------------------------------------------------------------- 1 | {"components":{"value":[{"value":{"data":{"value":{"text":{"value":"

Some words in the poem are either somewhat archaic to a modern ear or would have carried different meanings in the 17th century than they do today. For example, “lest” in line six might be defined as “in case”. Eight other words are highlighted in green. Below the poem, move these words into line with the highlighted word from the poem to which they correspond:

","type":"string"}},"type":"object"},"name":{"value":"Text","type":"string"}},"type":"object"},{"value":{"data":{"value":{"text":{"value":"When I consider how my light is spent,
Ere half my days in this dark world and wide,
And that one talent which is death to hide
Lodged with me useless, though my soul more bent
To serve therewith my Maker, and present
My true account, lest He returning chide;
“Doth God exact day-labour, light denied?”
I fondly ask. But Patience, to pevent
That murmur, soon replies, “God doth not need before
Either man’s work or His own gifts. Who best
Bear His mild yoke, they serve Him best. His state
Is kingly: thousands at His bidding speed,
And post o’er land and ocean without rest;
They also serve who only stand and wait.”","type":"string"}},"type":"object"},"name":{"value":"Poem","type":"string"}},"type":"object"},{"value":{"data":{"value":{"reveals":{"value":[{"value":"","type":"string"},{"value":"","type":"string"},{"value":"","type":"string"},{"value":"","type":"string"},{"value":"","type":"string"},{"value":"","type":"string"},{"value":"","type":"string"},{"value":"","type":"string"}],"type":"array"},"categories":{"value":[{"value":{"choices":{"value":[{"value":"exact","type":"string"},{"value":"murmur","type":"string"},{"value":"fondly","type":"string"},{"value":"chide","type":"string"},{"value":"post","type":"string"},{"value":"ere","type":"string"},{"value":"bent","type":"string"},{"value":"yoke","type":"string"}],"type":"array"}},"type":"object"},{"value":{"choices":{"value":[{"value":"demand","type":"string"},{"value":"discontent","type":"string"},{"value":"foolishly","type":"string"},{"value":"reproach (me)","type":"string"},{"value":"hurry","type":"string"},{"value":"before","type":"string"},{"value":"inclined","type":"string"},{"value":"harness","type":"string"}],"type":"array"}},"type":"object"}],"type":"array"}},"type":"object"},"name":{"value":"Arrow","type":"string"}},"type":"object"},{"value":{"data":{"value":{"text":{"value":"Milton’s compression is noteworthy – most of these words are single syllables, and all of the definitions have more than one syllable. Only ‘exact’ is not metrically shorter than the modern equivalent.","type":"string"}},"type":"object"},"name":{"value":"Reveal","type":"string"}},"type":"object"}],"type":"array"},"number":{"value":1,"type":"number"}} -------------------------------------------------------------------------------- /firebase/backup/bricks/milton/questions/10/10.json: -------------------------------------------------------------------------------- 1 | {"components":{"value":[{"value":{"name":{"value":"Text","type":"string"},"data":{"value":{"text":{"value":"The Parable of the Talents is very important in Liberation Theology, the radical South American movement which re-read the bible in Marxist terms to focus on social injustice and the plight of the poor. Liberation theologians identify one particular character in the parable as heroic and revolutionary. Which one?","type":"string"}},"type":"object"}},"type":"object"},{"value":{"data":{"value":{"reveals":{"value":[{"value":"speaks truth to power and suffers injustice - after all, he has stolen nothing","type":"string"},{"value":"plays by the rules of the system his master profits from","type":"string"},{"value":"plays by the rules of the system his master profits from","type":"string"},{"value":"seen as the archetype of exploitative capitalist aristocracy","type":"string"}],"type":"array"},"choices":{"value":[{"value":"the third servant","type":"string"},{"value":"the second servant","type":"string"},{"value":"the first servant","type":"string"},{"value":"the returning lord","type":"string"}],"type":"array"}},"type":"object"},"name":{"value":"SingleChoice","type":"string"}},"type":"object"}],"type":"array"},"number":{"value":10,"type":"number"}} -------------------------------------------------------------------------------- /firebase/backup/bricks/milton/questions/11/11.json: -------------------------------------------------------------------------------- 1 | {"number":{"value":11,"type":"number"},"components":{"value":[{"value":{"name":{"value":"Text","type":"string"},"data":{"value":{"text":{"value":"The sonnet is Petrarchan in form, its fourteen lines consisting of an octave followed by a sestet. In the boxes provided, write the possessive adjective (or determiner) which recurs frequently in each part of the poem:","type":"string"}},"type":"object"}},"type":"object"},{"value":{"data":{"value":{"text":{"value":"When I consider how my light is spent,
Ere half my days in this dark world and wide,
And that one talent which is death to hide
Lodged with me useless, though my soul more bent
To serve therewith my Maker, and present
My true account, lest He returning chide;
“Doth God exact day-labour, light denied?”
I fondly ask. But Patience, to prevent
","type":"string"}},"type":"object"},"name":{"value":"Poem","type":"string"}},"type":"object"},{"value":{"data":{"value":{"reveal":{"value":"","type":"string"},"text":{"value":"","type":"string"},"entries":{"value":[{"value":{"answer":{"value":"my","type":"string"},"name":{"value":"OCTAVE","type":"string"}},"type":"object"}],"type":"array"}},"type":"object"},"name":{"value":"ShortAnswer","type":"string"}},"type":"object"},{"value":{"data":{"value":{"text":{"value":"That murmur, soon replies, “God doth not need before
Either man’s work or His own gifts. Who best
Bear His mild yoke, they serve Him best. His state
Is kingly: thousands at His bidding speed,
And post o’er land and ocean without rest;
They also serve who only stand and wait.”","type":"string"}},"type":"object"},"name":{"value":"Poem","type":"string"}},"type":"object"},{"value":{"data":{"value":{"reveal":{"value":"","type":"string"},"text":{"value":"","type":"string"},"entries":{"value":[{"value":{"name":{"value":"SESTET","type":"string"},"answer":{"value":"His","type":"string"}},"type":"object"}],"type":"array"}},"type":"object"},"name":{"value":"ShortAnswer","type":"string"}},"type":"object"}],"type":"array"}} -------------------------------------------------------------------------------- /firebase/backup/bricks/milton/questions/12/12.json: -------------------------------------------------------------------------------- 1 | {"components":{"value":[{"value":{"data":{"value":{"text":{"value":"question 12","type":"string"}},"type":"object"},"name":{"value":"Text","type":"string"}},"type":"object"}],"type":"array"}} -------------------------------------------------------------------------------- /firebase/backup/bricks/milton/questions/13/13.json: -------------------------------------------------------------------------------- 1 | {"components":{"value":[{"value":{"data":{"value":{"text":{"value":"In the left-hand column, seven words from the passage are listed. Move the definitions on the right-hand side to align with the correct word.","type":"string"}},"type":"object"},"name":{"value":"Text","type":"string"}},"type":"object"},{"value":{"data":{"value":{"categories":{"value":[{"value":{"choices":{"value":[{"value":"Tiresias","type":"string"},{"value":"Popery","type":"string"},{"value":"Stuarts","type":"string"},{"value":"Restoration","type":"string"},{"value":"patriach","type":"string"},{"value":"Expulsion","type":"string"},{"value":"Puritan","type":"string"}],"type":"array"}},"type":"object"},{"value":{"choices":{"value":[{"value":"the blind prophet","type":"string"},{"value":"pejorative word for Roman Catholicism","type":"string"},{"value":"the royal family to which Charles 1st and his son belonged","type":"string"},{"value":"After Cromwell, Parliament re-introduced monarchy, crowning Charles 2nd on 'constitutional' terms","type":"string"},{"value":"the male head of a family, tribe or people","type":"string"},{"value":"after the Fall, the casting out of Adam and Eve out of Eden","type":"string"},{"value":"strict religious group who sought to 'purify' the Church of England from the taints of Catholicism","type":"string"}],"type":"array"}},"type":"object"}],"type":"array"},"reveals":{"value":[{"value":"","type":"string"},{"value":"","type":"string"}],"type":"array"}},"type":"object"},"name":{"value":"Arrow","type":"string"}},"type":"object"}],"type":"array"},"number":{"value":13,"type":"number"}} -------------------------------------------------------------------------------- /firebase/backup/bricks/milton/questions/14/14.json: -------------------------------------------------------------------------------- 1 | {"components":{"value":[{"value":{"data":{"value":{"text":{"value":"question 14","type":"string"}},"type":"object"},"name":{"value":"Text","type":"string"}},"type":"object"}],"type":"array"}} -------------------------------------------------------------------------------- /firebase/backup/bricks/milton/questions/2/2.json: -------------------------------------------------------------------------------- 1 | {"number":{"value":2,"type":"number"},"components":{"value":[{"value":{"data":{"value":{"text":{"value":"Now write the word which Milton uses twice as a metaphor for sight:","type":"string"}},"type":"object"},"name":{"value":"Text","type":"string"}},"type":"object"},{"value":{"data":{"value":{"reveal":{"value":"rhymes with sight","type":"string"},"text":{"value":"","type":"string"},"entries":{"value":[{"value":{"answer":{"value":"light","type":"string"},"name":{"value":"","type":"string"}},"type":"object"}],"type":"array"}},"type":"object"},"name":{"value":"ShortAnswer","type":"string"}},"type":"object"},{"value":{"data":{"value":{"text":{"value":"rhymes with sight","type":"string"}},"type":"object"},"name":{"value":"Reveal","type":"string"}},"type":"object"}],"type":"array"}} -------------------------------------------------------------------------------- /firebase/backup/bricks/milton/questions/3/3.json: -------------------------------------------------------------------------------- 1 | {"components":{"value":[{"value":{"data":{"value":{"text":{"value":"Below are six grievances, only four of which are expressed by Milton in the poem. Identify the two grievances which he does not express.","type":"string"}},"type":"object"},"name":{"value":"Text","type":"string"}},"type":"object"},{"value":{"data":{"value":{"reveals":{"value":[{"value":"the natural world is hardly suggested in the poem","type":"string"},{"value":"he seems to fear for his gift rather than his independence","type":"string"},{"value":"only \"half his days\" have been lived - he is in his prime","type":"string"},{"value":"his \"one talent\" suggests a special gift","type":"string"},{"value":"his \"true account\" is what his Maker expects of him","type":"string"},{"value":"\"spent\" is emphatic - it's gone","type":"string"}],"type":"array"},"choices":{"value":[{"value":"His blindness prevents him from seeing the beauties of Nature.","type":"string"},{"value":"His blindness makes him dependent on others.","type":"string"},{"value":"His blindness is premature and untimely.","type":"string"},{"value":"His blindness leaves him unable to do what he does best.","type":"string"},{"value":"His blindness undermines God’s intended purpose for him.","type":"string"},{"value":"His blindness cannot be undone.","type":"string"}],"type":"array"},"correctAnswers":{"value":2,"type":"number"}},"type":"object"},"name":{"value":"MultipleChoice","type":"string"}},"type":"object"},{"value":{"data":{"value":{"text":{"value":"NB Milton may have been more concerned with the effect of blindness on his parliamentary career – his ability to “serve” Cromwell’s government as Secretary for Foreign Tongues – than on his poetry. In fact, he continued in his government post (helped by able assistants like Andrew Marvell) and continued to compose poetry (helped by his daughters).","type":"string"}},"type":"object"},"name":{"value":"Text","type":"string"}},"type":"object"}],"type":"array"},"number":{"value":3,"type":"number"}} -------------------------------------------------------------------------------- /firebase/backup/bricks/milton/questions/4/4.json: -------------------------------------------------------------------------------- 1 | {"components":{"value":[{"value":{"data":{"value":{"text":{"value":"Much of the poem’s language relates directly to the Parable of the Talents. Two rhyming words relate closely to the parable. Write them in the order in which they appear –","type":"string"}},"type":"object"},"name":{"value":"Text","type":"string"}},"type":"object"},{"value":{"data":{"value":{"text":{"value":"","type":"string"},"entries":{"value":[{"value":{"answer":{"value":"hide","type":"string"},"name":{"value":"","type":"string"}},"type":"object"},{"value":{"name":{"value":"","type":"string"},"answer":{"value":"chide","type":"string"}},"type":"object"}],"type":"array"},"reveal":{"value":"","type":"string"}},"type":"object"},"name":{"value":"ShortAnswer","type":"string"}},"type":"object"},{"value":{"data":{"value":{"text":{"value":"they also rhyme with \"wide\" and \"chide\"","type":"string"}},"type":"object"},"name":{"value":"Reveal","type":"string"}},"type":"object"}],"type":"array"},"number":{"value":4,"type":"number"}} -------------------------------------------------------------------------------- /firebase/backup/bricks/milton/questions/5/5.json: -------------------------------------------------------------------------------- 1 | {"components":{"value":[{"value":{"name":{"value":"Text","type":"string"},"data":{"value":{"text":{"value":"Several words in the poem convey the poet’s anguish, bitterness and frustration. Other words are implicitly constructive and hopeful. Sort the following into two groups of four words -","type":"string"}},"type":"object"}},"type":"object"},{"value":{"data":{"value":{"choices":{"value":{"soul":{"value":0,"type":"number"},"useless":{"value":1,"type":"number"},"death":{"value":1,"type":"number"},"denied":{"value":1,"type":"number"},"speed":{"value":0,"type":"number"},"spent":{"value":1,"type":"number"},"true":{"value":0,"type":"number"},"mild":{"value":0,"type":"number"}},"type":"object"},"categories":{"value":[{"value":"hopeful","type":"string"},{"value":"pessimistic","type":"string"}],"type":"array"}},"type":"object"},"name":{"value":"Sort","type":"string"}},"type":"object"},{"value":{"data":{"value":{"text":{"value":"most of the anguish is in the first part of the poem","type":"string"}},"type":"object"},"name":{"value":"Reveal","type":"string"}},"type":"object"}],"type":"array"},"number":{"value":5,"type":"number"}} -------------------------------------------------------------------------------- /firebase/backup/bricks/milton/questions/6/6.json: -------------------------------------------------------------------------------- 1 | {"components":{"value":[{"value":{"data":{"value":{"text":{"value":"The poem takes the form of a dialogue. Critics have offered interesting ideas about the nature and identity of Patience, the voice which addresses Milton. Which of the following interpretations is most easily supported by the evidence of the text?","type":"string"}},"type":"object"},"name":{"value":"Text","type":"string"}},"type":"object"},{"value":{"data":{"value":{"choices":{"value":[{"value":"the personified voice of the heavenly virtue, Patience","type":"string"},{"value":"the visitation of an angel","type":"string"},{"value":"the voice of God","type":"string"},{"value":"the dramatized voice of the poet’s conscience, or better self","type":"string"}],"type":"array"},"reveals":{"value":[{"value":"capitalisation commonly connotes personification","type":"string"},{"value":"there is no explicit reference to angels","type":"string"},{"value":"God is referred to in the 3rd person","type":"string"},{"value":"sounds more Modernist than 17th Century.","type":"string"}],"type":"array"}},"type":"object"},"name":{"value":"SingleChoice","type":"string"}},"type":"object"}],"type":"array"},"number":{"value":6,"type":"number"}} -------------------------------------------------------------------------------- /firebase/backup/bricks/milton/questions/7/7.json: -------------------------------------------------------------------------------- 1 | {"components":{"value":[{"value":{"data":{"value":{"text":{"value":"The phrase “His state is kingly” can be read as an expression of God’s majesty and authority. But the word “kingly” is problematic in the context of Milton’s political life, particularly at the time of the sonnet’s composition in early-to-mid 1650s. This task helps you find out why. Use your judgement to interpret the descriptions incorrectly listed in the right hand column, then align them with the correct dates in the left hand column.","type":"string"}},"type":"object"},"name":{"value":"Text","type":"string"}},"type":"object"},{"value":{"data":{"value":{"reveals":{"value":[{"value":"","type":"string"},{"value":"","type":"string"},{"value":"","type":"string"},{"value":"","type":"string"},{"value":"","type":"string"},{"value":"","type":"string"},{"value":"","type":"string"},{"value":"","type":"string"},{"value":"","type":"string"},{"value":"","type":"string"},{"value":"","type":"string"},{"value":"","type":"string"}],"type":"array"},"categories":{"value":[{"value":{"choices":{"value":[{"value":"1615-1632","type":"string"},{"value":"1638-9","type":"string"},{"value":"1640","type":"string"},{"value":"1642","type":"string"},{"value":"1642-6","type":"string"},{"value":"1658-64","type":"string"},{"value":"1649-1660","type":"string"},{"value":"January 1649","type":"string"},{"value":"March 1649","type":"string"},{"value":"October 1649","type":"string"},{"value":"1652-5","type":"string"},{"value":"1660","type":"string"}],"type":"array"}},"type":"object"},{"value":{"choices":{"value":[{"value":"Educated at St Paul’s School and Christ’s College Cambridge","type":"string"},{"value":"As a young man, travels for a year in France and Italy","type":"string"},{"value":"Two years before the Civil Wars, he begins writing tracts for Puritan and Parliamentary causes","type":"string"},{"value":"Outbreak of the English Civil Wars (finally concluding, 1651)","type":"string"},{"value":"During the first phase of the Civil Wars, around 1644, Milton begins to lose his sight","type":"string"},{"value":"Commonwealth (Republican) Government lasts for more than a decade, ending two years after Oliver Cromwell’s death","type":"string"},{"value":"Execution of Charles 1st","type":"string"},{"value":"Appointed Secretary for Foreign Tongues, serving until 1660","type":"string"},{"value":"Within a few months of his appointment, publishes Eikonoklastes, his defence of the execution","type":"string"},{"value":"Around this time, by now completely blind, Milton composes ‘When I consider how my light is spent’","type":"string"},{"value":"Composition of Paradise Lost, first published in 1667","type":"string"},{"value":"Coronation of Charles II – the Restoration of the Monarchy","type":"string"}],"type":"array"}},"type":"object"}],"type":"array"}},"type":"object"},"name":{"value":"Arrow","type":"string"}},"type":"object"},{"value":{"data":{"value":{"text":{"value":"Charles II was crowned eleven years and three months after his father was executed.","type":"string"}},"type":"object"},"name":{"value":"Reveal","type":"string"}},"type":"object"}],"type":"array"},"number":{"value":7,"type":"number"}} -------------------------------------------------------------------------------- /firebase/backup/bricks/milton/questions/8/8.json: -------------------------------------------------------------------------------- 1 | {"components":{"value":[{"value":{"data":{"value":{"text":{"value":"Sonnets frequently have a turn – a point where a tone, or point of view begins to be countered or challenged by an alternative view. This sonnet has a very clear turn. Highlight the line where you think the turn takes place:","type":"string"}},"type":"object"},"name":{"value":"Text","type":"string"}},"type":"object"},{"value":{"data":{"value":{"text":{"value":"","type":"string"},"reveal":{"value":"","type":"string"},"words":{"value":[{"value":1,"type":"number"}],"type":"array"}},"type":"object"},"name":{"value":"TextHighlighting","type":"string"}},"type":"object"},{"value":{"data":{"value":{"text":{"value":"the transitional line is punctuated to create a break or caesura in the middle","type":"string"}},"type":"object"},"name":{"value":"Reveal","type":"string"}},"type":"object"}],"type":"array"}} -------------------------------------------------------------------------------- /firebase/backup/bricks/milton/questions/9/9.json: -------------------------------------------------------------------------------- 1 | {"components":{"value":[{"value":{"data":{"value":{"text":{"value":"question 9","type":"string"}},"type":"object"},"name":{"value":"Text","type":"string"}},"type":"object"}],"type":"array"}} -------------------------------------------------------------------------------- /firebase/backup/bricks/uYVMc4KbjLaBsBo9V7Wq/uYVMc4KbjLaBsBo9V7Wq.json: -------------------------------------------------------------------------------- 1 | {"totalUsers":{"value":420,"type":"number"},"highScore":{"value":69,"type":"number"},"prep":{"value":"The student should read this preparation text and perform what it says you should do. This text can have links and stuff as well.\n","type":"string"},"creator":{"value":"Josephelvet","type":"string"},"avgScore":{"value":16,"type":"number"},"brief":{"value":"The brief for the brick can go here.\n","type":"string"},"title":{"value":"Test Brick 2","type":"string"},"subject":{"value":"Maths","type":"string"},"creationDate":{"value":"2018-08-30T18:40:20.766Z","type":"timestamp"},"type":{"value":1,"type":"number"},"summary":{"value":"This is the summary and it should be longer than either the brief or prep. It's a really long piece of writing and it goes on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on and on. Hope you enjoyed that!\n","type":"string"},"pallet":{"value":{"_referencePath":{"segments":["pallets","bsjsJllNgYkos0w3Wrtv"],"projectId":"learning-fortress","databaseId":"(default)"}},"type":"documentReference"}} -------------------------------------------------------------------------------- /firebase/backup/classes/Z7vl6btzFHhPede4yZSi/Z7vl6btzFHhPede4yZSi.json: -------------------------------------------------------------------------------- 1 | {"teacher":{"value":{"_referencePath":{"segments":["teachers","pKOncFi85zWuDaOXbWnCW0ha9YN2"],"projectId":"learning-fortress","databaseId":"(default)"}},"type":"documentReference"}} -------------------------------------------------------------------------------- /firebase/backup/classes/Z7vl6btzFHhPede4yZSi/pallets/f0wIFtZ8CDCjvhD4SnFe/f0wIFtZ8CDCjvhD4SnFe.json: -------------------------------------------------------------------------------- 1 | {"pallet":{"value":{"_referencePath":{"segments":["pallets","bsjsJllNgYkos0w3Wrtv"],"projectId":"learning-fortress","databaseId":"(default)"}},"type":"documentReference"}} -------------------------------------------------------------------------------- /firebase/backup/classes/Z7vl6btzFHhPede4yZSi/students/0k5CTSiIHCIIxuzXALo7/0k5CTSiIHCIIxuzXALo7.json: -------------------------------------------------------------------------------- 1 | {"student":{"value":{"_referencePath":{"segments":["students","wYfB9tfvLySPQwvWs1v62DsaQiG3"],"projectId":"learning-fortress","databaseId":"(default)"}},"type":"documentReference"}} -------------------------------------------------------------------------------- /firebase/backup/pallets/bsjsJllNgYkos0w3Wrtv/bricks/6FD2XgVyNJ161KQFBpVo/6FD2XgVyNJ161KQFBpVo.json: -------------------------------------------------------------------------------- 1 | {"brick":{"value":{"_referencePath":{"segments":["bricks","08x0CLQKs5JIy6VGD7yO"],"projectId":"learning-fortress","databaseId":"(default)"}},"type":"documentReference"}} -------------------------------------------------------------------------------- /firebase/backup/pallets/bsjsJllNgYkos0w3Wrtv/bricks/b8nLILCRvFXMCUmcu4NF/b8nLILCRvFXMCUmcu4NF.json: -------------------------------------------------------------------------------- 1 | {"brick":{"value":{"_referencePath":{"segments":["bricks","uYVMc4KbjLaBsBo9V7Wq"],"projectId":"learning-fortress","databaseId":"(default)"}},"type":"documentReference"}} -------------------------------------------------------------------------------- /firebase/backup/pallets/bsjsJllNgYkos0w3Wrtv/bricks/milton/milton.json: -------------------------------------------------------------------------------- 1 | {"brick":{"value":{"_referencePath":{"segments":["bricks","milton"],"projectId":"learning-fortress","databaseId":"(default)"}},"type":"documentReference"}} -------------------------------------------------------------------------------- /firebase/backup/pallets/bsjsJllNgYkos0w3Wrtv/bricks/uAbpscbGDGoVleXCNtQH/uAbpscbGDGoVleXCNtQH.json: -------------------------------------------------------------------------------- 1 | {"brick":{"value":{"_referencePath":{"segments":["bricks","lh0pzfSRgVBSZ8UBaDJb"],"projectId":"learning-fortress","databaseId":"(default)"}},"type":"documentReference"}} -------------------------------------------------------------------------------- /firebase/backup/pallets/bsjsJllNgYkos0w3Wrtv/bsjsJllNgYkos0w3Wrtv.json: -------------------------------------------------------------------------------- 1 | {"name":{"value":"Hamlet","type":"string"},"subject":{"value":"English","type":"string"}} -------------------------------------------------------------------------------- /firebase/backup/pallets/demopallet/bricks/20min1/20min1.json: -------------------------------------------------------------------------------- 1 | {"brick":{"value":{"_referencePath":{"segments":["bricks","20min1"],"projectId":"learning-fortress","databaseId":"(default)"}},"type":"documentReference"}} -------------------------------------------------------------------------------- /firebase/backup/pallets/demopallet/bricks/20min2/20min2.json: -------------------------------------------------------------------------------- 1 | {"brick":{"value":{"_referencePath":{"segments":["bricks","20min2"],"projectId":"learning-fortress","databaseId":"(default)"}},"type":"documentReference"}} -------------------------------------------------------------------------------- /firebase/backup/pallets/demopallet/bricks/20min3/20min3.json: -------------------------------------------------------------------------------- 1 | {"brick":{"value":{"_referencePath":{"segments":["bricks","20min3"],"projectId":"learning-fortress","databaseId":"(default)"}},"type":"documentReference"}} -------------------------------------------------------------------------------- /firebase/backup/pallets/demopallet/bricks/40min1/40min1.json: -------------------------------------------------------------------------------- 1 | {"brick":{"value":{"_referencePath":{"segments":["bricks","40min1"],"projectId":"learning-fortress","databaseId":"(default)"}},"type":"documentReference"}} -------------------------------------------------------------------------------- /firebase/backup/pallets/demopallet/bricks/40min2/40min2.json: -------------------------------------------------------------------------------- 1 | {"brick":{"value":{"_referencePath":{"segments":["bricks","40min2"],"projectId":"learning-fortress","databaseId":"(default)"}},"type":"documentReference"}} -------------------------------------------------------------------------------- /firebase/backup/pallets/demopallet/bricks/milton/milton.json: -------------------------------------------------------------------------------- 1 | {"brick":{"value":{"_referencePath":{"segments":["bricks","milton"],"projectId":"learning-fortress","databaseId":"(default)"}},"type":"documentReference"}} -------------------------------------------------------------------------------- /firebase/backup/pallets/demopallet/demopallet.json: -------------------------------------------------------------------------------- 1 | {"name":{"value":"Demo Pallet","type":"string"},"subject":{"value":"English","type":"string"}} -------------------------------------------------------------------------------- /firebase/backup/pallets/testpallet1/bricks/Arrow/Arrow.json: -------------------------------------------------------------------------------- 1 | {"brick":{"value":{"_referencePath":{"segments":["bricks","Arrow"],"projectId":"learning-fortress","databaseId":"(default)"}},"type":"documentReference"}} -------------------------------------------------------------------------------- /firebase/backup/pallets/testpallet1/bricks/HorizontalShuffle/HorizontalShuffle.json: -------------------------------------------------------------------------------- 1 | {"brick":{"value":{"_referencePath":{"segments":["bricks","HorizontalShuffle"],"projectId":"learning-fortress","databaseId":"(default)"}},"type":"documentReference"}} -------------------------------------------------------------------------------- /firebase/backup/pallets/testpallet1/bricks/MultipleChoice/MultipleChoice.json: -------------------------------------------------------------------------------- 1 | {"brick":{"value":{"_referencePath":{"segments":["bricks","MultipleChoice"],"projectId":"learning-fortress","databaseId":"(default)"}},"type":"documentReference"}} -------------------------------------------------------------------------------- /firebase/backup/pallets/testpallet1/bricks/Order/Order.json: -------------------------------------------------------------------------------- 1 | {"brick":{"value":{"_referencePath":{"segments":["bricks","Order"],"projectId":"learning-fortress","databaseId":"(default)"}},"type":"documentReference"}} -------------------------------------------------------------------------------- /firebase/backup/pallets/testpallet1/bricks/ShortAnswer/ShortAnswer.json: -------------------------------------------------------------------------------- 1 | {"brick":{"value":{"_referencePath":{"segments":["bricks","ShortAnswer"],"projectId":"learning-fortress","databaseId":"(default)"}},"type":"documentReference"}} -------------------------------------------------------------------------------- /firebase/backup/pallets/testpallet1/bricks/SingleChoice/SingleChoice.json: -------------------------------------------------------------------------------- 1 | {"brick":{"value":{"_referencePath":{"segments":["bricks","SingleChoice"],"projectId":"learning-fortress","databaseId":"(default)"}},"type":"documentReference"}} -------------------------------------------------------------------------------- /firebase/backup/pallets/testpallet1/bricks/Sort/Sort.json: -------------------------------------------------------------------------------- 1 | {"brick":{"value":{"_referencePath":{"segments":["bricks","Sort"],"projectId":"learning-fortress","databaseId":"(default)"}},"type":"documentReference"}} -------------------------------------------------------------------------------- /firebase/backup/pallets/testpallet1/bricks/TextHighlighting/TextHighlighting.json: -------------------------------------------------------------------------------- 1 | {"brick":{"value":{"_referencePath":{"segments":["bricks","TextHighlighting"],"projectId":"learning-fortress","databaseId":"(default)"}},"type":"documentReference"}} -------------------------------------------------------------------------------- /firebase/backup/pallets/testpallet1/testpallet1.json: -------------------------------------------------------------------------------- 1 | {"subject":{"value":"English","type":"string"},"name":{"value":"Test Pallet","type":"string"}} -------------------------------------------------------------------------------- /firebase/backup/teachers/pKOncFi85zWuDaOXbWnCW0ha9YN2/classes/OWtS1woe6V52ICQZrKei/OWtS1woe6V52ICQZrKei.json: -------------------------------------------------------------------------------- 1 | {"class":{"value":{"_referencePath":{"segments":["classes","Z7vl6btzFHhPede4yZSi"],"projectId":"learning-fortress","databaseId":"(default)"}},"type":"documentReference"}} -------------------------------------------------------------------------------- /firebase/backup/teachers/pKOncFi85zWuDaOXbWnCW0ha9YN2/pKOncFi85zWuDaOXbWnCW0ha9YN2.json: -------------------------------------------------------------------------------- 1 | {"name":{"value":"Joseph Francis","type":"string"},"uid":{"value":"pKOncFi85zWuDaOXbWnCW0ha9YN2","type":"string"}} -------------------------------------------------------------------------------- /firebase/firebase-functions/.firebaserc: -------------------------------------------------------------------------------- 1 | { 2 | "projects": { 3 | "default": "learning-fortress" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /firebase/firebase-functions/firebase.json: -------------------------------------------------------------------------------- 1 | { 2 | "functions": { 3 | "predeploy": [ 4 | "npm --prefix \"$RESOURCE_DIR\" run lint" 5 | ] 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /firebase/firebase-functions/functions/index.js: -------------------------------------------------------------------------------- 1 | const functions = require('firebase-functions'); 2 | 3 | // // Create and Deploy Your First Cloud Functions 4 | // // https://firebase.google.com/docs/functions/write-firebase-functions 5 | // 6 | // exports.helloWorld = functions.https.onRequest((request, response) => { 7 | // response.send("Hello from Firebase!"); 8 | // }); 9 | 10 | // const admin = require("firebase-admin"); 11 | // admin.initializeApp(); 12 | 13 | // exports.blockSignup = functions.auth.user().onCreate(event => { 14 | // // console.log(event); 15 | // //if (!event.emailVerified) 16 | // return admin.auth().updateUser(event.uid, { disabled: true }) 17 | // .then(userRecord => console.log("Auto blocked user", userRecord.toJSON())) 18 | // .catch(error => console.log("Error auto blocking:", error)); 19 | // }); -------------------------------------------------------------------------------- /firebase/firebase-functions/functions/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "functions", 3 | "description": "Cloud Functions for Firebase", 4 | "scripts": { 5 | "lint": "eslint .", 6 | "serve": "firebase serve --only functions", 7 | "shell": "firebase functions:shell", 8 | "start": "npm run shell", 9 | "deploy": "firebase deploy --only functions", 10 | "logs": "firebase functions:log" 11 | }, 12 | "dependencies": { 13 | "firebase-admin": "~6.0.0", 14 | "firebase-functions": "^2.0.3" 15 | }, 16 | "devDependencies": { 17 | "eslint": "^4.12.0", 18 | "eslint-plugin-promise": "^3.6.0" 19 | }, 20 | "private": true 21 | } 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "learning-fortress-frontend", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "ng": "ng", 6 | "start": "node server.js", 7 | "debug": "ng serve --aot", 8 | "build": "ng build", 9 | "test": "ng test", 10 | "lint": "ng lint", 11 | "e2e": "ng e2e", 12 | "postinstall": "ng build --aot --prod" 13 | }, 14 | "engines": { 15 | "node": "8.11.3", 16 | "npm": "6.1.0" 17 | }, 18 | "private": true, 19 | "dependencies": { 20 | "@angular/animations": "^6.0.7", 21 | "@angular/cdk": "^6.3.1", 22 | "@angular/cli": "~6.0.8", 23 | "@angular/common": "^6.0.3", 24 | "@angular/compiler": "^6.0.3", 25 | "@angular/compiler-cli": "^6.0.3", 26 | "@angular/core": "^6.0.3", 27 | "@angular/flex-layout": "^6.0.0-beta.16", 28 | "@angular/forms": "^6.0.3", 29 | "@angular/http": "^6.0.3", 30 | "@angular/material": "^6.3.1", 31 | "@angular/platform-browser": "^6.0.3", 32 | "@angular/platform-browser-dynamic": "^6.0.3", 33 | "@angular/router": "^6.0.3", 34 | "@types/jquery": "^3.3.6", 35 | "angular-fittext": "^2.1.1", 36 | "angularfire2": "^5.0.0-rc.11", 37 | "body-parser": "^1.18.3", 38 | "core-js": "^2.5.4", 39 | "express": "^4.16.3", 40 | "firebase": "^5.3.0", 41 | "firebaseui": "^3.2.0", 42 | "firebaseui-angular": "^3.1.1", 43 | "ng-dynamic-component": "^3.0.0", 44 | "ng2-dragula": "^2.0.2", 45 | "ngx-pipes": "^2.2.0", 46 | "path": "^0.12.7", 47 | "rxjs": "^6.2.2", 48 | "rxjs-compat": "^6.2.2", 49 | "typescript": "~2.7.2", 50 | "url-parse": "^1.4.3", 51 | "zone.js": "^0.8.26" 52 | }, 53 | "devDependencies": { 54 | "@angular-devkit/build-angular": "~0.6.8", 55 | "@angular/language-service": "^6.0.3", 56 | "@types/jasmine": "~2.8.6", 57 | "@types/jasminewd2": "~2.0.3", 58 | "@types/node": "~8.9.4", 59 | "codelyzer": "~4.2.1", 60 | "enhanced-resolve": "^4.1.0", 61 | "firebase-admin": "^6.0.0", 62 | "firebase-functions": "^2.0.5", 63 | "jasmine-core": "~2.99.1", 64 | "jasmine-spec-reporter": "~4.2.1", 65 | "karma": "^3.0.0", 66 | "karma-chrome-launcher": "~2.2.0", 67 | "karma-coverage-istanbul-reporter": "~2.0.0", 68 | "karma-jasmine": "~1.1.1", 69 | "karma-jasmine-html-reporter": "^0.2.2", 70 | "node-sass": "^4.9.3", 71 | "protractor": "^5.4.0", 72 | "ts-node": "~5.0.1", 73 | "tslint": "~5.9.1" 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | //Install express server 2 | const express = require('express'); 3 | const path = require('path'); 4 | const bodyParser = require('body-parser'); 5 | 6 | const app = express(); 7 | 8 | app.use(bodyParser.json()); 9 | app.use(bodyParser.urlencoded({ extended: false })) 10 | 11 | // Serve only the static files form the dist directory 12 | app.use(express.static(__dirname + '/dist/learning-fortress-frontend')); 13 | 14 | app.get('*', (req, res) => { 15 | res.sendFile(path.join(__dirname, 'dist/learning-fortress-frontend/index.html')); 16 | }); 17 | 18 | // Start the app by listening on the default Heroku port 19 | app.listen(process.env.PORT || 8080); 20 | console.log('Listening on port %s', process.env.PORT || 8080); -------------------------------------------------------------------------------- /src/app/app.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { AngularFireAuth } from 'angularfire2/auth'; 3 | import { User } from 'firebase'; 4 | import { Observable } from 'rxjs'; 5 | import { AuthService } from './auth/auth.service'; 6 | 7 | @Component({ 8 | selector: 'app-root', 9 | templateUrl: './app.component.html', 10 | }) 11 | export class AppComponent { 12 | constructor(public authService: AuthService) { } 13 | } 14 | -------------------------------------------------------------------------------- /src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | // External Libraries 2 | import { NgModule } from '@angular/core'; 3 | import { BrowserModule } from '@angular/platform-browser'; 4 | import { HttpClientModule } from '@angular/common/http'; 5 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; 6 | import { FlexLayoutModule } from '@angular/flex-layout'; 7 | import { FormsModule } from '@angular/forms'; 8 | import { DragulaModule } from 'ng2-dragula'; 9 | 10 | // Routing, Styling, Environment 11 | import { AppRoutingModule } from './app.routing'; 12 | import { MaterialModule } from './material.module'; 13 | import { environment } from '../environments/environment'; 14 | 15 | // Component Modules 16 | import { AppComponent } from './app.component'; 17 | import { PlayModule } from './play/play.module'; 18 | import { LoginModule } from './login/login.module'; 19 | import { ManageModule } from './manage/manage.module'; 20 | import { BuildModule } from './build/build.module'; 21 | 22 | // Authentication 23 | import { AuthModule } from './auth/auth.module'; 24 | import { AngularFireModule } from 'angularfire2'; 25 | import { DatabaseModule } from './database/database.module'; 26 | 27 | 28 | @NgModule({ 29 | declarations: [ 30 | AppComponent 31 | ], 32 | imports: [ 33 | AppRoutingModule, 34 | BrowserModule, 35 | FormsModule, 36 | LoginModule, 37 | PlayModule, 38 | ManageModule, 39 | BuildModule, 40 | HttpClientModule, 41 | BrowserAnimationsModule, 42 | MaterialModule, 43 | FlexLayoutModule, 44 | AngularFireModule.initializeApp(environment.firebase), 45 | AuthModule, 46 | DatabaseModule, 47 | DragulaModule.forRoot() 48 | ], 49 | providers: [], 50 | bootstrap: [AppComponent] 51 | }) 52 | export class AppModule { } 53 | -------------------------------------------------------------------------------- /src/app/app.routing.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { RouterModule, Routes } from '@angular/router'; 3 | 4 | const routes : Routes = [ 5 | { path: "play", loadChildren: "./play/play.module#PlayModule" }, 6 | { path: "manage", loadChildren: "./manage/manage.module#ManageModule"}, 7 | { path: "build", loadChildren: "./build/build.module#BuildModule"}, 8 | { path: "", redirectTo: "play/dashboard", pathMatch: "full"} 9 | ] 10 | 11 | @NgModule({ 12 | imports: [ 13 | RouterModule.forRoot(routes), 14 | ], 15 | exports: [ 16 | RouterModule 17 | ], 18 | }) 19 | export class AppRoutingModule { } 20 | -------------------------------------------------------------------------------- /src/app/auth/auth.module.ts: -------------------------------------------------------------------------------- 1 | import { FirebaseUIAuthConfig, AuthProvider, AuthMethods, CredentialHelper, FirebaseUIModule } from "firebaseui-angular"; 2 | import { AngularFireModule } from "angularfire2" 3 | import { AngularFireAuthModule } from "angularfire2/auth"; 4 | import { NgModule } from "@angular/core"; 5 | import { environment } from "../../environments/environment"; 6 | import { AuthService } from "./auth.service"; 7 | 8 | // https://github.com/RaphaelJenni/FirebaseUI-Angular 9 | // 10 | const firebaseUiAuthConfig: FirebaseUIAuthConfig = { 11 | providers: [ 12 | AuthProvider.Password, 13 | AuthProvider.Google 14 | ], 15 | method: AuthMethods.Popup, 16 | // tos: '', 17 | credentialHelper: CredentialHelper.AccountChooser, 18 | autoUpgradeAnonymousUsers: false, 19 | disableSignInSuccessCallback: true, 20 | }; 21 | 22 | @NgModule({ 23 | imports: [ 24 | AngularFireAuthModule, 25 | FirebaseUIModule.forRoot(firebaseUiAuthConfig) 26 | ], 27 | exports: [ 28 | AngularFireAuthModule, 29 | FirebaseUIModule 30 | ], 31 | providers: [ 32 | AuthService 33 | ] 34 | }) 35 | export class AuthModule { } 36 | -------------------------------------------------------------------------------- /src/app/auth/auth.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from "@angular/core"; 2 | import { AngularFireAuth } from "angularfire2/auth"; 3 | import { User } from "firebase"; 4 | import { Observable } from "rxjs"; 5 | import { FirebaseUISignInSuccessWithAuthResult, FirebaseUISignInFailure } from "firebaseui-angular"; 6 | import { environment } from "../../environments/environment.prod"; 7 | import { Router } from "@angular/router"; 8 | 9 | @Injectable({ 10 | providedIn: 'root' 11 | }) 12 | export class AuthService { 13 | user: Observable; 14 | 15 | constructor(public afAuth: AngularFireAuth, private router: Router) { 16 | this.user = afAuth.user; 17 | } 18 | 19 | signInSuccess(event: FirebaseUISignInSuccessWithAuthResult) { 20 | console.log(`signed in as ${event.authResult.user.displayName} who is${event.authResult.additionalUserInfo.isNewUser?"":" not"} a new user.`); 21 | return true; 22 | } 23 | 24 | signInFailure(event: FirebaseUISignInFailure) { 25 | console.log(`sign in failed because ${event.code}`); 26 | return true; 27 | } 28 | 29 | logout() { 30 | this.afAuth.auth.signOut(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/app/build/brick/brick.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 |
5 |
6 | 7 |
8 |
9 | -------------------------------------------------------------------------------- /src/app/build/brick/brick.component.scss: -------------------------------------------------------------------------------- 1 | .fixed-center { 2 | margin:auto; 3 | } 4 | 5 | .spinner-container { 6 | position: fixed; 7 | top: 0; 8 | right: 0; 9 | bottom: 0; 10 | left: 0; 11 | display: flex; 12 | } 13 | 14 | .back-container { 15 | width: 100%; 16 | height: 100%; 17 | background-color: #ffffff; 18 | } 19 | 20 | .brick-container { 21 | max-width: 800px; 22 | margin: 0 auto; 23 | height: 100%; 24 | } 25 | 26 | .begin-row { 27 | margin-top: 15px; 28 | margin-bottom: 15px; 29 | } -------------------------------------------------------------------------------- /src/app/build/brick/brick.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | import { Router, ActivatedRoute, ParamMap } from '@angular/router'; 4 | 5 | import { BrickService } from './brick.service'; 6 | import { Brick } from '../../schema'; 7 | 8 | @Component({ 9 | templateUrl: './brick.component.html', 10 | styleUrls: ['./brick.component.scss'] 11 | }) 12 | export class BrickComponent { 13 | constructor( 14 | private bricks: BrickService, 15 | private route: ActivatedRoute 16 | ) { 17 | this.route.paramMap 18 | .subscribe((data: ParamMap) => { 19 | var id = data.get('id'); 20 | bricks.loadBrick(id); 21 | bricks.currentBrick.subscribe((data) => { 22 | this.brick = data; 23 | }) 24 | }) 25 | } 26 | 27 | brick: Brick; 28 | } -------------------------------------------------------------------------------- /src/app/build/brick/brick.routing.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { RouterModule, Routes } from '@angular/router'; 3 | 4 | import { BrickComponent } from './brick.component'; 5 | import { IntroductionComponent } from './introduction.component'; 6 | import { LiveComponent } from './live.component'; 7 | import { SummaryComponent } from './summary.component'; 8 | import { ReviewComponent } from './review.component'; 9 | import { EndingComponent } from './ending.component'; 10 | 11 | const brickRoutes = [ 12 | {path: ':id', component: BrickComponent, children: [ 13 | { path: 'intro', component: IntroductionComponent }, 14 | { path: 'live', component: LiveComponent }, 15 | { path: 'summary', component: SummaryComponent }, 16 | { path: 'review', component: ReviewComponent }, 17 | { path: 'ending', component: EndingComponent }, 18 | { path: '', redirectTo: 'intro', pathMatch: 'full' } 19 | ]} 20 | ] 21 | 22 | @NgModule({ 23 | imports: [ RouterModule.forChild(brickRoutes) ], 24 | exports: [ RouterModule ] 25 | }) 26 | export class BrickRoutingModule { } 27 | -------------------------------------------------------------------------------- /src/app/build/brick/brick.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { DatabaseService } from '../../database/database.service'; 3 | 4 | import { BehaviorSubject, Observable } from 'rxjs'; 5 | 6 | import { Brick, BrickAttempt, Pallet } from '../../schema'; 7 | 8 | @Injectable({ 9 | providedIn: 'root' 10 | }) 11 | export class BrickService { 12 | constructor(public database: DatabaseService) { 13 | this.currentBrick = new BehaviorSubject(null); 14 | } 15 | 16 | currentBrick: BehaviorSubject; 17 | currentPallet: Observable; 18 | currentBrickAttempt: BrickAttempt; 19 | 20 | loadBrick(id: string) { 21 | this.currentBrick = this.database.getBrick(id); 22 | this.currentBrick.subscribe(val => { 23 | if(val) { 24 | this.currentPallet = this.database.getPallet(val.pallet.id); 25 | } 26 | }) 27 | } 28 | 29 | publishBrickAttempt(ba: BrickAttempt) { 30 | this.database.createBrickAttempt(ba).subscribe((msg) => { 31 | console.log(msg); 32 | }); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/app/build/brick/brickTime.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | 3 | @Pipe({ name: 'brickTime' }) 4 | export class BrickTimePipe implements PipeTransform { 5 | transform(value: number, type: string): number { 6 | switch(type) { 7 | case "intro": switch(value) { 8 | case 1: return 300000; 9 | case 2: return 600000; 10 | case 3: return 900000; 11 | default: return 0; 12 | } 13 | case "live": switch(value) { 14 | case 1: return 500000; 15 | case 2: return 1000000; 16 | case 3: return 1500000; 17 | default: return 0; 18 | } 19 | case "summary": switch(value) { 20 | case 1: return 200000; 21 | case 2: return 400000; 22 | case 3: return 600000; 23 | default: return 0; 24 | } 25 | case "review": switch(value) { 26 | case 1: return 200000; 27 | case 2: return 400000; 28 | case 3: return 600000; 29 | default: return 0; 30 | } default: switch(value) { 31 | case 1: return 1200000; 32 | case 2: return 2400000; 33 | case 3: return 3600000; 34 | default: return 0; 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/app/build/brick/brickbuild.module.ts: -------------------------------------------------------------------------------- 1 | import { CommonModule } from '@angular/common'; 2 | import { NgModule } from '@angular/core'; 3 | import { MaterialModule } from '../../material.module'; 4 | import { MatIconModule } from '@angular/material/icon'; 5 | import { BrickRoutingModule } from './brick.routing'; 6 | import { CompModule } from './comp/comp.module'; 7 | 8 | import { BrickComponent } from './brick.component'; 9 | import { IntroductionComponent } from './introduction.component'; 10 | import { LiveComponent } from './live.component'; 11 | 12 | import { BrickTimePipe } from './brickTime.pipe'; 13 | import { QuestionComponent } from './question.component'; 14 | import { DragulaModule } from 'ng2-dragula'; 15 | import { NgArrayPipesModule } from 'ngx-pipes'; 16 | import { SummaryComponent } from './summary.component'; 17 | import { FlexLayoutModule } from '@angular/flex-layout'; 18 | import { ReviewComponent } from './review.component'; 19 | import { EndingComponent } from './ending.component'; 20 | import { TimerComponent } from './timer.component'; 21 | 22 | @NgModule({ 23 | imports: [ BrickRoutingModule, CommonModule, MaterialModule, MatIconModule, DragulaModule, CompModule, NgArrayPipesModule, FlexLayoutModule ], 24 | declarations: [ 25 | BrickComponent, TimerComponent, IntroductionComponent, LiveComponent, QuestionComponent, SummaryComponent, ReviewComponent, EndingComponent, BrickTimePipe 26 | ], 27 | providers: [ 28 | BrickTimePipe 29 | ] 30 | }) 31 | export class BrickModule { } 32 | -------------------------------------------------------------------------------- /src/app/build/brick/comp/comp.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input } from "@angular/core"; 2 | import { Comp, ComponentAttempt } from "../../../schema"; 3 | 4 | @Component({ 5 | template: `` 6 | }) 7 | export class CompComponent { 8 | constructor() { 9 | } 10 | 11 | @Input() data: Comp; 12 | @Input() attempt: ComponentAttempt; 13 | 14 | getAnswer() {}; 15 | 16 | getAttempt() : ComponentAttempt { 17 | let att = this.mark({ answer: this.getAnswer(), correct: null, marks: 0, maxMarks: 0 }, this.attempt); 18 | return att; 19 | }; 20 | 21 | mark(attempt: ComponentAttempt, prev: ComponentAttempt) : ComponentAttempt { return attempt; } 22 | } 23 | -------------------------------------------------------------------------------- /src/app/build/brick/comp/comp.module.ts: -------------------------------------------------------------------------------- 1 | import { CommonModule } from '@angular/common'; 2 | import { NgModule } from '@angular/core'; 3 | import { FormsModule } from '@angular/forms'; 4 | import { DynamicModule } from 'ng-dynamic-component'; 5 | import { NgArrayPipesModule } from 'ngx-pipes'; 6 | import { FlexLayoutModule } from '@angular/flex-layout'; 7 | import { DragulaModule } from 'ng2-dragula'; 8 | import { MaterialModule } from '../../../material.module'; 9 | import { CompComponent } from './comp.component'; 10 | import { MultipleChoiceComponent } from './comp_multiple_choice.component'; 11 | import { OrderComponent } from './comp_order.component'; 12 | import { SingleChoiceComponent } from './comp_single_choice.component'; 13 | import { SortComponent } from './comp_sort.component'; 14 | import { TextComponent } from './comp_text.component'; 15 | import { ShortAnswerComponent } from './comp_short_answer.component'; 16 | import { HorizontalShuffleComponent } from './comp_horizontal_shuffle.component'; 17 | import { TextHighlightingComponent } from './comp_text_highlighting.component'; 18 | import { HighlightDirective } from './highlight.directive'; 19 | import { ArrowComponent } from './comp_arrow.component'; 20 | 21 | let dModule = DynamicModule.withComponents([SingleChoiceComponent, MultipleChoiceComponent, 22 | TextComponent, OrderComponent, SortComponent, ShortAnswerComponent, HorizontalShuffleComponent, 23 | TextHighlightingComponent, ArrowComponent]) 24 | 25 | @NgModule({ 26 | imports: [ CommonModule, FormsModule, DragulaModule, MaterialModule, NgArrayPipesModule, FlexLayoutModule, dModule ], 27 | declarations: [ CompComponent, SingleChoiceComponent, MultipleChoiceComponent, 28 | TextComponent, OrderComponent, SortComponent, ShortAnswerComponent, HorizontalShuffleComponent, 29 | TextHighlightingComponent, ArrowComponent, 30 | HighlightDirective ], 31 | exports: [ CompComponent, SingleChoiceComponent, MultipleChoiceComponent , 32 | TextComponent, OrderComponent, SortComponent, ShortAnswerComponent, HorizontalShuffleComponent, 33 | TextHighlightingComponent, ArrowComponent, 34 | HighlightDirective, 35 | dModule.ngModule, FormsModule 36 | ], 37 | providers: [ dModule.providers ] 38 | }) 39 | export class CompModule { } -------------------------------------------------------------------------------- /src/app/build/brick/comp/comp_index.ts: -------------------------------------------------------------------------------- 1 | 2 | export const registry = new Map(); 3 | 4 | export function getComponent (name: string) : any { 5 | return registry.get(name); 6 | } 7 | 8 | export function getAllComponents () : any[] { 9 | return Array.from(registry.values()); 10 | } 11 | 12 | export function register (name: string) { 13 | return function(comp: any) { 14 | registry.set(name, comp); 15 | } 16 | } -------------------------------------------------------------------------------- /src/app/build/brick/comp/comp_reverse.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe } from '@angular/core'; 2 | 3 | @Pipe({ 4 | name: 'reverse' 5 | }) 6 | export class ReversePipe { 7 | transform(value) { 8 | return value.slice().reverse(); 9 | } 10 | } -------------------------------------------------------------------------------- /src/app/build/brick/comp/comp_single_choice.component.ts: -------------------------------------------------------------------------------- 1 | import { Comp, ComponentAttempt } from "../../../schema"; 2 | import { Component, Input } from "@angular/core"; 3 | 4 | import { register } from './comp_index'; 5 | import { CompComponent } from "./comp.component"; 6 | import { MAT_CHECKBOX_CLICK_ACTION } from "@angular/material/checkbox"; 7 | 8 | export class CompSingleChoice extends Comp { 9 | name = "Single Choice"; 10 | data: { choices:string[], reveals:string[] } 11 | 12 | constructor(data: { choices:string[], reveals:string[] }) { 13 | super(); 14 | this.data = data; 15 | } 16 | } 17 | 18 | @register("SingleChoice") 19 | @Component({ 20 | selector: "single-choice", 21 | template: ` 22 | 23 | 24 |
25 | 26 |
27 |
28 |
{{ choice }}
29 |
{{ data.data.reveals[getChoice(choice)] }}
30 |
31 |
32 |
33 |
34 |
35 | `, 36 | styleUrls: ["../live.component.scss"], 37 | providers: [ 38 | {provide: MAT_CHECKBOX_CLICK_ACTION, useValue: 'noop'} 39 | ] 40 | }) 41 | export class SingleChoiceComponent extends CompComponent { 42 | constructor() { super() } 43 | 44 | @Input() data: CompSingleChoice; 45 | answer: string; 46 | 47 | ngOnInit() { 48 | if(this.attempt) { 49 | this.answer = this.data.data.choices[this.attempt.answer]; 50 | } 51 | } 52 | 53 | getAnswer() : number { 54 | return this.data.data.choices.indexOf(this.answer); 55 | } 56 | 57 | getChoice(choice) : number { 58 | return this.data.data.choices.indexOf(choice); 59 | } 60 | 61 | getState(choice) : number { 62 | if(this.getChoice(choice) == this.attempt.answer) { 63 | if(this.getChoice(choice) == 0) { 64 | return 1; 65 | } else { 66 | return -1; 67 | } 68 | } else { 69 | return 0; 70 | } 71 | } 72 | 73 | mark(attempt: ComponentAttempt, prev: ComponentAttempt) : ComponentAttempt { 74 | // If the question is answered in review phase, add 2 to the mark and not 5. 75 | let markIncrement = prev ? 2 : 5; 76 | // set attempt.correct to true if the answer is 0. 77 | attempt.correct = (attempt.answer == 0); 78 | attempt.maxMarks = 5; 79 | // if the attempt is correct, add the mark increment. 80 | if(attempt.correct) attempt.marks = markIncrement; 81 | // if there is an answer given and the program is in the live phase, give the student an extra mark. 82 | else if (attempt.answer != null && !prev) attempt.marks = 1; 83 | else attempt.marks = 0; 84 | return attempt; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/app/build/brick/comp/comp_text.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input } from "@angular/core"; 2 | import { register } from "./comp_index"; 3 | import { Comp, ComponentAttempt } from "../../../schema"; 4 | import { CompComponent } from "./comp.component"; 5 | 6 | export class CompText extends Comp { 7 | data: { text: string } 8 | 9 | constructor(data: { text: string }) { 10 | super(); 11 | this.data = data; 12 | } 13 | } 14 | 15 | @register("Text") 16 | @Component({ 17 | selector: "text", 18 | styles: [ 19 | ':host >>> .background-green { background: green; }', 20 | ':host >>> .color-red { color: red; }' 21 | ], 22 | template: ` 23 |
24 |

25 |
26 | `, 27 | styleUrls: ["../live.component.scss"] 28 | }) 29 | export class TextComponent extends CompComponent { 30 | constructor() { super() } 31 | 32 | @Input() data: CompText; 33 | 34 | getAnswer() : null { return null; } 35 | } -------------------------------------------------------------------------------- /src/app/build/brick/comp/highlight.directive.ts: -------------------------------------------------------------------------------- 1 | import { Directive, Input, ElementRef } from "@angular/core"; 2 | 3 | @Directive({ 4 | selector: "[highlight]", 5 | }) 6 | export class HighlightDirective { 7 | _highlight: boolean; 8 | @Input() state: number; 9 | 10 | constructor(public el: ElementRef) { } 11 | 12 | @Input() set highlight(highlight: boolean) { 13 | this._highlight = highlight; 14 | this.onHighlightChange(); 15 | }; 16 | 17 | onHighlightChange() { 18 | if(!this.state) { 19 | this.el.nativeElement.style.backgroundColor = this._highlight ? "#00dece" : "#ffffff"; 20 | } else { 21 | this.el.nativeElement.style.backgroundColor = this._highlight ? (this.state == 1 ? "#55ff55" : "#ff5555") : "#ffffff" 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/app/build/brick/ending.component.html: -------------------------------------------------------------------------------- 1 |
3 |

{{ brick.subject }}/{{ (aPallet | async)?.name }}/{{ brick.type | brickTime | date: 'm' }} minutes

4 |

{{ brick.title }}

5 |
6 |
7 |
8 | 9 | 10 |
11 |
{{ (brickAttempt?.score * 100 / brickAttempt?.maxScore) | number:'0.0-0' }}%
12 |
{{ brickAttempt?.score }} / {{ brickAttempt?.maxScore }}
13 |
14 |
15 |
16 |
17 | 18 |
20 |

Your brick has been submitted. Click the button to go back home.

21 |
22 |

Home

play_arrow 23 |
24 | 25 | 26 | 27 | 28 | Other Information 29 | 30 | 31 |
32 |

Creator: {{ brick.creator }}

33 |
34 |
35 |

Total Users: {{ brick.totalUsers }}

36 |

average score {{ brick.avgScore }}

37 |

high score {{ brick.highScore }}

38 |
39 |
40 |
41 |
42 | -------------------------------------------------------------------------------- /src/app/build/brick/ending.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input } from "@angular/core"; 2 | import { BrickAttempt, Brick, Pallet } from "../../schema"; 3 | import { BrickService } from "./brick.service"; 4 | 5 | import { BehaviorSubject, Observable } from "rxjs"; 6 | import { ActivatedRoute, Router } from "@angular/router"; 7 | 8 | @Component({ 9 | selector: 'live-ending', 10 | templateUrl: './ending.component.html', 11 | styleUrls: ['./summary.component.scss'] 12 | }) 13 | export class EndingComponent { 14 | brickAttempt: BrickAttempt; 15 | aBrick: BehaviorSubject; 16 | aPallet: Observable; 17 | _brick: Brick 18 | 19 | constructor(private bricks: BrickService, private router: Router, private route: ActivatedRoute) { 20 | if(bricks.currentBrickAttempt == null) { 21 | router.navigate(["../live"], { relativeTo: route }); 22 | } 23 | this.aBrick = bricks.currentBrick; 24 | this.aBrick.subscribe(val => { 25 | if(val) { 26 | this.aPallet = bricks.currentPallet; 27 | this._brick = val; 28 | } 29 | }); 30 | this.brickAttempt = bricks.currentBrickAttempt; 31 | bricks.publishBrickAttempt(this.brickAttempt); 32 | } 33 | 34 | finish() { 35 | this.bricks.currentBrick = null; 36 | this.bricks.currentBrickAttempt = null; 37 | this.router.navigate(['play', 'pallet', this._brick.pallet.id]) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/app/build/brick/introduction.component.html: -------------------------------------------------------------------------------- 1 | 2 |
4 |

{{ brick.subject }}/{{ (aPallet | async)?.name }}/{{ ((brick.type | brickTime)/60000) }} minutes

5 |

{{ brick.title }}

6 | 7 | 8 | 9 | 10 | Brief 11 | 12 | 13 |
14 |
15 | 16 | 17 | 18 | Prep 19 | 20 | 21 |
22 |
23 |
24 |
26 |

When you have completed the Prep, click the button.

27 |
28 |

Begin

play_arrow 29 |
30 | 31 | 32 | 33 | 34 | Other Information 35 | 36 | 37 |
38 |

Creator: {{ brick.creator }}

39 |
40 |
41 |

Total Users: {{ brick.totalUsers }}

42 |

average score {{ brick.avgScore }}

43 |

high score {{ brick.highScore }}

44 |
45 |
46 |
47 |
48 |
49 | -------------------------------------------------------------------------------- /src/app/build/brick/introduction.component.scss: -------------------------------------------------------------------------------- 1 | .timer-container { 2 | position: fixed; 3 | bottom: 5px; 4 | right: 5px; 5 | display: flex; 6 | z-index: 100; 7 | } 8 | 9 | .timer { 10 | font-size: 20px; 11 | background-color: #fff; 12 | } 13 | 14 | .container { 15 | height: 100%; 16 | padding: 1vw 1vw 1vw 1vw; 17 | box-sizing: border-box; 18 | background-color: white; 19 | } 20 | 21 | .bottom-container { 22 | margin-top: auto; 23 | margin-bottom: 1vh; 24 | } 25 | 26 | .spacer { 27 | margin: auto; 28 | } 29 | 30 | .begin-label { 31 | font-size: 25px; 32 | margin-right: 10px; 33 | } 34 | 35 | .begin-row { 36 | margin-top: 15px; 37 | margin-bottom: 15px; 38 | } 39 | 40 | h1, h3, p { 41 | margin: 5px; 42 | } 43 | -------------------------------------------------------------------------------- /src/app/build/brick/introduction.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | import { ActivatedRoute, Router, ParamMap } from '@angular/router'; 4 | 5 | import { switchMap } from 'rxjs/operators'; 6 | 7 | import { Brick, Pallet } from '../../schema'; 8 | import { BrickService } from './brick.service'; 9 | import { BehaviorSubject, Observable } from 'rxjs'; 10 | import { TimerService, Timer } from './timer.service'; 11 | import { BrickTimePipe } from './brickTime.pipe'; 12 | 13 | @Component({ 14 | selector: 'introduction', 15 | templateUrl: './introduction.component.html', 16 | styleUrls: ['./introduction.component.scss'] 17 | }) 18 | export class IntroductionComponent { 19 | constructor(private bricks: BrickService, timer: TimerService, private brickTime: BrickTimePipe, private router: Router, private route: ActivatedRoute) { 20 | this.timer = timer.new(); 21 | this.timer.timeResolution = 1000; 22 | this.aBrick = bricks.currentBrick; 23 | this.aBrick.subscribe(val => { 24 | if(val != null) { 25 | this.aPallet = bricks.currentPallet; 26 | this.showBrick(val); 27 | } 28 | }); 29 | } 30 | 31 | aBrick: BehaviorSubject; 32 | aPallet: Observable; 33 | timer: Timer; 34 | 35 | showBrick(brick: Brick) { 36 | let time = this.brickTime.transform(brick.type, "intro"); 37 | this.timer.countDown(time); 38 | this.timer.timeRanOut.subscribe((t) => { 39 | this.startBrick(); 40 | }) 41 | } 42 | 43 | startBrick() { 44 | this.router.navigate(['../live'], { relativeTo: this.route }) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/app/build/brick/live.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | help_outline 5 | 6 | 7 | done 8 | 9 | 10 | Question {{index+1}} 11 | 12 | 13 | 14 |
16 |

When you have finished the brick, click the button.

17 |
18 |

Finish

play_arrow 19 |
20 | -------------------------------------------------------------------------------- /src/app/build/brick/live.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, ViewChildren, QueryList } from '@angular/core'; 2 | 3 | import { BrickService } from './brick.service'; 4 | 5 | import { Brick, Question, BrickAttempt, Student, Pallet } from '../../schema'; 6 | import { Observable } from 'rxjs'; 7 | import { TimerService, Timer } from './timer.service'; 8 | import { BrickTimePipe } from './brickTime.pipe'; 9 | 10 | import { CompComponent } from './comp/comp.component'; 11 | import { QuestionComponent } from './question.component'; 12 | import { Router, ActivatedRoute } from '@angular/router'; 13 | import { AuthService } from '../../auth/auth.service'; 14 | 15 | @Component({ 16 | selector: 'live', 17 | templateUrl: './live.component.html', 18 | styleUrls: ['./live.component.scss'], 19 | providers: [ ] 20 | }) 21 | export class LiveComponent { 22 | constructor(public bricks: BrickService, timer: TimerService, brickTime: BrickTimePipe, public router: Router, public route: ActivatedRoute, public auth: AuthService) { 23 | this.brick = bricks.currentBrick.asObservable(); 24 | this.timer = timer.new(); 25 | this.timer.timeResolution = 1000; 26 | this.brickTime = brickTime; 27 | bricks.currentBrick.subscribe((data) => { 28 | if(data != null) { 29 | this._brick = data; 30 | this.showBrick(this._brick); 31 | } 32 | }) 33 | } 34 | 35 | brick: Observable; 36 | timer : Timer; 37 | 38 | private _brick: Brick; 39 | private brickTime: BrickTimePipe; 40 | 41 | @ViewChildren(QuestionComponent) questions : QueryList; 42 | 43 | showBrick(brick: Brick) { 44 | let time = this.brickTime.transform(brick.type, "live"); 45 | this.timer.countDown(time); 46 | this.timer.timeRanOut.subscribe((t) => { 47 | this.finishBrick(); 48 | }) 49 | } 50 | 51 | finishBrick() { 52 | this.timer.stop(); 53 | console.log("finished in " + this.timer.timeElapsed.getTime() / 1000); 54 | 55 | // Get brick data 56 | this.auth.user.subscribe((user) => { 57 | let answers = this.questions.map((question) => { 58 | return question.getAttempt(); 59 | }) 60 | let score = answers.reduce((acc, answer) => acc + answer.marks, 0); 61 | let maxScore = answers.reduce((acc, answer) => acc + answer.maxMarks, 0); 62 | var ba : BrickAttempt = { 63 | brick: this._brick._ref, 64 | score: score, 65 | maxScore: maxScore, 66 | student: this.bricks.database.afs.doc("students/"+user.uid).ref, 67 | answers: answers 68 | }; 69 | console.log(`score is ${score} out of ${maxScore}, which is ${score * 100 / maxScore}%`); 70 | this.bricks.currentBrickAttempt = ba; 71 | this.router.navigate(["../summary"], { relativeTo: this.route }); 72 | }) 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /src/app/build/brick/question.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input, ViewChildren, QueryList } from "@angular/core"; 2 | import { Question, QuestionAttempt } from "../../schema"; 3 | import { CompComponent } from "./comp/comp.component"; 4 | import { DynamicComponent } from "ng-dynamic-component"; 5 | import { ShufflePipe } from "ngx-pipes"; 6 | 7 | @Component({ 8 | selector: 'question', 9 | template: ` 10 | 16 | ` 17 | }) 18 | export class QuestionComponent { 19 | @Input() question: Question; 20 | @Input() attempt: QuestionAttempt; 21 | 22 | @ViewChildren(DynamicComponent) private dynamicComponents: QueryList; 23 | components: CompComponent[]; 24 | 25 | constructor() { 26 | } 27 | 28 | getAttempt() : QuestionAttempt { 29 | this.components = this.dynamicComponents.map((dynComp) => { 30 | return dynComp.componentRef.instance as CompComponent; 31 | }); 32 | 33 | let compAttempts = this.components.map((comp) => { 34 | return comp.getAttempt(); 35 | }) 36 | 37 | let correct = compAttempts.every(attempt => attempt.correct || attempt.correct == null); 38 | let marks = compAttempts.reduce((acc, attempt) => acc + attempt.marks, 0); 39 | let maxMarks = compAttempts.reduce((acc, attempt) => acc + attempt.maxMarks, 0); 40 | 41 | var qa : QuestionAttempt = { 42 | question: this.question._ref, 43 | components: compAttempts, 44 | correct: correct, 45 | marks: marks, 46 | maxMarks: maxMarks 47 | }; 48 | return qa; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/app/build/brick/review.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Question {{index+1}} 5 | 6 |
Question is correct!
7 |
8 |
9 |
11 |

When you have finished the brick, click the button.

12 |
13 |

Finish

play_arrow 14 |
15 | -------------------------------------------------------------------------------- /src/app/build/brick/review.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, ViewChildren, QueryList } from "@angular/core"; 2 | import { Observable } from "rxjs"; 3 | import { Brick, BrickAttempt, QuestionAttempt } from "../../schema"; 4 | import { Timer, TimerService } from "./timer.service"; 5 | import { BrickTimePipe } from "./brickTime.pipe"; 6 | import { BrickService } from "./brick.service"; 7 | import { Router, ActivatedRoute } from "@angular/router"; 8 | import { AuthService } from "../../auth/auth.service"; 9 | import { QuestionComponent } from "./question.component"; 10 | 11 | @Component({ 12 | selector: 'live-review', 13 | templateUrl: './review.component.html', 14 | styleUrls: ['./live.component.scss'] 15 | }) 16 | export class ReviewComponent { 17 | constructor(public bricks: BrickService, timer: TimerService, brickTime: BrickTimePipe, public router: Router, public route: ActivatedRoute, public auth: AuthService) { 18 | this.brick = bricks.currentBrick.asObservable(); 19 | this.brickAttempt = bricks.currentBrickAttempt; 20 | if(!this.brickAttempt) { 21 | this.router.navigate(['../live'], {relativeTo: route}); 22 | } 23 | this.timer = timer.new(); 24 | this.timer.timeResolution = 1000; 25 | this.brickTime = brickTime; 26 | bricks.currentBrick.subscribe((data) => { 27 | if(data != null) { 28 | this._brick = data; 29 | this.showBrick(this._brick); 30 | } 31 | }) 32 | } 33 | 34 | brick: Observable; 35 | brickAttempt: BrickAttempt; 36 | timer : Timer; 37 | 38 | private _brick: Brick; 39 | private brickTime: BrickTimePipe; 40 | 41 | @ViewChildren(QuestionComponent) questions : QueryList; 42 | 43 | showBrick(brick: Brick) { 44 | let time = this.brickTime.transform(brick.type, "review"); 45 | this.timer.countDown(time); 46 | this.timer.timeRanOut.subscribe((t) => { 47 | this.finishBrick(); 48 | }) 49 | } 50 | 51 | finishBrick() { 52 | this.timer.stop(); 53 | console.log("finished in " + this.timer.timeElapsed.getTime() / 1000); 54 | 55 | // Get brick data 56 | this.auth.user.subscribe((user) => { 57 | let answers = this.questions.map((question) => { 58 | return question.getAttempt(); 59 | }) 60 | let score = answers.reduce((acc, answer) => acc + answer.marks, 0) + this.bricks.currentBrickAttempt.score; 61 | var ba : BrickAttempt = { 62 | brick: this._brick._ref, 63 | score: score, 64 | oldScore: this.bricks.currentBrickAttempt.score, 65 | maxScore: this.bricks.currentBrickAttempt.maxScore, 66 | student: this.bricks.database.afs.doc("students/"+user.uid).ref, 67 | answers: answers 68 | }; 69 | console.log(`score is ${score} out of ${this.bricks.currentBrickAttempt.maxScore}, which is ${score * 100 / this.bricks.currentBrickAttempt.maxScore}%`); 70 | this.bricks.currentBrickAttempt = ba; 71 | this.router.navigate(["../ending"], { relativeTo: this.route }); 72 | }) 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/app/build/brick/summary.component.html: -------------------------------------------------------------------------------- 1 | 2 |
4 |

{{ brick.subject }}/{{ (aPallet | async)?.name }}/{{ ((brick.type | brickTime)/60000) }} minutes

5 |

{{ brick.title }}

6 |

average score {{ brick.avgScore }}

7 |

high score {{ brick.highScore }}

8 | 9 |
10 | 11 | 12 | Summary 13 | 14 | {{ brick.summary }} 15 | 16 |
17 | 18 |
19 |
{{ (brickAttempt?.score * 100 / brickAttempt?.maxScore) | number:'0.0-0' }}%
20 |
{{ brickAttempt?.score }} / {{ brickAttempt?.maxScore }}
21 |
22 |
23 |
24 | 25 |
27 |

When you have read the summary, click the button.

28 |
29 |

Review

play_arrow 30 |
31 | 32 | 33 | 34 | 35 | Other Information 36 | 37 | 38 |
39 |

Creator: {{ brick.creator }}

40 |
41 |
42 |

Total Users: {{ brick.totalUsers }}

43 |

average score {{ brick.avgScore }}

44 |

high score {{ brick.highScore }}

45 |
46 |
47 |
48 |
49 |
50 | -------------------------------------------------------------------------------- /src/app/build/brick/summary.component.scss: -------------------------------------------------------------------------------- 1 | .container { 2 | padding: 1vw; 3 | height: 100%; 4 | bottom: 0; 5 | background-color: white; 6 | } 7 | 8 | .overflow { 9 | overflow: hidden auto; 10 | } 11 | 12 | .score-wheel { 13 | position: absolute; 14 | height: 350px; 15 | } 16 | 17 | .numbers-container { 18 | position: absolute; 19 | height: 350px; 20 | } 21 | 22 | .score-percentage { 23 | font-size: 50px; 24 | } 25 | 26 | .score-number { 27 | font-size: 30px; 28 | } 29 | 30 | .data-container { 31 | margin: 10px; 32 | } 33 | 34 | .begin-row { 35 | margin-top: 15px; 36 | margin-bottom: 15px; 37 | } 38 | 39 | h1, h3, p { 40 | margin: 5px; 41 | } 42 | -------------------------------------------------------------------------------- /src/app/build/brick/summary.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input } from "@angular/core"; 2 | import { BrickAttempt, Brick, Pallet } from "../../schema"; 3 | import { BrickService } from "./brick.service"; 4 | 5 | import { BehaviorSubject, Observable } from "rxjs"; 6 | import { ActivatedRoute, Router } from "@angular/router"; 7 | import { BrickTimePipe } from "./brickTime.pipe"; 8 | import { TimerService, Timer } from "./timer.service"; 9 | 10 | @Component({ 11 | selector: 'live-summary', 12 | templateUrl: './summary.component.html', 13 | styleUrls: ['./summary.component.scss'] 14 | }) 15 | export class SummaryComponent { 16 | brickAttempt: BrickAttempt; 17 | aBrick: BehaviorSubject; 18 | aPallet: Observable; 19 | 20 | timer: Timer; 21 | 22 | constructor(private bricks: BrickService, timer: TimerService, private brickTime: BrickTimePipe, private router: Router, private route: ActivatedRoute) { 23 | if(bricks.currentBrickAttempt == null) { 24 | router.navigate(["../live"], { relativeTo: route }); 25 | } 26 | this.aBrick = bricks.currentBrick; 27 | this.brickAttempt = bricks.currentBrickAttempt; 28 | this.timer = timer.new(); 29 | this.timer.timeResolution = 1000; 30 | 31 | this.aBrick.subscribe(val => { 32 | if(val) { 33 | this.aPallet = bricks.currentPallet; 34 | this.showBrick(val); 35 | } 36 | }); 37 | } 38 | 39 | showBrick(brick: Brick) { 40 | let time = this.brickTime.transform(brick.type, "summary"); 41 | this.timer.countDown(time); 42 | } 43 | 44 | startBrick() { 45 | this.router.navigate(['../review'], { relativeTo: this.route }) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/app/build/brick/timer.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input } from "@angular/core"; 2 | import { Timer } from "./timer.service"; 3 | 4 | @Component({ 5 | selector: 'timer', 6 | template: ` 7 |
8 | 9 | {{ timer.timeLeft?.valueOf() / 1000 }} seconds 10 | 11 |
12 | `, 13 | styles: [ 14 | ` 15 | .timer-container { 16 | position: fixed; 17 | bottom: 5px; 18 | right: 5px; 19 | display: flex; 20 | z-index: 100; 21 | } 22 | 23 | .timer { 24 | font-size: 20px; 25 | background-color: #fff; 26 | } 27 | ` 28 | ] 29 | }) 30 | export class TimerComponent { 31 | @Input() timer: Timer; 32 | } 33 | -------------------------------------------------------------------------------- /src/app/build/brick/timer.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, EventEmitter } from "@angular/core"; 2 | import { timer, Observable, Subscription } from "rxjs"; 3 | 4 | 5 | // TODO: Add call to API to make sure that a brick can't be reset. 6 | export class Timer { 7 | constructor () { } 8 | 9 | timeElapsed : Date; 10 | timeLeft : Date; 11 | timeLimit : Date; 12 | 13 | timeResolution: number; 14 | 15 | timer : Observable; 16 | timeRanOut: EventEmitter; 17 | private subscription : Subscription; 18 | 19 | countUp() { 20 | this.timer = timer(0, this.timeResolution); 21 | this.timeRanOut = null; 22 | this.subscription = this.timer.subscribe((t) => { 23 | this.timeElapsed = new Date(t*this.timeResolution); 24 | }) 25 | } 26 | 27 | countDown(timeLimit: number) { 28 | this.timer = timer(0, this.timeResolution); 29 | this.timeLimit = new Date(timeLimit); 30 | this.timeRanOut = new EventEmitter() 31 | this.subscription = this.timer.subscribe((t) => { 32 | this.timeElapsed = new Date(t * this.timeResolution); 33 | this.timeLeft = new Date(this.timeLimit.getTime() - this.timeElapsed.getTime()); 34 | if(this.timeLeft.getTime() <= 0) { 35 | this.timeRanOut.emit(); 36 | } 37 | }) 38 | } 39 | 40 | stop() { 41 | this.subscription.unsubscribe(); 42 | this.timer = null; 43 | } 44 | } 45 | 46 | @Injectable({ 47 | providedIn: 'root' 48 | }) 49 | export class TimerService { 50 | constructor () { } 51 | 52 | new () { 53 | return new Timer(); 54 | } 55 | } -------------------------------------------------------------------------------- /src/app/build/build.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { BuildComponent } from './build.component'; 4 | 5 | describe('BuildComponent', () => { 6 | let component: BuildComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ BuildComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(BuildComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/build/build.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-manage', 5 | template: '' 6 | }) 7 | export class BuildComponent implements OnInit { 8 | 9 | constructor() { } 10 | 11 | ngOnInit() { 12 | } 13 | 14 | } -------------------------------------------------------------------------------- /src/app/build/build.module.ts: -------------------------------------------------------------------------------- 1 | import { CommonModule } from '@angular/common'; 2 | import { NgModule } from '@angular/core'; 3 | import { FlexLayoutModule } from '@angular/flex-layout'; 4 | import { MaterialModule } from '../material.module'; 5 | import { BuildComponent } from './build.component'; 6 | import { BuildRoutingModule } from './build.routing'; 7 | import { NavigationModule } from '../navigation/navigation.module'; 8 | 9 | @NgModule({ 10 | imports: [ BuildRoutingModule, CommonModule, FlexLayoutModule, MaterialModule, NavigationModule ], 11 | declarations: [ BuildComponent ], 12 | }) 13 | export class BuildModule { } -------------------------------------------------------------------------------- /src/app/build/build.routing.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { RouterModule, Routes } from '@angular/router'; 3 | 4 | // Components 5 | import { BuildComponent } from './build.component'; 6 | import { NavigationComponent } from '../navigation/navigation.component'; 7 | 8 | const routes = [ 9 | { path: '', component: BuildComponent, children: [ 10 | { path: '', component: NavigationComponent, children: [ 11 | // Redirecting to default pallet 12 | // { path: 'dashboard', component: DashboardComponent }, 13 | // { path: 'dashboard', redirectTo: 'pallet/bsjsJllNgYkos0w3Wrtv', pathMatch: 'full' }, 14 | // { path: 'pallet/:id', component: PalletComponent } 15 | ]}, 16 | { path: 'brick', loadChildren: "./brick/brickbuild.module#BrickModule" } 17 | ]} 18 | ] 19 | 20 | @NgModule({ 21 | imports: [ RouterModule.forChild(routes) ], 22 | exports: [ RouterModule ] 23 | }) 24 | export class BuildRoutingModule { } 25 | -------------------------------------------------------------------------------- /src/app/database/database.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | 3 | import { AngularFirestoreModule } from 'angularfire2/firestore'; 4 | import { DatabaseService } from './database.service'; 5 | 6 | @NgModule({ 7 | imports: [ 8 | AngularFirestoreModule 9 | ], 10 | exports: [ 11 | AngularFirestoreModule 12 | ], 13 | providers: [ 14 | DatabaseService 15 | ] 16 | }) 17 | export class DatabaseModule { } 18 | -------------------------------------------------------------------------------- /src/app/database/database.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, async } from '@angular/core/testing' 2 | import { DatabaseService } from './database.service'; 3 | import { HttpClient, HttpClientModule } from '@angular/common/http'; 4 | import { Brick, BrickAttempt } from '../schema'; 5 | 6 | describe('DatabaseService', () => { 7 | let service: DatabaseService; 8 | let spy : jasmine.SpyObj; 9 | 10 | beforeEach(() => { 11 | spy = jasmine.createSpyObj('HttpClient', ['get', 'post']); 12 | TestBed.configureTestingModule({ 13 | imports: [ HttpClientModule ], 14 | providers: [ 15 | DatabaseService 16 | ] 17 | }); 18 | service = TestBed.get(DatabaseService); 19 | }); 20 | 21 | it('should use DatabaseService', () => { 22 | expect(service).toBeDefined(); 23 | }); 24 | 25 | it('#getBrick should return the brick with the correct ID (get called once)', () => { 26 | var req = service.getBrick("lh0pzfSRgVBSZ8UBaDJb") 27 | req.subscribe( 28 | brick => expect(Object.getPrototypeOf(brick)).toBe(Brick.prototype), 29 | fail 30 | ); 31 | }); 32 | 33 | it('#createBrickAttempt should return an observable showing the correct response', () => { 34 | var brick = new BrickAttempt({ 35 | // TODO: this should not rely on a specific brick ID in database 36 | brick: "bricks/lh0pzfSRgVBSZ8UBaDJb", 37 | score: 75, 38 | student: "students/wYfB9tfvLySPQwvWs1v62DsaQiG3", 39 | answers: [ 40 | { 41 | "ech": "lol" 42 | } 43 | ] 44 | }); 45 | var req = service.createBrickAttempt(brick); 46 | req.subscribe( 47 | response => expect(response.status).toBe(200), 48 | fail 49 | ); 50 | expect(spy.get.calls).toBe(1); 51 | }); 52 | }) -------------------------------------------------------------------------------- /src/app/login/login.component.scss: -------------------------------------------------------------------------------- 1 | .login-body { 2 | min-height:100%; 3 | width:100%; 4 | } 5 | 6 | .login-container { 7 | border-radius: 5px; 8 | width:450px; 9 | } 10 | 11 | .login-logo{ 12 | font-size:20px; 13 | text-align: center; 14 | color: #0076B4; 15 | padding: 5px 20px 0; 16 | margin:0; 17 | } 18 | 19 | .login-container h1 { 20 | margin: 5px; 21 | } 22 | -------------------------------------------------------------------------------- /src/app/login/login.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from "@angular/core"; 2 | import { AuthService } from "../auth/auth.service"; 3 | 4 | @Component({ 5 | selector: 'app-login', 6 | template: ` 7 | 17 | `, 18 | styleUrls: ['./login.component.scss'] 19 | }) 20 | export class LoginComponent { 21 | userType: number; 22 | 23 | sel: boolean = false; 24 | 25 | constructor(public authService: AuthService) { } 26 | 27 | selected(userType: number) { 28 | this.sel = true; 29 | this.userType = userType; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/app/login/login.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from "@angular/core"; 2 | import { CommonModule } from "@angular/common"; 3 | import { FlexLayoutModule } from "@angular/flex-layout"; 4 | import { LoginComponent } from "./login.component"; 5 | import { UserTypeComponent } from "./user-type.component"; 6 | import { SignInComponent } from "./sign-in.component"; 7 | import { AuthModule } from "../auth/auth.module"; 8 | import { MaterialModule } from "../material.module"; 9 | 10 | @NgModule({ 11 | imports: [ CommonModule, FlexLayoutModule, MaterialModule, AuthModule ], 12 | declarations: [ LoginComponent, UserTypeComponent, SignInComponent ], 13 | exports: [ LoginComponent ] 14 | }) 15 | export class LoginModule { } 16 | -------------------------------------------------------------------------------- /src/app/login/sign-in.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsguru-git/learning-fortress-frontend/06c21f38695e15334385964bff82dba2d712ac0a/src/app/login/sign-in.component.scss -------------------------------------------------------------------------------- /src/app/login/sign-in.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input } from "@angular/core"; 2 | import { AuthService } from "../auth/auth.service"; 3 | 4 | @Component({ 5 | selector: 'sign-in', 6 | template: ` 7 | 8 | `, 9 | styleUrls: ['./sign-in.component.scss'] 10 | }) 11 | export class SignInComponent { 12 | @Input() userType: number; 13 | 14 | constructor(public authService: AuthService) { } 15 | } 16 | -------------------------------------------------------------------------------- /src/app/login/user-type.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Output, EventEmitter } from "@angular/core"; 2 | import { Router } from "@angular/router"; 3 | import { MatSnackBar } from "@angular/material/snack-bar"; 4 | 5 | @Component({ 6 | selector: 'user-type', 7 | template: ` 8 | 22 | `, 23 | styles: [] 24 | }) 25 | export class UserTypeComponent { 26 | @Output() select = new EventEmitter(); 27 | 28 | constructor(private router: Router, private snackbar: MatSnackBar) { } 29 | 30 | signIn(userType: number) { 31 | switch(userType) { 32 | case 1: this.select.emit(userType); return; 33 | case 2: this.snackbar.open("Sorry, you can't log in as a teacher yet. Please log in as a student.", "", { duration: 3000 }); return; 34 | case 3: this.snackbar.open("Sorry, you can't log in as a builder yet. Please log in as a student.", "", { duration: 3000 }); return; 35 | default: this.snackbar.open("This option is invalid."); return; 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/app/manage/dashboard/dashboard.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

Manage Bricks

4 |
5 | 6 | 7 | {{brick.title}} 8 | 9 | 10 |
-------------------------------------------------------------------------------- /src/app/manage/dashboard/dashboard.component.scss: -------------------------------------------------------------------------------- 1 | .bricks-container { 2 | max-width: 150vh; 3 | margin: 0 auto; 4 | .bricks-list { 5 | mat-grid-tile { 6 | p { margin: 10px; } 7 | background: #111F33; 8 | color: #ffffff; 9 | } 10 | margin: 10px; 11 | } 12 | } 13 | 14 | /* To create the right kind of cursor when hovering over cards */ 15 | mat-grid-tile { 16 | cursor: pointer; 17 | } 18 | 19 | /* Set the alignment and padding of the text inside the brick */ 20 | .text-inside-grid { 21 | text-align: center; 22 | } 23 | 24 | .mat-figure { 25 | padding: 5px; 26 | } -------------------------------------------------------------------------------- /src/app/manage/dashboard/dashboard.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input } from "@angular/core"; 2 | import { Pallet, Brick } from "../../schema"; 3 | import { ActivatedRoute, ParamMap } from "@angular/router"; 4 | import { DatabaseService } from "../../database/database.service"; 5 | import { Observable } from "rxjs"; 6 | 7 | @Component({ 8 | selector: 'dashboard', 9 | templateUrl: './dashboard.component.html', 10 | styleUrls: ['./dashboard.component.scss'] 11 | }) 12 | export class DashboardComponent { 13 | bricks: Observable; 14 | 15 | constructor(public database: DatabaseService) { 16 | this.bricks = database.getBricks(); 17 | } 18 | // palletId: string; 19 | // pallet: Observable; 20 | // bricks: Observable; 21 | 22 | // constructor(public database: DatabaseService) { 23 | // this.pallet = database.getPallet('bsjsJllNgYkos0w3Wrtv'); 24 | // this.pallet.subscribe((pallet) => { 25 | // this.bricks = database.getBricksInPallet(pallet); 26 | // }) 27 | // } 28 | } -------------------------------------------------------------------------------- /src/app/manage/manage.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { ManageComponent } from './manage.component'; 4 | 5 | describe('ManageComponent', () => { 6 | let component: ManageComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ ManageComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(ManageComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/manage/manage.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | template: '' 5 | }) 6 | export class ManageComponent { } 7 | -------------------------------------------------------------------------------- /src/app/manage/manage.module.ts: -------------------------------------------------------------------------------- 1 | import { CommonModule } from '@angular/common'; 2 | import { NgModule } from '@angular/core'; 3 | import { FlexLayoutModule } from '@angular/flex-layout'; 4 | import { MaterialModule } from '../material.module'; 5 | import { ManageComponent } from './manage.component'; 6 | import { ManageRoutingModule } from './manage.routing'; 7 | import { NavigationModule } from '../navigation/navigation.module'; 8 | import { DashboardComponent } from './dashboard/dashboard.component'; 9 | 10 | @NgModule({ 11 | imports: [ ManageRoutingModule, CommonModule, FlexLayoutModule, MaterialModule, NavigationModule], 12 | declarations: [ ManageComponent, DashboardComponent ], 13 | exports: [ NavigationModule ] 14 | }) 15 | export class ManageModule { } -------------------------------------------------------------------------------- /src/app/manage/manage.routing.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { RouterModule } from '@angular/router'; 3 | 4 | // Components 5 | import { NavigationComponent } from '../navigation/navigation.component'; 6 | import { ManageComponent } from './manage.component'; 7 | import { DashboardComponent } from './dashboard/dashboard.component'; 8 | 9 | const routes = [ 10 | { path: '', component: ManageComponent, children: [ 11 | { path: '', component: NavigationComponent, children: [ 12 | { path: 'dashboard', component: DashboardComponent }, 13 | { path: '', redirectTo: 'dashboard', pathMatch: 'full' } 14 | ]} 15 | ]} 16 | ] 17 | 18 | @NgModule({ 19 | imports: [ RouterModule.forChild(routes) ], 20 | exports: [ RouterModule ] 21 | }) 22 | export class ManageRoutingModule { } -------------------------------------------------------------------------------- /src/app/material.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | 3 | import { MatToolbarModule } from '@angular/material/toolbar' 4 | import { MatButtonModule } from '@angular/material/button'; 5 | import { MatIconModule } from '@angular/material/icon'; 6 | import { MatSidenavModule } from '@angular/material/sidenav'; 7 | import { MatListModule } from '@angular/material/list'; 8 | import { MatChipsModule } from '@angular/material/chips'; 9 | import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; 10 | import { MatExpansionModule } from '@angular/material/expansion'; 11 | import { MatStepperModule } from '@angular/material/stepper'; 12 | import { MatButtonToggleModule } from '@angular/material/button-toggle'; 13 | import { MatCardModule } from '@angular/material/card'; 14 | import { MatMenuModule } from '@angular/material/menu'; 15 | import { MatFormFieldModule } from '@angular/material/form-field'; 16 | import { MatInputModule } from '@angular/material/input'; 17 | import { MatTreeModule } from '@angular/material/tree'; 18 | import { MatGridListModule } from '@angular/material/grid-list'; 19 | import { MatCheckboxModule } from '@angular/material/checkbox'; 20 | import { MatSnackBarModule } from '@angular/material/snack-bar'; 21 | 22 | @NgModule({ 23 | imports: [ 24 | MatToolbarModule, MatIconModule, MatButtonModule, MatSidenavModule, 25 | MatListModule, MatChipsModule, MatProgressSpinnerModule, MatExpansionModule, 26 | MatStepperModule, MatButtonToggleModule, MatCardModule, MatMenuModule, 27 | MatFormFieldModule, MatInputModule, MatTreeModule, MatGridListModule, 28 | MatCheckboxModule, MatSnackBarModule], 29 | exports: [ 30 | MatToolbarModule, MatIconModule, MatButtonModule, MatSidenavModule, 31 | MatListModule, MatChipsModule, MatProgressSpinnerModule, MatExpansionModule, 32 | MatStepperModule, MatButtonToggleModule, MatCardModule, MatMenuModule, 33 | MatFormFieldModule, MatInputModule, MatTreeModule, MatGridListModule, 34 | MatCheckboxModule, MatSnackBarModule], 35 | }) 36 | export class MaterialModule { } 37 | -------------------------------------------------------------------------------- /src/app/navigation/navigation.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 | 6 | Homepage 7 | 8 | home 9 | 10 | 11 | 12 | {{pallet.name}} 13 | 14 | 15 | {{pallet.subject}} 16 | 17 | 18 | 19 | 20 | 21 | 22 | Manage 23 | 24 | assignmant 25 | 26 | 27 | 28 | 29 | 30 |
31 | 35 | 38 |
39 | 40 | 41 | 42 |
43 | 48 |
49 |
50 | 51 | 55 | 56 | 57 |
58 |
59 |
60 | -------------------------------------------------------------------------------- /src/app/navigation/navigation.component.scss: -------------------------------------------------------------------------------- 1 | .sidenav-container { 2 | top: 0; 3 | bottom: 0; 4 | left: 0; 5 | right: 0; 6 | height: 100%; 7 | background: #eee; 8 | } 9 | 10 | .sidenav { 11 | width: 20vw; 12 | } 13 | 14 | .profile-picture { 15 | border-radius: 50%; 16 | width: 40px; 17 | margin-right: 10px; 18 | } -------------------------------------------------------------------------------- /src/app/navigation/navigation.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { AuthService } from '../auth/auth.service'; 3 | import { DatabaseService } from '../database/database.service'; 4 | import { Pallet } from '../schema'; 5 | import { Observable } from 'rxjs'; 6 | import { Router } from '@angular/router'; 7 | 8 | @Component({ 9 | selector: 'navigation', 10 | templateUrl: './navigation.component.html', 11 | styleUrls: ['./navigation.component.scss'], 12 | }) 13 | 14 | export class NavigationComponent { 15 | title = 'navigation'; 16 | pallets: Observable; 17 | 18 | constructor(public auth: AuthService, public database: DatabaseService, private router: Router) { 19 | auth.user.subscribe((user) => { 20 | database.getStudentPallets(user.uid).subscribe((studentPallets) => { 21 | this.pallets = database.getPallets(studentPallets); 22 | }) 23 | }) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/app/navigation/navigation.module.ts: -------------------------------------------------------------------------------- 1 | import { CommonModule } from '@angular/common'; 2 | import { NgModule } from '@angular/core'; 3 | import { FlexLayoutModule } from '@angular/flex-layout'; 4 | import { MaterialModule } from '../material.module'; 5 | import { RouterModule } from '@angular/router'; 6 | 7 | // Components 8 | import { NavigationComponent } from './navigation.component'; 9 | 10 | @NgModule({ 11 | imports: [ CommonModule, FlexLayoutModule, MaterialModule, RouterModule ], 12 | declarations: [ NavigationComponent ], 13 | exports: [ NavigationComponent ] 14 | }) 15 | export class NavigationModule { } -------------------------------------------------------------------------------- /src/app/play/brick/brick.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 |
5 |
6 | 7 |
8 |
9 | -------------------------------------------------------------------------------- /src/app/play/brick/brick.component.scss: -------------------------------------------------------------------------------- 1 | .fixed-center { 2 | margin:auto; 3 | } 4 | 5 | .spinner-container { 6 | position: fixed; 7 | top: 0; 8 | right: 0; 9 | bottom: 0; 10 | left: 0; 11 | display: flex; 12 | } 13 | 14 | .back-container { 15 | width: 100%; 16 | height: 100%; 17 | background-color: #ffffff; 18 | } 19 | 20 | .brick-container { 21 | max-width: 800px; 22 | margin: 0 auto; 23 | height: 100%; 24 | } 25 | 26 | .begin-row { 27 | margin-top: 15px; 28 | margin-bottom: 15px; 29 | } 30 | 31 | -------------------------------------------------------------------------------- /src/app/play/brick/brick.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | import { Router, ActivatedRoute, ParamMap } from '@angular/router'; 4 | 5 | import { BrickService } from './brick.service'; 6 | import { Brick } from '../../schema'; 7 | 8 | @Component({ 9 | templateUrl: './brick.component.html', 10 | styleUrls: ['./brick.component.scss'], 11 | styles: [ 12 | ':host >>> .background-green { background: green; }', 13 | ':host >>> .color-red { color: red; }', 14 | ':host >>> .nowrap { white-space: nowrap; }' 15 | ] 16 | }) 17 | export class BrickComponent { 18 | constructor( 19 | private bricks: BrickService, 20 | private route: ActivatedRoute 21 | ) { 22 | this.route.paramMap 23 | .subscribe((data: ParamMap) => { 24 | var id = data.get('id'); 25 | bricks.loadBrick(id); 26 | bricks.currentBrick.subscribe((data) => { 27 | this.brick = data; 28 | }) 29 | }) 30 | } 31 | 32 | brick: Brick; 33 | } -------------------------------------------------------------------------------- /src/app/play/brick/brick.module.ts: -------------------------------------------------------------------------------- 1 | import { CommonModule } from '@angular/common'; 2 | import { NgModule } from '@angular/core'; 3 | import { MaterialModule } from '../../material.module'; 4 | import { MatIconModule } from '@angular/material/icon'; 5 | import { BrickRoutingModule } from './brick.routing'; 6 | import { CompModule } from './comp/comp.module'; 7 | 8 | import { BrickComponent } from './brick.component'; 9 | import { IntroductionComponent } from './introduction.component'; 10 | import { LiveComponent } from './live.component'; 11 | 12 | import { BrickTimePipe } from './brickTime.pipe'; 13 | import { QuestionComponent } from './question.component'; 14 | import { DragulaModule } from 'ng2-dragula'; 15 | import { NgArrayPipesModule } from 'ngx-pipes'; 16 | import { SummaryComponent } from './summary.component'; 17 | import { FlexLayoutModule } from '@angular/flex-layout'; 18 | import { ReviewComponent } from './review.component'; 19 | import { EndingComponent } from './ending.component'; 20 | import { TimerComponent } from './timer.component'; 21 | import { ReversePipe } from '../../build/brick/comp/comp_reverse.pipe'; 22 | 23 | @NgModule({ 24 | imports: [ BrickRoutingModule, CommonModule, MaterialModule, MatIconModule, DragulaModule, CompModule, NgArrayPipesModule, FlexLayoutModule ], 25 | declarations: [ 26 | BrickComponent, TimerComponent, IntroductionComponent, LiveComponent, QuestionComponent, SummaryComponent, ReviewComponent, EndingComponent, BrickTimePipe, ReversePipe, 27 | ], 28 | providers: [ 29 | BrickTimePipe, ReversePipe 30 | ] 31 | }) 32 | export class BrickModule { } 33 | -------------------------------------------------------------------------------- /src/app/play/brick/brick.routing.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { RouterModule, Routes } from '@angular/router'; 3 | 4 | import { BrickComponent } from './brick.component'; 5 | import { IntroductionComponent } from './introduction.component'; 6 | import { LiveComponent } from './live.component'; 7 | import { SummaryComponent } from './summary.component'; 8 | import { ReviewComponent } from './review.component'; 9 | import { EndingComponent } from './ending.component'; 10 | 11 | const routes = [ 12 | {path: ':id', component: BrickComponent, children: [ 13 | { path: 'intro', component: IntroductionComponent }, 14 | { path: 'live', component: LiveComponent }, 15 | { path: 'summary', component: SummaryComponent }, 16 | { path: 'review', component: ReviewComponent }, 17 | { path: 'ending', component: EndingComponent }, 18 | { path: '', redirectTo: 'intro', pathMatch: 'full' } 19 | ]} 20 | ] 21 | 22 | @NgModule({ 23 | imports: [ RouterModule.forChild(routes) ], 24 | exports: [ RouterModule ] 25 | }) 26 | export class BrickRoutingModule { } 27 | -------------------------------------------------------------------------------- /src/app/play/brick/brick.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { DatabaseService } from '../../database/database.service'; 3 | 4 | import { BehaviorSubject, Observable } from 'rxjs'; 5 | 6 | import { Brick, BrickAttempt, Pallet } from '../../schema'; 7 | 8 | @Injectable({ 9 | providedIn: 'root' 10 | }) 11 | export class BrickService { 12 | constructor(public database: DatabaseService) { 13 | this.currentBrick = new BehaviorSubject(null); 14 | } 15 | 16 | currentBrick: BehaviorSubject; 17 | currentPallet: Observable; 18 | currentBrickAttempt: BrickAttempt; 19 | 20 | loadBrick(id: string) { 21 | this.currentBrick = this.database.getBrick(id); 22 | this.currentBrick.subscribe(val => { 23 | if(val) { 24 | this.currentPallet = this.database.getPallet(val.pallet.id); 25 | } 26 | }) 27 | } 28 | 29 | publishBrickAttempt(ba: BrickAttempt) { 30 | this.database.createBrickAttempt(ba).subscribe((msg) => { 31 | console.log(msg); 32 | }); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/app/play/brick/brickTime.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | 3 | @Pipe({ name: 'brickTime' }) 4 | export class BrickTimePipe implements PipeTransform { 5 | transform(value: number, type: string): number { 6 | switch(type) { 7 | case "intro": switch(value) { 8 | case 1: return 300000; 9 | case 2: return 600000; 10 | case 3: return 900000; 11 | default: return 0; 12 | } 13 | case "live": switch(value) { 14 | case 1: return 500000; 15 | case 2: return 1000000; 16 | case 3: return 1500000; 17 | default: return 0; 18 | } 19 | case "summary": switch(value) { 20 | case 1: return 200000; 21 | case 2: return 400000; 22 | case 3: return 600000; 23 | default: return 0; 24 | } 25 | case "review": switch(value) { 26 | case 1: return 200000; 27 | case 2: return 400000; 28 | case 3: return 600000; 29 | default: return 0; 30 | } default: switch(value) { 31 | case 1: return 1200000; 32 | case 2: return 2400000; 33 | case 3: return 3600000; 34 | default: return 0; 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/app/play/brick/comp/comp.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input } from "@angular/core"; 2 | import { Comp, ComponentAttempt } from "../../../schema"; 3 | 4 | @Component({ 5 | template: `` 6 | }) 7 | export class CompComponent { 8 | constructor() { 9 | } 10 | 11 | @Input() data: Comp; 12 | @Input() attempt: ComponentAttempt; 13 | 14 | getAnswer() {}; 15 | 16 | getAttempt() : ComponentAttempt { 17 | let att = this.mark({ answer: this.getAnswer(), correct: null, marks: 0, maxMarks: 0 }, this.attempt); 18 | return att; 19 | }; 20 | 21 | mark(attempt: ComponentAttempt, prev: ComponentAttempt) : ComponentAttempt { return attempt; } 22 | } 23 | -------------------------------------------------------------------------------- /src/app/play/brick/comp/comp.module.ts: -------------------------------------------------------------------------------- 1 | import { CommonModule } from '@angular/common'; 2 | import { NgModule } from '@angular/core'; 3 | import { FormsModule } from '@angular/forms'; 4 | import { DynamicModule } from 'ng-dynamic-component'; 5 | import { NgArrayPipesModule } from 'ngx-pipes'; 6 | import { FlexLayoutModule } from '@angular/flex-layout'; 7 | import { DragulaModule } from 'ng2-dragula'; 8 | import { MaterialModule } from '../../../material.module'; 9 | import { CompComponent } from './comp.component'; 10 | import { MultipleChoiceComponent } from './comp_multiple_choice.component'; 11 | import { OrderComponent } from './comp_order.component'; 12 | import { SingleChoiceComponent } from './comp_single_choice.component'; 13 | import { SortComponent } from './comp_sort.component'; 14 | import { TextComponent } from './comp_text.component'; 15 | import { ShortAnswerComponent } from './comp_short_answer.component'; 16 | import { HorizontalShuffleComponent } from './comp_horizontal_shuffle.component'; 17 | import { TextHighlightingComponent } from './comp_text_highlighting.component'; 18 | import { HighlightDirective } from './highlight.directive'; 19 | import { ArrowComponent } from './comp_arrow.component'; 20 | import { RevealComponent } from './comp_reveal.component'; 21 | import {AngularFittextModule} from 'angular-fittext'; 22 | import { PoemComponent } from './comp_poem.component'; 23 | 24 | let dModule = DynamicModule.withComponents([SingleChoiceComponent, MultipleChoiceComponent, 25 | TextComponent, OrderComponent, SortComponent, ShortAnswerComponent, HorizontalShuffleComponent, 26 | TextHighlightingComponent, ArrowComponent, RevealComponent, PoemComponent]) 27 | 28 | @NgModule({ 29 | imports: [ AngularFittextModule, CommonModule, FormsModule, DragulaModule, MaterialModule, NgArrayPipesModule, FlexLayoutModule, dModule ], 30 | declarations: [ CompComponent, SingleChoiceComponent, MultipleChoiceComponent, 31 | TextComponent, OrderComponent, SortComponent, ShortAnswerComponent, HorizontalShuffleComponent, 32 | TextHighlightingComponent, ArrowComponent, RevealComponent, PoemComponent, 33 | HighlightDirective ], 34 | exports: [ CompComponent, SingleChoiceComponent, MultipleChoiceComponent, 35 | TextComponent, OrderComponent, SortComponent, ShortAnswerComponent, HorizontalShuffleComponent, 36 | TextHighlightingComponent, ArrowComponent, RevealComponent, PoemComponent, 37 | HighlightDirective, 38 | dModule.ngModule, FormsModule 39 | ], 40 | providers: [ dModule.providers ] 41 | }) 42 | export class CompModule { } -------------------------------------------------------------------------------- /src/app/play/brick/comp/comp_index.ts: -------------------------------------------------------------------------------- 1 | 2 | export const registry = new Map(); 3 | 4 | export function getComponent (name: string) : any { 5 | return registry.get(name); 6 | } 7 | 8 | export function getAllComponents () : any[] { 9 | return Array.from(registry.values()); 10 | } 11 | 12 | export function register (name: string) { 13 | return function(comp: any) { 14 | registry.set(name, comp); 15 | } 16 | } -------------------------------------------------------------------------------- /src/app/play/brick/comp/comp_poem.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input } from "@angular/core"; 2 | import { register } from "./comp_index"; 3 | import { Comp, ComponentAttempt } from "../../../schema"; 4 | import { CompComponent } from "./comp.component"; 5 | 6 | export class CompText extends Comp { 7 | data: { text: string } 8 | 9 | constructor(data: { text: string }) { 10 | super(); 11 | this.data = data; 12 | } 13 | } 14 | 15 | @register("Poem") 16 | @Component({ 17 | selector: "poem", 18 | template: ` 19 |
20 |
21 |
22 | `, 23 | styleUrls: ["../live.component.scss"] 24 | }) 25 | export class PoemComponent extends CompComponent { 26 | constructor() { super() } 27 | 28 | @Input() data: CompText; 29 | 30 | getAnswer() : null { return null; } 31 | } -------------------------------------------------------------------------------- /src/app/play/brick/comp/comp_reveal.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input } from "@angular/core"; 2 | import { register } from "./comp_index"; 3 | import { Comp, ComponentAttempt } from "../../../schema"; 4 | import { CompComponent } from "./comp.component"; 5 | 6 | export class CompText extends Comp { 7 | data: { text: string } 8 | 9 | constructor(data: { text: string }) { 10 | super(); 11 | this.data = data; 12 | } 13 | } 14 | 15 | @register("Reveal") 16 | @Component({ 17 | selector: "reveal", 18 | template: ` 19 |
20 |
21 | {{data.data.text}} 22 |
23 |
24 | `, 25 | styleUrls: ["../live.component.scss"] 26 | }) 27 | export class RevealComponent extends CompComponent { 28 | constructor() { super() } 29 | 30 | @Input() data: CompText; 31 | 32 | getAnswer() : null { return null; } 33 | } -------------------------------------------------------------------------------- /src/app/play/brick/comp/comp_single_choice.component.ts: -------------------------------------------------------------------------------- 1 | import { Comp, ComponentAttempt } from "../../../schema"; 2 | import { Component, Input } from "@angular/core"; 3 | 4 | import { register } from './comp_index'; 5 | import { CompComponent } from "./comp.component"; 6 | import { MAT_CHECKBOX_CLICK_ACTION } from "@angular/material/checkbox"; 7 | 8 | export class CompSingleChoice extends Comp { 9 | name = "Single Choice"; 10 | data: { choices:string[], reveals:string[] } 11 | 12 | constructor(data: { choices:string[], reveals:string[] }) { 13 | super(); 14 | this.data = data; 15 | } 16 | } 17 | 18 | @register("SingleChoice") 19 | @Component({ 20 | selector: "single-choice", 21 | template: ` 22 | 23 | 24 |
25 | 26 |
27 |
28 |
{{ choice }}
29 |
{{ data.data.reveals[getChoice(choice)] }}
30 |
31 |
32 |
33 |
34 |
35 | `, 36 | styleUrls: ["../live.component.scss"], 37 | providers: [ 38 | {provide: MAT_CHECKBOX_CLICK_ACTION, useValue: 'noop'} 39 | ] 40 | }) 41 | export class SingleChoiceComponent extends CompComponent { 42 | constructor() { super() } 43 | 44 | @Input() data: CompSingleChoice; 45 | answer: string; 46 | 47 | ngOnInit() { 48 | if(this.attempt) { 49 | this.answer = this.data.data.choices[this.attempt.answer]; 50 | } 51 | } 52 | 53 | getAnswer() : number { 54 | return this.data.data.choices.indexOf(this.answer); 55 | } 56 | 57 | getChoice(choice) : number { 58 | return this.data.data.choices.indexOf(choice); 59 | } 60 | 61 | getState(choice) : number { 62 | if(this.getChoice(choice) == this.attempt.answer) { 63 | if(this.getChoice(choice) == 0) { 64 | return 1; 65 | } else { 66 | return -1; 67 | } 68 | } else { 69 | return 0; 70 | } 71 | } 72 | 73 | mark(attempt: ComponentAttempt, prev: ComponentAttempt) : ComponentAttempt { 74 | // If the question is answered in review phase, add 2 to the mark and not 5. 75 | let markIncrement = prev ? 2 : 5; 76 | // set attempt.correct to true if the answer is 0. 77 | attempt.correct = (attempt.answer == 0); 78 | attempt.maxMarks = 5; 79 | // if the attempt is correct, add the mark increment. 80 | if(attempt.correct) attempt.marks = markIncrement; 81 | // if there is an answer given and the program is in the live phase, give the student an extra mark. 82 | else if (attempt.answer != null && !prev) attempt.marks = 1; 83 | else attempt.marks = 0; 84 | return attempt; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/app/play/brick/comp/comp_text.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input } from "@angular/core"; 2 | import { register } from "./comp_index"; 3 | import { Comp, ComponentAttempt } from "../../../schema"; 4 | import { CompComponent } from "./comp.component"; 5 | 6 | export class CompText extends Comp { 7 | data: { text: string } 8 | 9 | constructor(data: { text: string }) { 10 | super(); 11 | this.data = data; 12 | } 13 | } 14 | 15 | @register("Text") 16 | @Component({ 17 | selector: "text", 18 | template: ` 19 |
20 |
21 |
22 | `, 23 | styleUrls: ["../live.component.scss"] 24 | }) 25 | export class TextComponent extends CompComponent { 26 | constructor() { super() } 27 | 28 | @Input() data: CompText; 29 | 30 | getAnswer() : null { return null; } 31 | } -------------------------------------------------------------------------------- /src/app/play/brick/comp/highlight.directive.ts: -------------------------------------------------------------------------------- 1 | import { Directive, Input, ElementRef } from "@angular/core"; 2 | 3 | @Directive({ 4 | selector: "[highlight]", 5 | }) 6 | export class HighlightDirective { 7 | _highlight: boolean; 8 | @Input() state: number; 9 | 10 | constructor(public el: ElementRef) { } 11 | 12 | @Input() set highlight(highlight: boolean) { 13 | this._highlight = highlight; 14 | this.onHighlightChange(); 15 | }; 16 | 17 | onHighlightChange() { 18 | if(!this.state) { 19 | this.el.nativeElement.style.backgroundColor = this._highlight ? "#00dece" : "#ffffff"; 20 | } else { 21 | this.el.nativeElement.style.backgroundColor = this._highlight ? (this.state == 1 ? "#55ff55" : "#ff5555") : "#ffffff" 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/app/play/brick/ending.component.html: -------------------------------------------------------------------------------- 1 |
3 |

{{ brick.subject }}/{{ (aPallet | async)?.name }}/{{ brick.type | brickTime | date: 'm' }} minutes

4 |

{{ brick.title }}

5 |
6 |
7 |
8 | 9 | 10 |
11 |
{{ (brickAttempt?.score * 100 / brickAttempt?.maxScore) | number:'0.0-0' }}%
12 |
{{ brickAttempt?.score }} / {{ brickAttempt?.maxScore }}
13 |
14 |
15 |
16 |
17 | 18 |
20 |
21 |

Return to Dashboard

play_arrow 22 |
23 | 24 | 25 | 26 | 27 | Other Information 28 | 29 | 30 |
31 |

Creator: {{ brick.creator }}

32 |
33 |
34 |

Total Users: {{ brick.totalUsers }}

35 |

average score {{ brick.avgScore }}

36 |

high score {{ brick.highScore }}

37 |
38 |
39 |
40 |
41 | -------------------------------------------------------------------------------- /src/app/play/brick/ending.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input } from "@angular/core"; 2 | import { BrickAttempt, Brick, Pallet } from "../../schema"; 3 | import { BrickService } from "./brick.service"; 4 | 5 | import { BehaviorSubject, Observable } from "rxjs"; 6 | import { ActivatedRoute, Router } from "@angular/router"; 7 | 8 | @Component({ 9 | selector: 'live-ending', 10 | templateUrl: './ending.component.html', 11 | styleUrls: ['./summary.component.scss'] 12 | }) 13 | export class EndingComponent { 14 | brickAttempt: BrickAttempt; 15 | aBrick: BehaviorSubject; 16 | aPallet: Observable; 17 | _brick: Brick 18 | 19 | constructor(private bricks: BrickService, private router: Router, private route: ActivatedRoute) { 20 | if(bricks.currentBrickAttempt == null) { 21 | router.navigate(["../live"], { relativeTo: route }); 22 | } 23 | this.aBrick = bricks.currentBrick; 24 | this.aBrick.subscribe(val => { 25 | if(val) { 26 | this.aPallet = bricks.currentPallet; 27 | this._brick = val; 28 | } 29 | }); 30 | this.brickAttempt = bricks.currentBrickAttempt; 31 | bricks.publishBrickAttempt(this.brickAttempt); 32 | } 33 | 34 | finish() { 35 | this.bricks.currentBrick = null; 36 | this.bricks.currentBrickAttempt = null; 37 | this.router.navigate(['play', 'pallet', this._brick.pallet.id]) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/app/play/brick/introduction.component.html: -------------------------------------------------------------------------------- 1 | 2 |
4 |

{{ brick.subject }}/{{ (aPallet | async)?.name }}/{{ ((brick.type | brickTime)/60000) }} minutes

5 |

{{ brick.title }}

6 | 7 | 8 | 9 | 10 | Brief 11 | 12 | 13 |
14 |
15 | 16 | 17 | 18 | Prep 19 | 20 | 21 |
22 |
23 |
24 |
26 |
27 |

Begin Brick

play_arrow 28 |
29 | 30 | 31 | 32 | 33 | Other Information 34 | 35 | 36 |
37 |

Creator: {{ brick.creator }}

38 |
39 |
40 |

Total Users: {{ brick.totalUsers }}

41 |

average score {{ brick.avgScore }}

42 |

high score {{ brick.highScore }}

43 |
44 |
45 |
46 |
47 |
48 | -------------------------------------------------------------------------------- /src/app/play/brick/introduction.component.scss: -------------------------------------------------------------------------------- 1 | .timer-container { 2 | position: fixed; 3 | bottom: 5px; 4 | right: 5px; 5 | display: flex; 6 | z-index: 100; 7 | } 8 | 9 | .timer { 10 | font-size: 20px; 11 | background-color: #fff; 12 | } 13 | 14 | .container { 15 | height: 100%; 16 | padding: 1vw 1vw 1vw 1vw; 17 | box-sizing: border-box; 18 | background-color: white; 19 | } 20 | 21 | .bottom-container { 22 | margin-top: auto; 23 | margin-bottom: 1vh; 24 | } 25 | 26 | .spacer { 27 | margin: auto; 28 | } 29 | 30 | .begin-label { 31 | font-size: 25px; 32 | margin-right: 10px; 33 | } 34 | 35 | .begin-row { 36 | margin-top: 15px; 37 | margin-bottom: 15px; 38 | } 39 | 40 | h1, h3, p { 41 | margin: 5px; 42 | } 43 | 44 | mat-panel-title { 45 | font-size: 24px; 46 | font-weight: 400; 47 | } -------------------------------------------------------------------------------- /src/app/play/brick/introduction.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | import { ActivatedRoute, Router, ParamMap } from '@angular/router'; 4 | 5 | import { switchMap } from 'rxjs/operators'; 6 | 7 | import { Brick, Pallet } from '../../schema'; 8 | import { BrickService } from './brick.service'; 9 | import { BehaviorSubject, Observable } from 'rxjs'; 10 | import { TimerService, Timer } from './timer.service'; 11 | import { BrickTimePipe } from './brickTime.pipe'; 12 | 13 | @Component({ 14 | selector: 'introduction', 15 | templateUrl: './introduction.component.html', 16 | styleUrls: ['./introduction.component.scss'] 17 | }) 18 | export class IntroductionComponent { 19 | constructor(private bricks: BrickService, timer: TimerService, private brickTime: BrickTimePipe, private router: Router, private route: ActivatedRoute) { 20 | this.timer = timer.new(); 21 | this.timer.timeResolution = 1000; 22 | this.aBrick = bricks.currentBrick; 23 | this.aBrick.subscribe(val => { 24 | if(val != null) { 25 | this.aPallet = bricks.currentPallet; 26 | this.showBrick(val); 27 | } 28 | }); 29 | } 30 | 31 | aBrick: BehaviorSubject; 32 | aPallet: Observable; 33 | timer: Timer; 34 | 35 | showBrick(brick: Brick) { 36 | let time = this.brickTime.transform(brick.type, "intro"); 37 | this.timer.countDown(time); 38 | this.timer.timeRanOut.subscribe((t) => { 39 | this.startBrick(); 40 | }) 41 | } 42 | 43 | startBrick() { 44 | this.router.navigate(['../live'], { relativeTo: this.route }) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/app/play/brick/live.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | help_outline 5 | 6 | 7 | done 8 | 9 | 10 | Question {{index+1}} 11 | 12 | 13 | 14 |
16 |
17 |

Submit Initial Brick Attempt

play_arrow 18 |
19 | -------------------------------------------------------------------------------- /src/app/play/brick/live.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, ViewChildren, QueryList } from '@angular/core'; 2 | 3 | import { BrickService } from './brick.service'; 4 | 5 | import { Brick, Question, BrickAttempt, Student, Pallet } from '../../schema'; 6 | import { Observable } from 'rxjs'; 7 | import { TimerService, Timer } from './timer.service'; 8 | import { BrickTimePipe } from './brickTime.pipe'; 9 | 10 | import { CompComponent } from './comp/comp.component'; 11 | import { QuestionComponent } from './question.component'; 12 | import { Router, ActivatedRoute } from '@angular/router'; 13 | import { AuthService } from '../../auth/auth.service'; 14 | import * as $ from 'jquery'; 15 | 16 | @Component({ 17 | selector: 'live', 18 | templateUrl: './live.component.html', 19 | styleUrls: ['./live.component.scss'], 20 | providers: [ ] 21 | }) 22 | export class LiveComponent { 23 | constructor(public bricks: BrickService, timer: TimerService, brickTime: BrickTimePipe, public router: Router, public route: ActivatedRoute, public auth: AuthService) { 24 | this.brick = bricks.currentBrick.asObservable(); 25 | this.timer = timer.new(); 26 | this.timer.timeResolution = 1000; 27 | this.brickTime = brickTime; 28 | bricks.currentBrick.subscribe((data) => { 29 | if(data != null) { 30 | this._brick = data; 31 | this.showBrick(this._brick); 32 | } 33 | }); 34 | } 35 | 36 | brick: Observable; 37 | timer : Timer; 38 | 39 | private _brick: Brick; 40 | private brickTime: BrickTimePipe; 41 | 42 | @ViewChildren(QuestionComponent) questions : QueryList; 43 | 44 | showBrick(brick: Brick) { 45 | let time = this.brickTime.transform(brick.type, "live"); 46 | this.timer.countDown(time); 47 | this.timer.timeRanOut.subscribe((t) => { 48 | this.finishBrick(); 49 | }) 50 | } 51 | 52 | finishBrick() { 53 | this.timer.stop(); 54 | console.log("finished in " + this.timer.timeElapsed.getTime() / 1000); 55 | 56 | // Get brick data 57 | this.auth.user.subscribe((user) => { 58 | let answers = this.questions.map((question) => { 59 | return question.getAttempt(); 60 | }) 61 | let score = answers.reduce((acc, answer) => acc + answer.marks, 0); 62 | let maxScore = answers.reduce((acc, answer) => acc + answer.maxMarks, 0); 63 | var ba : BrickAttempt = { 64 | brick: this._brick._ref, 65 | score: score, 66 | maxScore: maxScore, 67 | student: this.bricks.database.afs.doc("students/"+user.uid).ref, 68 | answers: answers 69 | }; 70 | console.log(`score is ${score} out of ${maxScore}, which is ${score * 100 / maxScore}%`); 71 | this.bricks.currentBrickAttempt = ba; 72 | this.router.navigate(["../summary"], { relativeTo: this.route }); 73 | }) 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /src/app/play/brick/question.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input, ViewChildren, QueryList } from "@angular/core"; 2 | import { Question, QuestionAttempt } from "../../schema"; 3 | import { CompComponent } from "./comp/comp.component"; 4 | import { DynamicComponent } from "ng-dynamic-component"; 5 | import { ShufflePipe } from "ngx-pipes"; 6 | 7 | @Component({ 8 | selector: 'question', 9 | template: ` 10 | 16 | ` 17 | }) 18 | export class QuestionComponent { 19 | @Input() question: Question; 20 | @Input() attempt: QuestionAttempt; 21 | 22 | @ViewChildren(DynamicComponent) private dynamicComponents: QueryList; 23 | components: CompComponent[]; 24 | 25 | constructor() { 26 | } 27 | 28 | getAttempt() : QuestionAttempt { 29 | this.components = this.dynamicComponents.map((dynComp) => { 30 | return dynComp.componentRef.instance as CompComponent; 31 | }); 32 | 33 | let compAttempts = this.components.map((comp) => { 34 | return comp.getAttempt(); 35 | }) 36 | 37 | let correct = compAttempts.every(attempt => attempt.correct || attempt.correct == null); 38 | let marks = compAttempts.reduce((acc, attempt) => acc + attempt.marks, 0); 39 | let maxMarks = compAttempts.reduce((acc, attempt) => acc + attempt.maxMarks, 0); 40 | 41 | var qa : QuestionAttempt = { 42 | question: this.question._ref, 43 | components: compAttempts, 44 | correct: correct, 45 | marks: marks, 46 | maxMarks: maxMarks 47 | }; 48 | return qa; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/app/play/brick/review.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Question {{index+1}} 5 | 6 |
Question is correct!
7 |
8 |
9 |
11 |
12 |

Finish Brick Attempt

play_arrow 13 |
14 | -------------------------------------------------------------------------------- /src/app/play/brick/review.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, ViewChildren, QueryList } from "@angular/core"; 2 | import { Observable } from "rxjs"; 3 | import { Brick, BrickAttempt, QuestionAttempt } from "../../schema"; 4 | import { Timer, TimerService } from "./timer.service"; 5 | import { BrickTimePipe } from "./brickTime.pipe"; 6 | import { BrickService } from "./brick.service"; 7 | import { Router, ActivatedRoute } from "@angular/router"; 8 | import { AuthService } from "../../auth/auth.service"; 9 | import { QuestionComponent } from "./question.component"; 10 | 11 | @Component({ 12 | selector: 'live-review', 13 | templateUrl: './review.component.html', 14 | styleUrls: ['./live.component.scss'] 15 | }) 16 | export class ReviewComponent { 17 | constructor(public bricks: BrickService, timer: TimerService, brickTime: BrickTimePipe, public router: Router, public route: ActivatedRoute, public auth: AuthService) { 18 | this.brick = bricks.currentBrick.asObservable(); 19 | this.brickAttempt = bricks.currentBrickAttempt; 20 | if(!this.brickAttempt) { 21 | this.router.navigate(['../live'], {relativeTo: route}); 22 | } 23 | this.timer = timer.new(); 24 | this.timer.timeResolution = 1000; 25 | this.brickTime = brickTime; 26 | bricks.currentBrick.subscribe((data) => { 27 | if(data != null) { 28 | this._brick = data; 29 | this.showBrick(this._brick); 30 | } 31 | }) 32 | } 33 | 34 | brick: Observable; 35 | brickAttempt: BrickAttempt; 36 | timer : Timer; 37 | 38 | private _brick: Brick; 39 | private brickTime: BrickTimePipe; 40 | 41 | @ViewChildren(QuestionComponent) questions : QueryList; 42 | 43 | showBrick(brick: Brick) { 44 | let time = this.brickTime.transform(brick.type, "review"); 45 | this.timer.countDown(time); 46 | this.timer.timeRanOut.subscribe((t) => { 47 | this.finishBrick(); 48 | }) 49 | } 50 | 51 | finishBrick() { 52 | this.timer.stop(); 53 | console.log("finished in " + this.timer.timeElapsed.getTime() / 1000); 54 | 55 | // Get brick data 56 | this.auth.user.subscribe((user) => { 57 | let answers = this.questions.map((question) => { 58 | return question.getAttempt(); 59 | }) 60 | let score = answers.reduce((acc, answer) => acc + answer.marks, 0) + this.bricks.currentBrickAttempt.score; 61 | var ba : BrickAttempt = { 62 | brick: this._brick._ref, 63 | score: score, 64 | oldScore: this.bricks.currentBrickAttempt.score, 65 | maxScore: this.bricks.currentBrickAttempt.maxScore, 66 | student: this.bricks.database.afs.doc("students/"+user.uid).ref, 67 | answers: answers 68 | }; 69 | console.log(`score is ${score} out of ${this.bricks.currentBrickAttempt.maxScore}, which is ${score * 100 / this.bricks.currentBrickAttempt.maxScore}%`); 70 | this.bricks.currentBrickAttempt = ba; 71 | this.router.navigate(["../ending"], { relativeTo: this.route }); 72 | }) 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/app/play/brick/summary.component.html: -------------------------------------------------------------------------------- 1 | 2 |
4 |

{{ brick.subject }}/{{ (aPallet | async)?.name }}/{{ ((brick.type | brickTime)/60000) }} minutes

5 |

{{ brick.title }}

6 | 7 |
8 |
9 | 10 | 11 | Explanatory Review 12 | 13 | 14 | 15 |
16 | 17 |
18 |
{{ (brickAttempt?.score * 100 / brickAttempt?.maxScore) | number:'0.0-0' }}%
19 |
{{ brickAttempt?.score }} / {{ brickAttempt?.maxScore }}
20 |
21 |
22 |
23 |
24 | 25 |
27 |
28 |

Review Brick Attempt

play_arrow 29 |
30 | 31 | 32 | 33 | 34 | Other Information 35 | 36 | 37 |
38 |

Creator: {{ brick.creator }}

39 |
40 |
41 |

Total Users: {{ brick.totalUsers }}

42 |

average score {{ brick.avgScore }}

43 |

high score {{ brick.highScore }}

44 |
45 |
46 |
47 |
48 |
49 | -------------------------------------------------------------------------------- /src/app/play/brick/summary.component.scss: -------------------------------------------------------------------------------- 1 | .container { 2 | padding: 1vw; 3 | height: 100%; 4 | bottom: 0; 5 | background-color: white; 6 | } 7 | 8 | /* hide overflow to stop whitespace on right of summary phase on iphone5 width */ 9 | .score-container { 10 | overflow: hidden; 11 | } 12 | 13 | .score-wheel { 14 | position: absolute; 15 | height: 350px; 16 | } 17 | 18 | .numbers-container { 19 | position: absolute; 20 | height: 350px; 21 | } 22 | 23 | .score-percentage { 24 | font-size: 50px; 25 | } 26 | 27 | .score-number { 28 | font-size: 30px; 29 | } 30 | 31 | .data-container { 32 | margin: 10px; 33 | } 34 | 35 | .begin-row { 36 | margin-top: 15px; 37 | margin-bottom: 15px; 38 | } 39 | 40 | h1, h3, p { 41 | margin: 5px; 42 | } 43 | -------------------------------------------------------------------------------- /src/app/play/brick/summary.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input } from "@angular/core"; 2 | import { BrickAttempt, Brick, Pallet } from "../../schema"; 3 | import { BrickService } from "./brick.service"; 4 | 5 | import { BehaviorSubject, Observable } from "rxjs"; 6 | import { ActivatedRoute, Router } from "@angular/router"; 7 | import { BrickTimePipe } from "./brickTime.pipe"; 8 | import { TimerService, Timer } from "./timer.service"; 9 | 10 | @Component({ 11 | selector: 'live-summary', 12 | templateUrl: './summary.component.html', 13 | styleUrls: ['./summary.component.scss'] 14 | }) 15 | export class SummaryComponent { 16 | brickAttempt: BrickAttempt; 17 | aBrick: BehaviorSubject; 18 | aPallet: Observable; 19 | 20 | timer: Timer; 21 | 22 | constructor(private bricks: BrickService, timer: TimerService, private brickTime: BrickTimePipe, private router: Router, private route: ActivatedRoute) { 23 | if(bricks.currentBrickAttempt == null) { 24 | router.navigate(["../live"], { relativeTo: route }); 25 | } 26 | this.aBrick = bricks.currentBrick; 27 | this.brickAttempt = bricks.currentBrickAttempt; 28 | this.timer = timer.new(); 29 | this.timer.timeResolution = 1000; 30 | 31 | this.aBrick.subscribe(val => { 32 | if(val) { 33 | this.aPallet = bricks.currentPallet; 34 | this.showBrick(val); 35 | } 36 | }); 37 | } 38 | 39 | showBrick(brick: Brick) { 40 | let time = this.brickTime.transform(brick.type, "summary"); 41 | this.timer.countDown(time); 42 | } 43 | 44 | startBrick() { 45 | this.router.navigate(['../review'], { relativeTo: this.route }) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/app/play/brick/timer.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input } from "@angular/core"; 2 | import { Timer } from "./timer.service"; 3 | 4 | @Component({ 5 | selector: 'timer', 6 | template: ` 7 |
8 | 9 | {{ timer.timeLeft?.valueOf() / 1000 }} seconds 10 | 11 |
12 | `, 13 | styles: [ 14 | ` 15 | .timer-container { 16 | position: fixed; 17 | bottom: 5px; 18 | right: 5px; 19 | display: flex; 20 | z-index: 100; 21 | } 22 | 23 | .timer { 24 | font-size: 20px; 25 | background-color: #fff; 26 | } 27 | ` 28 | ] 29 | }) 30 | export class TimerComponent { 31 | @Input() timer: Timer; 32 | } 33 | -------------------------------------------------------------------------------- /src/app/play/brick/timer.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, EventEmitter } from "@angular/core"; 2 | import { timer, Observable, Subscription } from "rxjs"; 3 | 4 | 5 | // TODO: Add call to API to make sure that a brick can't be reset. 6 | export class Timer { 7 | constructor () { } 8 | 9 | timeElapsed : Date; 10 | timeLeft : Date; 11 | timeLimit : Date; 12 | 13 | timeResolution: number; 14 | 15 | timer : Observable; 16 | timeRanOut: EventEmitter; 17 | private subscription : Subscription; 18 | 19 | countUp() { 20 | this.timer = timer(0, this.timeResolution); 21 | this.timeRanOut = null; 22 | this.subscription = this.timer.subscribe((t) => { 23 | this.timeElapsed = new Date(t*this.timeResolution); 24 | }) 25 | } 26 | 27 | countDown(timeLimit: number) { 28 | this.timer = timer(0, this.timeResolution); 29 | this.timeLimit = new Date(timeLimit); 30 | this.timeRanOut = new EventEmitter() 31 | this.subscription = this.timer.subscribe((t) => { 32 | this.timeElapsed = new Date(t * this.timeResolution); 33 | this.timeLeft = new Date(this.timeLimit.getTime() - this.timeElapsed.getTime()); 34 | if(this.timeLeft.getTime() <= 0) { 35 | this.timeRanOut.emit(); 36 | } 37 | }) 38 | } 39 | 40 | stop() { 41 | this.subscription.unsubscribe(); 42 | this.timer = null; 43 | } 44 | } 45 | 46 | @Injectable({ 47 | providedIn: 'root' 48 | }) 49 | export class TimerService { 50 | constructor () { } 51 | 52 | new () { 53 | return new Timer(); 54 | } 55 | } -------------------------------------------------------------------------------- /src/app/play/dashboard/dashboard.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 |
5 |

Lorem ipsum dolor sit amet consectetur adipisicing elit. Molestias quo commodi nostrum porro enim minus fugiat quam? Id, eveniet est praesentium quae eaque adipisci enim maiores ullam, laborum accusamus voluptate.

6 |

Nostrum eos in non quas officiis debitis, vitae laborum neque quidem at numquam dolore commodi saepe vel exercitationem laboriosam! Necessitatibus odit animi vel? Voluptate minima inventore quam saepe et sint.

7 |

Modi unde laboriosam iure? Natus libero cupiditate laudantium incidunt ipsum asperiores, vitae delectus quod totam necessitatibus, odit officia voluptas nam rerum excepturi ex minus beatae vel, quos sed fugit? Totam?

8 |
9 |
10 |
-------------------------------------------------------------------------------- /src/app/play/dashboard/dashboard.component.scss: -------------------------------------------------------------------------------- 1 | .dashboard-profile { 2 | margin: 20px 3 | } 4 | 5 | .dashboard-info { 6 | margin: 0; 7 | } 8 | -------------------------------------------------------------------------------- /src/app/play/dashboard/dashboard.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from "@angular/core"; 2 | import { AuthService } from "../../auth/auth.service"; 3 | 4 | @Component({ 5 | selector: 'dashboard', 6 | templateUrl: './dashboard.component.html', 7 | styleUrls: ['./dashboard.component.scss'] 8 | }) 9 | export class DashboardComponent { 10 | constructor(public auth: AuthService) { } 11 | } -------------------------------------------------------------------------------- /src/app/play/pallet/pallet.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

{{(pallet | async)?.name}}

4 |
5 | 6 | 7 | {{brick.title}} 8 | 9 | 10 |
11 | -------------------------------------------------------------------------------- /src/app/play/pallet/pallet.component.scss: -------------------------------------------------------------------------------- 1 | .pallets-container { 2 | max-width: 75vh; 3 | margin: 0 auto; 4 | .pallet-list { 5 | mat-grid-tile { 6 | p { margin: 10px; } 7 | } 8 | margin: 10px; 9 | } 10 | } 11 | 12 | /* To create the right kind of cursor when hovering over cards */ 13 | mat-grid-tile { 14 | cursor: pointer; 15 | } 16 | 17 | .text-inside-grid { 18 | text-align: center; 19 | padding: 2px; 20 | } -------------------------------------------------------------------------------- /src/app/play/pallet/pallet.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input } from "@angular/core"; 2 | import { Pallet, Brick } from "../../schema"; 3 | import { ActivatedRoute, ParamMap } from "@angular/router"; 4 | import { DatabaseService } from "../../database/database.service"; 5 | import { Observable } from "rxjs"; 6 | 7 | @Component({ 8 | selector: 'pallets', 9 | templateUrl: './pallet.component.html', 10 | styleUrls: ['./pallet.component.scss'] 11 | }) 12 | export class PalletComponent { 13 | palletId: string; 14 | pallet: Observable; 15 | bricks: Observable; 16 | 17 | constructor(public database: DatabaseService, public route: ActivatedRoute) { 18 | this.route.paramMap 19 | .subscribe((data: ParamMap) => { 20 | this.palletId = data.get('id'); 21 | this.pallet = database.getPallet(this.palletId) 22 | this.pallet.subscribe((pallet) => { 23 | this.bricks = database.getBricksInPallet(pallet); 24 | }) 25 | }) 26 | } 27 | } -------------------------------------------------------------------------------- /src/app/play/play.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | template: '' 5 | }) 6 | 7 | export class PlayComponent { } -------------------------------------------------------------------------------- /src/app/play/play.module.ts: -------------------------------------------------------------------------------- 1 | import { CommonModule } from '@angular/common'; 2 | import { NgModule } from '@angular/core'; 3 | import { FlexLayoutModule } from '@angular/flex-layout'; 4 | import { MaterialModule } from '../material.module'; 5 | 6 | // Components 7 | import { DashboardComponent } from './dashboard/dashboard.component'; 8 | import { PlayComponent } from './play.component'; 9 | import { PalletComponent } from './pallet/pallet.component'; 10 | import { BrickModule } from './brick/brick.module'; 11 | import { NavigationModule } from '../navigation/navigation.module'; 12 | 13 | // Routing 14 | import { PlayRoutingModule } from './play.routing'; 15 | 16 | @NgModule({ 17 | imports: [ PlayRoutingModule, CommonModule, FlexLayoutModule, MaterialModule, BrickModule, NavigationModule ], 18 | declarations: [ PlayComponent, DashboardComponent, PalletComponent ], 19 | exports: [ NavigationModule ] 20 | }) 21 | export class PlayModule { } -------------------------------------------------------------------------------- /src/app/play/play.routing.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { RouterModule } from '@angular/router'; 3 | 4 | // Components 5 | import { PlayComponent } from './play.component'; 6 | import { PalletComponent } from './pallet/pallet.component'; 7 | //https://stackoverflow.com/questions/50374284/angular2-shared-component-thats-used-for-routing 8 | import { NavigationComponent } from '../navigation/navigation.component'; 9 | 10 | const routes = [ 11 | { path: '', component: PlayComponent, children: [ 12 | { path: '', component: NavigationComponent, children: [ 13 | // Redirecting to default pallet 14 | // { path: 'dashboard', component: DashboardComponent }, 15 | { path: 'dashboard', redirectTo: 'pallet/demopallet', pathMatch: 'full' }, 16 | { path: 'pallet/:id', component: PalletComponent } 17 | ]}, 18 | { path: 'brick', loadChildren: "./brick/brick.module#BrickModule" } 19 | ]} 20 | ] 21 | 22 | @NgModule({ 23 | imports: [ RouterModule.forChild(routes) ], 24 | exports: [ RouterModule ] 25 | }) 26 | export class PlayRoutingModule { } -------------------------------------------------------------------------------- /src/assets/lflogo-English.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsguru-git/learning-fortress-frontend/06c21f38695e15334385964bff82dba2d712ac0a/src/assets/lflogo-English.png -------------------------------------------------------------------------------- /src/assets/lflogo-Maths.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsguru-git/learning-fortress-frontend/06c21f38695e15334385964bff82dba2d712ac0a/src/assets/lflogo-Maths.png -------------------------------------------------------------------------------- /src/assets/lflogo-Mixed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsguru-git/learning-fortress-frontend/06c21f38695e15334385964bff82dba2d712ac0a/src/assets/lflogo-Mixed.png -------------------------------------------------------------------------------- /src/assets/lflogo-White.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsguru-git/learning-fortress-frontend/06c21f38695e15334385964bff82dba2d712ac0a/src/assets/lflogo-White.png -------------------------------------------------------------------------------- /src/assets/lflogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsguru-git/learning-fortress-frontend/06c21f38695e15334385964bff82dba2d712ac0a/src/assets/lflogo.png -------------------------------------------------------------------------------- /src/assets/milton-q1-poem.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsguru-git/learning-fortress-frontend/06c21f38695e15334385964bff82dba2d712ac0a/src/assets/milton-q1-poem.png -------------------------------------------------------------------------------- /src/browserslist: -------------------------------------------------------------------------------- 1 | # This file is currently used by autoprefixer to adjust CSS to support the below specified browsers 2 | # For additional information regarding the format and rule options, please see: 3 | # https://github.com/browserslist/browserslist#queries 4 | # For IE 9-11 support, please uncomment the last line of the file and adjust as needed 5 | > 0.5% 6 | last 2 versions 7 | Firefox ESR 8 | not dead 9 | # IE 9-11 -------------------------------------------------------------------------------- /src/colors.scss: -------------------------------------------------------------------------------- 1 | @import './palette.scss'; 2 | 3 | $color-english-1: map-get($map: $lf-palette, $key: A200); 4 | $color-english-2: map-get($map: $lf-palette, $key: 500); 5 | $color-english-3: map-get($map: $lf-palette, $key: 900); 6 | .theme-English { 7 | .pallet-tile-1 { background-color: $color-english-1; } 8 | .pallet-tile-2 { background-color: $color-english-2; } 9 | .pallet-tile-3 { background-color: $color-english-3; color: white; } 10 | } 11 | 12 | $color-maths-1: #90E201; 13 | $color-maths-2: #74B501; 14 | $color-maths-3: #0C660C; 15 | .theme-Maths{ 16 | .pallet-tile-1 { background-color: $color-maths-1; } 17 | .pallet-tile-2 { background-color: $color-maths-2; } 18 | .pallet-tile-3 { background-color: $color-maths-3; color: white; } 19 | } 20 | 21 | $color-mixed-1: #F44336; 22 | $color-mixed-2: #00BCD4; 23 | $color-mixed-3: #4CAF50; 24 | .theme-Mixed{ 25 | .pallet-tile-1 { background-color: $color-mixed-1; color: white; } 26 | .pallet-tile-2 { background-color: $color-mixed-2; color: white; } 27 | .pallet-tile-3 { background-color: $color-mixed-3; color: white; } 28 | } 29 | -------------------------------------------------------------------------------- /src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true, 3 | firebase: { 4 | apiKey: "AIzaSyDZAppRL0CfjhcMo4-9l0DIIRdDXXexvx4", 5 | authDomain: "learning-fortress.firebaseapp.com", 6 | databaseURL: "https://learning-fortress.firebaseio.com", 7 | projectId: "learning-fortress", 8 | storageBucket: "learning-fortress.appspot.com", 9 | messagingSenderId: "45503831861" 10 | } 11 | }; -------------------------------------------------------------------------------- /src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // This file can be replaced during build by using the `fileReplacements` array. 2 | // `ng build ---prod` replaces `environment.ts` with `environment.prod.ts`. 3 | // The list of file replacements can be found in `angular.json`. 4 | 5 | export const environment = { 6 | production: false, 7 | firebase: 8 | // From your project page on Firebase click the little cog top left then 'Project settings' 9 | { 10 | // Project ID 11 | projectId: "learning-fortress-dev", 12 | // Web API Key 13 | apiKey: "AIzaSyDMaRdWEVVJ_m6XZfhqCGP0O8adcZLvxW4", 14 | // .firebaseapp.com 15 | authDomain: "learning-fortress-dev.firebaseapp.com", 16 | // .appspot.com 17 | storageBucket: "learning-fortress-dev.appspot.com", 18 | // https://.firebaseio.com 19 | databaseURL: "https://learning-fortress-dev.firebaseio.com", 20 | // Click on the next tab 'Cloud Messaging' and look for the 'Sender ID' section 21 | messagingSenderId: "890224168065" 22 | } 23 | }; 24 | 25 | /* 26 | * In development mode, to ignore zone related error stack frames such as 27 | * `zone.run`, `zoneDelegate.invokeTask` for easier debugging, you can 28 | * import the following file, but please comment it out in production mode 29 | * because it will have performance impact when throw error 30 | */ 31 | // import 'zone.js/dist/zone-error'; // Included with Angular CLI. 32 | -------------------------------------------------------------------------------- /src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsguru-git/learning-fortress-frontend/06c21f38695e15334385964bff82dba2d712ac0a/src/favicon.ico -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Learning Fortress by Scholar6 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage-istanbul-reporter'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client: { 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | dir: require('path').join(__dirname, '../coverage'), 20 | reports: ['html', 'lcovonly'], 21 | fixWebpackSourcePaths: true 22 | }, 23 | reporters: ['progress', 'kjhtml'], 24 | port: 9876, 25 | colors: true, 26 | logLevel: config.LOG_INFO, 27 | autoWatch: true, 28 | browsers: ['Chrome'], 29 | singleRun: false 30 | }); 31 | }; -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | 4 | import { AppModule } from './app/app.module'; 5 | import { environment } from './environments/environment'; 6 | 7 | if (environment.production) { 8 | enableProdMode(); 9 | } 10 | 11 | platformBrowserDynamic().bootstrapModule(AppModule) 12 | .catch(err => console.log(err)); 13 | -------------------------------------------------------------------------------- /src/palette.scss: -------------------------------------------------------------------------------- 1 | $lf-palette: ( 2 | 50 : #e0eff6, 3 | 100 : #b3d6e9, 4 | 200 : #80bbda, 5 | 300 : #4d9fcb, 6 | 400 : #268bbf, 7 | 500 : #0076b4, 8 | 600 : #006ead, 9 | 700 : #0063a4, 10 | 800 : #00599c, 11 | 900 : #0a3b7e, 12 | A100 : #b8d6ff, 13 | A200 : #5bc1ff, 14 | A400 : #529cff, 15 | A700 : #398dff, 16 | contrast: ( 17 | 50 : #000000, 18 | 100 : #000000, 19 | 200 : #000000, 20 | 300 : #000000, 21 | 400 : #ffffff, 22 | 500 : #ffffff, 23 | 600 : #ffffff, 24 | 700 : #ffffff, 25 | 800 : #ffffff, 26 | 900 : #ffffff, 27 | A100 : #000000, 28 | A200 : #000000, 29 | A400 : #000000, 30 | A700 : #000000, 31 | ) 32 | ); 33 | -------------------------------------------------------------------------------- /src/polyfills.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file includes polyfills needed by Angular and is loaded before the app. 3 | * You can add your own extra polyfills to this file. 4 | * 5 | * This file is divided into 2 sections: 6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. 7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main 8 | * file. 9 | * 10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that 11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), 12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. 13 | * 14 | * Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html 15 | */ 16 | 17 | /*************************************************************************************************** 18 | * BROWSER POLYFILLS 19 | */ 20 | 21 | /** IE9, IE10 and IE11 requires all of the following polyfills. **/ 22 | // import 'core-js/es6/symbol'; 23 | // import 'core-js/es6/object'; 24 | // import 'core-js/es6/function'; 25 | // import 'core-js/es6/parse-int'; 26 | // import 'core-js/es6/parse-float'; 27 | // import 'core-js/es6/number'; 28 | // import 'core-js/es6/math'; 29 | // import 'core-js/es6/string'; 30 | // import 'core-js/es6/date'; 31 | // import 'core-js/es6/array'; 32 | // import 'core-js/es6/regexp'; 33 | // import 'core-js/es6/map'; 34 | // import 'core-js/es6/weak-map'; 35 | // import 'core-js/es6/set'; 36 | 37 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */ 38 | // import 'classlist.js'; // Run `npm install --save classlist.js`. 39 | 40 | /** IE10 and IE11 requires the following for the Reflect API. */ 41 | // import 'core-js/es6/reflect'; 42 | 43 | 44 | /** Evergreen browsers require these. **/ 45 | // Used for reflect-metadata in JIT. If you use AOT (and only Angular decorators), you can remove. 46 | import 'core-js/es7/reflect'; 47 | 48 | 49 | /** 50 | * Web Animations `@angular/platform-browser/animations` 51 | * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari. 52 | * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0). 53 | **/ 54 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 55 | 56 | /** 57 | * By default, zone.js will patch all possible macroTask and DomEvents 58 | * user can disable parts of macroTask/DomEvents patch by setting following flags 59 | */ 60 | 61 | // (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame 62 | // (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick 63 | // (window as any).__zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames 64 | 65 | /* 66 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js 67 | * with the following flag, it will bypass `zone.js` patch for IE/Edge 68 | */ 69 | // (window as any).__Zone_enable_cross_context_check = true; 70 | 71 | /*************************************************************************************************** 72 | * Zone JS is required by default for Angular itself. 73 | */ 74 | import 'zone.js/dist/zone'; // Included with Angular CLI. 75 | 76 | 77 | 78 | /*************************************************************************************************** 79 | * APPLICATION IMPORTS 80 | */ 81 | (window as any).global = window; -------------------------------------------------------------------------------- /src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js/dist/zone-testing'; 4 | import { getTestBed } from '@angular/core/testing'; 5 | import { 6 | BrowserDynamicTestingModule, 7 | platformBrowserDynamicTesting 8 | } from '@angular/platform-browser-dynamic/testing'; 9 | 10 | declare const require: any; 11 | 12 | // First, initialize the Angular testing environment. 13 | getTestBed().initTestEnvironment( 14 | BrowserDynamicTestingModule, 15 | platformBrowserDynamicTesting() 16 | ); 17 | // Then we find all the tests. 18 | const context = require.context('./', true, /\.spec\.ts$/); 19 | // And load the modules. 20 | context.keys().map(context); 21 | -------------------------------------------------------------------------------- /src/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/app", 5 | "module": "es2015", 6 | "types": [] 7 | }, 8 | "exclude": [ 9 | "src/test.ts", 10 | "**/*.spec.ts" 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /src/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/spec", 5 | "module": "commonjs", 6 | "types": [ 7 | "jasmine", 8 | "node" 9 | ] 10 | }, 11 | "files": [ 12 | "test.ts", 13 | "polyfills.ts" 14 | ], 15 | "include": [ 16 | "**/*.spec.ts", 17 | "**/*.d.ts" 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /src/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tslint.json", 3 | "rules": { 4 | "directive-selector": [ 5 | true, 6 | "attribute", 7 | "app", 8 | "camelCase" 9 | ], 10 | "component-selector": [ 11 | true, 12 | "element", 13 | "app", 14 | "kebab-case" 15 | ] 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "baseUrl": "./", 5 | "outDir": "./dist/out-tsc", 6 | "sourceMap": true, 7 | "declaration": false, 8 | "moduleResolution": "node", 9 | "emitDecoratorMetadata": true, 10 | "experimentalDecorators": true, 11 | "target": "es5", 12 | "typeRoots": [ 13 | "node_modules/@types" 14 | ], 15 | "lib": [ 16 | "es2017", 17 | "dom" 18 | ] 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": [ 3 | "node_modules/codelyzer" 4 | ], 5 | "rules": { 6 | "arrow-return-shorthand": true, 7 | "callable-types": true, 8 | "class-name": true, 9 | "comment-format": [ 10 | true, 11 | "check-space" 12 | ], 13 | "curly": true, 14 | "deprecation": { 15 | "severity": "warn" 16 | }, 17 | "eofline": true, 18 | "forin": true, 19 | "import-blacklist": [ 20 | true, 21 | "rxjs/Rx" 22 | ], 23 | "import-spacing": true, 24 | "indent": [ 25 | true, 26 | "spaces" 27 | ], 28 | "interface-over-type-literal": true, 29 | "label-position": true, 30 | "max-line-length": [ 31 | true, 32 | 140 33 | ], 34 | "member-access": false, 35 | "member-ordering": [ 36 | true, 37 | { 38 | "order": [ 39 | "static-field", 40 | "instance-field", 41 | "static-method", 42 | "instance-method" 43 | ] 44 | } 45 | ], 46 | "no-arg": true, 47 | "no-bitwise": true, 48 | "no-console": [ 49 | true, 50 | "debug", 51 | "info", 52 | "time", 53 | "timeEnd", 54 | "trace" 55 | ], 56 | "no-construct": true, 57 | "no-debugger": true, 58 | "no-duplicate-super": true, 59 | "no-empty": false, 60 | "no-empty-interface": true, 61 | "no-eval": true, 62 | "no-inferrable-types": [ 63 | true, 64 | "ignore-params" 65 | ], 66 | "no-misused-new": true, 67 | "no-non-null-assertion": true, 68 | "no-shadowed-variable": true, 69 | "no-string-literal": false, 70 | "no-string-throw": true, 71 | "no-switch-case-fall-through": true, 72 | "no-trailing-whitespace": true, 73 | "no-unnecessary-initializer": true, 74 | "no-unused-expression": true, 75 | "no-use-before-declare": true, 76 | "no-var-keyword": true, 77 | "object-literal-sort-keys": false, 78 | "one-line": [ 79 | true, 80 | "check-open-brace", 81 | "check-catch", 82 | "check-else", 83 | "check-whitespace" 84 | ], 85 | "prefer-const": true, 86 | "quotemark": [ 87 | true, 88 | "single" 89 | ], 90 | "radix": true, 91 | "semicolon": [ 92 | true, 93 | "always" 94 | ], 95 | "triple-equals": [ 96 | true, 97 | "allow-null-check" 98 | ], 99 | "typedef-whitespace": [ 100 | true, 101 | { 102 | "call-signature": "nospace", 103 | "index-signature": "nospace", 104 | "parameter": "nospace", 105 | "property-declaration": "nospace", 106 | "variable-declaration": "nospace" 107 | } 108 | ], 109 | "unified-signatures": true, 110 | "variable-name": false, 111 | "whitespace": [ 112 | true, 113 | "check-branch", 114 | "check-decl", 115 | "check-operator", 116 | "check-separator", 117 | "check-type" 118 | ], 119 | "no-output-on-prefix": true, 120 | "use-input-property-decorator": true, 121 | "use-output-property-decorator": true, 122 | "use-host-property-decorator": true, 123 | "no-input-rename": true, 124 | "no-output-rename": true, 125 | "use-life-cycle-interface": true, 126 | "use-pipe-transform-interface": true, 127 | "component-class-suffix": true, 128 | "directive-class-suffix": true 129 | } 130 | } 131 | --------------------------------------------------------------------------------