From 7a381fb55ec98b3f53c3c69f530b369d94597cbd Mon Sep 17 00:00:00 2001 From: Hiroki Terashima Date: Thu, 7 Jan 2021 12:35:43 -0800 Subject: [PATCH 01/26] Refactored ProjectService.getAllPaths(), consolidatePaths(), getParentGroup() and added tests. #2396 --- .../src/app/services/projectService.spec.ts | 65 ++- .../curriculum/OneBranchTwoPaths.project.json | 429 +++++++++++++++ .../curriculum/TwoSteps.project.json | 196 +++++++ .../webapp/wise5/services/projectService.ts | 491 +++++++++--------- 4 files changed, 920 insertions(+), 261 deletions(-) create mode 100644 src/main/webapp/site/src/app/services/sampleData/curriculum/OneBranchTwoPaths.project.json create mode 100644 src/main/webapp/site/src/app/services/sampleData/curriculum/TwoSteps.project.json diff --git a/src/main/webapp/site/src/app/services/projectService.spec.ts b/src/main/webapp/site/src/app/services/projectService.spec.ts index bdb4cc0ffd..3b1fbaf6ef 100644 --- a/src/main/webapp/site/src/app/services/projectService.spec.ts +++ b/src/main/webapp/site/src/app/services/projectService.spec.ts @@ -5,8 +5,10 @@ import { ProjectService } from '../../../../wise5/services/projectService'; import { ConfigService } from '../../../../wise5/services/configService'; import { UtilService } from '../../../../wise5/services/utilService'; import demoProjectJSON_import from './sampleData/curriculum/Demo.project.json'; +import oneBranchTwoPathsProjectJSON_import from './sampleData/curriculum/OneBranchTwoPaths.project.json'; import scootersProjectJSON_import from './sampleData/curriculum/SelfPropelledVehiclesChallenge.project.json'; import { getAuthServiceConfigs } from '../app.module'; +import twoStepsProjectJSON_import from './sampleData/curriculum/TwoSteps.project.json'; import { SessionService } from '../../../../wise5/services/sessionService'; const projectIdDefault = 1; const projectBaseURL = 'http://localhost:8080/curriculum/12345/'; @@ -19,7 +21,9 @@ let sessionService: SessionService; let utilService: UtilService; let http: HttpTestingController; let demoProjectJSON: any; +let oneBranchTwoPathsProjectJSON: any; let scootersProjectJSON: any; +let twoStepsProjectJSON: any; describe('ProjectService', () => { beforeEach(() => { @@ -34,7 +38,9 @@ describe('ProjectService', () => { spyOn(utilService, 'broadcastEventInRootScope').and.callFake(() => {}); service = TestBed.get(ProjectService); demoProjectJSON = JSON.parse(JSON.stringify(demoProjectJSON_import)); + oneBranchTwoPathsProjectJSON = JSON.parse(JSON.stringify(oneBranchTwoPathsProjectJSON_import)); scootersProjectJSON = JSON.parse(JSON.stringify(scootersProjectJSON_import)); + twoStepsProjectJSON = JSON.parse(JSON.stringify(twoStepsProjectJSON_import)); }); shouldReplaceAssetPathsInNonHtmlComponentContent(); shouldReplaceAssetPathsInHtmlComponentContent(); @@ -87,9 +93,10 @@ describe('ProjectService', () => { deleteAllStepsInAnActivity(); getTags(); addCurrentUserToAuthors_CM_shouldAddUserInfo(); + getAllPaths(); + consolidatePaths(); + getParentGroup(); // TODO: add test for service.getFlattenedProjectAsNodeIds() - // TODO: add test for service.getAllPaths() - // TODO: add test for service.consolidatePaths() // TODO: add test for service.consumePathsUntilNodeId() // TODO: add test for service.getFirstNodeIdInPathAtIndex() // TODO: add test for service.removeNodeIdFromPaths() @@ -1060,3 +1067,57 @@ function addCurrentUserToAuthors_CM_shouldAddUserInfo() { expect(authors[0].id).toEqual(1); }); } + +function getAllPaths() { + describe('getAllPaths()', () => { + it ('should get all paths in a unit with no branches', () => { + service.setProject(twoStepsProjectJSON); + const allPaths = service.getAllPaths([], service.getStartNodeId(), true); + expect(allPaths.length).toEqual(1); + expect(allPaths[0]).toEqual(['group1', 'node1', 'node2']); + }); + it ('should get all paths in a unit with a branch with two paths', () => { + service.setProject(oneBranchTwoPathsProjectJSON); + const allPaths = service.getAllPaths([], service.getStartNodeId(), true); + expect(allPaths.length).toEqual(2); + expect(allPaths[0]).toEqual(['group1', 'node1', 'node2', 'node3', 'node4', 'node8']); + expect(allPaths[1]).toEqual(['group1', 'node1', 'node2', 'node5', 'node6', 'node7', 'node8']); + }); + it ('should get all paths in a unit starting with a node in a branch path', () => { + service.setProject(oneBranchTwoPathsProjectJSON); + const allPaths1 = service.getAllPaths(['group1', 'node1', 'node2'], 'node3', true); + expect(allPaths1.length).toEqual(1); + expect(allPaths1[0]).toEqual(['node3', 'node4', 'node8']); + const allPaths2 = service.getAllPaths(['group1', 'node1', 'node2'], 'node5', true); + expect(allPaths2.length).toEqual(1); + expect(allPaths2[0]).toEqual(['node5', 'node6', 'node7', 'node8']); + }); + }); +} + +function consolidatePaths() { + describe('consolidatePaths()', () => { + it('should consolidate all the paths into a linear list of node ids', () => { + service.setProject(oneBranchTwoPathsProjectJSON); + const allPaths = service.getAllPaths([], service.getStartNodeId(), true); + const consolidatedPaths = service.consolidatePaths(allPaths); + expect(consolidatedPaths).toEqual(['group1', 'node1', 'node2', 'node3', 'node4', 'node5', + 'node6', 'node7', 'node8']); + }); + }); +} + +function getParentGroup() { + describe('getParentGroup()', () => { + it('should get the parent group of an active node', () => { + service.setProject(oneBranchTwoPathsProjectJSON); + const parentGroup = service.getParentGroup('node1'); + expect(parentGroup.id).toEqual('group1'); + }); + it('should get the parent group of an inactive node', () => { + service.setProject(twoStepsProjectJSON); + const parentGroup = service.getParentGroup('node3'); + expect(parentGroup.id).toEqual('group2'); + }); + }); +} diff --git a/src/main/webapp/site/src/app/services/sampleData/curriculum/OneBranchTwoPaths.project.json b/src/main/webapp/site/src/app/services/sampleData/curriculum/OneBranchTwoPaths.project.json new file mode 100644 index 0000000000..943517f32b --- /dev/null +++ b/src/main/webapp/site/src/app/services/sampleData/curriculum/OneBranchTwoPaths.project.json @@ -0,0 +1,429 @@ +{ + "nodes": [ + { + "id": "group0", + "type": "group", + "title": "Master", + "startId": "group1", + "ids": [ + "group1" + ] + }, + { + "id": "group1", + "type": "group", + "title": "First Lesson", + "startId": "node1", + "ids": [ + "node1", + "node2", + "node3", + "node4", + "node5", + "node6", + "node7", + "node8" + ], + "icons": { + "default": { + "color": "#2196F3", + "type": "font", + "fontSet": "material-icons", + "fontName": "info" + } + }, + "transitionLogic": { + "transitions": [] + } + }, + { + "id": "node1", + "type": "node", + "title": "First Step", + "components": [], + "constraints": [], + "showSaveButton": false, + "showSubmitButton": false, + "transitionLogic": { + "transitions": [ + { + "to": "node2" + } + ] + } + }, + { + "id": "node2", + "title": "Branch point", + "type": "node", + "constraints": [], + "transitionLogic": { + "transitions": [ + { + "to": "node3" + }, + { + "to": "node5" + } + ], + "howToChooseAmongAvailablePaths": "random", + "whenToChoosePath": "enterNode", + "canChangePath": false, + "maxPathsVisitable": 1 + }, + "showSaveButton": false, + "showSubmitButton": false, + "components": [] + }, + { + "id": "node3", + "title": "Path 1 first step", + "type": "node", + "constraints": [ + { + "id": "node3Constraint1", + "action": "makeThisNodeNotVisible", + "targetId": "node3", + "removalConditional": "all", + "removalCriteria": [ + { + "name": "branchPathTaken", + "params": { + "fromNodeId": "node2", + "toNodeId": "node3" + } + } + ] + }, + { + "id": "node3Constraint2", + "action": "makeThisNodeNotVisitable", + "targetId": "node3", + "removalConditional": "all", + "removalCriteria": [ + { + "name": "branchPathTaken", + "params": { + "fromNodeId": "node2", + "toNodeId": "node3" + } + } + ] + } + ], + "transitionLogic": { + "transitions": [ + { + "to": "node4" + } + ] + }, + "showSaveButton": false, + "showSubmitButton": false, + "components": [] + }, + { + "id": "node4", + "title": "Path 1 second step", + "type": "node", + "constraints": [ + { + "id": "node4Constraint1", + "action": "makeThisNodeNotVisible", + "targetId": "node4", + "removalConditional": "all", + "removalCriteria": [ + { + "name": "branchPathTaken", + "params": { + "fromNodeId": "node2", + "toNodeId": "node3" + } + } + ] + }, + { + "id": "node4Constraint2", + "action": "makeThisNodeNotVisitable", + "targetId": "node4", + "removalConditional": "all", + "removalCriteria": [ + { + "name": "branchPathTaken", + "params": { + "fromNodeId": "node2", + "toNodeId": "node3" + } + } + ] + } + ], + "transitionLogic": { + "transitions": [ + { + "to": "node8" + } + ] + }, + "showSaveButton": false, + "showSubmitButton": false, + "components": [] + }, + { + "id": "node5", + "title": "Path 2 first step", + "type": "node", + "constraints": [ + { + "id": "node5Constraint1", + "action": "makeThisNodeNotVisible", + "targetId": "node5", + "removalConditional": "all", + "removalCriteria": [ + { + "name": "branchPathTaken", + "params": { + "fromNodeId": "node2", + "toNodeId": "node5" + } + } + ] + }, + { + "id": "node5Constraint2", + "action": "makeThisNodeNotVisitable", + "targetId": "node5", + "removalConditional": "all", + "removalCriteria": [ + { + "name": "branchPathTaken", + "params": { + "fromNodeId": "node2", + "toNodeId": "node5" + } + } + ] + } + ], + "transitionLogic": { + "transitions": [ + { + "to": "node6" + } + ] + }, + "showSaveButton": false, + "showSubmitButton": false, + "components": [] + }, + { + "id": "node6", + "title": "Path 2 second step", + "type": "node", + "constraints": [ + { + "id": "node6Constraint1", + "action": "makeThisNodeNotVisible", + "targetId": "node6", + "removalConditional": "all", + "removalCriteria": [ + { + "name": "branchPathTaken", + "params": { + "fromNodeId": "node2", + "toNodeId": "node5" + } + } + ] + }, + { + "id": "node6Constraint2", + "action": "makeThisNodeNotVisitable", + "targetId": "node6", + "removalConditional": "all", + "removalCriteria": [ + { + "name": "branchPathTaken", + "params": { + "fromNodeId": "node2", + "toNodeId": "node5" + } + } + ] + } + ], + "transitionLogic": { + "transitions": [ + { + "to": "node7" + } + ] + }, + "showSaveButton": false, + "showSubmitButton": false, + "components": [] + }, + { + "id": "node7", + "title": "Path 2 third step", + "type": "node", + "constraints": [ + { + "id": "node7Constraint1", + "action": "makeThisNodeNotVisible", + "targetId": "node7", + "removalConditional": "all", + "removalCriteria": [ + { + "name": "branchPathTaken", + "params": { + "fromNodeId": "node2", + "toNodeId": "node5" + } + } + ] + }, + { + "id": "node7Constraint2", + "action": "makeThisNodeNotVisitable", + "targetId": "node7", + "removalConditional": "all", + "removalCriteria": [ + { + "name": "branchPathTaken", + "params": { + "fromNodeId": "node2", + "toNodeId": "node5" + } + } + ] + } + ], + "transitionLogic": { + "transitions": [ + { + "to": "node8" + } + ] + }, + "showSaveButton": false, + "showSubmitButton": false, + "components": [] + }, + { + "id": "node8", + "title": "merge step", + "type": "node", + "constraints": [], + "transitionLogic": { + "transitions": [] + }, + "showSaveButton": false, + "showSubmitButton": false, + "components": [] + } + ], + "constraints": [], + "startGroupId": "group0", + "startNodeId": "node1", + "navigationMode": "guided", + "layout": { + "template": "starmap|leftNav|rightNav" + }, + "metadata": { + "title": "Just one branch project", + "authors": [ + { + "firstName": "h", + "lastName": "t", + "id": 3, + "username": "ht" + } + ] + }, + "notebook": { + "enabled": false, + "label": "Notebook", + "enableAddNew": true, + "itemTypes": { + "note": { + "type": "note", + "enabled": true, + "enableLink": true, + "enableAddNote": true, + "enableClipping": true, + "enableStudentUploads": true, + "requireTextOnEveryNote": false, + "label": { + "singular": "note", + "plural": "notes", + "link": "Notes", + "icon": "note", + "color": "#1565C0" + } + }, + "report": { + "enabled": false, + "label": { + "singular": "report", + "plural": "reports", + "link": "Report", + "icon": "assignment", + "color": "#AD1457" + }, + "notes": [ + { + "reportId": "finalReport", + "title": "Final Report", + "description": "Final summary report of what you learned in this unit", + "prompt": "Use this space to write your final report using evidence from your notebook.", + "content": "

This is a heading

This is a paragraph.

" + } + ] + } + } + }, + "teacherNotebook": { + "enabled": true, + "label": "Teacher Notebook", + "enableAddNew": true, + "itemTypes": { + "note": { + "type": "note", + "enabled": false, + "enableLink": true, + "enableAddNote": true, + "enableClipping": true, + "enableStudentUploads": true, + "requireTextOnEveryNote": false, + "label": { + "singular": "note", + "plural": "notes", + "link": "Notes", + "icon": "note", + "color": "#1565C0" + } + }, + "report": { + "enabled": true, + "label": { + "singular": "teacher notes", + "plural": "teacher notes", + "link": "Teacher Notes", + "icon": "assignment", + "color": "#AD1457" + }, + "notes": [ + { + "reportId": "teacherReport", + "title": "Teacher Notes", + "description": "Notes for the teacher as they're running the WISE unit", + "prompt": "Use this space to take notes for this unit", + "content": "

Use this space to take notes for this unit

" + } + ] + } + } + }, + "inactiveNodes": [] +} diff --git a/src/main/webapp/site/src/app/services/sampleData/curriculum/TwoSteps.project.json b/src/main/webapp/site/src/app/services/sampleData/curriculum/TwoSteps.project.json new file mode 100644 index 0000000000..b09930d70e --- /dev/null +++ b/src/main/webapp/site/src/app/services/sampleData/curriculum/TwoSteps.project.json @@ -0,0 +1,196 @@ +{ + "nodes": [ + { + "id": "group0", + "type": "group", + "title": "Master", + "startId": "group1", + "ids": [ + "group1" + ], + "transitionLogic": { + "transitions": [] + } + }, + { + "id": "group1", + "type": "group", + "title": "First Lesson", + "startId": "node1", + "ids": [ + "node1", + "node2" + ], + "icons": { + "default": { + "color": "#2196F3", + "type": "font", + "fontSet": "material-icons", + "fontName": "info" + } + }, + "transitionLogic": { + "transitions": [] + } + }, + { + "id": "node1", + "type": "node", + "title": "First Step", + "components": [], + "constraints": [], + "showSaveButton": false, + "showSubmitButton": false, + "transitionLogic": { + "transitions": [ + { + "to": "node2" + } + ] + } + }, + { + "id": "node2", + "title": "Second Step", + "type": "node", + "constraints": [], + "transitionLogic": { + "transitions": [] + }, + "showSaveButton": false, + "showSubmitButton": false, + "components": [] + } + ], + "constraints": [], + "startGroupId": "group0", + "startNodeId": "node1", + "navigationMode": "guided", + "layout": { + "template": "starmap|leftNav|rightNav" + }, + "metadata": { + "title": "Just two steps project", + "authors": [ + { + "firstName": "h", + "lastName": "t", + "id": 3, + "username": "ht" + } + ] + }, + "notebook": { + "enabled": false, + "label": "Notebook", + "enableAddNew": true, + "itemTypes": { + "note": { + "type": "note", + "enabled": true, + "enableLink": true, + "enableAddNote": true, + "enableClipping": true, + "enableStudentUploads": true, + "requireTextOnEveryNote": false, + "label": { + "singular": "note", + "plural": "notes", + "link": "Notes", + "icon": "note", + "color": "#1565C0" + } + }, + "report": { + "enabled": false, + "label": { + "singular": "report", + "plural": "reports", + "link": "Report", + "icon": "assignment", + "color": "#AD1457" + }, + "notes": [ + { + "reportId": "finalReport", + "title": "Final Report", + "description": "Final summary report of what you learned in this unit", + "prompt": "Use this space to write your final report using evidence from your notebook.", + "content": "

This is a heading

This is a paragraph.

" + } + ] + } + } + }, + "teacherNotebook": { + "enabled": true, + "label": "Teacher Notebook", + "enableAddNew": true, + "itemTypes": { + "note": { + "type": "note", + "enabled": false, + "enableLink": true, + "enableAddNote": true, + "enableClipping": true, + "enableStudentUploads": true, + "requireTextOnEveryNote": false, + "label": { + "singular": "note", + "plural": "notes", + "link": "Notes", + "icon": "note", + "color": "#1565C0" + } + }, + "report": { + "enabled": true, + "label": { + "singular": "teacher notes", + "plural": "teacher notes", + "link": "Teacher Notes", + "icon": "assignment", + "color": "#AD1457" + }, + "notes": [ + { + "reportId": "teacherReport", + "title": "Teacher Notes", + "description": "Notes for the teacher as they're running the WISE unit", + "prompt": "Use this space to take notes for this unit", + "content": "

Use this space to take notes for this unit

" + } + ] + } + } + }, + "inactiveNodes": [ + { + "id": "group2", + "type": "group", + "title": "inactive lesson", + "startId": "node3", + "constraints": [], + "transitionLogic": { + "transitions": [] + }, + "ids": [ + "node3" + ], + "checked": false + }, + { + "id": "node3", + "title": "first inactive step", + "type": "node", + "constraints": [], + "transitionLogic": { + "transitions": [] + }, + "showSaveButton": false, + "showSubmitButton": false, + "components": [], + "checked": false + } + ] +} diff --git a/src/main/webapp/wise5/services/projectService.ts b/src/main/webapp/wise5/services/projectService.ts index 6a3cb97be1..4db8585017 100644 --- a/src/main/webapp/wise5/services/projectService.ts +++ b/src/main/webapp/wise5/services/projectService.ts @@ -722,35 +722,23 @@ export class ProjectService { return nodeIcon; } - getParentGroup(nodeId) { - if (nodeId != null) { - const node = this.getNodeById(nodeId); - if (node != null) { - // Check if the node is a child of an active group. - const groupNodes = this.getGroupNodes(); - for (let groupNode of groupNodes) { - if (this.isNodeDirectChildOfGroup(node, groupNode)) { - return groupNode; - } + getParentGroup(nodeId = '') { + const node = this.getNodeById(nodeId); + if (node != null) { + for (const groupNode of this.getGroupNodes()) { + if (this.isNodeDirectChildOfGroup(node, groupNode)) { + return groupNode; } - - // Check if the node is a child of an inactive group. - const inactiveGroupNodes = this.getInactiveGroupNodes(); - for (let inactiveGroupNode of inactiveGroupNodes) { - if (this.isNodeDirectChildOfGroup(node, inactiveGroupNode)) { - return inactiveGroupNode; - } + } + for (const inactiveGroupNode of this.getInactiveGroupNodes()) { + if (this.isNodeDirectChildOfGroup(node, inactiveGroupNode)) { + return inactiveGroupNode; } } } return null; } - /** - * Get the parent group id - * @param nodeId the parent group id - * @returns the parent group id - */ getParentGroupId(nodeId) { if (nodeId != null) { const parentGroup = this.getParentGroup(nodeId); @@ -1018,7 +1006,7 @@ export class ProjectService { * @param fromNodeId the node to get transitions from * @returns {Array} an array of transitions */ - getTransitionsByFromNodeId(fromNodeId) { + getTransitionsByFromNodeId(fromNodeId: string) { const transitionLogic = this.getTransitionLogicByFromNodeId(fromNodeId); return transitionLogic.transitions; } @@ -1294,19 +1282,9 @@ export class ProjectService { * depth first */ const pathsSoFar = []; - - // get all the possible paths through the project const allPaths = this.getAllPaths(pathsSoFar, startNodeId); - - // consolidate all the paths to create a single list of node ids const nodeIds = this.consolidatePaths(allPaths); - - /* - * Remember the flattened node ids so that we don't have to calculate - * it again. - */ - this.flattenedProjectAsNodeIds = nodeIds; - + this.flattenedProjectAsNodeIds = nodeIds; // cache flatted node ids return nodeIds; } @@ -1320,238 +1298,236 @@ export class ProjectService { * @param includeGroups whether to include the group node ids in the paths * @return an array of paths. each path is an array of node ids. */ - getAllPaths(pathSoFar: string[], nodeId: string, includeGroups: boolean = false) { + getAllPaths(pathSoFar: string[], nodeId: string = '', includeGroups: boolean = false) { const allPaths = []; - if (nodeId != null) { - if (this.isApplicationNode(nodeId)) { - const path = []; - const transitions = this.getTransitionsByFromNodeId(nodeId); - if (transitions != null) { - if (includeGroups) { - const parentGroup = this.getParentGroup(nodeId); - if (parentGroup != null) { - const parentGroupId = parentGroup.id; - if (parentGroupId != null && pathSoFar.indexOf(parentGroupId) == -1) { - pathSoFar.push(parentGroup.id); - } + if (this.isApplicationNode(nodeId)) { + const path = []; + const transitions = this.getTransitionsByFromNodeId(nodeId); + if (transitions != null) { + if (includeGroups) { + const parentGroup = this.getParentGroup(nodeId); + if (parentGroup != null) { + const parentGroupId = parentGroup.id; + if (parentGroupId != null && pathSoFar.indexOf(parentGroupId) == -1) { + pathSoFar.push(parentGroup.id); } } + } + /* + * add the node id to the path so far so we can later check + * which nodes are already in the path to prevent looping + * back in the path + */ + pathSoFar.push(nodeId); + + if (transitions.length === 0) { /* - * add the node id to the path so far so we can later check - * which nodes are already in the path to prevent looping - * back in the path + * there are no transitions from the node id so we will + * look for a transition in the parent group */ - pathSoFar.push(nodeId); - if (transitions.length === 0) { - /* - * there are no transitions from the node id so we will - * look for a transition in the parent group - */ + let addedCurrentNodeId = false; + const parentGroupId = this.getParentGroupId(nodeId); + const parentGroupTransitions = this.getTransitionsByFromNodeId(parentGroupId); - let addedCurrentNodeId = false; - const parentGroupId = this.getParentGroupId(nodeId); - const parentGroupTransitions = this.getTransitionsByFromNodeId(parentGroupId); - - if (parentGroupTransitions != null) { - for (let parentGroupTransition of parentGroupTransitions) { - if (parentGroupTransition != null) { - const toNodeId = parentGroupTransition.to; - if (pathSoFar.indexOf(toNodeId) == -1) { - /* - * recursively get the paths by getting all - * the paths for the to node - */ - const allPathsFromToNode = this.getAllPaths(pathSoFar, toNodeId, includeGroups); + if (parentGroupTransitions != null) { + for (let parentGroupTransition of parentGroupTransitions) { + if (parentGroupTransition != null) { + const toNodeId = parentGroupTransition.to; + if (pathSoFar.indexOf(toNodeId) == -1) { + /* + * recursively get the paths by getting all + * the paths for the to node + */ + const allPathsFromToNode = this.getAllPaths(pathSoFar, toNodeId, includeGroups); - for (let tempPath of allPathsFromToNode) { - tempPath.unshift(nodeId); - allPaths.push(tempPath); - addedCurrentNodeId = true; - } + for (let tempPath of allPathsFromToNode) { + tempPath.unshift(nodeId); + allPaths.push(tempPath); + addedCurrentNodeId = true; } } } } + } - if (!addedCurrentNodeId) { - /* - * if the parent group doesn't have any transitions we will - * need to add the current node id to the path - */ - path.push(nodeId); - allPaths.push(path); - } - } else { - // there are transitions from this node id - - for (let transition of transitions) { - if (transition != null) { - const toNodeId = transition.to; - if (toNodeId != null && pathSoFar.indexOf(toNodeId) == -1) { - // we have not found the to node in the path yet so we can traverse it + if (!addedCurrentNodeId) { + /* + * if the parent group doesn't have any transitions we will + * need to add the current node id to the path + */ + path.push(nodeId); + allPaths.push(path); + } + } else { + // there are transitions from this node id - /* - * recursively get the paths by getting all - * the paths from the to node - */ - const allPathsFromToNode = this.getAllPaths(pathSoFar, toNodeId, includeGroups); + for (let transition of transitions) { + if (transition != null) { + const toNodeId = transition.to; + if (toNodeId != null && pathSoFar.indexOf(toNodeId) == -1) { + // we have not found the to node in the path yet so we can traverse it - if (allPathsFromToNode != null) { - for (let tempPath of allPathsFromToNode) { - if (includeGroups) { - // we need to add the group id to the path - - if (tempPath.length > 0) { - const firstNodeId = tempPath[0]; - const firstParentGroupId = this.getParentGroupId(firstNodeId); - const parentGroupId = this.getParentGroupId(nodeId); - if (parentGroupId != firstParentGroupId) { - /* - * the parent ids are different which means this is a boundary - * between two groups. for example if the project looked like - * group1>node1>node2>group2>node3>node4 - * and the current node was node2 then the first node in the - * path would be node3 which means we would need to place - * group2 on the path before node3 - */ - tempPath.unshift(firstParentGroupId); - } + /* + * recursively get the paths by getting all + * the paths from the to node + */ + const allPathsFromToNode = this.getAllPaths(pathSoFar, toNodeId, includeGroups); + + if (allPathsFromToNode != null) { + for (let tempPath of allPathsFromToNode) { + if (includeGroups) { + // we need to add the group id to the path + + if (tempPath.length > 0) { + const firstNodeId = tempPath[0]; + const firstParentGroupId = this.getParentGroupId(firstNodeId); + const parentGroupId = this.getParentGroupId(nodeId); + if (parentGroupId != firstParentGroupId) { + /* + * the parent ids are different which means this is a boundary + * between two groups. for example if the project looked like + * group1>node1>node2>group2>node3>node4 + * and the current node was node2 then the first node in the + * path would be node3 which means we would need to place + * group2 on the path before node3 + */ + tempPath.unshift(firstParentGroupId); } } - - tempPath.unshift(nodeId); - allPaths.push(tempPath); } + + tempPath.unshift(nodeId); + allPaths.push(tempPath); } - } else { - /* - * the node is already in the path so far which means - * the transition is looping back to a previous node. - * we do not want to take this transition because - * it will lead to an infinite loop. we will just - * add the current node id to the path and not take - * the transition which essentially ends the path. - */ - path.push(nodeId); - allPaths.push(path); } + } else { + /* + * the node is already in the path so far which means + * the transition is looping back to a previous node. + * we do not want to take this transition because + * it will lead to an infinite loop. we will just + * add the current node id to the path and not take + * the transition which essentially ends the path. + */ + path.push(nodeId); + allPaths.push(path); } } } + } - if (pathSoFar.length > 0) { - const lastNodeId = pathSoFar[pathSoFar.length - 1]; - if (this.isGroupNode(lastNodeId)) { - /* - * the last node id is a group id so we will remove it - * since we are moving back up the path as we traverse - * the nodes depth first - */ - pathSoFar.pop(); - } + if (pathSoFar.length > 0) { + const lastNodeId = pathSoFar[pathSoFar.length - 1]; + if (this.isGroupNode(lastNodeId)) { + /* + * the last node id is a group id so we will remove it + * since we are moving back up the path as we traverse + * the nodes depth first + */ + pathSoFar.pop(); } + } - /* - * remove the latest node id (this will be a step node id) - * since we are moving back up the path as we traverse the - * nodes depth first - */ - pathSoFar.pop(); + /* + * remove the latest node id (this will be a step node id) + * since we are moving back up the path as we traverse the + * nodes depth first + */ + pathSoFar.pop(); - if (includeGroups) { - if (pathSoFar.length == 1) { - /* - * we are including groups and we have traversed - * back up to the start node id for the project. - * the only node id left in pathSoFar is now the - * parent group of the start node id. we will - * now add this parent group of the start node id - * to all of the paths - */ + if (includeGroups) { + if (pathSoFar.length == 1) { + /* + * we are including groups and we have traversed + * back up to the start node id for the project. + * the only node id left in pathSoFar is now the + * parent group of the start node id. we will + * now add this parent group of the start node id + * to all of the paths + */ - for (let path of allPaths) { - if (path != null) { - /* - * prepend the parent group of the start node id - * to the path - */ - path.unshift(pathSoFar[0]); - } + for (let path of allPaths) { + if (path != null) { + /* + * prepend the parent group of the start node id + * to the path + */ + path.unshift(pathSoFar[0]); } - - /* - * remove the parent group of the start node id from - * pathSoFar which leaves us with an empty pathSoFar - * which means we are completely done with - * calculating all the paths - */ - pathSoFar.pop(); } + + /* + * remove the parent group of the start node id from + * pathSoFar which leaves us with an empty pathSoFar + * which means we are completely done with + * calculating all the paths + */ + pathSoFar.pop(); } } - } else if (this.isGroupNode(nodeId)) { - /* - * add the node id to the path so far so we can later check - * which nodes are already in the path to prevent looping - * back in the path - */ - pathSoFar.push(nodeId); + } + } else { + /* + * add the node id to the path so far so we can later check + * which nodes are already in the path to prevent looping + * back in the path + */ + pathSoFar.push(nodeId); - const groupNode = this.getNodeById(nodeId); - if (groupNode != null) { - const startId = groupNode.startId; - if (startId == null || startId == '') { - // there is no start id so we will take the transition from the group - // TODO? there is no start id so we will loop through all the child nodes + const groupNode = this.getNodeById(nodeId); + if (groupNode != null) { + const startId = groupNode.startId; + if (startId == null || startId == '') { + // there is no start id so we will take the transition from the group + // TODO? there is no start id so we will loop through all the child nodes - const transitions = this.getTransitionsByFromNodeId(groupNode.id); - if (transitions != null && transitions.length > 0) { - for (let transition of transitions) { - if (transition != null) { - const toNodeId = transition.to; + const transitions = this.getTransitionsByFromNodeId(groupNode.id); + if (transitions != null && transitions.length > 0) { + for (let transition of transitions) { + if (transition != null) { + const toNodeId = transition.to; - const allPathsFromToNode = this.getAllPaths(pathSoFar, toNodeId, includeGroups); + const allPathsFromToNode = this.getAllPaths(pathSoFar, toNodeId, includeGroups); - if (allPathsFromToNode != null) { - for (let tempPath of allPathsFromToNode) { - tempPath.unshift(nodeId); - allPaths.push(tempPath); - } + if (allPathsFromToNode != null) { + for (let tempPath of allPathsFromToNode) { + tempPath.unshift(nodeId); + allPaths.push(tempPath); } } } - } else { - /* - * this activity does not have any transitions so - * we have reached the end of this path - */ - - const tempPath = []; - tempPath.unshift(nodeId); - allPaths.push(tempPath); } } else { - // there is a start id so we will traverse it + /* + * this activity does not have any transitions so + * we have reached the end of this path + */ - const allPathsFromToNode = this.getAllPaths(pathSoFar, startId, includeGroups); + const tempPath = []; + tempPath.unshift(nodeId); + allPaths.push(tempPath); + } + } else { + // there is a start id so we will traverse it - if (allPathsFromToNode != null) { - for (let tempPath of allPathsFromToNode) { - tempPath.unshift(nodeId); - allPaths.push(tempPath); - } + const allPathsFromToNode = this.getAllPaths(pathSoFar, startId, includeGroups); + + if (allPathsFromToNode != null) { + for (let tempPath of allPathsFromToNode) { + tempPath.unshift(nodeId); + allPaths.push(tempPath); } } } - - /* - * remove the latest node id since we are moving back - * up the path as we traverse the nodes depth first - */ - pathSoFar.pop(); } + + /* + * remove the latest node id since we are moving back + * up the path as we traverse the nodes depth first + */ + pathSoFar.pop(); } return allPaths; } @@ -1561,59 +1537,56 @@ export class ProjectService { * @param paths an array of paths. each path is an array of node ids. * @return an array of node ids that have been properly ordered */ - consolidatePaths(paths) { + consolidatePaths(paths = []) { let consolidatedPath = []; + /* + * continue until all the paths are empty. as we consolidate + * node ids, we will remove them from the paths. once all the + * paths are empty we will be done consolidating the paths. + */ + while (!this.arePathsEmpty(paths)) { + // start with the first path + const currentPath = this.getNonEmptyPathIndex(paths); - if (paths != null) { - /* - * continue until all the paths are empty. as we consolidate - * node ids, we will remove them from the paths. once all the - * paths are empty we will be done consolidating the paths. - */ - while (!this.arePathsEmpty(paths)) { - // start with the first path - const currentPath = this.getNonEmptyPathIndex(paths); - - // get the first node id in the current path - const nodeId = this.getFirstNodeIdInPathAtIndex(paths, currentPath); - if (this.areFirstNodeIdsInPathsTheSame(paths)) { - // the first node ids in all the paths are the same + // get the first node id in the current path + const nodeId = this.getFirstNodeIdInPathAtIndex(paths, currentPath); + if (this.areFirstNodeIdsInPathsTheSame(paths)) { + // the first node ids in all the paths are the same - // remove the node id from all the paths - this.removeNodeIdFromPaths(nodeId, paths); + // remove the node id from all the paths + this.removeNodeIdFromPaths(nodeId, paths); - // add the node id to our consolidated path - consolidatedPath.push(nodeId); - } else { - // not all the top node ids are the same which means we have branched + // add the node id to our consolidated path + consolidatedPath.push(nodeId); + } else { + // not all the top node ids are the same which means we have branched - // get all the paths that contain the node id - const pathsThatContainNodeId = this.getPathsThatContainNodeId(nodeId, paths); + // get all the paths that contain the node id + const pathsThatContainNodeId = this.getPathsThatContainNodeId(nodeId, paths); - if (pathsThatContainNodeId != null) { - if (pathsThatContainNodeId.length === 1) { - // only the current path we are on has the node id + if (pathsThatContainNodeId != null) { + if (pathsThatContainNodeId.length === 1) { + // only the current path we are on has the node id - // remove the node id from the path - this.removeNodeIdFromPath(nodeId, paths, currentPath); + // remove the node id from the path + this.removeNodeIdFromPath(nodeId, paths, currentPath); - // add the node id to our consolidated path - consolidatedPath.push(nodeId); - } else { - // there are multiple paths that have this node id + // add the node id to our consolidated path + consolidatedPath.push(nodeId); + } else { + // there are multiple paths that have this node id - // consume all the node ids up to the given node id - const consumedPath = this.consumePathsUntilNodeId(paths, nodeId); + // consume all the node ids up to the given node id + const consumedPath = this.consumePathsUntilNodeId(paths, nodeId); - // remove the node id from the paths - this.removeNodeIdFromPaths(nodeId, paths); + // remove the node id from the paths + this.removeNodeIdFromPaths(nodeId, paths); - // add the node id to the end of the consumed path - consumedPath.push(nodeId); + // add the node id to the end of the consumed path + consumedPath.push(nodeId); - // add the consumed path to our consolidated path - consolidatedPath = consolidatedPath.concat(consumedPath); - } + // add the consumed path to our consolidated path + consolidatedPath = consolidatedPath.concat(consumedPath); } } } From cb25b76a3b9a51b52ddbb89025b925cd15f1665b Mon Sep 17 00:00:00 2001 From: Hiroki Terashima Date: Thu, 7 Jan 2021 14:25:49 -0800 Subject: [PATCH 02/26] Removed outer null check in getParentGroupId() and specified return types. Cleaned up test. #2396 --- .../site/src/app/services/projectService.spec.ts | 11 +++++------ src/main/webapp/wise5/services/projectService.ts | 12 +++++------- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/src/main/webapp/site/src/app/services/projectService.spec.ts b/src/main/webapp/site/src/app/services/projectService.spec.ts index 3b1fbaf6ef..44fa1ce76a 100644 --- a/src/main/webapp/site/src/app/services/projectService.spec.ts +++ b/src/main/webapp/site/src/app/services/projectService.spec.ts @@ -1109,15 +1109,14 @@ function consolidatePaths() { function getParentGroup() { describe('getParentGroup()', () => { + beforeEach(() => { + service.setProject(twoStepsProjectJSON); + }); it('should get the parent group of an active node', () => { - service.setProject(oneBranchTwoPathsProjectJSON); - const parentGroup = service.getParentGroup('node1'); - expect(parentGroup.id).toEqual('group1'); + expect(service.getParentGroup('node1').id).toEqual('group1'); }); it('should get the parent group of an inactive node', () => { - service.setProject(twoStepsProjectJSON); - const parentGroup = service.getParentGroup('node3'); - expect(parentGroup.id).toEqual('group2'); + expect(service.getParentGroup('node3').id).toEqual('group2'); }); }); } diff --git a/src/main/webapp/wise5/services/projectService.ts b/src/main/webapp/wise5/services/projectService.ts index 4db8585017..78d7e3dd23 100644 --- a/src/main/webapp/wise5/services/projectService.ts +++ b/src/main/webapp/wise5/services/projectService.ts @@ -722,7 +722,7 @@ export class ProjectService { return nodeIcon; } - getParentGroup(nodeId = '') { + getParentGroup(nodeId = ''): any { const node = this.getNodeById(nodeId); if (node != null) { for (const groupNode of this.getGroupNodes()) { @@ -739,12 +739,10 @@ export class ProjectService { return null; } - getParentGroupId(nodeId) { - if (nodeId != null) { - const parentGroup = this.getParentGroup(nodeId); - if (parentGroup != null) { - return parentGroup.id; - } + getParentGroupId(nodeId = ''): string { + const parentGroup = this.getParentGroup(nodeId); + if (parentGroup != null) { + return parentGroup.id; } return null; } From bcf6433747e7d17bdfce0afe07596c8602d75d0c Mon Sep 17 00:00:00 2001 From: Hiroki Terashima Date: Fri, 8 Jan 2021 10:37:07 -0800 Subject: [PATCH 03/26] Ran prettier-format script on all typescript files in src. --- .../src/app/about/about-routing.module.ts | 12 +- .../src/app/about/about.component.spec.ts | 9 +- .../site/src/app/about/about.component.ts | 7 +- .../webapp/site/src/app/about/about.module.ts | 25 +- src/main/webapp/site/src/app/animations.ts | 480 ++++++++++------- .../announcement.component.spec.ts | 20 +- .../announcement/announcement.component.ts | 21 +- .../webapp/site/src/app/app-routing.module.ts | 52 +- .../webapp/site/src/app/app.component.spec.ts | 24 +- src/main/webapp/site/src/app/app.component.ts | 44 +- src/main/webapp/site/src/app/app.module.ts | 43 +- ...e-new-component-location.component.spec.ts | 15 +- ...choose-new-component-location.component.ts | 68 ++- .../choose-new-component.component.ts | 36 +- .../edit-advanced-component.component.ts | 9 +- ...ditAdvancedComponentAngularJSController.ts | 30 +- .../edit-component-json.component.ts | 49 +- .../edit-component-max-score.component.ts | 23 +- .../edit-component-rubric.component.ts | 21 +- .../edit-component-tags.component.ts | 34 +- .../edit-component-width.component.ts | 23 +- .../choose-import-step-location.component.ts | 8 +- .../choose-import-step.component.ts | 12 +- .../component-new-work-badge.component.ts | 40 +- .../component-select.component.ts | 21 +- .../milestones/milestones.component.ts | 51 +- .../nav-item-progress.component.ts | 3 +- .../status-icon/status-icon.component.ts | 3 +- .../step-info/step-info.component.ts | 8 +- .../workgroup-node-status.component.ts | 5 +- ...workgroup-select-autocomplete.component.ts | 27 +- .../workgroup-select-dropdown.component.ts | 13 +- .../workgroup-select.component.ts | 38 +- .../src/app/common-hybrid-angular.module.ts | 6 +- .../contact-form.component.spec.ts | 32 +- .../contact-form/contact-form.component.ts | 99 ++-- .../src/app/contact/contact-routing.module.ts | 14 +- .../site/src/app/contact/contact.module.ts | 13 +- src/main/webapp/site/src/app/domain/news.ts | 2 +- .../site/src/app/domain/notification.ts | 1 - .../webapp/site/src/app/domain/project.ts | 10 +- .../webapp/site/src/app/domain/run.spec.ts | 2 +- src/main/webapp/site/src/app/domain/run.ts | 28 +- .../webapp/site/src/app/domain/student.ts | 2 +- .../webapp/site/src/app/domain/teacher.ts | 2 +- .../app/features/features-routing.module.ts | 12 +- .../app/features/features.component.spec.ts | 9 +- .../src/app/features/features.component.ts | 7 +- .../site/src/app/features/features.module.ts | 19 +- .../forgot-home/forgot-home.component.spec.ts | 4 +- .../forgot-home/forgot-home.component.ts | 7 +- .../src/app/forgot/forgot-routing.module.ts | 2 +- .../src/app/forgot/forgot.component.spec.ts | 9 +- .../site/src/app/forgot/forgot.component.ts | 7 +- .../site/src/app/forgot/forgot.module.ts | 6 +- ...-student-password-change.component.spec.ts | 33 +- ...orgot-student-password-change.component.ts | 20 +- ...tudent-password-complete.component.spec.ts | 12 +- ...got-student-password-complete.component.ts | 7 +- ...tudent-password-security.component.spec.ts | 26 +- ...got-student-password-security.component.ts | 22 +- .../forgot-student-password.component.spec.ts | 26 +- .../forgot-student-password.component.ts | 21 +- .../forgot-student-username.component.spec.ts | 14 +- .../forgot-student-username.component.ts | 44 +- .../forgot-student.component.spec.ts | 4 +- .../forgot-student.component.ts | 7 +- ...-teacher-password-change.component.spec.ts | 55 +- ...orgot-teacher-password-change.component.ts | 20 +- ...eacher-password-complete.component.spec.ts | 12 +- ...got-teacher-password-complete.component.ts | 7 +- ...-teacher-password-verify.component.spec.ts | 35 +- ...orgot-teacher-password-verify.component.ts | 20 +- .../forgot-teacher-password.component.spec.ts | 43 +- .../forgot-teacher-password.component.ts | 22 +- ...eacher-username-complete.component.spec.ts | 10 +- ...got-teacher-username-complete.component.ts | 7 +- .../forgot-teacher-username.component.spec.ts | 25 +- .../forgot-teacher-username.component.ts | 15 +- .../forgot-teacher.component.spec.ts | 4 +- .../forgot-teacher.component.ts | 7 +- .../getting-started.component.spec.ts | 25 +- .../getting-started.component.ts | 8 +- .../help-home/help-home.component.spec.ts | 9 +- .../app/help/help-home/help-home.component.ts | 7 +- .../site/src/app/help/help-routing.module.ts | 18 +- .../site/src/app/help/help.component.spec.ts | 9 +- .../site/src/app/help/help.component.ts | 7 +- .../webapp/site/src/app/help/help.module.ts | 12 +- .../student-faq/student-faq.component.spec.ts | 25 +- .../help/student-faq/student-faq.component.ts | 8 +- .../teacher-faq/teacher-faq.component.spec.ts | 25 +- .../help/teacher-faq/teacher-faq.component.ts | 7 +- .../site/src/app/home/home-routing.module.ts | 10 +- .../site/src/app/home/home.component.spec.ts | 11 +- .../site/src/app/home/home.component.ts | 48 +- .../webapp/site/src/app/home/home.module.ts | 27 +- .../site/src/app/http-error.interceptor.ts | 18 +- ...in-google-user-not-found.component.spec.ts | 9 +- .../login-google-user-not-found.component.ts | 7 +- .../login-home/login-home.component.spec.ts | 33 +- .../login/login-home/login-home.component.ts | 28 +- .../src/app/login/login-routing.module.ts | 14 +- .../src/app/login/login.component.spec.ts | 9 +- .../site/src/app/login/login.component.ts | 7 +- .../webapp/site/src/app/login/login.module.ts | 10 +- .../modules/footer/footer.component.spec.ts | 13 +- .../app/modules/footer/footer.component.ts | 3 +- .../src/app/modules/footer/footer.module.ts | 16 +- .../header-account-menu.component.spec.ts | 44 +- .../header-account-menu.component.ts | 15 +- .../header-links.component.spec.ts | 23 +- .../header-links/header-links.component.ts | 11 +- .../header-signin.component.spec.ts | 7 +- .../header-signin/header-signin.component.ts | 7 +- .../modules/header/header.component.spec.ts | 41 +- .../app/modules/header/header.component.ts | 26 +- .../src/app/modules/header/header.module.ts | 26 +- .../community-library.component.spec.ts | 21 +- .../community-library.component.ts | 15 +- .../copy-project-dialog.component.spec.ts | 41 +- .../copy-project-dialog.component.ts | 49 +- ...ome-page-project-library.component.spec.ts | 19 +- .../home-page-project-library.component.ts | 6 +- .../library-filters.component.spec.ts | 15 +- .../library-filters.component.ts | 61 ++- .../library-group-thumbs.component.spec.ts | 13 +- .../library-group-thumbs.component.ts | 3 +- .../library-project-details.component.spec.ts | 101 ++-- .../library-project-details.component.ts | 54 +- ...rary-project-disciplines.component.spec.ts | 11 +- .../library-project-disciplines.component.ts | 27 +- .../library-project-menu.component.spec.ts | 40 +- .../library-project-menu.component.ts | 2 +- .../library-project.component.spec.ts | 15 +- .../library-project.component.ts | 14 +- .../src/app/modules/library/library.module.ts | 32 +- .../library/library/library.component.ts | 33 +- .../modules/library/libraryPaginatorIntl.ts | 9 +- .../src/app/modules/library/libraryProject.ts | 2 +- .../src/app/modules/library/ngssStandards.ts | 2 +- .../official-library.component.spec.ts | 23 +- .../official-library.component.ts | 8 +- .../personal-library.component.spec.ts | 23 +- .../personal-library.component.ts | 48 +- .../modules/library/sampleLibraryProjects.ts | 94 ++-- .../library/select-menu-test.helper.ts | 5 +- .../share-item-dialog.component.ts | 46 +- .../share-project-dialog.component.spec.ts | 38 +- .../share-project-dialog.component.ts | 47 +- .../teacher-project-library.component.spec.ts | 25 +- .../teacher-project-library.component.ts | 19 +- .../mobile-menu/mobile-menu.component.spec.ts | 37 +- .../mobile-menu/mobile-menu.component.ts | 25 +- .../modules/mobile-menu/mobile-menu.module.ts | 8 +- .../shared/blurb/blurb.component.spec.ts | 9 +- .../modules/shared/blurb/blurb.component.ts | 11 +- .../call-to-action.component.spec.ts | 9 +- .../call-to-action.component.ts | 20 +- .../edit-password.component.spec.ts | 29 +- .../edit-password/edit-password.component.ts | 49 +- .../hero-section.component.spec.ts | 11 +- .../hero-section/hero-section.component.ts | 14 +- .../search-bar/search-bar.component.spec.ts | 11 +- .../shared/search-bar/search-bar.component.ts | 20 +- .../select-menu/select-menu.component.spec.ts | 9 +- .../select-menu/select-menu.component.ts | 23 +- .../src/app/modules/shared/shared.module.ts | 2 +- .../timeline-item.component.spec.ts | 9 +- .../timeline-item/timeline-item.component.ts | 10 +- .../app/modules/timeline/timeline.module.ts | 23 +- .../timeline/timeline.component.spec.ts | 9 +- .../timeline/timeline/timeline.component.ts | 9 +- .../site/src/app/news/news-routing.module.ts | 12 +- .../site/src/app/news/news.component.spec.ts | 28 +- .../site/src/app/news/news.component.ts | 8 +- .../webapp/site/src/app/news/news.module.ts | 12 +- .../possible-score.component.ts | 4 +- ...hoose-branch-path-dialog.component.spec.ts | 9 +- .../choose-branch-path-dialog.component.ts | 3 +- .../site/src/app/preview/preview.module.ts | 17 +- .../src/app/privacy/privacy.component.spec.ts | 9 +- .../site/src/app/privacy/privacy.component.ts | 7 +- ...ogle-user-already-exists.component.spec.ts | 13 +- ...er-google-user-already-exists.component.ts | 8 +- .../register-home.component.spec.ts | 9 +- .../register-home/register-home.component.ts | 7 +- .../app/register/register-routing.module.ts | 24 +- ...egister-student-complete.component.spec.ts | 19 +- .../register-student-complete.component.ts | 16 +- .../register-student-form.component.spec.ts | 111 ++-- .../register-student-form.component.ts | 123 +++-- .../register-student.component.spec.ts | 45 +- .../register-student.component.ts | 58 +-- ...egister-teacher-complete.component.spec.ts | 23 +- .../register-teacher-complete.component.ts | 16 +- .../register-teacher-form.component.spec.ts | 112 ++-- .../register-teacher-form.component.ts | 104 ++-- .../register-teacher.component.spec.ts | 45 +- .../register-teacher.component.ts | 56 +- .../register-user-form-spec-helpers.ts | 1 - .../register-user-form.component.ts | 6 +- .../app/register/register.component.spec.ts | 9 +- .../src/app/register/register.component.ts | 8 +- .../site/src/app/register/register.module.ts | 10 +- .../src/app/services/animationService.spec.ts | 36 +- .../services/audioOscillatorService.spec.ts | 38 +- .../src/app/services/cRaterService.spec.ts | 54 +- .../app/services/conceptMapService.spec.ts | 244 ++++++--- .../src/app/services/config.service.spec.ts | 4 +- .../site/src/app/services/config.service.ts | 15 +- .../src/app/services/configService.spec.ts | 16 +- .../src/app/services/data.service.spec.ts | 11 +- .../site/src/app/services/data.service.ts | 21 +- .../app/services/discussionService.spec.ts | 115 ++-- .../site/src/app/services/drawService.spec.ts | 75 +-- .../src/app/services/embeddedService.spec.ts | 32 +- .../src/app/services/graphService.spec.ts | 79 +-- .../site/src/app/services/htmlService.spec.ts | 29 +- .../src/app/services/labelService.spec.ts | 102 ++-- .../src/app/services/library.service.spec.ts | 51 +- .../site/src/app/services/library.service.ts | 17 +- .../src/app/services/matchService.spec.ts | 55 +- .../src/app/services/milestoneService.spec.ts | 177 +++---- .../services/multipleChoiceService.spec.ts | 69 ++- .../src/app/services/news.service.spec.ts | 8 +- .../site/src/app/services/news.service.ts | 5 +- .../src/app/services/notebookService.spec.ts | 60 ++- .../app/services/notificationService.spec.ts | 45 +- .../app/services/openResponseService.spec.ts | 57 +- .../app/services/outsideURLService.spec.ts | 29 +- .../app/services/projectAssetService.spec.ts | 8 +- .../src/app/services/projectAssetService.ts | 2 +- .../src/app/services/projectService.spec.ts | 229 ++++---- .../src/app/services/sessionService.spec.ts | 36 +- .../src/app/services/spaceService.spec.ts | 14 +- .../app/services/studentAssetService.spec.ts | 24 +- .../app/services/studentDataService.spec.ts | 242 ++++----- .../app/services/studentStatusService.spec.ts | 18 +- .../src/app/services/summaryService.spec.ts | 84 ++- .../src/app/services/tableService.spec.ts | 57 +- .../site/src/app/services/tagService.spec.ts | 11 +- .../services/teacherProjectService.spec.ts | 21 +- .../src/app/services/test.config.service.ts | 4 +- .../src/app/services/user.service.spec.ts | 15 +- .../site/src/app/services/user.service.ts | 114 ++-- .../src/app/services/util.service.spec.ts | 6 +- .../site/src/app/services/util.service.ts | 13 +- .../site/src/app/services/utilService.spec.ts | 14 +- .../app/services/vleProjectService.spec.ts | 4 +- .../src/app/student-hybrid-angular.module.ts | 31 +- .../edit-profile.component.spec.ts | 25 +- .../edit-profile/edit-profile.component.ts | 39 +- .../account/edit/edit.component.spec.ts | 19 +- .../student/account/edit/edit.component.ts | 3 +- .../add-project-dialog.component.ts | 6 +- .../site/src/app/student/auth.guard.spec.ts | 20 +- .../webapp/site/src/app/student/auth.guard.ts | 10 +- .../student-home.component.spec.ts | 21 +- .../student-home/student-home.component.ts | 18 +- .../src/app/student/student-routing.module.ts | 18 +- .../student-run-list-item.component.spec.ts | 56 +- .../student-run-list-item.component.ts | 38 +- .../student-run-list.component.spec.ts | 30 +- .../student-run-list.component.ts | 52 +- .../src/app/student/student.component.spec.ts | 11 +- .../site/src/app/student/student.component.ts | 7 +- .../site/src/app/student/student.module.ts | 33 +- .../src/app/student/student.service.spec.ts | 21 +- .../site/src/app/student/student.service.ts | 29 +- .../team-sign-in-dialog.component.spec.ts | 66 ++- .../team-sign-in-dialog.component.ts | 290 ++++++----- .../src/app/teacher-hybrid-angular.module.ts | 4 +- .../edit-profile.component.spec.ts | 23 +- .../edit-profile/edit-profile.component.ts | 60 ++- .../account/edit/edit.component.spec.ts | 9 +- .../teacher/account/edit/edit.component.ts | 6 +- .../site/src/app/teacher/auth.guard.spec.ts | 25 +- .../webapp/site/src/app/teacher/auth.guard.ts | 13 +- .../create-run-dialog.component.spec.ts | 102 ++-- .../create-run-dialog.component.ts | 114 ++-- ...iscourse-recent-activity.component.spec.ts | 12 +- .../discourse-recent-activity.component.ts | 23 +- .../edit-run-warning-dialog.component.spec.ts | 29 +- .../edit-run-warning-dialog.component.ts | 15 +- ...classroom-courses-dialog.component.spec.ts | 50 +- ...list-classroom-courses-dialog.component.ts | 57 +- .../milestone-report-data.component.spec.ts | 2 +- .../milestone-report-data.component.ts | 8 +- .../run-menu/run-menu.component.spec.ts | 33 +- .../run-settings-dialog.component.spec.ts | 35 +- .../run-settings-dialog.component.ts | 133 ++--- .../share-run-code-dialog.component.spec.ts | 49 +- .../share-run-code-dialog.component.ts | 21 +- .../share-run-dialog.component.spec.ts | 77 ++- .../share-run-dialog.component.ts | 101 ++-- .../teacher-home.component.spec.ts | 19 +- .../teacher-home/teacher-home.component.ts | 24 +- .../src/app/teacher/teacher-routing.module.ts | 28 +- .../teacher-run-list-item.component.spec.ts | 32 +- .../teacher-run-list-item.component.ts | 26 +- .../teacher-run-list.component.spec.ts | 39 +- .../teacher-run-list.component.ts | 37 +- .../site/src/app/teacher/teacher-run.ts | 2 +- .../src/app/teacher/teacher.component.spec.ts | 13 +- .../site/src/app/teacher/teacher.component.ts | 9 +- .../site/src/app/teacher/teacher.module.ts | 41 +- .../src/app/teacher/teacher.service.spec.ts | 74 ++- .../site/src/app/teacher/teacher.service.ts | 86 +-- ...ith-class-warning-dialog.component.spec.ts | 21 +- ...use-with-class-warning-dialog.component.ts | 15 +- .../site/src/app/track-scroll.directive.ts | 6 +- src/main/webapp/site/src/main.ts | 5 +- src/main/webapp/site/src/polyfills.ts | 10 +- src/main/webapp/site/src/test.ts | 5 +- .../addComponent/addComponentModule.ts | 62 ++- .../advanced-project-authoring.component.ts | 13 +- .../asset/projectAssetController.ts | 39 +- .../authoringTool/authoringToolController.ts | 23 +- .../component-authoring.component.ts | 34 +- .../components/editComponentController.ts | 51 +- .../preview-component/previewComponent.ts | 41 +- .../components/shared/mainMenu/mainMenu.ts | 5 +- .../components/shared/stepTools/stepTools.ts | 11 +- .../components/shared/toolbar/toolbar.ts | 22 +- .../components/shared/topBar/topBar.ts | 1 - .../choose-component-location.component.ts | 76 ++- .../choose-component.component.ts | 30 +- .../importComponent/importComponentModule.ts | 46 +- .../importStep/importStepModule.ts | 16 +- .../info/projectInfoController.ts | 16 +- .../main/authoringToolMainController.ts | 6 +- .../milestonesAuthoringController.ts | 56 +- ...ode-advanced-branch-authoring.component.ts | 78 +-- ...advanced-constraint-authoring.component.ts | 490 +++++++++--------- ...de-advanced-general-authoring.component.ts | 14 +- .../node-advanced-json-authoring.component.ts | 29 +- .../node-advanced-authoring.component.ts | 27 +- .../node-advanced-path-authoring.component.ts | 109 ++-- .../node/editRubric/edit-rubric.component.ts | 33 +- .../node/editRubric/editRubricModule.ts | 12 +- .../notebook/authorNotebookController.ts | 4 +- .../project/projectController.ts | 30 +- .../rubric/rubric-authoring.component.ts | 15 +- ...automatedAssessmentChooseItemController.ts | 43 +- .../automatedAssessmentConfigureController.ts | 29 +- .../structure/configureStructureController.ts | 16 +- .../guidanceChoiceController.ts | 11 +- .../structure/jigsaw/jigsawController.ts | 24 +- .../kiCycleUsingOERController.ts | 11 +- .../peerReviewAndRevisionController.ts | 11 +- .../selfDirectedInvestigationController.ts | 11 +- .../structure/structureAuthoringModule.ts | 2 +- .../manageStudents/manageStudentsModule.ts | 7 +- .../milestoneDetails/milestoneDetails.ts | 11 +- .../milestones/milestoneEdit/milestoneEdit.ts | 8 +- .../nodeGrading/nodeGrading.ts | 12 +- .../nodeGradingView/nodeGradingView.ts | 70 +-- .../nodeGrading/stepTools/stepTools.ts | 11 +- .../workgroupInfo/workgroup-info.component.ts | 1 - .../nodeProgress/navItem/navItem.ts | 80 +-- .../navItemScore/nav-item-score.component.ts | 3 +- .../nodeProgress/nodeProgress.ts | 12 +- .../nodeProgressView/nodeProgressView.ts | 34 +- .../notebook/notebook.ts | 2 +- .../notebookItemGrading.ts | 26 +- .../notebookWorkgroupGrading.ts | 15 +- .../componentGrading/componentGrading.ts | 21 +- .../shared/nodeIcon/node-icon.component.ts | 1 - .../notificationsMenu/notificationsMenu.ts | 2 +- .../shared/periodSelect/periodSelect.ts | 9 +- .../shared/shared.ts | 44 +- .../shared/topBar/topBar.ts | 19 +- .../workgroupComponentRevisions.ts | 13 +- .../workgroupNodeGrading.ts | 2 +- .../workgroup-node-score.component.ts | 3 +- .../studentGrading/stepItem/stepItem.ts | 4 +- .../studentGrading/studentGrading.ts | 6 +- .../studentGradingTools.ts | 22 +- .../classroomMonitorController.ts | 19 +- .../dataExport/dataExportController.ts | 94 ++-- .../dataExport/exportController.ts | 2 +- .../dataExport/exportVisitsController.ts | 119 +++-- .../manage-students-component.ts | 9 +- .../notebook/notebookGradingController.ts | 22 +- .../studentGradingController.ts | 116 +++-- .../studentProgressController.ts | 32 +- .../webapp/wise5/common-angular-js-module.ts | 172 +++--- .../animation/animationAuthoring.ts | 4 +- .../animationAuthoringComponentModule.ts | 2 +- .../animation/animationComponentModule.ts | 2 +- .../animation/animationController.ts | 8 +- .../components/animation/animationService.ts | 18 +- .../edit-animation-advanced.component.ts | 7 +- .../audioOscillatorAuthoring.ts | 4 +- ...audioOscillatorAuthoringComponentModule.ts | 2 +- .../audioOscillatorComponentModule.ts | 2 +- .../audioOscillator/audioOscillatorService.ts | 16 +- ...dit-audio-oscillator-advanced.component.ts | 7 +- .../wise5/components/componentController.ts | 282 +++++----- .../wise5/components/componentService.ts | 5 +- .../conceptMap/conceptMapAuthoring.ts | 5 +- .../conceptMapAuthoringComponentModule.ts | 2 +- .../conceptMap/conceptMapComponentModule.ts | 2 +- .../conceptMap/conceptMapController.ts | 57 +- .../components/conceptMap/conceptMapLink.ts | 4 +- .../conceptMap/conceptMapService.ts | 104 ++-- .../edit-concept-map-advanced.component.ts | 19 +- .../discussion/classResponseController.ts | 2 +- .../discussion/discussionAuthoring.ts | 8 +- .../discussionAuthoringComponentModule.ts | 2 +- .../discussion/discussionComponentModule.ts | 2 +- .../discussion/discussionController.ts | 70 +-- .../discussion/discussionService.ts | 26 +- .../edit-discussion-advanced.component.ts | 9 +- .../wise5/components/draw/drawAuthoring.ts | 5 +- .../draw/drawAuthoringComponentModule.ts | 2 +- .../components/draw/drawComponentModule.ts | 2 +- .../wise5/components/draw/drawController.ts | 30 +- .../wise5/components/draw/drawService.ts | 22 +- .../edit-draw-advanced.component.ts | 10 +- .../edit-embedded-advanced.component.ts | 23 +- .../components/embedded/embeddedAuthoring.ts | 30 +- .../embeddedAuthoringComponentModule.ts | 2 +- .../embedded/embeddedComponentModule.ts | 5 +- .../components/embedded/embeddedController.ts | 82 +-- .../components/embedded/embeddedService.ts | 21 +- .../edit-graph-advanced.component.ts | 23 +- .../wise5/components/graph/graphAuthoring.ts | 65 ++- .../wise5/components/graph/graphController.ts | 46 +- .../wise5/components/graph/graphService.ts | 43 +- .../edit-html-advanced.component.ts | 7 +- .../html/htmlAuthoringComponentModule.ts | 2 +- .../components/html/htmlComponentModule.ts | 2 +- .../wise5/components/html/htmlService.ts | 9 +- .../edit-label-advanced.component.ts | 15 +- .../wise5/components/label/labelAuthoring.ts | 8 +- .../label/labelAuthoringComponentModule.ts | 2 +- .../components/label/labelComponentModule.ts | 2 +- .../wise5/components/label/labelController.ts | 45 +- .../wise5/components/label/labelService.ts | 90 ++-- .../edit-match-advanced.component.ts | 10 +- .../wise5/components/match/matchAuthoring.ts | 3 +- .../match/matchAuthoringComponentModule.ts | 2 +- .../components/match/matchComponentModule.ts | 2 +- .../wise5/components/match/matchController.ts | 45 +- .../wise5/components/match/matchService.ts | 20 +- ...edit-multiple-choice-advanced.component.ts | 16 +- .../multipleChoice/multipleChoiceAuthoring.ts | 3 +- .../multipleChoiceAuthoringComponentModule.ts | 2 +- .../multipleChoiceComponentModule.ts | 2 +- .../multipleChoiceController.ts | 4 +- .../multipleChoice/multipleChoiceService.ts | 42 +- .../edit-open-response-advanced.component.ts | 20 +- .../open-response-authoring.component.ts | 4 +- .../openResponseAuthoringComponentModule.ts | 8 +- .../openResponseComponentModule.ts | 2 +- .../openResponse/openResponseController.ts | 99 ++-- .../openResponse/openResponseService.ts | 30 +- .../edit-outside-url-advanced.component.ts | 7 +- .../outsideURL/outsideURLAuthoring.ts | 2 +- .../outsideURLAuthoringComponentModule.ts | 8 +- .../outsideURL/outsideURLComponentModule.ts | 2 +- .../outsideURL/outsideURLService.ts | 20 +- .../edit-summary-advanced.component.ts | 8 +- .../components/summary/summaryAuthoring.ts | 5 +- .../summaryAuthoringComponentModule.ts | 2 +- .../summary/summaryComponentModule.ts | 2 +- .../components/summary/summaryService.ts | 8 +- .../edit-table-advanced.component.ts | 11 +- .../wise5/components/table/tableAuthoring.ts | 4 +- .../table/tableAuthoringComponentModule.ts | 2 +- .../components/table/tableComponentModule.ts | 2 +- .../wise5/components/table/tableController.ts | 168 +++--- .../wise5/components/table/tableService.ts | 14 +- .../wise5/directives/component/component.ts | 33 +- .../component-annotations.component.ts | 69 +-- .../webapp/wise5/directives/components.ts | 18 +- ...authoring-tinymce-editor.component.spec.ts | 36 +- ...wise-authoring-tinymce-editor.component.ts | 29 +- .../wise-tinymce-editor.component.spec.ts | 70 ++- .../wise-tinymce-editor.component.ts | 47 +- .../wise5/services/achievementService.ts | 182 ++++--- .../wise5/services/annotationService.ts | 380 +++++++++----- .../wise5/services/audioRecorderService.ts | 16 +- .../webapp/wise5/services/cRaterService.ts | 72 +-- .../webapp/wise5/services/configService.ts | 91 ++-- .../webapp/wise5/services/milestoneService.ts | 192 +++---- src/main/webapp/wise5/services/nodeService.ts | 69 ++- .../webapp/wise5/services/notebookService.ts | 283 ++++++---- .../wise5/services/notificationService.ts | 170 +++--- .../webapp/wise5/services/projectService.ts | 205 +++++--- .../webapp/wise5/services/sessionService.ts | 23 +- .../wise5/services/studentAssetService.ts | 206 ++++---- .../wise5/services/studentDataService.ts | 245 +++++---- .../wise5/services/studentStatusService.ts | 54 +- .../wise5/services/studentWebSocketService.ts | 135 ++--- src/main/webapp/wise5/services/tagService.ts | 30 +- .../wise5/services/teacherDataService.ts | 232 +++++---- .../wise5/services/teacherProjectService.ts | 246 ++++----- .../wise5/services/teacherWebSocketService.ts | 61 +-- src/main/webapp/wise5/services/utilService.ts | 67 ++- src/main/webapp/wise5/teacher/src/test.ts | 5 +- .../teacher/teacher-angular-js-module.ts | 275 +++++----- .../wise5/themes/default/themeComponents.ts | 17 +- .../helpIcon/help-icon.component.ts | 7 +- .../node-status-icon.component.ts | 4 +- .../wise5/vle/nav-item/nav-item.component.ts | 108 ++-- .../vle/navigation/navigationController.ts | 15 +- .../webapp/wise5/vle/node/nodeController.ts | 205 ++++---- src/main/webapp/wise5/vle/src/test.ts | 5 +- .../wise5/vle/student-angular-js-module.ts | 422 +++++++-------- .../wise5/vle/studentAsset/studentAsset.ts | 2 +- .../studentAsset/studentAssetController.ts | 6 +- src/main/webapp/wise5/vle/vleController.ts | 123 ++--- .../webapp/wise5/vle/vleProjectService.ts | 15 +- 516 files changed, 9943 insertions(+), 8584 deletions(-) diff --git a/src/main/webapp/site/src/app/about/about-routing.module.ts b/src/main/webapp/site/src/app/about/about-routing.module.ts index 8d61ff5a17..8e84c53cc3 100644 --- a/src/main/webapp/site/src/app/about/about-routing.module.ts +++ b/src/main/webapp/site/src/app/about/about-routing.module.ts @@ -1,13 +1,11 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; -import { AboutComponent } from "./about.component"; +import { AboutComponent } from './about.component'; -const aboutRoutes: Routes = [ - { path: '', component: AboutComponent } -]; +const aboutRoutes: Routes = [{ path: '', component: AboutComponent }]; @NgModule({ - imports: [ RouterModule.forChild(aboutRoutes) ], - exports: [ RouterModule ] + imports: [RouterModule.forChild(aboutRoutes)], + exports: [RouterModule] }) -export class AboutRoutingModule { } +export class AboutRoutingModule {} diff --git a/src/main/webapp/site/src/app/about/about.component.spec.ts b/src/main/webapp/site/src/app/about/about.component.spec.ts index 98fb7b34f0..d765555dd1 100644 --- a/src/main/webapp/site/src/app/about/about.component.spec.ts +++ b/src/main/webapp/site/src/app/about/about.component.spec.ts @@ -1,5 +1,5 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; -import { NO_ERRORS_SCHEMA } from "@angular/core"; +import { NO_ERRORS_SCHEMA } from '@angular/core'; import { AboutComponent } from './about.component'; @@ -9,10 +9,9 @@ describe('AboutComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ AboutComponent ], - schemas: [ NO_ERRORS_SCHEMA ] - }) - .compileComponents(); + declarations: [AboutComponent], + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); })); beforeEach(() => { diff --git a/src/main/webapp/site/src/app/about/about.component.ts b/src/main/webapp/site/src/app/about/about.component.ts index 9a3c3894ec..7b5cce85bb 100644 --- a/src/main/webapp/site/src/app/about/about.component.ts +++ b/src/main/webapp/site/src/app/about/about.component.ts @@ -6,10 +6,7 @@ import { Component, OnInit } from '@angular/core'; styleUrls: ['./about.component.scss'] }) export class AboutComponent implements OnInit { + constructor() {} - constructor() { } - - ngOnInit() { - } - + ngOnInit() {} } diff --git a/src/main/webapp/site/src/app/about/about.module.ts b/src/main/webapp/site/src/app/about/about.module.ts index 8e63a9bb56..a32af05ee9 100644 --- a/src/main/webapp/site/src/app/about/about.module.ts +++ b/src/main/webapp/site/src/app/about/about.module.ts @@ -3,24 +3,13 @@ import { CommonModule } from '@angular/common'; import { FlexLayoutModule } from '@angular/flex-layout'; import { MatIconModule } from '@angular/material/icon'; -import { AboutComponent } from "./about.component"; -import { AboutRoutingModule } from "./about-routing.module"; -import { SharedModule } from "../modules/shared/shared.module"; +import { AboutComponent } from './about.component'; +import { AboutRoutingModule } from './about-routing.module'; +import { SharedModule } from '../modules/shared/shared.module'; @NgModule({ - imports: [ - CommonModule, - FlexLayoutModule, - MatIconModule, - AboutRoutingModule, - SharedModule - ], - declarations: [ - AboutComponent - ], - exports: [ - AboutComponent, - SharedModule - ] + imports: [CommonModule, FlexLayoutModule, MatIconModule, AboutRoutingModule, SharedModule], + declarations: [AboutComponent], + exports: [AboutComponent, SharedModule] }) -export class AboutModule { } +export class AboutModule {} diff --git a/src/main/webapp/site/src/app/animations.ts b/src/main/webapp/site/src/app/animations.ts index c1c858616b..aa03a6b9bb 100644 --- a/src/main/webapp/site/src/app/animations.ts +++ b/src/main/webapp/site/src/app/animations.ts @@ -1,223 +1,307 @@ -import { - animate, - keyframes, - state, - style, - transition, - trigger } from '@angular/animations'; +import { animate, keyframes, state, style, transition, trigger } from '@angular/animations'; -export const bounceIn = - trigger('bounceIn', [ - state('void', style({ +export const bounceIn = trigger('bounceIn', [ + state( + 'void', + style({ opacity: 0 - })), - state('*', style({ + }) + ), + state( + '*', + style({ opacity: 1 - })), - transition('void => *', [ - animate('{{ duration }} {{ delay }} ease-in-out', keyframes([ - style({ - transform: 'scale3d(0.3, 0.3, 0.3)', - opacity: 0, - offset: 0}), - style({ - transform: 'scale3d(1.1, 1.1, 1.1)', - opacity: 1, - offset: .2}), - style({ - transform: 'scale3d(0.9, 0.9, 0.9)', - offset: .4}), - style({ - transform: 'scale3d(1.03, 1.03, 1.03)', - offset: .6}), - style({ - transform: 'scale3d(1, 1, 1))', - offset: .8}), - style({ - transform: 'scale3d(1, 1, 1)', - offset: 1}) - ])) - ], {params: { duration: '1s', delay: '0s'}}) - ]); + }) + ), + transition( + 'void => *', + [ + animate( + '{{ duration }} {{ delay }} ease-in-out', + keyframes([ + style({ + transform: 'scale3d(0.3, 0.3, 0.3)', + opacity: 0, + offset: 0 + }), + style({ + transform: 'scale3d(1.1, 1.1, 1.1)', + opacity: 1, + offset: 0.2 + }), + style({ + transform: 'scale3d(0.9, 0.9, 0.9)', + offset: 0.4 + }), + style({ + transform: 'scale3d(1.03, 1.03, 1.03)', + offset: 0.6 + }), + style({ + transform: 'scale3d(1, 1, 1))', + offset: 0.8 + }), + style({ + transform: 'scale3d(1, 1, 1)', + offset: 1 + }) + ]) + ) + ], + { params: { duration: '1s', delay: '0s' } } + ) +]); -export const flipInX = - trigger('flipInX', [ - state('void', style({ +export const flipInX = trigger('flipInX', [ + state( + 'void', + style({ opacity: 0, backfaceVisibility: 'visible !important' - })), - state('*', style({ + }) + ), + state( + '*', + style({ opacity: 1, backfaceVisibility: 'visible !important' - })), - transition('void => *', [ - animate('{{ duration }} {{ delay }}', keyframes([ - style({ - transform: 'perspective(400px) rotate3d(1, 0, 0, 90deg)', - animationTimingFunction: 'ease-in', - opacity: 0, - offset: 0 - }), - style({ - transform: 'perspective(400px) rotate3d(1, 0, 0, -20deg)', - animationTimingFunction: 'ease-in', - opacity: 1, - offset: .4 - }), - style({ - transform: 'perspective(400px) rotate3d(1, 0, 0, 10deg)', - offset: .6 - }), - style({ - transform: 'perspective(400px) rotate3d(1, 0, 0, -5deg)', - offset: .8 - }), - style({ - transform: 'perspective(400px)', - offset: 1 - }) - ])) - ], {params: { duration: '1s', delay: '0s'}}) - ]); + }) + ), + transition( + 'void => *', + [ + animate( + '{{ duration }} {{ delay }}', + keyframes([ + style({ + transform: 'perspective(400px) rotate3d(1, 0, 0, 90deg)', + animationTimingFunction: 'ease-in', + opacity: 0, + offset: 0 + }), + style({ + transform: 'perspective(400px) rotate3d(1, 0, 0, -20deg)', + animationTimingFunction: 'ease-in', + opacity: 1, + offset: 0.4 + }), + style({ + transform: 'perspective(400px) rotate3d(1, 0, 0, 10deg)', + offset: 0.6 + }), + style({ + transform: 'perspective(400px) rotate3d(1, 0, 0, -5deg)', + offset: 0.8 + }), + style({ + transform: 'perspective(400px)', + offset: 1 + }) + ]) + ) + ], + { params: { duration: '1s', delay: '0s' } } + ) +]); -export const flipInY = - trigger('flipInY', [ - state('void', style({ +export const flipInY = trigger('flipInY', [ + state( + 'void', + style({ opacity: 0, backfaceVisibility: 'visible !important' - })), - state('*', style({ + }) + ), + state( + '*', + style({ opacity: 1, backfaceVisibility: 'visible !important' - })), - transition('void => *', [ - animate('{{ duration }} {{ delay }}', keyframes([ - style({ - transform: 'perspective(400px) rotate3d(0, 1, 0, 90deg)', - animationTimingFunction: 'ease-in', - opacity: 0, - offset: 0 - }), - style({ - transform: 'perspective(400px) rotate3d(0, 1, 0, -20deg)', - animationTimingFunction: 'ease-in', - opacity: 1, - offset: .4 - }), - style({ - transform: 'perspective(400px) rotate3d(0, 1, 0, 10deg)', - offset: .6 - }), - style({ - transform: 'perspective(400px) rotate3d(0, 1, 0, -5deg)', - offset: .8 - }), - style({ - transform: 'perspective(400px)', - offset: 1 - }) - ])) - ], {params: { duration: '1s', delay: '0s'}}) - ]); + }) + ), + transition( + 'void => *', + [ + animate( + '{{ duration }} {{ delay }}', + keyframes([ + style({ + transform: 'perspective(400px) rotate3d(0, 1, 0, 90deg)', + animationTimingFunction: 'ease-in', + opacity: 0, + offset: 0 + }), + style({ + transform: 'perspective(400px) rotate3d(0, 1, 0, -20deg)', + animationTimingFunction: 'ease-in', + opacity: 1, + offset: 0.4 + }), + style({ + transform: 'perspective(400px) rotate3d(0, 1, 0, 10deg)', + offset: 0.6 + }), + style({ + transform: 'perspective(400px) rotate3d(0, 1, 0, -5deg)', + offset: 0.8 + }), + style({ + transform: 'perspective(400px)', + offset: 1 + }) + ]) + ) + ], + { params: { duration: '1s', delay: '0s' } } + ) +]); -export const rotateIn = - trigger('rotateIn', [ - state('void', style({ +export const rotateIn = trigger('rotateIn', [ + state( + 'void', + style({ opacity: 0 - })), - state('*', style({ + }) + ), + state( + '*', + style({ opacity: 1 - })), - transition('void => *', [ - animate('{{ duration }} {{ delay }}', keyframes([ - style({ - transformOrigin: 'center', - transform: 'rotate3d(0, 0, 1, -200deg)', - opacity: 0, - offset: 0 - }), - style({ - transformOrigin: 'center', - transform: 'rotate3d(0, 0, 0, 0)', - opacity: 1, - offset: .5 - }) - ])) - ], {params: { duration: '1s', delay: '0s'}}) - ]); + }) + ), + transition( + 'void => *', + [ + animate( + '{{ duration }} {{ delay }}', + keyframes([ + style({ + transformOrigin: 'center', + transform: 'rotate3d(0, 0, 1, -200deg)', + opacity: 0, + offset: 0 + }), + style({ + transformOrigin: 'center', + transform: 'rotate3d(0, 0, 0, 0)', + opacity: 1, + offset: 0.5 + }) + ]) + ) + ], + { params: { duration: '1s', delay: '0s' } } + ) +]); -export const zoomIn = - trigger('zoomIn', [ - state('void', style({ +export const zoomIn = trigger('zoomIn', [ + state( + 'void', + style({ opacity: 0 - })), - state('*', style({ + }) + ), + state( + '*', + style({ opacity: 1 - })), - transition('void => *', [ - animate('{{ duration }} {{ delay }}', keyframes([ - style({ - transform: 'scale3d(0.3, 0.3, 0.3)', - opacity: 0, - offset: 0 - }), - style({ - transform: 'scale3d(1, 1, 1)', - opacity: 1, - offset: .5 - }) - ])) - ], {params: { duration: '1s', delay: '0s'}}) - ]); + }) + ), + transition( + 'void => *', + [ + animate( + '{{ duration }} {{ delay }}', + keyframes([ + style({ + transform: 'scale3d(0.3, 0.3, 0.3)', + opacity: 0, + offset: 0 + }), + style({ + transform: 'scale3d(1, 1, 1)', + opacity: 1, + offset: 0.5 + }) + ]) + ) + ], + { params: { duration: '1s', delay: '0s' } } + ) +]); -export const jackInTheBox = - trigger('jackInTheBox', [ - state('void', style({ +export const jackInTheBox = trigger('jackInTheBox', [ + state( + 'void', + style({ opacity: 0 - })), - state('*', style({ + }) + ), + state( + '*', + style({ opacity: 1 - })), - transition('void => *', [ - animate('{{ duration }} {{ delay }}', keyframes([ - style({ - opacity: 0, - transform: 'scale(0.1) rotate(30deg)', - transformOrigin: 'center bottom', - offset: 0 - }), - style({ - transform: 'rotate(-10deg)', - opacity: 1, - offset: .3 - }), - style({ - transform: 'rotate(3deg)', - offset: .6 - }), - style({ - transform: 'scale(1)', - offset: .8 - }) - ])) - ], {params: { duration: '1s', delay: '0s'}}) - ]); + }) + ), + transition( + 'void => *', + [ + animate( + '{{ duration }} {{ delay }}', + keyframes([ + style({ + opacity: 0, + transform: 'scale(0.1) rotate(30deg)', + transformOrigin: 'center bottom', + offset: 0 + }), + style({ + transform: 'rotate(-10deg)', + opacity: 1, + offset: 0.3 + }), + style({ + transform: 'rotate(3deg)', + offset: 0.6 + }), + style({ + transform: 'scale(1)', + offset: 0.8 + }) + ]) + ) + ], + { params: { duration: '1s', delay: '0s' } } + ) +]); - export const flash = - trigger('flash', [ - state('void', style({ +export const flash = trigger('flash', [ + state( + 'void', + style({ opacity: 0 - })), - state('*', style({ + }) + ), + state( + '*', + style({ opacity: 1 - })), - transition('void => *', [ - animate('{{ duration }} {{ delay }}', keyframes([ - style({ opacity: 1 }), - style({ opacity: 0 }), - style({ opacity: 1 }), - style({ opacity: 0 }), - style({ opacity: 1 }) - ])) - ], {params: { duration: '2s', delay: '0s'}}) - ]); + }) + ), + transition( + 'void => *', + [ + animate( + '{{ duration }} {{ delay }}', + keyframes([ + style({ opacity: 1 }), + style({ opacity: 0 }), + style({ opacity: 1 }), + style({ opacity: 0 }), + style({ opacity: 1 }) + ]) + ) + ], + { params: { duration: '2s', delay: '0s' } } + ) +]); diff --git a/src/main/webapp/site/src/app/announcement/announcement.component.spec.ts b/src/main/webapp/site/src/app/announcement/announcement.component.spec.ts index db6cf8f800..66dce515eb 100644 --- a/src/main/webapp/site/src/app/announcement/announcement.component.spec.ts +++ b/src/main/webapp/site/src/app/announcement/announcement.component.spec.ts @@ -1,5 +1,5 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { NO_ERRORS_SCHEMA } from "@angular/core"; +import { NO_ERRORS_SCHEMA } from '@angular/core'; import { AnnouncementComponent } from './announcement.component'; import { configureTestSuite } from 'ng-bullet'; import { Announcement } from '../domain/announcement'; @@ -12,15 +12,16 @@ describe('AnnouncementComponent', () => { configureTestSuite(() => { TestBed.configureTestingModule({ - declarations: [ AnnouncementComponent ], + declarations: [AnnouncementComponent], providers: [ - { provide: MatDialog, useValue: { - closeAll: () => { - } + { + provide: MatDialog, + useValue: { + closeAll: () => {} } } ], - schemas: [ NO_ERRORS_SCHEMA ] + schemas: [NO_ERRORS_SCHEMA] }); }); @@ -35,7 +36,7 @@ describe('AnnouncementComponent', () => { }); it('should show the banner text and button', () => { - component.announcement = new Announcement() + component.announcement = new Announcement(); component.announcement.visible = true; component.announcement.bannerText = 'This is an announcement.'; component.announcement.bannerButton = 'Do something'; @@ -45,9 +46,10 @@ describe('AnnouncementComponent', () => { expect(compiled.textContent).toContain('Do something'); }); - it('should emit dismiss event on dismiss button click', async() => { + it('should emit dismiss event on dismiss button click', async () => { spyOn(component.doDismiss, 'emit'); - const dismissButton = fixture.debugElement.query(By.css('.announcement__dismiss')).nativeElement; + const dismissButton = fixture.debugElement.query(By.css('.announcement__dismiss')) + .nativeElement; dismissButton.click(); fixture.detectChanges(); expect(component.doDismiss.emit).toHaveBeenCalled(); diff --git a/src/main/webapp/site/src/app/announcement/announcement.component.ts b/src/main/webapp/site/src/app/announcement/announcement.component.ts index 4b660e93da..4c41956d46 100644 --- a/src/main/webapp/site/src/app/announcement/announcement.component.ts +++ b/src/main/webapp/site/src/app/announcement/announcement.component.ts @@ -1,11 +1,4 @@ -import { - Component, - EventEmitter, - Input, - Output, - ViewEncapsulation, - Inject -} from '@angular/core'; +import { Component, EventEmitter, Input, Output, ViewEncapsulation, Inject } from '@angular/core'; import { Announcement } from '../domain/announcement'; import { MatDialogRef, MAT_DIALOG_DATA, MatDialog } from '@angular/material/dialog'; @@ -25,7 +18,7 @@ export class AnnouncementComponent { @Output('dismiss') doDismiss: EventEmitter = new EventEmitter(); - constructor(public dialog: MatDialog) { } + constructor(public dialog: MatDialog) {} dismiss() { this.doDismiss.emit(); @@ -41,11 +34,11 @@ export class AnnouncementComponent { @Component({ selector: 'announcement-dialog', - templateUrl: 'announcement-dialog.component.html', + templateUrl: 'announcement-dialog.component.html' }) export class AnnouncementDialogComponent { - constructor(public dialogRef: MatDialogRef, - @Inject(MAT_DIALOG_DATA) public data: Announcement) { - } + constructor( + public dialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) public data: Announcement + ) {} } - diff --git a/src/main/webapp/site/src/app/app-routing.module.ts b/src/main/webapp/site/src/app/app-routing.module.ts index 936907df01..8f9ef625ad 100644 --- a/src/main/webapp/site/src/app/app-routing.module.ts +++ b/src/main/webapp/site/src/app/app-routing.module.ts @@ -3,26 +3,43 @@ import { Injectable, NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { FormsModule } from '@angular/forms'; -import { PrivacyComponent } from "./privacy/privacy.component"; +import { PrivacyComponent } from './privacy/privacy.component'; const routes: Routes = [ - { path: '', loadChildren: () => import('./home/home.module').then(m => m.HomeModule) }, - { path: 'about', loadChildren: () => import('./about/about.module').then(m => m.AboutModule) }, - { path: 'contact', loadChildren: () => import('./contact/contact.module').then(m => m.ContactModule) }, - { path: 'features', loadChildren: () => import('./features/features.module').then(m => m.FeaturesModule) }, - { path: 'forgot', loadChildren: () => import('./forgot/forgot.module').then(m => m.ForgotModule) }, - { path: 'help', loadChildren: () => import('./help/help.module').then(m => m.HelpModule) }, - { path: 'join', loadChildren: () => import('./register/register.module').then(m => m.RegisterModule) }, - { path: 'login', loadChildren: () => import('./login/login.module').then(m => m.LoginModule) }, - { path: 'news', loadChildren: () => import('./news/news.module').then(m => m.NewsModule) }, + { path: '', loadChildren: () => import('./home/home.module').then((m) => m.HomeModule) }, + { path: 'about', loadChildren: () => import('./about/about.module').then((m) => m.AboutModule) }, + { + path: 'contact', + loadChildren: () => import('./contact/contact.module').then((m) => m.ContactModule) + }, + { + path: 'features', + loadChildren: () => import('./features/features.module').then((m) => m.FeaturesModule) + }, + { + path: 'forgot', + loadChildren: () => import('./forgot/forgot.module').then((m) => m.ForgotModule) + }, + { path: 'help', loadChildren: () => import('./help/help.module').then((m) => m.HelpModule) }, + { + path: 'join', + loadChildren: () => import('./register/register.module').then((m) => m.RegisterModule) + }, + { path: 'login', loadChildren: () => import('./login/login.module').then((m) => m.LoginModule) }, + { path: 'news', loadChildren: () => import('./news/news.module').then((m) => m.NewsModule) }, { path: 'privacy', component: PrivacyComponent }, - { path: 'student', loadChildren: () => import('./student/student.module').then(m => m.StudentModule) }, - { path: 'teacher', loadChildren: () => import('./teacher/teacher.module').then(m => m.TeacherModule) } + { + path: 'student', + loadChildren: () => import('./student/student.module').then((m) => m.StudentModule) + }, + { + path: 'teacher', + loadChildren: () => import('./teacher/teacher.module').then((m) => m.TeacherModule) + } ]; @Injectable() export class XhrInterceptor implements HttpInterceptor { - intercept(req: HttpRequest, next: HttpHandler) { const xhr = req.clone({ headers: req.headers.set('X-Requested-With', 'XMLHttpRequest') @@ -32,10 +49,9 @@ export class XhrInterceptor implements HttpInterceptor { } @NgModule({ - declarations: [ PrivacyComponent ], - imports: [ RouterModule.forRoot(routes), FormsModule ], - exports: [ RouterModule ], + declarations: [PrivacyComponent], + imports: [RouterModule.forRoot(routes), FormsModule], + exports: [RouterModule], providers: [{ provide: HTTP_INTERCEPTORS, useClass: XhrInterceptor, multi: true }] }) - -export class AppRoutingModule { } +export class AppRoutingModule {} diff --git a/src/main/webapp/site/src/app/app.component.spec.ts b/src/main/webapp/site/src/app/app.component.spec.ts index 32a3dd5186..22878ef487 100644 --- a/src/main/webapp/site/src/app/app.component.spec.ts +++ b/src/main/webapp/site/src/app/app.component.spec.ts @@ -1,25 +1,25 @@ import { TestBed, async, ComponentFixture } from '@angular/core/testing'; import { AppComponent } from './app.component'; -import { Component } from "@angular/core"; +import { Component } from '@angular/core'; import { NO_ERRORS_SCHEMA } from '@angular/core'; import { RouterTestingModule } from '@angular/router/testing'; import { MediaChange, MediaObserver } from '@angular/flex-layout'; import { Observable, config, BehaviorSubject } from 'rxjs'; -import { UtilService } from "./services/util.service"; +import { UtilService } from './services/util.service'; import { configureTestSuite } from 'ng-bullet'; import { Announcement } from './domain/announcement'; import { ConfigService } from './services/config.service'; -import { Config } from "./domain/config"; +import { Config } from './domain/config'; import { environment } from '../environments/environment'; -@Component({selector: 'router-outlet', template: ''}) -class RouterOutletStubComponent { } +@Component({ selector: 'router-outlet', template: '' }) +class RouterOutletStubComponent {} export class MockConfigService { private config$: BehaviorSubject = new BehaviorSubject(null); - + getAnnouncement(): Observable { - return Observable.create(observer => { + return Observable.create((observer) => { const announcement: Announcement = new Announcement(); announcement.visible = true; observer.next(announcement); @@ -41,7 +41,7 @@ export class MockConfigService { export class MockUtilService { getMobileMenuState(): Observable { - return Observable.create(observer => { + return Observable.create((observer) => { const state: boolean = false; observer.next(state); observer.complete(); @@ -55,7 +55,7 @@ export class MockObservableMedia { } asObservable(): Observable { - return Observable.create(observer => { + return Observable.create((observer) => { observer.next(new MediaChange()); observer.complete(); }); @@ -74,9 +74,9 @@ describe('AppComponent', () => { { provide: UtilService, useClass: MockUtilService }, { provide: MediaObserver, useClass: MockObservableMedia } ], - declarations: [ AppComponent ], - imports: [ RouterTestingModule ], - schemas: [ NO_ERRORS_SCHEMA ] + declarations: [AppComponent], + imports: [RouterTestingModule], + schemas: [NO_ERRORS_SCHEMA] }); }); diff --git a/src/main/webapp/site/src/app/app.component.ts b/src/main/webapp/site/src/app/app.component.ts index a610b0bb3e..7c9f523a99 100644 --- a/src/main/webapp/site/src/app/app.component.ts +++ b/src/main/webapp/site/src/app/app.component.ts @@ -5,8 +5,8 @@ import { DomSanitizer } from '@angular/platform-browser'; import { MatIconRegistry } from '@angular/material/icon'; import { Subscription } from 'rxjs'; import { MediaChange, MediaObserver } from '@angular/flex-layout'; -import { UtilService } from "./services/util.service"; -import { ConfigService } from "./services/config.service"; +import { UtilService } from './services/util.service'; +import { ConfigService } from './services/config.service'; import { Announcement } from './domain/announcement'; import { environment } from '../environments/environment'; declare let gtag: Function; @@ -36,13 +36,15 @@ export class AppComponent { '/teacher/edit': 'author-theme' }; - constructor(private router: Router, - iconRegistry: MatIconRegistry, - sanitizer: DomSanitizer, - utilService: UtilService, - media: MediaObserver, - private configService: ConfigService, - @Inject(DOCUMENT) private document: Document) { + constructor( + private router: Router, + iconRegistry: MatIconRegistry, + sanitizer: DomSanitizer, + utilService: UtilService, + media: MediaObserver, + private configService: ConfigService, + @Inject(DOCUMENT) private document: Document + ) { iconRegistry.addSvgIcon( 'ki-elicit', sanitizer.bypassSecurityTrustResourceUrl('assets/img/icons/ki-elicit.svg') @@ -87,7 +89,7 @@ export class AppComponent { 'google-classroom', sanitizer.bypassSecurityTrustResourceUrl('assets/img/icons/google-classroom.svg') ); - utilService.getMobileMenuState().subscribe(state => { + utilService.getMobileMenuState().subscribe((state) => { this.showMobileMenu = state; }); this.mediaWatcher = media.asObservable().subscribe((change: MediaChange[]) => { @@ -146,18 +148,18 @@ export class AppComponent { function gtag(){dataLayer.push(arguments);} gtag('js', new Date());`; this.document.head.appendChild(script); - gtag('config', this.googleAnalyticsId, { 'page_path': this.router.url }); + gtag('config', this.googleAnalyticsId, { page_path: this.router.url }); this.router.events.subscribe((ev: any) => { if (ev instanceof NavigationEnd) { - gtag('config', this.googleAnalyticsId, { 'page_path': ev.urlAfterRedirects }); + gtag('config', this.googleAnalyticsId, { page_path: ev.urlAfterRedirects }); } }); } } fixScrollTop(ev: any) { - const topElement = document.querySelector('.top-content',); + const topElement = document.querySelector('.top-content'); if (!topElement) { return; } @@ -172,10 +174,12 @@ export class AppComponent { } isShowDefaultMode(): boolean { - return !this.router.url.includes('/login') && + return ( + !this.router.url.includes('/login') && !this.router.url.includes('/join') && !this.router.url.includes('/contact') && - !this.router.url.includes('/forgot'); + !this.router.url.includes('/forgot') + ); } isShowHeaderAndFooter() { @@ -183,14 +187,16 @@ export class AppComponent { } isAngularJSRoute() { - return this.router.url.includes('/teacher/manage') || + return ( + this.router.url.includes('/teacher/manage') || this.router.url.includes('/teacher/edit') || this.router.url.includes('/student/unit') || - this.router.url.includes('/preview/unit'); + this.router.url.includes('/preview/unit') + ); } setTheme() { - Object.keys(this.themeClasses).forEach(path => { + Object.keys(this.themeClasses).forEach((path) => { if (this.router.url.includes(path)) { document.body.classList.add(this.themeClasses[path]); } else { @@ -203,7 +209,7 @@ export class AppComponent { this.hasAnnouncement = false; } - onYPositionChange(el:HTMLElement) { + onYPositionChange(el: HTMLElement) { this.pageY = el.scrollTop; this.scroll = this.pageY > 360 && this.pageY < this.prevPageY; this.prevPageY = this.pageY; diff --git a/src/main/webapp/site/src/app/app.module.ts b/src/main/webapp/site/src/app/app.module.ts index 9bc9120a75..cc84a1539c 100644 --- a/src/main/webapp/site/src/app/app.module.ts +++ b/src/main/webapp/site/src/app/app.module.ts @@ -2,8 +2,8 @@ import { APP_INITIALIZER, NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { FormsModule } from '@angular/forms'; import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http'; -import { HttpErrorInterceptor } from "./http-error.interceptor"; -import { BrowserAnimationsModule } from "@angular/platform-browser/animations"; +import { HttpErrorInterceptor } from './http-error.interceptor'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { RouterModule } from '@angular/router'; import { MatDialogModule } from '@angular/material/dialog'; import { MatSidenavModule } from '@angular/material/sidenav'; @@ -12,32 +12,36 @@ import { MAT_SNACK_BAR_DEFAULT_OPTIONS } from '@angular/material/snack-bar'; import { SocialLoginModule, AuthServiceConfig, - GoogleLoginProvider, LoginOpt, -} from "angularx-social-login"; + GoogleLoginProvider, + LoginOpt +} from 'angularx-social-login'; import { AppComponent } from './app.component'; import { AppRoutingModule } from './app-routing.module'; -import { ConfigService } from "./services/config.service"; +import { ConfigService } from './services/config.service'; import { HeaderModule } from './modules/header/header.module'; -import { HomeModule } from "./home/home.module"; +import { HomeModule } from './home/home.module'; import { FooterModule } from './modules/footer/footer.module'; import { StudentService } from './student/student.service'; import { UserService } from './services/user.service'; -import { TeacherService } from "./teacher/teacher.service"; -import { MobileMenuModule } from "./modules/mobile-menu/mobile-menu.module"; +import { TeacherService } from './teacher/teacher.service'; +import { MobileMenuModule } from './modules/mobile-menu/mobile-menu.module'; import { AnnouncementComponent } from './announcement/announcement.component'; import { AnnouncementDialogComponent } from './announcement/announcement.component'; import { TrackScrollDirective } from './track-scroll.directive'; import { PreviewModule } from './preview/preview.module'; -export function initialize(configService: ConfigService, userService: UserService): () => Promise { +export function initialize( + configService: ConfigService, + userService: UserService +): () => Promise { return (): Promise => { return userService.retrieveUserPromise().then((user) => { userService.getUser().subscribe((user) => { configService.retrieveConfig(); }); }); - } + }; } export function getAuthServiceConfigs(configService: ConfigService) { @@ -48,8 +52,10 @@ export function getAuthServiceConfigs(configService: ConfigService) { configService.getConfig().subscribe((config) => { if (config != null) { if (configService.getGoogleClientId() != null) { - autServiceConfig.providers.set(GoogleLoginProvider.PROVIDER_ID, - new GoogleLoginProvider(configService.getGoogleClientId(), googleLoginOptions)); + autServiceConfig.providers.set( + GoogleLoginProvider.PROVIDER_ID, + new GoogleLoginProvider(configService.getGoogleClientId(), googleLoginOptions) + ); } } }); @@ -80,7 +86,7 @@ export function getAuthServiceConfigs(configService: ConfigService) { MatDialogModule, RouterModule.forRoot([], { scrollPositionRestoration: 'enabled', - anchorScrolling: 'enabled', + anchorScrolling: 'enabled' }) ], providers: [ @@ -91,18 +97,13 @@ export function getAuthServiceConfigs(configService: ConfigService) { { provide: APP_INITIALIZER, useFactory: initialize, - deps: [ - ConfigService, - UserService - ], + deps: [ConfigService, UserService], multi: true }, { provide: AuthServiceConfig, useFactory: getAuthServiceConfigs, - deps: [ - ConfigService - ] + deps: [ConfigService] }, { provide: MAT_SNACK_BAR_DEFAULT_OPTIONS, @@ -120,4 +121,4 @@ export function getAuthServiceConfigs(configService: ConfigService) { ], bootstrap: [AppComponent] }) -export class AppModule { } +export class AppModule {} diff --git a/src/main/webapp/site/src/app/authoring-tool/add-component/choose-new-component-location/choose-new-component-location.component.spec.ts b/src/main/webapp/site/src/app/authoring-tool/add-component/choose-new-component-location/choose-new-component-location.component.spec.ts index 4d81e06dc8..6a872a7325 100644 --- a/src/main/webapp/site/src/app/authoring-tool/add-component/choose-new-component-location/choose-new-component-location.component.spec.ts +++ b/src/main/webapp/site/src/app/authoring-tool/add-component/choose-new-component-location/choose-new-component-location.component.spec.ts @@ -8,11 +8,14 @@ import { UtilService } from '../../../../../../wise5/services/utilService'; import { ChooseNewComponentLocation } from './choose-new-component-location.component'; const nodeId = 'node1'; -const components = [{id:'comp1', type:'OpenResponse'},{id:'comp2', type:'MultipleChoice'}]; +const components = [ + { id: 'comp1', type: 'OpenResponse' }, + { id: 'comp2', type: 'MultipleChoice' } +]; class MockProjectService { createComponent(componentType) { - return {id: "comp3", type: componentType}; + return { id: 'comp3', type: componentType }; } getComponentsByNodeId() { @@ -33,9 +36,9 @@ class MockTeacherDataService { class MockUpgradeModule { $injector: any = { get() { - return { componentType: 'Discussion'} + return { componentType: 'Discussion' }; } - } + }; } let component: ChooseNewComponentLocation; @@ -44,7 +47,7 @@ let projectService: ProjectService; describe('ChooseNewComponentLocation', () => { beforeEach(() => { TestBed.configureTestingModule({ - imports: [ HttpClientTestingModule ], + imports: [HttpClientTestingModule], providers: [ ChooseNewComponentLocation, ConfigService, @@ -65,7 +68,7 @@ describe('ChooseNewComponentLocation', () => { }); it('insertComponentAfter() should create a new component and save the project', () => { - spyOn(projectService, 'createComponent').and.returnValue({'id':'comp3', type:'Discussion'}) + spyOn(projectService, 'createComponent').and.returnValue({ id: 'comp3', type: 'Discussion' }); spyOn(projectService, 'saveProject').and.returnValue(new Promise(() => {})); component.insertComponentAfter('comp2'); expect(projectService.createComponent).toHaveBeenCalled(); diff --git a/src/main/webapp/site/src/app/authoring-tool/add-component/choose-new-component-location/choose-new-component-location.component.ts b/src/main/webapp/site/src/app/authoring-tool/add-component/choose-new-component-location/choose-new-component-location.component.ts index 776feb52fa..28c4d45a2f 100644 --- a/src/main/webapp/site/src/app/authoring-tool/add-component/choose-new-component-location/choose-new-component-location.component.ts +++ b/src/main/webapp/site/src/app/authoring-tool/add-component/choose-new-component-location/choose-new-component-location.component.ts @@ -1,23 +1,25 @@ -import { UtilService } from "../../../../../../wise5/services/utilService"; -import { ProjectService } from "../../../../../../wise5/services/projectService"; -import { TeacherDataService } from "../../../../../../wise5/services/teacherDataService"; -import { ConfigService } from "../../../../../../wise5/services/configService"; -import { Component } from "@angular/core"; -import { UpgradeModule } from "@angular/upgrade/static"; +import { UtilService } from '../../../../../../wise5/services/utilService'; +import { ProjectService } from '../../../../../../wise5/services/projectService'; +import { TeacherDataService } from '../../../../../../wise5/services/teacherDataService'; +import { ConfigService } from '../../../../../../wise5/services/configService'; +import { Component } from '@angular/core'; +import { UpgradeModule } from '@angular/upgrade/static'; @Component({ selector: 'choose-new-component-location', templateUrl: 'choose-new-component-location.component.html' }) export class ChooseNewComponentLocation { - components: any; nodeId: string; - constructor(private upgrade: UpgradeModule, private ConfigService: ConfigService, - private ProjectService: ProjectService, private TeacherDataService: TeacherDataService, - private UtilService: UtilService) { - } + constructor( + private upgrade: UpgradeModule, + private ConfigService: ConfigService, + private ProjectService: ProjectService, + private TeacherDataService: TeacherDataService, + private UtilService: UtilService + ) {} ngOnInit() { this.nodeId = this.TeacherDataService.getCurrentNodeId(); @@ -33,28 +35,50 @@ export class ChooseNewComponentLocation { } insertComponentAfter(insertAfterComponentId = null) { - const newComponent = this.ProjectService.createComponent(this.nodeId, - this.upgrade.$injector.get('$stateParams').componentType, insertAfterComponentId); + const newComponent = this.ProjectService.createComponent( + this.nodeId, + this.upgrade.$injector.get('$stateParams').componentType, + insertAfterComponentId + ); this.ProjectService.saveProject().then(() => { this.saveAddComponentEvent(newComponent); - this.upgrade.$injector.get('$state').go('root.at.project.node', - { projectId: this.ConfigService.getProjectId(), - nodeId: this.nodeId, newComponents: [newComponent]}) + this.upgrade.$injector + .get('$state') + .go('root.at.project.node', { + projectId: this.ConfigService.getProjectId(), + nodeId: this.nodeId, + newComponents: [newComponent] + }); }); } saveAddComponentEvent(newComponent: any) { - this.TeacherDataService.saveEvent('AuthoringTool', this.nodeId, null, null, 'Authoring', - 'componentCreated', { componentId: newComponent.id, componentType: newComponent.type }); + this.TeacherDataService.saveEvent( + 'AuthoringTool', + this.nodeId, + null, + null, + 'Authoring', + 'componentCreated', + { componentId: newComponent.id, componentType: newComponent.type } + ); } goToChooseNewComponent() { - this.upgrade.$injector.get('$state').go('root.at.project.node.add-component.choose-component', - this.upgrade.$injector.get('$stateParams')); + this.upgrade.$injector + .get('$state') + .go( + 'root.at.project.node.add-component.choose-component', + this.upgrade.$injector.get('$stateParams') + ); } cancel() { - this.upgrade.$injector.get('$state').go('root.at.project.node', - { projectId: this.ConfigService.getProjectId(), nodeId: this.nodeId }); + this.upgrade.$injector + .get('$state') + .go('root.at.project.node', { + projectId: this.ConfigService.getProjectId(), + nodeId: this.nodeId + }); } } diff --git a/src/main/webapp/site/src/app/authoring-tool/add-component/choose-new-component/choose-new-component.component.ts b/src/main/webapp/site/src/app/authoring-tool/add-component/choose-new-component/choose-new-component.component.ts index 5abed7e38f..f2ccf8d0fd 100644 --- a/src/main/webapp/site/src/app/authoring-tool/add-component/choose-new-component/choose-new-component.component.ts +++ b/src/main/webapp/site/src/app/authoring-tool/add-component/choose-new-component/choose-new-component.component.ts @@ -1,8 +1,8 @@ -import { UtilService } from "../../../../../../wise5/services/utilService"; -import { ConfigService } from "../../../../../../wise5/services/configService"; -import { TeacherDataService } from "../../../../../../wise5/services/teacherDataService"; -import { Component } from "@angular/core"; -import { UpgradeModule } from "@angular/upgrade/static"; +import { UtilService } from '../../../../../../wise5/services/utilService'; +import { ConfigService } from '../../../../../../wise5/services/configService'; +import { TeacherDataService } from '../../../../../../wise5/services/teacherDataService'; +import { Component } from '@angular/core'; +import { UpgradeModule } from '@angular/upgrade/static'; @Component({ selector: 'choose-new-component', @@ -10,13 +10,15 @@ import { UpgradeModule } from "@angular/upgrade/static"; templateUrl: 'choose-new-component.component.html' }) export class ChooseNewComponent { - componentTypes: any; selectedComponentType: string; - constructor(private upgrade: UpgradeModule, private ConfigService: ConfigService, - private TeacherDataService: TeacherDataService, private UtilService: UtilService) { - } + constructor( + private upgrade: UpgradeModule, + private ConfigService: ConfigService, + private TeacherDataService: TeacherDataService, + private UtilService: UtilService + ) {} ngOnInit() { this.componentTypes = [ @@ -71,13 +73,19 @@ export class ChooseNewComponent { } chooseLocation() { - this.upgrade.$injector.get('$state').go('root.at.project.node.add-component.choose-location', - { componentType: this.selectedComponentType }); + this.upgrade.$injector + .get('$state') + .go('root.at.project.node.add-component.choose-location', { + componentType: this.selectedComponentType + }); } cancel() { - this.upgrade.$injector.get('$state').go('root.at.project.node', - { projectId: this.ConfigService.getProjectId(), - nodeId: this.TeacherDataService.getCurrentNodeId() }); + this.upgrade.$injector + .get('$state') + .go('root.at.project.node', { + projectId: this.ConfigService.getProjectId(), + nodeId: this.TeacherDataService.getCurrentNodeId() + }); } } diff --git a/src/main/webapp/site/src/app/authoring-tool/edit-advanced-component/edit-advanced-component.component.ts b/src/main/webapp/site/src/app/authoring-tool/edit-advanced-component/edit-advanced-component.component.ts index 9e9576f514..c838c7f4a1 100644 --- a/src/main/webapp/site/src/app/authoring-tool/edit-advanced-component/edit-advanced-component.component.ts +++ b/src/main/webapp/site/src/app/authoring-tool/edit-advanced-component/edit-advanced-component.component.ts @@ -1,9 +1,8 @@ -import { Directive, Input } from "@angular/core"; -import { TeacherProjectService } from "../../../../../wise5/services/teacherProjectService"; +import { Directive, Input } from '@angular/core'; +import { TeacherProjectService } from '../../../../../wise5/services/teacherProjectService'; @Directive() export abstract class EditAdvancedComponentComponent { - authoringComponentContent: any; @Input() componentId: string; @@ -14,6 +13,8 @@ export abstract class EditAdvancedComponentComponent { ngOnInit() { this.authoringComponentContent = this.ProjectService.getComponentByNodeIdAndComponentId( - this.nodeId, this.componentId); + this.nodeId, + this.componentId + ); } } diff --git a/src/main/webapp/site/src/app/authoring-tool/edit-advanced-component/editAdvancedComponentAngularJSController.ts b/src/main/webapp/site/src/app/authoring-tool/edit-advanced-component/editAdvancedComponentAngularJSController.ts index ddc5eda2db..a664038fce 100644 --- a/src/main/webapp/site/src/app/authoring-tool/edit-advanced-component/editAdvancedComponentAngularJSController.ts +++ b/src/main/webapp/site/src/app/authoring-tool/edit-advanced-component/editAdvancedComponentAngularJSController.ts @@ -1,8 +1,7 @@ -import { NodeService } from "../../../../../wise5/services/nodeService"; -import { TeacherProjectService } from "../../../../../wise5/services/teacherProjectService"; +import { NodeService } from '../../../../../wise5/services/nodeService'; +import { TeacherProjectService } from '../../../../../wise5/services/teacherProjectService'; export class EditAdvancedComponentAngularJSController { - authoringComponentContent: any; componentId: string; nodeId: string; @@ -11,12 +10,16 @@ export class EditAdvancedComponentAngularJSController { static $inject = ['NodeService', 'ProjectService']; - constructor(protected NodeService: NodeService, protected ProjectService: TeacherProjectService) { - } + constructor( + protected NodeService: NodeService, + protected ProjectService: TeacherProjectService + ) {} $onInit(): void { this.authoringComponentContent = this.ProjectService.getComponentByNodeIdAndComponentId( - this.nodeId, this.componentId); + this.nodeId, + this.componentId + ); this.idToOrder = this.ProjectService.idToOrder; } @@ -42,8 +45,10 @@ export class EditAdvancedComponentAngularJSController { let numberOfAllowedComponents = 0; let allowedComponent = null; for (const component of this.ProjectService.getComponentsByNodeId(connectedComponent.nodeId)) { - if (this.isConnectedComponentTypeAllowed(component.type) && - component.id != this.componentId) { + if ( + this.isConnectedComponentTypeAllowed(component.type) && + component.id != this.componentId + ) { numberOfAllowedComponents += 1; allowedComponent = component; } @@ -82,8 +87,7 @@ export class EditAdvancedComponentAngularJSController { this.automaticallySetConnectedComponentFieldsIfPossible(connectedComponent); } - automaticallySetConnectedComponentFieldsIfPossible(connectedComponent: any): void { - } + automaticallySetConnectedComponentFieldsIfPossible(connectedComponent: any): void {} createConnectedComponent(): any { return { @@ -128,14 +132,12 @@ export class EditAdvancedComponentAngularJSController { showSubmitButton: show }); } - - getConnectedComponentType( - {nodeId, componentId}: { nodeId: string, componentId: string }) { + + getConnectedComponentType({ nodeId, componentId }: { nodeId: string; componentId: string }) { const component = this.ProjectService.getComponentByNodeIdAndComponentId(nodeId, componentId); if (component != null) { return component.type; } return null; } - } diff --git a/src/main/webapp/site/src/app/authoring-tool/edit-component-json/edit-component-json.component.ts b/src/main/webapp/site/src/app/authoring-tool/edit-component-json/edit-component-json.component.ts index c1c117f684..c43ec64b63 100644 --- a/src/main/webapp/site/src/app/authoring-tool/edit-component-json/edit-component-json.component.ts +++ b/src/main/webapp/site/src/app/authoring-tool/edit-component-json/edit-component-json.component.ts @@ -1,7 +1,7 @@ import * as angular from 'angular'; -import { Component, Input } from "@angular/core"; -import { UpgradeModule } from "@angular/upgrade/static"; -import { NotificationService } from "../../../../../wise5/services/notificationService"; +import { Component, Input } from '@angular/core'; +import { UpgradeModule } from '@angular/upgrade/static'; +import { NotificationService } from '../../../../../wise5/services/notificationService'; import { TeacherProjectService } from '../../../../../wise5/services/teacherProjectService'; import { Subject, Subscription } from 'rxjs'; import { debounceTime, distinctUntilChanged } from 'rxjs/operators'; @@ -12,7 +12,6 @@ import { debounceTime, distinctUntilChanged } from 'rxjs/operators'; styleUrls: ['edit-component-json.component.scss'] }) export class EditComponentJsonComponent { - validComponentContentJSONString: string; componentContentJSONString: string; @Input() @@ -24,25 +23,24 @@ export class EditComponentJsonComponent { jsonChanged: Subject = new Subject(); jsonChangedSubscription: Subscription; - constructor(private upgrade: UpgradeModule, private NotificationService: NotificationService, - private ProjectService: TeacherProjectService) { - } + constructor( + private upgrade: UpgradeModule, + private NotificationService: NotificationService, + private ProjectService: TeacherProjectService + ) {} ngOnInit() { - this.setComponentContentJsonString() + this.setComponentContentJsonString(); this.jsonChangedSubscription = this.jsonChanged - .pipe( - debounceTime(1000), - distinctUntilChanged() - ) - .subscribe(() => { - if (this.isJSONValid()) { - this.rememberRecentValidJSON(); - this.NotificationService.showJSONValidMessage(); - } else { - this.NotificationService.showJSONInvalidMessage(); - } - }); + .pipe(debounceTime(1000), distinctUntilChanged()) + .subscribe(() => { + if (this.isJSONValid()) { + this.rememberRecentValidJSON(); + this.NotificationService.showJSONValidMessage(); + } else { + this.NotificationService.showJSONInvalidMessage(); + } + }); this.nodeChangedSubscription = this.ProjectService.nodeChanged$.subscribe(() => { this.setComponentContentJsonString(); }); @@ -55,7 +53,9 @@ export class EditComponentJsonComponent { setComponentContentJsonString() { const authoringComponentContent = this.ProjectService.getComponentByNodeIdAndComponentId( - this.nodeId, this.componentId); + this.nodeId, + this.componentId + ); this.componentContentJSONString = angular.toJson(authoringComponentContent, 4); } @@ -65,8 +65,9 @@ export class EditComponentJsonComponent { this.saveChanges(); this.showJSONAuthoring = false; } else { - const doRollback = - confirm(this.upgrade.$injector.get('$filter')('translate')('jsonInvalidErrorMessage')); + const doRollback = confirm( + this.upgrade.$injector.get('$filter')('translate')('jsonInvalidErrorMessage') + ); if (doRollback) { this.rollbackToRecentValidJSON(); this.saveChanges(); @@ -92,7 +93,7 @@ export class EditComponentJsonComponent { const editedComponentContent = angular.fromJson(this.componentContentJSONString); this.ProjectService.replaceComponent(this.nodeId, this.componentId, editedComponentContent); this.ProjectService.componentChanged(); - } catch(e) { + } catch (e) { this.NotificationService.showJSONInvalidMessage(); } } diff --git a/src/main/webapp/site/src/app/authoring-tool/edit-component-max-score/edit-component-max-score.component.ts b/src/main/webapp/site/src/app/authoring-tool/edit-component-max-score/edit-component-max-score.component.ts index 5d99e87ab1..d28787e4b0 100644 --- a/src/main/webapp/site/src/app/authoring-tool/edit-component-max-score/edit-component-max-score.component.ts +++ b/src/main/webapp/site/src/app/authoring-tool/edit-component-max-score/edit-component-max-score.component.ts @@ -1,31 +1,26 @@ -import { Component, Input } from "@angular/core"; -import { Subject, Subscription } from "rxjs"; -import { debounceTime, distinctUntilChanged } from "rxjs/operators"; -import { TeacherProjectService } from "../../../../../wise5/services/teacherProjectService"; +import { Component, Input } from '@angular/core'; +import { Subject, Subscription } from 'rxjs'; +import { debounceTime, distinctUntilChanged } from 'rxjs/operators'; +import { TeacherProjectService } from '../../../../../wise5/services/teacherProjectService'; @Component({ selector: 'edit-component-max-score', templateUrl: 'edit-component-max-score.component.html' }) export class EditComponentMaxScoreComponent { - @Input() authoringComponentContent: any; maxScoreChanged: Subject = new Subject(); maxScoreChangedSubscription: Subscription; - constructor(private ProjectService: TeacherProjectService) { - } + constructor(private ProjectService: TeacherProjectService) {} ngOnInit() { this.maxScoreChangedSubscription = this.maxScoreChanged - .pipe( - debounceTime(1000), - distinctUntilChanged() - ) - .subscribe(() => { - this.ProjectService.componentChanged(); - }); + .pipe(debounceTime(1000), distinctUntilChanged()) + .subscribe(() => { + this.ProjectService.componentChanged(); + }); } ngOnDestroy() { diff --git a/src/main/webapp/site/src/app/authoring-tool/edit-component-rubric/edit-component-rubric.component.ts b/src/main/webapp/site/src/app/authoring-tool/edit-component-rubric/edit-component-rubric.component.ts index 39a7bba45f..3a07b70f25 100644 --- a/src/main/webapp/site/src/app/authoring-tool/edit-component-rubric/edit-component-rubric.component.ts +++ b/src/main/webapp/site/src/app/authoring-tool/edit-component-rubric/edit-component-rubric.component.ts @@ -1,6 +1,6 @@ -import { Component, Input } from "@angular/core"; -import { ConfigService } from "../../../../../wise5/services/configService"; -import { TeacherProjectService } from "../../../../../wise5/services/teacherProjectService"; +import { Component, Input } from '@angular/core'; +import { ConfigService } from '../../../../../wise5/services/configService'; +import { TeacherProjectService } from '../../../../../wise5/services/teacherProjectService'; @Component({ selector: 'edit-component-rubric', @@ -8,18 +8,20 @@ import { TeacherProjectService } from "../../../../../wise5/services/teacherProj styleUrls: ['edit-component-rubric.component.scss'] }) export class EditComponentRubricComponent { - @Input() authoringComponentContent: any; rubric: string; showRubricAuthoring: boolean = false; - constructor(private ConfigService: ConfigService, private ProjectService: TeacherProjectService) { - } + constructor( + private ConfigService: ConfigService, + private ProjectService: TeacherProjectService + ) {} ngOnInit() { const componentContent = this.ConfigService.replaceStudentNames( - this.ProjectService.injectAssetPaths(this.authoringComponentContent)); + this.ProjectService.injectAssetPaths(this.authoringComponentContent) + ); if (componentContent.rubric == null) { this.rubric = ''; } else { @@ -28,8 +30,9 @@ export class EditComponentRubricComponent { } rubricChanged(): void { - this.authoringComponentContent.rubric = - this.ConfigService.removeAbsoluteAssetPaths(this.rubric); + this.authoringComponentContent.rubric = this.ConfigService.removeAbsoluteAssetPaths( + this.rubric + ); this.ProjectService.componentChanged(); } } diff --git a/src/main/webapp/site/src/app/authoring-tool/edit-component-tags/edit-component-tags.component.ts b/src/main/webapp/site/src/app/authoring-tool/edit-component-tags/edit-component-tags.component.ts index 0f735a1a82..f764ce5b4a 100644 --- a/src/main/webapp/site/src/app/authoring-tool/edit-component-tags/edit-component-tags.component.ts +++ b/src/main/webapp/site/src/app/authoring-tool/edit-component-tags/edit-component-tags.component.ts @@ -1,8 +1,8 @@ -import { Component, Input } from "@angular/core"; -import { UpgradeModule } from "@angular/upgrade/static"; -import { Subject, Subscription } from "rxjs"; -import { debounceTime, distinctUntilChanged } from "rxjs/operators"; -import { TeacherProjectService } from "../../../../../wise5/services/teacherProjectService"; +import { Component, Input } from '@angular/core'; +import { UpgradeModule } from '@angular/upgrade/static'; +import { Subject, Subscription } from 'rxjs'; +import { debounceTime, distinctUntilChanged } from 'rxjs/operators'; +import { TeacherProjectService } from '../../../../../wise5/services/teacherProjectService'; @Component({ selector: 'edit-component-tags', @@ -10,25 +10,20 @@ import { TeacherProjectService } from "../../../../../wise5/services/teacherProj styleUrls: ['edit-component-tags.component.scss'] }) export class EditComponentTagsComponent { - @Input() authoringComponentContent: any; tagChanged: Subject = new Subject(); tagChangedSubscription: Subscription; - constructor(private ProjectService: TeacherProjectService, private upgrade: UpgradeModule) { - } + constructor(private ProjectService: TeacherProjectService, private upgrade: UpgradeModule) {} ngOnInit(): void { this.tagChangedSubscription = this.tagChanged - .pipe( - debounceTime(1000), - distinctUntilChanged() - ) - .subscribe(({tagIndex, tag}) => { - this.authoringComponentContent.tags[tagIndex] = tag; - this.ProjectService.componentChanged(); - }); + .pipe(debounceTime(1000), distinctUntilChanged()) + .subscribe(({ tagIndex, tag }) => { + this.authoringComponentContent.tags[tagIndex] = tag; + this.ProjectService.componentChanged(); + }); } ngOnDestroy(): void { @@ -62,8 +57,11 @@ export class EditComponentTagsComponent { } deleteTag(indexOfTagToDelete: number): void { - if (confirm( - this.upgrade.$injector.get('$filter')('translate')('areYouSureYouWantToDeleteThisTag'))) { + if ( + confirm( + this.upgrade.$injector.get('$filter')('translate')('areYouSureYouWantToDeleteThisTag') + ) + ) { this.authoringComponentContent.tags.splice(indexOfTagToDelete, 1); this.ProjectService.componentChanged(); } diff --git a/src/main/webapp/site/src/app/authoring-tool/edit-component-width/edit-component-width.component.ts b/src/main/webapp/site/src/app/authoring-tool/edit-component-width/edit-component-width.component.ts index 59c2f912db..475d01d3f2 100644 --- a/src/main/webapp/site/src/app/authoring-tool/edit-component-width/edit-component-width.component.ts +++ b/src/main/webapp/site/src/app/authoring-tool/edit-component-width/edit-component-width.component.ts @@ -1,31 +1,26 @@ -import { Component, Input } from "@angular/core"; -import { Subject, Subscription } from "rxjs"; -import { debounceTime, distinctUntilChanged } from "rxjs/operators"; -import { TeacherProjectService } from "../../../../../wise5/services/teacherProjectService"; +import { Component, Input } from '@angular/core'; +import { Subject, Subscription } from 'rxjs'; +import { debounceTime, distinctUntilChanged } from 'rxjs/operators'; +import { TeacherProjectService } from '../../../../../wise5/services/teacherProjectService'; @Component({ selector: 'edit-component-width', templateUrl: 'edit-component-width.component.html' }) export class EditComponentWidthComponent { - @Input() authoringComponentContent: any; widthChanged: Subject = new Subject(); widthChangedSubscription: Subscription; - constructor(private ProjectService: TeacherProjectService) { - } + constructor(private ProjectService: TeacherProjectService) {} ngOnInit() { this.widthChangedSubscription = this.widthChanged - .pipe( - debounceTime(1000), - distinctUntilChanged() - ) - .subscribe(() => { - this.ProjectService.componentChanged(); - }); + .pipe(debounceTime(1000), distinctUntilChanged()) + .subscribe(() => { + this.ProjectService.componentChanged(); + }); } ngOnDestroy() { diff --git a/src/main/webapp/site/src/app/authoring-tool/import-step/choose-import-step-location/choose-import-step-location.component.ts b/src/main/webapp/site/src/app/authoring-tool/import-step/choose-import-step-location/choose-import-step-location.component.ts index 5b329eb38b..e6e14f96b2 100644 --- a/src/main/webapp/site/src/app/authoring-tool/import-step/choose-import-step-location/choose-import-step-location.component.ts +++ b/src/main/webapp/site/src/app/authoring-tool/import-step/choose-import-step-location/choose-import-step-location.component.ts @@ -8,11 +8,13 @@ import { TeacherProjectService } from '../../../../../../wise5/services/teacherP templateUrl: 'choose-import-step-location.component.html' }) export class ChooseImportStepLocationComponent { - nodeIds: string[]; - constructor(private upgrade: UpgradeModule, private ConfigService: ConfigService, - private ProjectService: TeacherProjectService) { + constructor( + private upgrade: UpgradeModule, + private ConfigService: ConfigService, + private ProjectService: TeacherProjectService + ) { this.nodeIds = Object.keys(this.ProjectService.idToOrder); this.nodeIds.shift(); // remove the 'group0' master root node from consideration } diff --git a/src/main/webapp/site/src/app/authoring-tool/import-step/choose-import-step/choose-import-step.component.ts b/src/main/webapp/site/src/app/authoring-tool/import-step/choose-import-step/choose-import-step.component.ts index 3e6f7b52ec..5b2d268e7c 100644 --- a/src/main/webapp/site/src/app/authoring-tool/import-step/choose-import-step/choose-import-step.component.ts +++ b/src/main/webapp/site/src/app/authoring-tool/import-step/choose-import-step/choose-import-step.component.ts @@ -8,7 +8,6 @@ import { TeacherProjectService } from '../../../../../../wise5/services/teacherP templateUrl: 'choose-import-step.component.html' }) export class ChooseImportStepComponent { - $state: any; importLibraryProjectId: number; importMyProjectId: number; @@ -19,14 +18,17 @@ export class ChooseImportStepComponent { libraryProjectsList: any[]; myProjectsList: any[]; - constructor(private upgrade: UpgradeModule, private ConfigService: ConfigService, - private ProjectService: TeacherProjectService) { + constructor( + private upgrade: UpgradeModule, + private ConfigService: ConfigService, + private ProjectService: TeacherProjectService + ) { this.$state = this.upgrade.$injector.get('$state'); } ngOnInit() { this.myProjectsList = this.ConfigService.getAuthorableProjects(); - this.ProjectService.getLibraryProjects().then(libraryProjects => { + this.ProjectService.getLibraryProjects().then((libraryProjects) => { this.libraryProjectsList = this.ProjectService.sortAndFilterUniqueLibraryProjects( libraryProjects ); @@ -45,7 +47,7 @@ export class ChooseImportStepComponent { showProject(importProjectId) { this.importProjectId = importProjectId; - this.ProjectService.retrieveProjectById(this.importProjectId).then(projectJSON => { + this.ProjectService.retrieveProjectById(this.importProjectId).then((projectJSON) => { this.importProject = projectJSON; const nodeOrderOfProject = this.ProjectService.getNodeOrderOfProject(this.importProject); this.importProjectIdToOrder = Object.values(nodeOrderOfProject.idToOrder); diff --git a/src/main/webapp/site/src/app/classroom-monitor/component-new-work-badge/component-new-work-badge.component.ts b/src/main/webapp/site/src/app/classroom-monitor/component-new-work-badge/component-new-work-badge.component.ts index b1f7c872df..281f600a51 100644 --- a/src/main/webapp/site/src/app/classroom-monitor/component-new-work-badge/component-new-work-badge.component.ts +++ b/src/main/webapp/site/src/app/classroom-monitor/component-new-work-badge/component-new-work-badge.component.ts @@ -8,7 +8,6 @@ import { TeacherDataService } from '../../../../../wise5/services/teacherDataSer template: `New` }) export class ComponentNewWorkBadgeComponent { - annotationSavedToServerSubscription: Subscription; @Input() @@ -22,20 +21,22 @@ export class ComponentNewWorkBadgeComponent { @Input() workgroupId: number; - constructor(private AnnotationService: AnnotationService, - private TeacherDataService: TeacherDataService) { - } + constructor( + private AnnotationService: AnnotationService, + private TeacherDataService: TeacherDataService + ) {} ngOnInit() { this.checkHasNewWork(); - this.annotationSavedToServerSubscription = - this.AnnotationService.annotationSavedToServer$.subscribe(({ annotation }) => { - const annotationNodeId = annotation.nodeId; - const annotationComponentId = annotation.componentId; - if (this.nodeId === annotationNodeId && this.componentId === annotationComponentId) { - this.checkHasNewWork(); + this.annotationSavedToServerSubscription = this.AnnotationService.annotationSavedToServer$.subscribe( + ({ annotation }) => { + const annotationNodeId = annotation.nodeId; + const annotationComponentId = annotation.componentId; + if (this.nodeId === annotationNodeId && this.componentId === annotationComponentId) { + this.checkHasNewWork(); + } } - }); + ); } ngOnDestroy() { @@ -47,11 +48,18 @@ export class ComponentNewWorkBadgeComponent { } checkHasNewWork() { - const latestComponentState = - this.TeacherDataService.getLatestComponentStateByWorkgroupIdNodeIdAndComponentId( - this.workgroupId, this.nodeId, this.componentId); - const latestAnnotations = this.AnnotationService.getLatestComponentAnnotations(this.nodeId, - this.componentId, this.workgroupId, null, 'comment'); + const latestComponentState = this.TeacherDataService.getLatestComponentStateByWorkgroupIdNodeIdAndComponentId( + this.workgroupId, + this.nodeId, + this.componentId + ); + const latestAnnotations = this.AnnotationService.getLatestComponentAnnotations( + this.nodeId, + this.componentId, + this.workgroupId, + null, + 'comment' + ); if (latestComponentState) { let latestTeacherComment = null; if (latestAnnotations && latestAnnotations.comment) { diff --git a/src/main/webapp/site/src/app/classroom-monitor/component-select/component-select.component.ts b/src/main/webapp/site/src/app/classroom-monitor/component-select/component-select.component.ts index 513f225bd6..a059028d4b 100644 --- a/src/main/webapp/site/src/app/classroom-monitor/component-select/component-select.component.ts +++ b/src/main/webapp/site/src/app/classroom-monitor/component-select/component-select.component.ts @@ -1,7 +1,7 @@ -import { Component, EventEmitter, Input, Output } from "@angular/core"; -import { UpgradeModule } from "@angular/upgrade/static"; -import { TeacherProjectService } from "../../../../../wise5/services/teacherProjectService"; -import { UtilService } from "../../../../../wise5/services/utilService"; +import { Component, EventEmitter, Input, Output } from '@angular/core'; +import { UpgradeModule } from '@angular/upgrade/static'; +import { TeacherProjectService } from '../../../../../wise5/services/teacherProjectService'; +import { UtilService } from '../../../../../wise5/services/utilService'; @Component({ selector: 'component-select', @@ -9,7 +9,6 @@ import { UtilService } from "../../../../../wise5/services/utilService"; templateUrl: 'component-select.component.html' }) export class ComponentSelectComponent { - components: any[]; @Output() @@ -20,15 +19,17 @@ export class ComponentSelectComponent { selectedComponents: any[]; - constructor(private upgrade: UpgradeModule, private ProjectService: TeacherProjectService, - private UtilService: UtilService) { - } + constructor( + private upgrade: UpgradeModule, + private ProjectService: TeacherProjectService, + private UtilService: UtilService + ) {} ngOnInit() { - this.components = this.ProjectService.getComponentsByNodeId(this.nodeId).filter(component => { + this.components = this.ProjectService.getComponentsByNodeId(this.nodeId).filter((component) => { return this.ProjectService.componentHasWork(component); }); - this.selectedComponents = this.components.map(component => { + this.selectedComponents = this.components.map((component) => { return component.id; }); } diff --git a/src/main/webapp/site/src/app/classroom-monitor/milestones/milestones.component.ts b/src/main/webapp/site/src/app/classroom-monitor/milestones/milestones.component.ts index a0b792a919..e1fbecfc0a 100644 --- a/src/main/webapp/site/src/app/classroom-monitor/milestones/milestones.component.ts +++ b/src/main/webapp/site/src/app/classroom-monitor/milestones/milestones.component.ts @@ -15,36 +15,43 @@ export class MilestonesComponent { currentPeriodChangedSubscription: any; newStudentAchievementSubscription: any; - constructor(private AchievementService: AchievementService, - private AnnotationService: AnnotationService, private MilestoneService: MilestoneService, - private TeacherDataService: TeacherDataService) { - } + constructor( + private AchievementService: AchievementService, + private AnnotationService: AnnotationService, + private MilestoneService: MilestoneService, + private TeacherDataService: TeacherDataService + ) {} ngOnInit() { this.loadProjectMilestones(); - this.newStudentAchievementSubscription = - this.AchievementService.newStudentAchievement$.subscribe((args: any) => { - const studentAchievement = args.studentAchievement; - this.AchievementService.addOrUpdateStudentAchievement(studentAchievement); - this.updateMilestoneStatus(studentAchievement.achievementId); - }); - - this.currentPeriodChangedSubscription = this.TeacherDataService.currentPeriodChanged$ - .subscribe(() => { - for (const milestone of this.milestones) { - this.updateMilestoneStatus(milestone.id); + this.newStudentAchievementSubscription = this.AchievementService.newStudentAchievement$.subscribe( + (args: any) => { + const studentAchievement = args.studentAchievement; + this.AchievementService.addOrUpdateStudentAchievement(studentAchievement); + this.updateMilestoneStatus(studentAchievement.achievementId); } - }); + ); - this.annotationReceivedSubscription = - this.AnnotationService.annotationReceived$.subscribe(({ annotation }) => { - for (const milestone of this.milestones) { - if (milestone.nodeId === annotation.nodeId && - milestone.componentId === annotation.componentId) { + this.currentPeriodChangedSubscription = this.TeacherDataService.currentPeriodChanged$.subscribe( + () => { + for (const milestone of this.milestones) { this.updateMilestoneStatus(milestone.id); } } - }); + ); + + this.annotationReceivedSubscription = this.AnnotationService.annotationReceived$.subscribe( + ({ annotation }) => { + for (const milestone of this.milestones) { + if ( + milestone.nodeId === annotation.nodeId && + milestone.componentId === annotation.componentId + ) { + this.updateMilestoneStatus(milestone.id); + } + } + } + ); } ngOnDestroy() { diff --git a/src/main/webapp/site/src/app/classroom-monitor/nav-item-progress/nav-item-progress.component.ts b/src/main/webapp/site/src/app/classroom-monitor/nav-item-progress/nav-item-progress.component.ts index 042aada854..cc7f62afd3 100644 --- a/src/main/webapp/site/src/app/classroom-monitor/nav-item-progress/nav-item-progress.component.ts +++ b/src/main/webapp/site/src/app/classroom-monitor/nav-item-progress/nav-item-progress.component.ts @@ -1,4 +1,4 @@ -import { Component, Input } from "@angular/core"; +import { Component, Input } from '@angular/core'; @Component({ selector: 'nav-item-progress', @@ -6,7 +6,6 @@ import { Component, Input } from "@angular/core"; templateUrl: 'nav-item-progress.component.html' }) export class NavItemProgressComponent { - @Input() nodeCompletion: string; diff --git a/src/main/webapp/site/src/app/classroom-monitor/status-icon/status-icon.component.ts b/src/main/webapp/site/src/app/classroom-monitor/status-icon/status-icon.component.ts index 9aaa1e676e..913110b625 100644 --- a/src/main/webapp/site/src/app/classroom-monitor/status-icon/status-icon.component.ts +++ b/src/main/webapp/site/src/app/classroom-monitor/status-icon/status-icon.component.ts @@ -1,4 +1,4 @@ -import { Component, Input } from "@angular/core"; +import { Component, Input } from '@angular/core'; @Component({ selector: 'status-icon', @@ -6,7 +6,6 @@ import { Component, Input } from "@angular/core"; templateUrl: 'status-icon.component.html' }) export class StatusIconComponent { - @Input() iconClass: string; diff --git a/src/main/webapp/site/src/app/classroom-monitor/step-info/step-info.component.ts b/src/main/webapp/site/src/app/classroom-monitor/step-info/step-info.component.ts index 31304dc186..6be011f05b 100644 --- a/src/main/webapp/site/src/app/classroom-monitor/step-info/step-info.component.ts +++ b/src/main/webapp/site/src/app/classroom-monitor/step-info/step-info.component.ts @@ -22,16 +22,16 @@ export class StepInfoComponent { rubricIconLabel: string; stepTitle: string; - constructor(private ProjectService: TeacherProjectService) { - } + constructor(private ProjectService: TeacherProjectService) {} ngOnInit() { this.stepTitle = this.ProjectService.getNodePositionAndTitleByNodeId(this.nodeId); if (this.hasAlert) { this.alertIconClass = this.hasNewAlert ? 'warn' : 'text-disabled'; this.alertIconName = 'notifications'; - this.alertIconLabel = this.hasNewAlert ? $localize`Has new alert(s)`: - $localize`Has dismissed alert(s)`; + this.alertIconLabel = this.hasNewAlert + ? $localize`Has new alert(s)` + : $localize`Has dismissed alert(s)`; } this.hasRubrics = this.ProjectService.getNumberOfRubricsByNodeId(this.nodeId) > 0; this.rubricIconLabel = $localize`Step has rubrics/teaching tips`; diff --git a/src/main/webapp/site/src/app/classroom-monitor/workgroup-node-status/workgroup-node-status.component.ts b/src/main/webapp/site/src/app/classroom-monitor/workgroup-node-status/workgroup-node-status.component.ts index 517a78c30d..bc53c22ae7 100644 --- a/src/main/webapp/site/src/app/classroom-monitor/workgroup-node-status/workgroup-node-status.component.ts +++ b/src/main/webapp/site/src/app/classroom-monitor/workgroup-node-status/workgroup-node-status.component.ts @@ -1,11 +1,10 @@ -import { Component, Input } from "@angular/core"; +import { Component, Input } from '@angular/core'; @Component({ selector: 'workgroup-node-status', - template: `{{statusText}}` + template: `{{ statusText }}` }) export class WorkgroupNodeStatusComponent { - @Input() statusClass: string = 'text-secondary'; diff --git a/src/main/webapp/site/src/app/classroom-monitor/workgroup-select/workgroup-select-autocomplete/workgroup-select-autocomplete.component.ts b/src/main/webapp/site/src/app/classroom-monitor/workgroup-select/workgroup-select-autocomplete/workgroup-select-autocomplete.component.ts index 596aedb5af..6be5826b04 100644 --- a/src/main/webapp/site/src/app/classroom-monitor/workgroup-select/workgroup-select-autocomplete/workgroup-select-autocomplete.component.ts +++ b/src/main/webapp/site/src/app/classroom-monitor/workgroup-select/workgroup-select-autocomplete/workgroup-select-autocomplete.component.ts @@ -15,12 +15,13 @@ import { filter, map, startWith } from 'rxjs/operators'; encapsulation: ViewEncapsulation.None }) export class WorkgroupSelectAutocompleteComponent extends WorkgroupSelectComponent { - myControl = new FormControl(); filteredWorkgroups: Observable; - constructor(protected ConfigService: ConfigService, - protected TeacherDataService: TeacherDataService) { + constructor( + protected ConfigService: ConfigService, + protected TeacherDataService: TeacherDataService + ) { super(ConfigService, TeacherDataService); } @@ -34,12 +35,11 @@ export class WorkgroupSelectAutocompleteComponent extends WorkgroupSelectCompone } private updateFilteredWorkgroups() { - this.filteredWorkgroups = this.myControl.valueChanges - .pipe( - startWith(''), - filter(value => typeof value === 'string'), - map(value => this.filterByTypedKeyword(value)) - ); + this.filteredWorkgroups = this.myControl.valueChanges.pipe( + startWith(''), + filter((value) => typeof value === 'string'), + map((value) => this.filterByTypedKeyword(value)) + ); } displayWith(workgroup) { @@ -47,7 +47,9 @@ export class WorkgroupSelectAutocompleteComponent extends WorkgroupSelectCompone } private filterByTypedKeyword(value: string) { - return this.workgroups.filter(workgroup => workgroup.displayNames.toLowerCase().includes(value.toLowerCase())); + return this.workgroups.filter((workgroup) => + workgroup.displayNames.toLowerCase().includes(value.toLowerCase()) + ); } currentPeriodChanged() { @@ -58,8 +60,9 @@ export class WorkgroupSelectAutocompleteComponent extends WorkgroupSelectCompone this.workgroups = this.ConfigService.getClassmateUserInfos(); this.filterWorkgroupsBySelectedPeriod(); const students = this.getStudentsFromWorkgroups(); - this.workgroups = this.canViewStudentNames ? this.sortByDisplayNames(students) - : this.sortByField(students, 'userId'); + this.workgroups = this.canViewStudentNames + ? this.sortByDisplayNames(students) + : this.sortByField(students, 'userId'); this.updateFilteredWorkgroups(); } diff --git a/src/main/webapp/site/src/app/classroom-monitor/workgroup-select/workgroup-select-dropdown/workgroup-select-dropdown.component.ts b/src/main/webapp/site/src/app/classroom-monitor/workgroup-select/workgroup-select-dropdown/workgroup-select-dropdown.component.ts index 6123b4f4a9..65c501d4ed 100644 --- a/src/main/webapp/site/src/app/classroom-monitor/workgroup-select/workgroup-select-dropdown/workgroup-select-dropdown.component.ts +++ b/src/main/webapp/site/src/app/classroom-monitor/workgroup-select/workgroup-select-dropdown/workgroup-select-dropdown.component.ts @@ -12,11 +12,12 @@ import { WorkgroupSelectComponent } from '../workgroup-select.component'; encapsulation: ViewEncapsulation.None }) export class WorkgroupSelectDropdownComponent extends WorkgroupSelectComponent { - searchTerm: string = ''; - constructor(protected ConfigService: ConfigService, - protected TeacherDataService: TeacherDataService) { + constructor( + protected ConfigService: ConfigService, + protected TeacherDataService: TeacherDataService + ) { super(ConfigService, TeacherDataService); } @@ -29,8 +30,10 @@ export class WorkgroupSelectDropdownComponent extends WorkgroupSelectComponent { filterWorkgroupsBySearchTerm(): void { this.workgroups = this.workgroups.filter((workgroup) => { - return workgroup.displayNames.concat(workgroup.workgroupId) - .toLowerCase().includes(this.searchTerm); + return workgroup.displayNames + .concat(workgroup.workgroupId) + .toLowerCase() + .includes(this.searchTerm); }); } diff --git a/src/main/webapp/site/src/app/classroom-monitor/workgroup-select/workgroup-select.component.ts b/src/main/webapp/site/src/app/classroom-monitor/workgroup-select/workgroup-select.component.ts index 87de89ca69..3288242c9f 100644 --- a/src/main/webapp/site/src/app/classroom-monitor/workgroup-select/workgroup-select.component.ts +++ b/src/main/webapp/site/src/app/classroom-monitor/workgroup-select/workgroup-select.component.ts @@ -6,7 +6,6 @@ import { TeacherDataService } from '../../../../../wise5/services/teacherDataSer @Directive() export class WorkgroupSelectComponent { - @Input() customClass: string; canViewStudentNames: boolean; @@ -16,26 +15,29 @@ export class WorkgroupSelectComponent { currentPeriodChangedSubscription: any; currentWorkgroupChangedSubscription: any; - constructor(protected ConfigService: ConfigService, - protected TeacherDataService: TeacherDataService) { - } + constructor( + protected ConfigService: ConfigService, + protected TeacherDataService: TeacherDataService + ) {} ngOnInit() { this.canViewStudentNames = this.ConfigService.getPermissions().canViewStudentNames; this.periodId = this.TeacherDataService.getCurrentPeriod().periodId; this.setWorkgroups(); - this.currentWorkgroupChangedSubscription = - this.TeacherDataService.currentWorkgroupChanged$.subscribe(({ currentWorkgroup }) => { - if (currentWorkgroup != null) { + this.currentWorkgroupChangedSubscription = this.TeacherDataService.currentWorkgroupChanged$.subscribe( + ({ currentWorkgroup }) => { + if (currentWorkgroup != null) { + this.setWorkgroups(); + } + } + ); + this.currentPeriodChangedSubscription = this.TeacherDataService.currentPeriodChanged$.subscribe( + ({ currentPeriod }) => { + this.periodId = currentPeriod.periodId; this.setWorkgroups(); + this.currentPeriodChanged(); } - }); - this.currentPeriodChangedSubscription = this.TeacherDataService.currentPeriodChanged$ - .subscribe(({ currentPeriod }) => { - this.periodId = currentPeriod.periodId; - this.setWorkgroups(); - this.currentPeriodChanged(); - }); + ); } ngOnDestroy() { @@ -43,11 +45,9 @@ export class WorkgroupSelectComponent { this.currentWorkgroupChangedSubscription.unsubscribe(); } - setWorkgroups() { - } + setWorkgroups() {} - currentPeriodChanged() { - } + currentPeriodChanged() {} sortByField(arr: any[], field: string): any[] { return arr.sort((workgroup1, workgroup2) => { @@ -62,7 +62,7 @@ export class WorkgroupSelectComponent { } filterWorkgroupsBySelectedPeriod() { - this.workgroups = this.ConfigService.getClassmateUserInfos().filter(workgroup => { + this.workgroups = this.ConfigService.getClassmateUserInfos().filter((workgroup) => { return this.periodId === -1 || workgroup.periodId === this.periodId; }); } diff --git a/src/main/webapp/site/src/app/common-hybrid-angular.module.ts b/src/main/webapp/site/src/app/common-hybrid-angular.module.ts index 7872602136..8e03c4e976 100644 --- a/src/main/webapp/site/src/app/common-hybrid-angular.module.ts +++ b/src/main/webapp/site/src/app/common-hybrid-angular.module.ts @@ -56,7 +56,7 @@ import { EditorModule, TINYMCE_SCRIPT_SRC } from '@tinymce/tinymce-angular'; import { WiseTinymceEditorComponent } from '../../../wise5/directives/wise-tinymce-editor/wise-tinymce-editor.component'; import { MatAutocompleteModule } from '@angular/material/autocomplete'; -@Component({template: ``}) +@Component({ template: `` }) export class EmptyComponent {} @NgModule({ @@ -88,9 +88,7 @@ export class EmptyComponent {} MatTooltipModule, MomentModule, ReactiveFormsModule, - RouterModule.forChild([ - {path: '**', component: EmptyComponent} - ]) + RouterModule.forChild([{ path: '**', component: EmptyComponent }]) ], providers: [ AchievementService, diff --git a/src/main/webapp/site/src/app/contact/contact-form/contact-form.component.spec.ts b/src/main/webapp/site/src/app/contact/contact-form/contact-form.component.spec.ts index 3e8dc57631..be02238c14 100644 --- a/src/main/webapp/site/src/app/contact/contact-form/contact-form.component.spec.ts +++ b/src/main/webapp/site/src/app/contact/contact-form/contact-form.component.spec.ts @@ -1,15 +1,15 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { ContactFormComponent } from './contact-form.component'; -import { ReactiveFormsModule } from "@angular/forms"; -import { RouterTestingModule } from "@angular/router/testing"; -import { BrowserAnimationsModule } from "@angular/platform-browser/animations"; +import { ReactiveFormsModule } from '@angular/forms'; +import { RouterTestingModule } from '@angular/router/testing'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { MatInputModule } from '@angular/material/input'; import { MatSelectModule } from '@angular/material/select'; -import { UserService } from "../../services/user.service"; -import { NO_ERRORS_SCHEMA } from "@angular/core"; -import { ConfigService } from "../../services/config.service"; -import { StudentService } from "../../student/student.service"; -import { User } from "../../domain/user"; +import { UserService } from '../../services/user.service'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { ConfigService } from '../../services/config.service'; +import { StudentService } from '../../student/student.service'; +import { User } from '../../domain/user'; import { BehaviorSubject, Observable } from 'rxjs'; import { configureTestSuite } from 'ng-bullet'; import { LibraryService } from '../../services/library.service'; @@ -45,12 +45,9 @@ export class MockStudentService { getTeacherList(): Observable { return Observable.create(new User()); } - } -export class MockLibraryService { - -} +export class MockLibraryService {} describe('ContactFormComponent', () => { let component: ContactFormComponent; @@ -58,7 +55,7 @@ describe('ContactFormComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ ContactFormComponent ], + declarations: [ContactFormComponent], imports: [ BrowserAnimationsModule, RouterTestingModule, @@ -72,9 +69,8 @@ describe('ContactFormComponent', () => { { provide: StudentService, useClass: MockStudentService }, { provide: LibraryService, useClass: MockLibraryService } ], - schemas: [ NO_ERRORS_SCHEMA ] - }) - .compileComponents(); + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); })); beforeEach(() => { @@ -129,10 +125,10 @@ describe('ContactFormComponent', () => { it('should auto populate the name field if the user is signed in', () => { const nameInput = fixture.debugElement.nativeElement.querySelector('input[name="name"]'); const name = nameInput.valueOf().value; - expect(name).toBe("Demo User"); + expect(name).toBe('Demo User'); }); - it('should have its submit button disabled if the form isn\'t filled out', () => { + it("should have its submit button disabled if the form isn't filled out", () => { const submitButton = fixture.debugElement.nativeElement.querySelector('button'); expect(submitButton.disabled).toBe(true); }); diff --git a/src/main/webapp/site/src/app/contact/contact-form/contact-form.component.ts b/src/main/webapp/site/src/app/contact/contact-form/contact-form.component.ts index 6fea0f7209..594e7ff099 100644 --- a/src/main/webapp/site/src/app/contact/contact-form/contact-form.component.ts +++ b/src/main/webapp/site/src/app/contact/contact-form/contact-form.component.ts @@ -1,12 +1,12 @@ import { Component, OnInit, ViewEncapsulation } from '@angular/core'; -import { FormControl, FormGroup, Validators, FormBuilder } from "@angular/forms"; -import { ActivatedRoute, Router } from "@angular/router"; +import { FormControl, FormGroup, Validators, FormBuilder } from '@angular/forms'; +import { ActivatedRoute, Router } from '@angular/router'; import { finalize } from 'rxjs/operators'; -import { UserService } from "../../services/user.service"; -import { Teacher } from "../../domain/teacher"; -import { Student } from "../../domain/student"; -import { ConfigService } from "../../services/config.service"; -import { StudentService } from "../../student/student.service"; +import { UserService } from '../../services/user.service'; +import { Teacher } from '../../domain/teacher'; +import { Student } from '../../domain/student'; +import { ConfigService } from '../../services/config.service'; +import { StudentService } from '../../student/student.service'; import { LibraryService } from '../../services/library.service'; @Component({ @@ -16,10 +16,9 @@ import { LibraryService } from '../../services/library.service'; encapsulation: ViewEncapsulation.None }) export class ContactFormComponent implements OnInit { - issueTypes: object[] = []; contactFormGroup: FormGroup = this.fb.group({ - name: new FormControl( '', [Validators.required]), + name: new FormControl('', [Validators.required]), issueType: new FormControl('', [Validators.required]), summary: new FormControl('', [Validators.required]), description: new FormControl('', [Validators.required]) @@ -31,20 +30,21 @@ export class ContactFormComponent implements OnInit { isSignedIn: boolean = false; isSendingRequest: boolean = false; isRecaptchaEnabled: boolean = false; - recaptchaPublicKey: string = ""; - recaptchaResponse: string = ""; + recaptchaPublicKey: string = ''; + recaptchaResponse: string = ''; teachers: any[] = []; failure: boolean = false; complete: boolean = false; - constructor(private fb: FormBuilder, - private userService: UserService, - private configService: ConfigService, - private studentService: StudentService, - private libraryService: LibraryService, - private route: ActivatedRoute, - private router: Router) { - } + constructor( + private fb: FormBuilder, + private userService: UserService, + private configService: ConfigService, + private studentService: StudentService, + private libraryService: LibraryService, + private route: ActivatedRoute, + private router: Router + ) {} ngOnInit() { this.isSignedIn = this.userService.isSignedIn(); @@ -59,16 +59,16 @@ export class ContactFormComponent implements OnInit { } obtainRunIdOrProjectIdIfNecessary() { - this.route.queryParams.subscribe(params => { + this.route.queryParams.subscribe((params) => { this.runId = params['runId']; this.projectId = params['projectId']; if (this.runId) { - this.studentService.getRunInfoById(this.runId).subscribe(runInfo => { + this.studentService.getRunInfoById(this.runId).subscribe((runInfo) => { this.projectName = runInfo.name; }); } if (this.projectId) { - this.libraryService.getProjectInfo(this.projectId).subscribe(project => { + this.libraryService.getProjectInfo(this.projectId).subscribe((project) => { this.projectName = project.name; }); } @@ -95,7 +95,10 @@ export class ContactFormComponent implements OnInit { if (this.isStudent) { this.contactFormGroup.removeControl('email'); } else { - this.contactFormGroup.addControl('email', new FormControl('', [Validators.required, Validators.email])); + this.contactFormGroup.addControl( + 'email', + new FormControl('', [Validators.required, Validators.email]) + ); } } @@ -105,7 +108,10 @@ export class ContactFormComponent implements OnInit { if (config != null) { this.recaptchaPublicKey = this.configService.getRecaptchaPublicKey(); if (this.recaptchaPublicKey != null && this.recaptchaPublicKey != '') { - this.contactFormGroup.addControl('recaptcha', new FormControl('', [Validators.required])); + this.contactFormGroup.addControl( + 'recaptcha', + new FormControl('', [Validators.required]) + ); this.isRecaptchaEnabled = true; } } @@ -132,28 +138,28 @@ export class ContactFormComponent implements OnInit { populateIssueTypes() { if (this.isStudent) { this.issueTypes = [ - { key: "TROUBLE_LOGGING_IN", value: $localize`Trouble Signing In` }, - { key: "NEED_HELP_USING_WISE", value: $localize`Need Help Using WISE` }, - { key: "PROJECT_PROBLEMS", value: $localize`Problems with a WISE Unit` }, - { key: "FEEDBACK", value: $localize`Feedback to WISE` }, - { key: "OTHER", value: $localize`Other` } + { key: 'TROUBLE_LOGGING_IN', value: $localize`Trouble Signing In` }, + { key: 'NEED_HELP_USING_WISE', value: $localize`Need Help Using WISE` }, + { key: 'PROJECT_PROBLEMS', value: $localize`Problems with a WISE Unit` }, + { key: 'FEEDBACK', value: $localize`Feedback to WISE` }, + { key: 'OTHER', value: $localize`Other` } ]; } else { this.issueTypes = [ - { key: "TROUBLE_LOGGING_IN", value: $localize`Trouble Signing In` }, - { key: "NEED_HELP_USING_WISE", value: $localize`Need Help Using WISE` }, - { key: "PROJECT_PROBLEMS", value: $localize`Problems with a WISE Unit` }, - { key: "STUDENT_MANAGEMENT", value: $localize`Student Management` }, - { key: "AUTHORING", value: $localize`Need Help with Authoring` }, - { key: "FEEDBACK", value: $localize`Feedback to WISE` }, - { key: "OTHER", value: $localize`Other` } + { key: 'TROUBLE_LOGGING_IN', value: $localize`Trouble Signing In` }, + { key: 'NEED_HELP_USING_WISE', value: $localize`Need Help Using WISE` }, + { key: 'PROJECT_PROBLEMS', value: $localize`Problems with a WISE Unit` }, + { key: 'STUDENT_MANAGEMENT', value: $localize`Student Management` }, + { key: 'AUTHORING', value: $localize`Need Help with Authoring` }, + { key: 'FEEDBACK', value: $localize`Feedback to WISE` }, + { key: 'OTHER', value: $localize`Other` } ]; } } setIssueTypeIfNecessary() { if (this.runId != null) { - this.setControlFieldValue('issueType', 'PROJECT_PROBLEMS') + this.setControlFieldValue('issueType', 'PROJECT_PROBLEMS'); } } @@ -170,8 +176,19 @@ export class ContactFormComponent implements OnInit { const userAgent = this.getUserAgent(); const recaptchaResponse = this.getRecaptchaResponse(); this.setIsSendingRequest(true); - this.userService.sendContactMessage(name, email, teacherUsername, issueType, - summary, description, runId, projectId, userAgent, recaptchaResponse) + this.userService + .sendContactMessage( + name, + email, + teacherUsername, + issueType, + summary, + description, + runId, + projectId, + userAgent, + recaptchaResponse + ) .pipe( finalize(() => { this.setIsSendingRequest(false); @@ -183,9 +200,9 @@ export class ContactFormComponent implements OnInit { } handleSendContactMessageResponse(response: any) { - if (response.status === "success") { + if (response.status === 'success') { this.complete = true; - } else if (response.status === "error") { + } else if (response.status === 'error') { this.failure = true; if (this.isRecaptchaEnabled) { this.resetRecaptcha(); diff --git a/src/main/webapp/site/src/app/contact/contact-routing.module.ts b/src/main/webapp/site/src/app/contact/contact-routing.module.ts index 2dbd4380de..b4df920b39 100644 --- a/src/main/webapp/site/src/app/contact/contact-routing.module.ts +++ b/src/main/webapp/site/src/app/contact/contact-routing.module.ts @@ -1,16 +1,10 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; -import { ContactFormComponent } from "./contact-form/contact-form.component"; -const contactRoutes: Routes = [ - { path: '', component: ContactFormComponent } -]; +import { ContactFormComponent } from './contact-form/contact-form.component'; +const contactRoutes: Routes = [{ path: '', component: ContactFormComponent }]; @NgModule({ - imports: [ - RouterModule.forChild(contactRoutes) - ], - exports: [ - RouterModule - ] + imports: [RouterModule.forChild(contactRoutes)], + exports: [RouterModule] }) export class ContactRoutingModule {} diff --git a/src/main/webapp/site/src/app/contact/contact.module.ts b/src/main/webapp/site/src/app/contact/contact.module.ts index f2c0d1b134..ff51897487 100644 --- a/src/main/webapp/site/src/app/contact/contact.module.ts +++ b/src/main/webapp/site/src/app/contact/contact.module.ts @@ -18,7 +18,7 @@ import { MatRadioModule } from '@angular/material/radio'; import { MatTableModule } from '@angular/material/table'; import { MatTabsModule } from '@angular/material/tabs'; import { MatTooltipModule } from '@angular/material/tooltip'; -import { SharedModule } from "../modules/shared/shared.module"; +import { SharedModule } from '../modules/shared/shared.module'; import { ContactFormComponent } from './contact-form/contact-form.component'; import { RecaptchaModule, RecaptchaFormsModule } from 'ng-recaptcha'; @@ -52,12 +52,7 @@ const materialModules = [ RecaptchaModule, RecaptchaFormsModule ], - declarations: [ - ContactFormComponent - ], - exports: [ - ContactFormComponent, - materialModules - ] + declarations: [ContactFormComponent], + exports: [ContactFormComponent, materialModules] }) -export class ContactModule { } +export class ContactModule {} diff --git a/src/main/webapp/site/src/app/domain/news.ts b/src/main/webapp/site/src/app/domain/news.ts index 54b6cff14c..dedf70f798 100644 --- a/src/main/webapp/site/src/app/domain/news.ts +++ b/src/main/webapp/site/src/app/domain/news.ts @@ -1,4 +1,4 @@ -import { User } from "./user"; +import { User } from './user'; export class News { id: number; diff --git a/src/main/webapp/site/src/app/domain/notification.ts b/src/main/webapp/site/src/app/domain/notification.ts index 269fca6dc2..cb93825f13 100644 --- a/src/main/webapp/site/src/app/domain/notification.ts +++ b/src/main/webapp/site/src/app/domain/notification.ts @@ -1,4 +1,3 @@ - export class Notification { id: number; runId: number; diff --git a/src/main/webapp/site/src/app/domain/project.ts b/src/main/webapp/site/src/app/domain/project.ts index fef610a078..6558c9ad94 100644 --- a/src/main/webapp/site/src/app/domain/project.ts +++ b/src/main/webapp/site/src/app/domain/project.ts @@ -42,13 +42,15 @@ export class Project { } public canView(userId) { - return this.isOwner(userId) || - this.isSharedOwnerWithPermission(userId, Project.VIEW_PERMISSION); + return ( + this.isOwner(userId) || this.isSharedOwnerWithPermission(userId, Project.VIEW_PERMISSION) + ); } public canEdit(userId) { - return this.isOwner(userId) || - this.isSharedOwnerWithPermission(userId, Project.EDIT_PERMISSION); + return ( + this.isOwner(userId) || this.isSharedOwnerWithPermission(userId, Project.EDIT_PERMISSION) + ); } public isChild() { diff --git a/src/main/webapp/site/src/app/domain/run.spec.ts b/src/main/webapp/site/src/app/domain/run.spec.ts index 280956ee82..e9421b67c9 100644 --- a/src/main/webapp/site/src/app/domain/run.spec.ts +++ b/src/main/webapp/site/src/app/domain/run.spec.ts @@ -1,5 +1,5 @@ import { async } from '@angular/core/testing'; -import {Run} from "./run"; +import { Run } from './run'; describe('Run', () => { const run = new Run({ diff --git a/src/main/webapp/site/src/app/domain/run.ts b/src/main/webapp/site/src/app/domain/run.ts index d795886ad5..20a0734184 100644 --- a/src/main/webapp/site/src/app/domain/run.ts +++ b/src/main/webapp/site/src/app/domain/run.ts @@ -1,5 +1,5 @@ -import { Project } from "./project"; -import { User } from "./user"; +import { Project } from './project'; +import { User } from './user'; import { BehaviorSubject } from 'rxjs/internal/BehaviorSubject'; export class Run { @@ -26,11 +26,11 @@ export class Run { constructor(jsonObject: any = {}) { for (let key of Object.keys(jsonObject)) { const value = jsonObject[key]; - if (key == "owner") { + if (key == 'owner') { this[key] = new User(value); - } else if (key == "project") { + } else if (key == 'project') { this[key] = new Project(value); - } else if (key == "sharedOwners") { + } else if (key == 'sharedOwners') { const sharedOwners: User[] = []; for (let sharedOwner of value) { sharedOwners.push(new User(sharedOwner)); @@ -43,18 +43,24 @@ export class Run { } public canViewStudentWork(userId) { - return this.isOwner(userId) || - this.isSharedOwnerWithPermission(userId, Run.VIEW_STUDENT_WORK_PERMISSION); + return ( + this.isOwner(userId) || + this.isSharedOwnerWithPermission(userId, Run.VIEW_STUDENT_WORK_PERMISSION) + ); } public canGradeAndManage(userId) { - return this.isOwner(userId) || - this.isSharedOwnerWithPermission(userId, Run.GRADE_AND_MANAGE_PERMISSION); + return ( + this.isOwner(userId) || + this.isSharedOwnerWithPermission(userId, Run.GRADE_AND_MANAGE_PERMISSION) + ); } public canViewStudentNames(userId) { - return this.isOwner(userId) || - this.isSharedOwnerWithPermission(userId, Run.VIEW_STUDENT_NAMES_PERMISSION); + return ( + this.isOwner(userId) || + this.isSharedOwnerWithPermission(userId, Run.VIEW_STUDENT_NAMES_PERMISSION) + ); } isOwner(userId) { diff --git a/src/main/webapp/site/src/app/domain/student.ts b/src/main/webapp/site/src/app/domain/student.ts index b0c161f03d..0f38075682 100644 --- a/src/main/webapp/site/src/app/domain/student.ts +++ b/src/main/webapp/site/src/app/domain/student.ts @@ -1,4 +1,4 @@ -import { User } from "./user"; +import { User } from './user'; export class Student extends User { gender: string; diff --git a/src/main/webapp/site/src/app/domain/teacher.ts b/src/main/webapp/site/src/app/domain/teacher.ts index 4c34fe42a4..c833a76eb4 100644 --- a/src/main/webapp/site/src/app/domain/teacher.ts +++ b/src/main/webapp/site/src/app/domain/teacher.ts @@ -1,4 +1,4 @@ -import { User } from "./user"; +import { User } from './user'; export class Teacher extends User { displayName: string; diff --git a/src/main/webapp/site/src/app/features/features-routing.module.ts b/src/main/webapp/site/src/app/features/features-routing.module.ts index 9cecbbb94c..7a4515b28a 100644 --- a/src/main/webapp/site/src/app/features/features-routing.module.ts +++ b/src/main/webapp/site/src/app/features/features-routing.module.ts @@ -1,13 +1,11 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; -import { FeaturesComponent } from "./features.component"; +import { FeaturesComponent } from './features.component'; -const featuresRoutes: Routes = [ - { path: '', component: FeaturesComponent } -]; +const featuresRoutes: Routes = [{ path: '', component: FeaturesComponent }]; @NgModule({ - imports: [ RouterModule.forChild(featuresRoutes) ], - exports: [ RouterModule ] + imports: [RouterModule.forChild(featuresRoutes)], + exports: [RouterModule] }) -export class FeaturesRoutingModule { } +export class FeaturesRoutingModule {} diff --git a/src/main/webapp/site/src/app/features/features.component.spec.ts b/src/main/webapp/site/src/app/features/features.component.spec.ts index 3a05c23b26..32175d54f5 100644 --- a/src/main/webapp/site/src/app/features/features.component.spec.ts +++ b/src/main/webapp/site/src/app/features/features.component.spec.ts @@ -1,5 +1,5 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; -import { NO_ERRORS_SCHEMA } from "@angular/core"; +import { NO_ERRORS_SCHEMA } from '@angular/core'; import { FeaturesComponent } from './features.component'; @@ -9,10 +9,9 @@ describe('FeaturesComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ FeaturesComponent ], - schemas: [ NO_ERRORS_SCHEMA ] - }) - .compileComponents(); + declarations: [FeaturesComponent], + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); })); beforeEach(() => { diff --git a/src/main/webapp/site/src/app/features/features.component.ts b/src/main/webapp/site/src/app/features/features.component.ts index 26b0cabc0f..b547fbbb09 100644 --- a/src/main/webapp/site/src/app/features/features.component.ts +++ b/src/main/webapp/site/src/app/features/features.component.ts @@ -6,10 +6,7 @@ import { Component, OnInit } from '@angular/core'; styleUrls: ['./features.component.scss'] }) export class FeaturesComponent implements OnInit { + constructor() {} - constructor() { } - - ngOnInit() { - } - + ngOnInit() {} } diff --git a/src/main/webapp/site/src/app/features/features.module.ts b/src/main/webapp/site/src/app/features/features.module.ts index 2b71d61ed7..5de6395816 100644 --- a/src/main/webapp/site/src/app/features/features.module.ts +++ b/src/main/webapp/site/src/app/features/features.module.ts @@ -1,11 +1,11 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { FlexLayoutModule } from '@angular/flex-layout'; -import { MatDividerModule } from '@angular/material/divider'; +import { MatDividerModule } from '@angular/material/divider'; import { MatIconModule } from '@angular/material/icon'; -import { FeaturesComponent } from "./features.component"; -import { FeaturesRoutingModule } from "./features-routing.module"; -import { SharedModule } from "../modules/shared/shared.module"; +import { FeaturesComponent } from './features.component'; +import { FeaturesRoutingModule } from './features-routing.module'; +import { SharedModule } from '../modules/shared/shared.module'; @NgModule({ imports: [ @@ -16,12 +16,7 @@ import { SharedModule } from "../modules/shared/shared.module"; FeaturesRoutingModule, SharedModule ], - declarations: [ - FeaturesComponent - ], - exports: [ - FeaturesComponent, - SharedModule - ] + declarations: [FeaturesComponent], + exports: [FeaturesComponent, SharedModule] }) -export class FeaturesModule { } +export class FeaturesModule {} diff --git a/src/main/webapp/site/src/app/forgot/forgot-home/forgot-home.component.spec.ts b/src/main/webapp/site/src/app/forgot/forgot-home/forgot-home.component.spec.ts index d20b1a179c..c75fdf3925 100644 --- a/src/main/webapp/site/src/app/forgot/forgot-home/forgot-home.component.spec.ts +++ b/src/main/webapp/site/src/app/forgot/forgot-home/forgot-home.component.spec.ts @@ -9,8 +9,8 @@ describe('ForgotHomeComponent', () => { configureTestSuite(() => { TestBed.configureTestingModule({ - declarations: [ ForgotHomeComponent ], - schemas: [ NO_ERRORS_SCHEMA ] + declarations: [ForgotHomeComponent], + schemas: [NO_ERRORS_SCHEMA] }); }); diff --git a/src/main/webapp/site/src/app/forgot/forgot-home/forgot-home.component.ts b/src/main/webapp/site/src/app/forgot/forgot-home/forgot-home.component.ts index 5f2764db0d..913a40599c 100644 --- a/src/main/webapp/site/src/app/forgot/forgot-home/forgot-home.component.ts +++ b/src/main/webapp/site/src/app/forgot/forgot-home/forgot-home.component.ts @@ -6,10 +6,7 @@ import { Component, OnInit } from '@angular/core'; styleUrls: ['./forgot-home.component.scss'] }) export class ForgotHomeComponent implements OnInit { + constructor() {} - constructor() { } - - ngOnInit() { - } - + ngOnInit() {} } diff --git a/src/main/webapp/site/src/app/forgot/forgot-routing.module.ts b/src/main/webapp/site/src/app/forgot/forgot-routing.module.ts index 0eecb78244..0299364045 100644 --- a/src/main/webapp/site/src/app/forgot/forgot-routing.module.ts +++ b/src/main/webapp/site/src/app/forgot/forgot-routing.module.ts @@ -43,4 +43,4 @@ const routes: Routes = [ imports: [RouterModule.forChild(routes)], exports: [RouterModule] }) -export class ForgotRoutingModule { } +export class ForgotRoutingModule {} diff --git a/src/main/webapp/site/src/app/forgot/forgot.component.spec.ts b/src/main/webapp/site/src/app/forgot/forgot.component.spec.ts index fe548bd782..ba90e566c4 100644 --- a/src/main/webapp/site/src/app/forgot/forgot.component.spec.ts +++ b/src/main/webapp/site/src/app/forgot/forgot.component.spec.ts @@ -10,11 +10,10 @@ describe('ForgotComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - imports: [ RouterTestingModule ], - declarations: [ ForgotComponent ], - schemas: [ NO_ERRORS_SCHEMA ] - }) - .compileComponents(); + imports: [RouterTestingModule], + declarations: [ForgotComponent], + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); })); beforeEach(() => { diff --git a/src/main/webapp/site/src/app/forgot/forgot.component.ts b/src/main/webapp/site/src/app/forgot/forgot.component.ts index a30f04c606..d6fd11a1e5 100644 --- a/src/main/webapp/site/src/app/forgot/forgot.component.ts +++ b/src/main/webapp/site/src/app/forgot/forgot.component.ts @@ -6,10 +6,7 @@ import { Component, OnInit } from '@angular/core'; styleUrls: ['./forgot.component.scss'] }) export class ForgotComponent implements OnInit { + constructor() {} - constructor() { } - - ngOnInit() { - } - + ngOnInit() {} } diff --git a/src/main/webapp/site/src/app/forgot/forgot.module.ts b/src/main/webapp/site/src/app/forgot/forgot.module.ts index 6e72714f4f..aa821ad0b6 100644 --- a/src/main/webapp/site/src/app/forgot/forgot.module.ts +++ b/src/main/webapp/site/src/app/forgot/forgot.module.ts @@ -49,8 +49,6 @@ import { ForgotTeacherPasswordVerifyComponent } from './teacher/forgot-teacher-p ForgotTeacherPasswordCompleteComponent, ForgotTeacherPasswordVerifyComponent ], - exports: [ - ForgotComponent - ] + exports: [ForgotComponent] }) -export class ForgotModule { } +export class ForgotModule {} diff --git a/src/main/webapp/site/src/app/forgot/student/forgot-student-password-change/forgot-student-password-change.component.spec.ts b/src/main/webapp/site/src/app/forgot/student/forgot-student-password-change/forgot-student-password-change.component.spec.ts index 9d36ca6d38..2c4ff7f0a2 100644 --- a/src/main/webapp/site/src/app/forgot/student/forgot-student-password-change/forgot-student-password-change.component.spec.ts +++ b/src/main/webapp/site/src/app/forgot/student/forgot-student-password-change/forgot-student-password-change.component.spec.ts @@ -10,8 +10,13 @@ import { Observable } from 'rxjs/index'; import { configureTestSuite } from 'ng-bullet'; export class MockStudentService { - changePassword(username: string, answer: string, password: string, confirmPassword: string): Observable { - return Observable.create(observer => { + changePassword( + username: string, + answer: string, + password: string, + confirmPassword: string + ): Observable { + return Observable.create((observer) => { observer.next({ status: 'success', messageCode: 'passwordChanged' @@ -34,7 +39,7 @@ describe('ForgotStudentPasswordChangeComponent', () => { }; const createObservableResponse = (status, messageCode) => { - const observableResponse = Observable.create(observer => { + const observableResponse = Observable.create((observer) => { const response = { status: status, messageCode: messageCode @@ -56,17 +61,11 @@ describe('ForgotStudentPasswordChangeComponent', () => { configureTestSuite(() => { TestBed.configureTestingModule({ - declarations: [ ForgotStudentPasswordChangeComponent ], - imports: [ - RouterTestingModule, - BrowserAnimationsModule, - ReactiveFormsModule - ], - providers: [ - { provide: StudentService, useClass: MockStudentService } - ], - schemas: [ NO_ERRORS_SCHEMA ] - }) + declarations: [ForgotStudentPasswordChangeComponent], + imports: [RouterTestingModule, BrowserAnimationsModule, ReactiveFormsModule], + providers: [{ provide: StudentService, useClass: MockStudentService }], + schemas: [NO_ERRORS_SCHEMA] + }); }); beforeEach(() => { @@ -115,7 +114,9 @@ describe('ForgotStudentPasswordChangeComponent', () => { const params = { username: username }; - expect(navigateSpy).toHaveBeenCalledWith(['/forgot/student/password/complete'], - {queryParams: params, skipLocationChange: true}); + expect(navigateSpy).toHaveBeenCalledWith(['/forgot/student/password/complete'], { + queryParams: params, + skipLocationChange: true + }); }); }); diff --git a/src/main/webapp/site/src/app/forgot/student/forgot-student-password-change/forgot-student-password-change.component.ts b/src/main/webapp/site/src/app/forgot/student/forgot-student-password-change/forgot-student-password-change.component.ts index 5bfdf17f3c..56df82b20b 100644 --- a/src/main/webapp/site/src/app/forgot/student/forgot-student-password-change/forgot-student-password-change.component.ts +++ b/src/main/webapp/site/src/app/forgot/student/forgot-student-password-change/forgot-student-password-change.component.ts @@ -10,7 +10,6 @@ import { finalize } from 'rxjs/operators'; styleUrls: ['./forgot-student-password-change.component.scss'] }) export class ForgotStudentPasswordChangeComponent implements OnInit { - username: string; questionKey: string; answer: string; @@ -21,10 +20,12 @@ export class ForgotStudentPasswordChangeComponent implements OnInit { message: string = ''; processing: boolean = false; - constructor(private fb: FormBuilder, - private router: Router, - private route: ActivatedRoute, - private studentService: StudentService) { } + constructor( + private fb: FormBuilder, + private router: Router, + private route: ActivatedRoute, + private studentService: StudentService + ) {} ngOnInit() { this.username = this.route.snapshot.queryParamMap.get('username'); @@ -38,7 +39,8 @@ export class ForgotStudentPasswordChangeComponent implements OnInit { const confirmPassword = this.getConfirmPassword(); if (this.isPasswordsMatch(password, confirmPassword)) { this.processing = true; - this.studentService.changePassword(this.username, this.answer, password, confirmPassword) + this.studentService + .changePassword(this.username, this.answer, password, confirmPassword) .pipe( finalize(() => { this.processing = false; @@ -112,7 +114,9 @@ export class ForgotStudentPasswordChangeComponent implements OnInit { const params = { username: this.username }; - this.router.navigate(['/forgot/student/password/complete'], - {queryParams: params, skipLocationChange: true}); + this.router.navigate(['/forgot/student/password/complete'], { + queryParams: params, + skipLocationChange: true + }); } } diff --git a/src/main/webapp/site/src/app/forgot/student/forgot-student-password-complete/forgot-student-password-complete.component.spec.ts b/src/main/webapp/site/src/app/forgot/student/forgot-student-password-complete/forgot-student-password-complete.component.spec.ts index 86a615f68a..0fd8de2056 100644 --- a/src/main/webapp/site/src/app/forgot/student/forgot-student-password-complete/forgot-student-password-complete.component.spec.ts +++ b/src/main/webapp/site/src/app/forgot/student/forgot-student-password-complete/forgot-student-password-complete.component.spec.ts @@ -15,10 +15,10 @@ describe('ForgotStudentPasswordCompleteComponent', () => { configureTestSuite(() => { TestBed.configureTestingModule({ - declarations: [ ForgotStudentPasswordCompleteComponent ], - imports: [ RouterTestingModule ], - schemas: [ NO_ERRORS_SCHEMA ] - }) + declarations: [ForgotStudentPasswordCompleteComponent], + imports: [RouterTestingModule], + schemas: [NO_ERRORS_SCHEMA] + }); }); beforeEach(() => { @@ -32,7 +32,9 @@ describe('ForgotStudentPasswordCompleteComponent', () => { }); it('should show the password successfully changed message', () => { - expect(fixture.debugElement.nativeElement.textContent).toContain('Your password has been successfully changed'); + expect(fixture.debugElement.nativeElement.textContent).toContain( + 'Your password has been successfully changed' + ); }); it('should show the sign in button', () => { diff --git a/src/main/webapp/site/src/app/forgot/student/forgot-student-password-complete/forgot-student-password-complete.component.ts b/src/main/webapp/site/src/app/forgot/student/forgot-student-password-complete/forgot-student-password-complete.component.ts index ef5521b474..e54ff16baa 100644 --- a/src/main/webapp/site/src/app/forgot/student/forgot-student-password-complete/forgot-student-password-complete.component.ts +++ b/src/main/webapp/site/src/app/forgot/student/forgot-student-password-complete/forgot-student-password-complete.component.ts @@ -1,5 +1,5 @@ import { Component, OnInit } from '@angular/core'; -import {ActivatedRoute, Router} from '@angular/router'; +import { ActivatedRoute, Router } from '@angular/router'; @Component({ selector: 'app-forgot-student-password-complete', @@ -7,11 +7,9 @@ import {ActivatedRoute, Router} from '@angular/router'; styleUrls: ['./forgot-student-password-complete.component.scss'] }) export class ForgotStudentPasswordCompleteComponent implements OnInit { - username: string = null; - constructor(private router: Router, - private route: ActivatedRoute) { } + constructor(private router: Router, private route: ActivatedRoute) {} ngOnInit() { const username = this.route.snapshot.queryParamMap.get('username'); @@ -27,5 +25,4 @@ export class ForgotStudentPasswordCompleteComponent implements OnInit { } this.router.navigate(['/login', params]); } - } diff --git a/src/main/webapp/site/src/app/forgot/student/forgot-student-password-security/forgot-student-password-security.component.spec.ts b/src/main/webapp/site/src/app/forgot/student/forgot-student-password-security/forgot-student-password-security.component.spec.ts index 4abe740e22..f67b3d81eb 100644 --- a/src/main/webapp/site/src/app/forgot/student/forgot-student-password-security/forgot-student-password-security.component.spec.ts +++ b/src/main/webapp/site/src/app/forgot/student/forgot-student-password-security/forgot-student-password-security.component.spec.ts @@ -11,7 +11,7 @@ import { configureTestSuite } from 'ng-bullet'; export class MockStudentService { checkSecurityAnswer(username: string, answer: string): Observable { - return Observable.create(observer => { + return Observable.create((observer) => { observer.next({ status: 'success', messageCode: 'correctAnswer' @@ -34,7 +34,7 @@ describe('ForgotStudentPasswordSecurityComponent', () => { }; const createObservableResponse = (status, messageCode) => { - const observableResponse = Observable.create(observer => { + const observableResponse = Observable.create((observer) => { const response = { status: status, messageCode: messageCode @@ -56,17 +56,11 @@ describe('ForgotStudentPasswordSecurityComponent', () => { configureTestSuite(() => { TestBed.configureTestingModule({ - declarations: [ ForgotStudentPasswordSecurityComponent ], - imports: [ - RouterTestingModule, - BrowserAnimationsModule, - ReactiveFormsModule - ], - providers: [ - { provide: StudentService, useClass: MockStudentService } - ], - schemas: [ NO_ERRORS_SCHEMA ] - }) + declarations: [ForgotStudentPasswordSecurityComponent], + imports: [RouterTestingModule, BrowserAnimationsModule, ReactiveFormsModule], + providers: [{ provide: StudentService, useClass: MockStudentService }], + schemas: [NO_ERRORS_SCHEMA] + }); }); beforeEach(() => { @@ -112,7 +106,9 @@ describe('ForgotStudentPasswordSecurityComponent', () => { questionKey: questionKey, answer: answer }; - expect(navigateSpy).toHaveBeenCalledWith(['/forgot/student/password/change'], - {queryParams: params, skipLocationChange: true}); + expect(navigateSpy).toHaveBeenCalledWith(['/forgot/student/password/change'], { + queryParams: params, + skipLocationChange: true + }); }); }); diff --git a/src/main/webapp/site/src/app/forgot/student/forgot-student-password-security/forgot-student-password-security.component.ts b/src/main/webapp/site/src/app/forgot/student/forgot-student-password-security/forgot-student-password-security.component.ts index 0b0f27bc95..77517d44f2 100644 --- a/src/main/webapp/site/src/app/forgot/student/forgot-student-password-security/forgot-student-password-security.component.ts +++ b/src/main/webapp/site/src/app/forgot/student/forgot-student-password-security/forgot-student-password-security.component.ts @@ -1,7 +1,7 @@ import { Component, OnInit } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms'; -import {StudentService} from '../../../student/student.service'; +import { StudentService } from '../../../student/student.service'; import { finalize } from 'rxjs/operators'; @Component({ @@ -10,7 +10,6 @@ import { finalize } from 'rxjs/operators'; styleUrls: ['./forgot-student-password-security.component.scss'] }) export class ForgotStudentPasswordSecurityComponent implements OnInit { - username: string; questionKey: string; question: string; @@ -21,10 +20,12 @@ export class ForgotStudentPasswordSecurityComponent implements OnInit { message: string; processing: boolean = false; - constructor(private fb: FormBuilder, - private router: Router, - private route: ActivatedRoute, - private studentService: StudentService) { } + constructor( + private fb: FormBuilder, + private router: Router, + private route: ActivatedRoute, + private studentService: StudentService + ) {} ngOnInit() { this.username = this.route.snapshot.queryParamMap.get('username'); @@ -35,7 +36,8 @@ export class ForgotStudentPasswordSecurityComponent implements OnInit { submit() { this.processing = true; this.clearMessage(); - this.studentService.checkSecurityAnswer(this.username, this.getAnswer()) + this.studentService + .checkSecurityAnswer(this.username, this.getAnswer()) .pipe( finalize(() => { this.processing = false; @@ -83,7 +85,9 @@ export class ForgotStudentPasswordSecurityComponent implements OnInit { questionKey: this.questionKey, answer: this.getAnswer() }; - this.router.navigate(['/forgot/student/password/change'], - {queryParams: params, skipLocationChange: true}); + this.router.navigate(['/forgot/student/password/change'], { + queryParams: params, + skipLocationChange: true + }); } } diff --git a/src/main/webapp/site/src/app/forgot/student/forgot-student-password/forgot-student-password.component.spec.ts b/src/main/webapp/site/src/app/forgot/student/forgot-student-password/forgot-student-password.component.spec.ts index edb69abc92..62dd71462f 100644 --- a/src/main/webapp/site/src/app/forgot/student/forgot-student-password/forgot-student-password.component.spec.ts +++ b/src/main/webapp/site/src/app/forgot/student/forgot-student-password/forgot-student-password.component.spec.ts @@ -11,7 +11,7 @@ import { configureTestSuite } from 'ng-bullet'; export class MockStudentService { getSecurityQuestion(username: string): Observable { - return Observable.create(observer => { + return Observable.create((observer) => { observer.next({ status: 'success', messageCode: 'usernameFound' @@ -34,7 +34,7 @@ describe('ForgotStudentPasswordComponent', () => { }; const createObservableResponse = (status, messageCode) => { - const observableResponse = Observable.create(observer => { + const observableResponse = Observable.create((observer) => { const response = { status: status, messageCode: messageCode @@ -56,17 +56,11 @@ describe('ForgotStudentPasswordComponent', () => { configureTestSuite(() => { TestBed.configureTestingModule({ - declarations: [ ForgotStudentPasswordComponent ], - imports: [ - RouterTestingModule, - BrowserAnimationsModule, - ReactiveFormsModule - ], - providers: [ - { provide: StudentService, useClass: MockStudentService } - ], - schemas: [ NO_ERRORS_SCHEMA ] - }) + declarations: [ForgotStudentPasswordComponent], + imports: [RouterTestingModule, BrowserAnimationsModule, ReactiveFormsModule], + providers: [{ provide: StudentService, useClass: MockStudentService }], + schemas: [NO_ERRORS_SCHEMA] + }); }); beforeEach(() => { @@ -109,7 +103,9 @@ describe('ForgotStudentPasswordComponent', () => { questionKey: questionKey, question: question }; - expect(navigateSpy).toHaveBeenCalledWith(['/forgot/student/password/security'], - {queryParams: params, skipLocationChange: true}); + expect(navigateSpy).toHaveBeenCalledWith(['/forgot/student/password/security'], { + queryParams: params, + skipLocationChange: true + }); }); }); diff --git a/src/main/webapp/site/src/app/forgot/student/forgot-student-password/forgot-student-password.component.ts b/src/main/webapp/site/src/app/forgot/student/forgot-student-password/forgot-student-password.component.ts index b631ff38ab..4a82f46d83 100644 --- a/src/main/webapp/site/src/app/forgot/student/forgot-student-password/forgot-student-password.component.ts +++ b/src/main/webapp/site/src/app/forgot/student/forgot-student-password/forgot-student-password.component.ts @@ -10,7 +10,6 @@ import { finalize } from 'rxjs/operators'; styleUrls: ['./forgot-student-password.component.scss'] }) export class ForgotStudentPasswordComponent implements OnInit { - forgotStudentPasswordFormGroup: FormGroup = this.fb.group({ username: new FormControl('', [Validators.required]) }); @@ -18,19 +17,21 @@ export class ForgotStudentPasswordComponent implements OnInit { showForgotUsernameLink: boolean = false; processing: boolean = false; - constructor(private fb: FormBuilder, - private router: Router, - private studentService: StudentService) { } + constructor( + private fb: FormBuilder, + private router: Router, + private studentService: StudentService + ) {} - ngOnInit() { - } + ngOnInit() {} submit() { this.processing = true; this.clearMessage(); this.showForgotUsernameLink = false; const username = this.getUsername(); - this.studentService.getSecurityQuestion(username) + this.studentService + .getSecurityQuestion(username) .pipe( finalize(() => { this.processing = false; @@ -66,8 +67,10 @@ export class ForgotStudentPasswordComponent implements OnInit { questionKey: questionKey, question: question }; - this.router.navigate(['/forgot/student/password/security'], - {queryParams: params, skipLocationChange: true}); + this.router.navigate(['/forgot/student/password/security'], { + queryParams: params, + skipLocationChange: true + }); } setUsernameNotFoundMessage() { diff --git a/src/main/webapp/site/src/app/forgot/student/forgot-student-username/forgot-student-username.component.spec.ts b/src/main/webapp/site/src/app/forgot/student/forgot-student-username/forgot-student-username.component.spec.ts index 04bb98f8b9..b2a2a54e77 100644 --- a/src/main/webapp/site/src/app/forgot/student/forgot-student-username/forgot-student-username.component.spec.ts +++ b/src/main/webapp/site/src/app/forgot/student/forgot-student-username/forgot-student-username.component.spec.ts @@ -10,9 +10,7 @@ import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { Router } from '@angular/router'; import { configureTestSuite } from 'ng-bullet'; -export class MockStudentService { - -} +export class MockStudentService {} describe('ForgotStudentUsernameComponent', () => { let component: ForgotStudentUsernameComponent; @@ -24,7 +22,7 @@ describe('ForgotStudentUsernameComponent', () => { configureTestSuite(() => { TestBed.configureTestingModule({ - declarations: [ ForgotStudentUsernameComponent ], + declarations: [ForgotStudentUsernameComponent], imports: [ RouterTestingModule, BrowserAnimationsModule, @@ -32,11 +30,9 @@ describe('ForgotStudentUsernameComponent', () => { MatSelectModule, MatInputModule ], - providers: [ - { provide: StudentService, useClass: MockStudentService } - ], - schemas: [ NO_ERRORS_SCHEMA ] - }) + providers: [{ provide: StudentService, useClass: MockStudentService }], + schemas: [NO_ERRORS_SCHEMA] + }); }); beforeEach(() => { diff --git a/src/main/webapp/site/src/app/forgot/student/forgot-student-username/forgot-student-username.component.ts b/src/main/webapp/site/src/app/forgot/student/forgot-student-username/forgot-student-username.component.ts index 1398a2940a..c7cf91d66d 100644 --- a/src/main/webapp/site/src/app/forgot/student/forgot-student-username/forgot-student-username.component.ts +++ b/src/main/webapp/site/src/app/forgot/student/forgot-student-username/forgot-student-username.component.ts @@ -11,27 +11,26 @@ import { finalize } from 'rxjs/operators'; styleUrls: ['./forgot-student-username.component.scss'] }) export class ForgotStudentUsernameComponent implements OnInit { - months: any[] = [ - { value: 1, text: $localize`01 (Jan)`}, - { value: 2, text: $localize`02 (Feb)`}, - { value: 3, text: $localize`03 (Mar)`}, - { value: 4, text: $localize`04 (Apr)`}, - { value: 5, text: $localize`05 (May)`}, - { value: 6, text: $localize`06 (Jun)`}, - { value: 7, text: $localize`07 (Jul)`}, - { value: 8, text: $localize`08 (Aug)`}, - { value: 9, text: $localize`09 (Sep)`}, - { value: 10, text: $localize`10 (Oct)`}, - { value: 11, text: $localize`11 (Nov)`}, - { value: 12, text: $localize`12 (Dec)`} + { value: 1, text: $localize`01 (Jan)` }, + { value: 2, text: $localize`02 (Feb)` }, + { value: 3, text: $localize`03 (Mar)` }, + { value: 4, text: $localize`04 (Apr)` }, + { value: 5, text: $localize`05 (May)` }, + { value: 6, text: $localize`06 (Jun)` }, + { value: 7, text: $localize`07 (Jul)` }, + { value: 8, text: $localize`08 (Aug)` }, + { value: 9, text: $localize`09 (Sep)` }, + { value: 10, text: $localize`10 (Oct)` }, + { value: 11, text: $localize`11 (Nov)` }, + { value: 12, text: $localize`12 (Dec)` } ]; days: string[] = []; forgotStudentUsernameFormGroup: FormGroup = this.fb.group({ firstName: new FormControl('', [Validators.required]), lastName: new FormControl('', [Validators.required]), birthMonth: new FormControl('', [Validators.required]), - birthDay: new FormControl({value: '', disabled: true}, [Validators.required]) + birthDay: new FormControl({ value: '', disabled: true }, [Validators.required]) }); foundUsernames: string[] = []; message: string; @@ -39,13 +38,15 @@ export class ForgotStudentUsernameComponent implements OnInit { showSearchResults: boolean = false; processing: boolean = false; - constructor(private fb: FormBuilder, - private router: Router, - private utilService: UtilService, - private studentService: StudentService) { } + constructor( + private fb: FormBuilder, + private router: Router, + private utilService: UtilService, + private studentService: StudentService + ) {} ngOnInit() { - this.forgotStudentUsernameFormGroup.controls['birthMonth'].valueChanges.subscribe(value => { + this.forgotStudentUsernameFormGroup.controls['birthMonth'].valueChanges.subscribe((value) => { this.setBirthDayOptions(); }); } @@ -67,7 +68,8 @@ export class ForgotStudentUsernameComponent implements OnInit { const lastName = this.getControlFieldValue('lastName'); const birthMonth = parseInt(this.getControlFieldValue('birthMonth')); const birthDay = parseInt(this.getControlFieldValue('birthDay')); - this.studentService.getStudentUsernames(firstName, lastName, birthMonth, birthDay) + this.studentService + .getStudentUsernames(firstName, lastName, birthMonth, birthDay) .pipe( finalize(() => { this.processing = false; @@ -88,7 +90,7 @@ export class ForgotStudentUsernameComponent implements OnInit { this.isErrorMessage = true; } else if (foundUsernamesCount === 1) { this.setSingleMatchMessage(); - this.isErrorMessage = false + this.isErrorMessage = false; } else if (foundUsernamesCount > 1) { this.setMultipleMatchMessage(); this.isErrorMessage = false; diff --git a/src/main/webapp/site/src/app/forgot/student/forgot-student/forgot-student.component.spec.ts b/src/main/webapp/site/src/app/forgot/student/forgot-student/forgot-student.component.spec.ts index 393358d55a..fee7b27f32 100644 --- a/src/main/webapp/site/src/app/forgot/student/forgot-student/forgot-student.component.spec.ts +++ b/src/main/webapp/site/src/app/forgot/student/forgot-student/forgot-student.component.spec.ts @@ -9,8 +9,8 @@ describe('ForgotStudentComponent', () => { configureTestSuite(() => { TestBed.configureTestingModule({ - declarations: [ ForgotStudentComponent ], - schemas: [ NO_ERRORS_SCHEMA ] + declarations: [ForgotStudentComponent], + schemas: [NO_ERRORS_SCHEMA] }); }); diff --git a/src/main/webapp/site/src/app/forgot/student/forgot-student/forgot-student.component.ts b/src/main/webapp/site/src/app/forgot/student/forgot-student/forgot-student.component.ts index 1788c05d24..03ed7ace36 100644 --- a/src/main/webapp/site/src/app/forgot/student/forgot-student/forgot-student.component.ts +++ b/src/main/webapp/site/src/app/forgot/student/forgot-student/forgot-student.component.ts @@ -6,10 +6,7 @@ import { Component, OnInit } from '@angular/core'; styleUrls: ['./forgot-student.component.scss'] }) export class ForgotStudentComponent implements OnInit { + constructor() {} - constructor() { } - - ngOnInit() { - } - + ngOnInit() {} } diff --git a/src/main/webapp/site/src/app/forgot/teacher/forgot-teacher-password-change/forgot-teacher-password-change.component.spec.ts b/src/main/webapp/site/src/app/forgot/teacher/forgot-teacher-password-change/forgot-teacher-password-change.component.spec.ts index bfefbc3893..3ff2039dd2 100644 --- a/src/main/webapp/site/src/app/forgot/teacher/forgot-teacher-password-change/forgot-teacher-password-change.component.spec.ts +++ b/src/main/webapp/site/src/app/forgot/teacher/forgot-teacher-password-change/forgot-teacher-password-change.component.spec.ts @@ -1,17 +1,21 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { ForgotTeacherPasswordChangeComponent } from './forgot-teacher-password-change.component'; -import {NO_ERRORS_SCHEMA} from '@angular/core'; -import {RouterTestingModule} from '@angular/router/testing'; -import {ReactiveFormsModule} from '@angular/forms'; -import {TeacherService} from '../../../teacher/teacher.service'; -import {Observable} from 'rxjs/index'; -import {Router} from '@angular/router'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { RouterTestingModule } from '@angular/router/testing'; +import { ReactiveFormsModule } from '@angular/forms'; +import { TeacherService } from '../../../teacher/teacher.service'; +import { Observable } from 'rxjs/index'; +import { Router } from '@angular/router'; import { configureTestSuite } from 'ng-bullet'; export class MockTeacherService { - changePassword(username: string, verificationCode: string, password: string, - confirmPassword: string): Observable { - return Observable.create(observer => { + changePassword( + username: string, + verificationCode: string, + password: string, + confirmPassword: string + ): Observable { + return Observable.create((observer) => { observer.next({ status: 'success', messageCode: 'verificationCodeCorrect' @@ -34,7 +38,7 @@ describe('ForgotTeacherPasswordChangeComponent', () => { }; const createObservableResponse = (status, messageCode) => { - const observableResponse = Observable.create(observer => { + const observableResponse = Observable.create((observer) => { const response = { status: status, messageCode: messageCode @@ -52,16 +56,11 @@ describe('ForgotTeacherPasswordChangeComponent', () => { configureTestSuite(() => { TestBed.configureTestingModule({ - declarations: [ ForgotTeacherPasswordChangeComponent ], - imports: [ - RouterTestingModule, - ReactiveFormsModule - ], - providers: [ - { provide: TeacherService, useClass: MockTeacherService } - ], - schemas: [ NO_ERRORS_SCHEMA ] - }) + declarations: [ForgotTeacherPasswordChangeComponent], + imports: [RouterTestingModule, ReactiveFormsModule], + providers: [{ provide: TeacherService, useClass: MockTeacherService }], + schemas: [NO_ERRORS_SCHEMA] + }); }); beforeEach(() => { @@ -76,7 +75,9 @@ describe('ForgotTeacherPasswordChangeComponent', () => { it('should show the too many verification code attempts message', () => { submitAndReceiveResponse('changePassword', 'failure', 'tooManyVerificationCodeAttempts'); - expect(getErrorMessage()).toContain('You have submitted an invalid verification code too many times'); + expect(getErrorMessage()).toContain( + 'You have submitted an invalid verification code too many times' + ); }); it('should show the verification code expired message', () => { @@ -106,8 +107,10 @@ describe('ForgotTeacherPasswordChangeComponent', () => { const params = { username: null }; - expect(navigateSpy).toHaveBeenCalledWith(['/forgot/teacher/password/complete'], - {queryParams: params, skipLocationChange: true}); + expect(navigateSpy).toHaveBeenCalledWith(['/forgot/teacher/password/complete'], { + queryParams: params, + skipLocationChange: true + }); }); it('should navigate to the complete page after successfully submitting the new password', () => { @@ -122,7 +125,9 @@ describe('ForgotTeacherPasswordChangeComponent', () => { const params = { username: 'SpongebobSquarepants' }; - expect(navigateSpy).toHaveBeenCalledWith(['/forgot/teacher/password/complete'], - {queryParams: params, skipLocationChange: true}); + expect(navigateSpy).toHaveBeenCalledWith(['/forgot/teacher/password/complete'], { + queryParams: params, + skipLocationChange: true + }); }); }); diff --git a/src/main/webapp/site/src/app/forgot/teacher/forgot-teacher-password-change/forgot-teacher-password-change.component.ts b/src/main/webapp/site/src/app/forgot/teacher/forgot-teacher-password-change/forgot-teacher-password-change.component.ts index e1fd6ae0cf..fede7b7a2a 100644 --- a/src/main/webapp/site/src/app/forgot/teacher/forgot-teacher-password-change/forgot-teacher-password-change.component.ts +++ b/src/main/webapp/site/src/app/forgot/teacher/forgot-teacher-password-change/forgot-teacher-password-change.component.ts @@ -10,7 +10,6 @@ import { finalize } from 'rxjs/operators'; styleUrls: ['./forgot-teacher-password-change.component.scss'] }) export class ForgotTeacherPasswordChangeComponent implements OnInit { - username: string; verificationCode: string; changePasswordFormGroup: FormGroup = this.fb.group({ @@ -22,10 +21,12 @@ export class ForgotTeacherPasswordChangeComponent implements OnInit { isSubmitButtonEnabled: boolean = true; showForgotPasswordLink = false; - constructor(private fb: FormBuilder, - private router: Router, - private route: ActivatedRoute, - private teacherService: TeacherService) { } + constructor( + private fb: FormBuilder, + private router: Router, + private route: ActivatedRoute, + private teacherService: TeacherService + ) {} ngOnInit() { this.username = this.route.snapshot.queryParamMap.get('username'); @@ -39,7 +40,8 @@ export class ForgotTeacherPasswordChangeComponent implements OnInit { this.showForgotPasswordLink = false; if (this.isPasswordsMatch(password, confirmPassword)) { this.processing = true; - this.teacherService.changePassword(this.username, this.verificationCode, password, confirmPassword) + this.teacherService + .changePassword(this.username, this.verificationCode, password, confirmPassword) .pipe( finalize(() => { this.processing = false; @@ -147,7 +149,9 @@ export class ForgotTeacherPasswordChangeComponent implements OnInit { const params = { username: this.username }; - this.router.navigate(['/forgot/teacher/password/complete'], - {queryParams: params, skipLocationChange: true}); + this.router.navigate(['/forgot/teacher/password/complete'], { + queryParams: params, + skipLocationChange: true + }); } } diff --git a/src/main/webapp/site/src/app/forgot/teacher/forgot-teacher-password-complete/forgot-teacher-password-complete.component.spec.ts b/src/main/webapp/site/src/app/forgot/teacher/forgot-teacher-password-complete/forgot-teacher-password-complete.component.spec.ts index 6da0c658b0..08fd2f337b 100644 --- a/src/main/webapp/site/src/app/forgot/teacher/forgot-teacher-password-complete/forgot-teacher-password-complete.component.spec.ts +++ b/src/main/webapp/site/src/app/forgot/teacher/forgot-teacher-password-complete/forgot-teacher-password-complete.component.spec.ts @@ -1,8 +1,8 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { ForgotTeacherPasswordCompleteComponent } from './forgot-teacher-password-complete.component'; -import {NO_ERRORS_SCHEMA} from '@angular/core'; -import {RouterTestingModule} from '@angular/router/testing'; -import {Router} from '@angular/router'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { RouterTestingModule } from '@angular/router/testing'; +import { Router } from '@angular/router'; import { configureTestSuite } from 'ng-bullet'; describe('ForgotTeacherPasswordCompleteComponent', () => { @@ -11,9 +11,9 @@ describe('ForgotTeacherPasswordCompleteComponent', () => { configureTestSuite(() => { TestBed.configureTestingModule({ - declarations: [ ForgotTeacherPasswordCompleteComponent ], - imports: [ RouterTestingModule ], - schemas: [ NO_ERRORS_SCHEMA ] + declarations: [ForgotTeacherPasswordCompleteComponent], + imports: [RouterTestingModule], + schemas: [NO_ERRORS_SCHEMA] }); }); diff --git a/src/main/webapp/site/src/app/forgot/teacher/forgot-teacher-password-complete/forgot-teacher-password-complete.component.ts b/src/main/webapp/site/src/app/forgot/teacher/forgot-teacher-password-complete/forgot-teacher-password-complete.component.ts index de94306e86..ee62f2b20f 100644 --- a/src/main/webapp/site/src/app/forgot/teacher/forgot-teacher-password-complete/forgot-teacher-password-complete.component.ts +++ b/src/main/webapp/site/src/app/forgot/teacher/forgot-teacher-password-complete/forgot-teacher-password-complete.component.ts @@ -1,5 +1,5 @@ import { Component, OnInit } from '@angular/core'; -import {ActivatedRoute, Router} from '@angular/router'; +import { ActivatedRoute, Router } from '@angular/router'; @Component({ selector: 'app-forgot-teacher-password-complete', @@ -7,11 +7,9 @@ import {ActivatedRoute, Router} from '@angular/router'; styleUrls: ['./forgot-teacher-password-complete.component.scss'] }) export class ForgotTeacherPasswordCompleteComponent implements OnInit { - username: string = null; - constructor(private router: Router, - private route: ActivatedRoute) { } + constructor(private router: Router, private route: ActivatedRoute) {} ngOnInit() { const username = this.route.snapshot.queryParamMap.get('username'); @@ -27,5 +25,4 @@ export class ForgotTeacherPasswordCompleteComponent implements OnInit { } this.router.navigate(['/login', params]); } - } diff --git a/src/main/webapp/site/src/app/forgot/teacher/forgot-teacher-password-verify/forgot-teacher-password-verify.component.spec.ts b/src/main/webapp/site/src/app/forgot/teacher/forgot-teacher-password-verify/forgot-teacher-password-verify.component.spec.ts index e1bfb28850..4f36bd41ad 100644 --- a/src/main/webapp/site/src/app/forgot/teacher/forgot-teacher-password-verify/forgot-teacher-password-verify.component.spec.ts +++ b/src/main/webapp/site/src/app/forgot/teacher/forgot-teacher-password-verify/forgot-teacher-password-verify.component.spec.ts @@ -10,7 +10,7 @@ import { configureTestSuite } from 'ng-bullet'; export class MockTeacherService { checkVerificationCode(username: string, verificationCode: string): Observable { - return Observable.create(observer => { + return Observable.create((observer) => { observer.next({ status: 'success', messageCode: 'verificationCodeCorrect' @@ -33,7 +33,7 @@ describe('ForgotTeacherPasswordVerifyComponent', () => { }; const createObservableResponse = (status, messageCode) => { - const observableResponse = Observable.create(observer => { + const observableResponse = Observable.create((observer) => { const response = { status: status, messageCode: messageCode @@ -51,16 +51,11 @@ describe('ForgotTeacherPasswordVerifyComponent', () => { configureTestSuite(() => { TestBed.configureTestingModule({ - declarations: [ ForgotTeacherPasswordVerifyComponent ], - imports: [ - RouterTestingModule, - ReactiveFormsModule - ], - providers: [ - { provide: TeacherService, useClass: MockTeacherService } - ], - schemas: [ NO_ERRORS_SCHEMA ] - }) + declarations: [ForgotTeacherPasswordVerifyComponent], + imports: [RouterTestingModule, ReactiveFormsModule], + providers: [{ provide: TeacherService, useClass: MockTeacherService }], + schemas: [NO_ERRORS_SCHEMA] + }); }); beforeEach(() => { @@ -85,7 +80,9 @@ describe('ForgotTeacherPasswordVerifyComponent', () => { it('should show the too many verification code attempts message', () => { submitAndReceiveResponse('checkVerificationCode', 'failure', 'tooManyVerificationCodeAttempts'); - expect(getErrorMessage()).toContain('You have submitted an invalid verification code too many times'); + expect(getErrorMessage()).toContain( + 'You have submitted an invalid verification code too many times' + ); }); it('should navigate to the change password page', () => { @@ -96,8 +93,10 @@ describe('ForgotTeacherPasswordVerifyComponent', () => { username: null, verificationCode: '' }; - expect(navigateSpy).toHaveBeenCalledWith(['/forgot/teacher/password/change'], - {queryParams: params, skipLocationChange: true}); + expect(navigateSpy).toHaveBeenCalledWith(['/forgot/teacher/password/change'], { + queryParams: params, + skipLocationChange: true + }); }); it('should navigate to the change password page after successfully submitting the verification code', () => { @@ -111,7 +110,9 @@ describe('ForgotTeacherPasswordVerifyComponent', () => { username: 'SpongebobSquarepants', verificationCode: '123456' }; - expect(navigateSpy).toHaveBeenCalledWith(['/forgot/teacher/password/change'], - {queryParams: params, skipLocationChange: true}); + expect(navigateSpy).toHaveBeenCalledWith(['/forgot/teacher/password/change'], { + queryParams: params, + skipLocationChange: true + }); }); }); diff --git a/src/main/webapp/site/src/app/forgot/teacher/forgot-teacher-password-verify/forgot-teacher-password-verify.component.ts b/src/main/webapp/site/src/app/forgot/teacher/forgot-teacher-password-verify/forgot-teacher-password-verify.component.ts index 679c442c68..7d9d780f96 100644 --- a/src/main/webapp/site/src/app/forgot/teacher/forgot-teacher-password-verify/forgot-teacher-password-verify.component.ts +++ b/src/main/webapp/site/src/app/forgot/teacher/forgot-teacher-password-verify/forgot-teacher-password-verify.component.ts @@ -10,7 +10,6 @@ import { finalize } from 'rxjs/operators'; styleUrls: ['./forgot-teacher-password-verify.component.scss'] }) export class ForgotTeacherPasswordVerifyComponent implements OnInit { - username: string; verificationCodeFormGroup: FormGroup = this.fb.group({ verificationCode: new FormControl('', [Validators.required]) @@ -21,10 +20,12 @@ export class ForgotTeacherPasswordVerifyComponent implements OnInit { isSubmitButtonEnabled: boolean = true; showForgotPasswordLink: boolean = false; - constructor(private fb: FormBuilder, - private router: Router, - private route: ActivatedRoute, - private teacherService: TeacherService) { } + constructor( + private fb: FormBuilder, + private router: Router, + private route: ActivatedRoute, + private teacherService: TeacherService + ) {} ngOnInit() { this.username = this.route.snapshot.queryParamMap.get('username'); @@ -47,7 +48,8 @@ export class ForgotTeacherPasswordVerifyComponent implements OnInit { this.clearMessage(); this.showForgotPasswordLink = false; const verificationCode = this.getControlFieldValue('verificationCode'); - this.teacherService.checkVerificationCode(this.username, verificationCode) + this.teacherService + .checkVerificationCode(this.username, verificationCode) .pipe( finalize(() => { this.processing = false; @@ -110,7 +112,9 @@ export class ForgotTeacherPasswordVerifyComponent implements OnInit { username: this.username, verificationCode: this.getControlFieldValue('verificationCode') }; - this.router.navigate(['/forgot/teacher/password/change'], - {queryParams: params, skipLocationChange: true}); + this.router.navigate(['/forgot/teacher/password/change'], { + queryParams: params, + skipLocationChange: true + }); } } diff --git a/src/main/webapp/site/src/app/forgot/teacher/forgot-teacher-password/forgot-teacher-password.component.spec.ts b/src/main/webapp/site/src/app/forgot/teacher/forgot-teacher-password/forgot-teacher-password.component.spec.ts index ac76765f5e..1ea5d32f24 100644 --- a/src/main/webapp/site/src/app/forgot/teacher/forgot-teacher-password/forgot-teacher-password.component.spec.ts +++ b/src/main/webapp/site/src/app/forgot/teacher/forgot-teacher-password/forgot-teacher-password.component.spec.ts @@ -10,7 +10,7 @@ import { configureTestSuite } from 'ng-bullet'; export class MockTeacherService { getVerificationCodeEmail(username: string): Observable { - return Observable.create(observer => { + return Observable.create((observer) => { observer.next({ status: 'success', messageCode: 'emailSent' @@ -33,7 +33,7 @@ describe('ForgotTeacherPasswordComponent', () => { }; const createObservableResponse = (status, messageCode) => { - const observableResponse = Observable.create(observer => { + const observableResponse = Observable.create((observer) => { const response = { status: status, messageCode: messageCode @@ -51,16 +51,11 @@ describe('ForgotTeacherPasswordComponent', () => { configureTestSuite(() => { TestBed.configureTestingModule({ - declarations: [ ForgotTeacherPasswordComponent ], - imports: [ - RouterTestingModule, - ReactiveFormsModule - ], - providers: [ - { provide: TeacherService, useClass: MockTeacherService } - ], - schemas: [ NO_ERRORS_SCHEMA ] - }) + declarations: [ForgotTeacherPasswordComponent], + imports: [RouterTestingModule, ReactiveFormsModule], + providers: [{ provide: TeacherService, useClass: MockTeacherService }], + schemas: [NO_ERRORS_SCHEMA] + }); }); beforeEach(() => { @@ -79,13 +74,21 @@ describe('ForgotTeacherPasswordComponent', () => { }); it('should show the too many verification code attempts message', () => { - submitAndReceiveResponse('getVerificationCodeEmail', 'failure', 'tooManyVerificationCodeAttempts'); - expect(getErrorMessage()).toContain('You have submitted an invalid verification code too many times'); + submitAndReceiveResponse( + 'getVerificationCodeEmail', + 'failure', + 'tooManyVerificationCodeAttempts' + ); + expect(getErrorMessage()).toContain( + 'You have submitted an invalid verification code too many times' + ); }); it('should show the failed to send email message', () => { submitAndReceiveResponse('getVerificationCodeEmail', 'failure', 'failedToSendEmail'); - expect(getErrorMessage()).toContain('The server has encountered an error and was unable to send you an email'); + expect(getErrorMessage()).toContain( + 'The server has encountered an error and was unable to send you an email' + ); }); it('should navigate to the verify code page', () => { @@ -95,7 +98,10 @@ describe('ForgotTeacherPasswordComponent', () => { const params = { username: '' }; - expect(navigateSpy).toHaveBeenCalledWith(['/forgot/teacher/password/verify'], {queryParams: params, skipLocationChange: true}); + expect(navigateSpy).toHaveBeenCalledWith(['/forgot/teacher/password/verify'], { + queryParams: params, + skipLocationChange: true + }); }); it('should navigate to the verify code page after successfully sending a valid username', () => { @@ -107,6 +113,9 @@ describe('ForgotTeacherPasswordComponent', () => { const params = { username: 'SpongebobSquarepants' }; - expect(navigateSpy).toHaveBeenCalledWith(['/forgot/teacher/password/verify'], {queryParams: params, skipLocationChange: true}); + expect(navigateSpy).toHaveBeenCalledWith(['/forgot/teacher/password/verify'], { + queryParams: params, + skipLocationChange: true + }); }); }); diff --git a/src/main/webapp/site/src/app/forgot/teacher/forgot-teacher-password/forgot-teacher-password.component.ts b/src/main/webapp/site/src/app/forgot/teacher/forgot-teacher-password/forgot-teacher-password.component.ts index b7b2471820..6ef7e52d02 100644 --- a/src/main/webapp/site/src/app/forgot/teacher/forgot-teacher-password/forgot-teacher-password.component.ts +++ b/src/main/webapp/site/src/app/forgot/teacher/forgot-teacher-password/forgot-teacher-password.component.ts @@ -10,7 +10,6 @@ import { finalize } from 'rxjs/operators'; styleUrls: ['./forgot-teacher-password.component.scss'] }) export class ForgotTeacherPasswordComponent implements OnInit { - forgotTeacherPasswordFormGroup: FormGroup = this.fb.group({ username: new FormControl('', [Validators.required]) }); @@ -18,12 +17,13 @@ export class ForgotTeacherPasswordComponent implements OnInit { showForgotUsernameLink: boolean = false; processing: boolean = false; - constructor(private fb: FormBuilder, - private router: Router, - private teacherService: TeacherService) { } + constructor( + private fb: FormBuilder, + private router: Router, + private teacherService: TeacherService + ) {} - ngOnInit() { - } + ngOnInit() {} getControlFieldValue(fieldName) { return this.forgotTeacherPasswordFormGroup.get(fieldName).value; @@ -38,7 +38,8 @@ export class ForgotTeacherPasswordComponent implements OnInit { this.clearMessage(); this.showForgotUsernameLink = false; const username = this.getControlFieldValue('username'); - this.teacherService.getVerificationCodeEmail(username) + this.teacherService + .getVerificationCodeEmail(username) .pipe( finalize(() => { this.processing = false; @@ -87,8 +88,9 @@ export class ForgotTeacherPasswordComponent implements OnInit { const params = { username: this.getControlFieldValue('username') }; - this.router.navigate(['/forgot/teacher/password/verify'], - {queryParams: params, skipLocationChange: true}); + this.router.navigate(['/forgot/teacher/password/verify'], { + queryParams: params, + skipLocationChange: true + }); } - } diff --git a/src/main/webapp/site/src/app/forgot/teacher/forgot-teacher-username-complete/forgot-teacher-username-complete.component.spec.ts b/src/main/webapp/site/src/app/forgot/teacher/forgot-teacher-username-complete/forgot-teacher-username-complete.component.spec.ts index 6cc543d43b..7efc94eada 100644 --- a/src/main/webapp/site/src/app/forgot/teacher/forgot-teacher-username-complete/forgot-teacher-username-complete.component.spec.ts +++ b/src/main/webapp/site/src/app/forgot/teacher/forgot-teacher-username-complete/forgot-teacher-username-complete.component.spec.ts @@ -11,9 +11,9 @@ describe('ForgotTeacherUsernameCompleteComponent', () => { configureTestSuite(() => { TestBed.configureTestingModule({ - declarations: [ ForgotTeacherUsernameCompleteComponent ], - imports: [ RouterTestingModule ], - schemas: [ NO_ERRORS_SCHEMA ] + declarations: [ForgotTeacherUsernameCompleteComponent], + imports: [RouterTestingModule], + schemas: [NO_ERRORS_SCHEMA] }); }); @@ -28,6 +28,8 @@ describe('ForgotTeacherUsernameCompleteComponent', () => { }); it('should show the confirmation message', () => { - expect(fixture.debugElement.nativeElement.textContent).toContain('Your username has been sent to your email'); + expect(fixture.debugElement.nativeElement.textContent).toContain( + 'Your username has been sent to your email' + ); }); }); diff --git a/src/main/webapp/site/src/app/forgot/teacher/forgot-teacher-username-complete/forgot-teacher-username-complete.component.ts b/src/main/webapp/site/src/app/forgot/teacher/forgot-teacher-username-complete/forgot-teacher-username-complete.component.ts index 119da08a87..abead1d873 100644 --- a/src/main/webapp/site/src/app/forgot/teacher/forgot-teacher-username-complete/forgot-teacher-username-complete.component.ts +++ b/src/main/webapp/site/src/app/forgot/teacher/forgot-teacher-username-complete/forgot-teacher-username-complete.component.ts @@ -7,10 +7,7 @@ import { Router } from '@angular/router'; styleUrls: ['./forgot-teacher-username-complete.component.scss'] }) export class ForgotTeacherUsernameCompleteComponent implements OnInit { + constructor(private router: Router) {} - constructor(private router: Router) { } - - ngOnInit() { - } - + ngOnInit() {} } diff --git a/src/main/webapp/site/src/app/forgot/teacher/forgot-teacher-username/forgot-teacher-username.component.spec.ts b/src/main/webapp/site/src/app/forgot/teacher/forgot-teacher-username/forgot-teacher-username.component.spec.ts index ab76351a5d..48f051c61d 100644 --- a/src/main/webapp/site/src/app/forgot/teacher/forgot-teacher-username/forgot-teacher-username.component.spec.ts +++ b/src/main/webapp/site/src/app/forgot/teacher/forgot-teacher-username/forgot-teacher-username.component.spec.ts @@ -10,7 +10,7 @@ import { configureTestSuite } from 'ng-bullet'; export class MockTeacherService { sendForgotUsernameEmail(email: string): Observable { - return Observable.create(observer => { + return Observable.create((observer) => { observer.next({ status: 'success', messageCode: 'emailSent' @@ -33,7 +33,7 @@ describe('ForgotTeacherUsernameComponent', () => { }; const createObservableResponse = (status, messageCode) => { - const observableResponse = Observable.create(observer => { + const observableResponse = Observable.create((observer) => { const response = { status: status, messageCode: messageCode @@ -51,16 +51,11 @@ describe('ForgotTeacherUsernameComponent', () => { configureTestSuite(() => { TestBed.configureTestingModule({ - declarations: [ ForgotTeacherUsernameComponent ], - imports: [ - RouterTestingModule.withRoutes([]), - ReactiveFormsModule - ], - providers: [ - { provide: TeacherService, useClass: MockTeacherService } - ], - schemas: [ NO_ERRORS_SCHEMA ] - }) + declarations: [ForgotTeacherUsernameComponent], + imports: [RouterTestingModule.withRoutes([]), ReactiveFormsModule], + providers: [{ provide: TeacherService, useClass: MockTeacherService }], + schemas: [NO_ERRORS_SCHEMA] + }); }); beforeEach(() => { @@ -80,12 +75,14 @@ describe('ForgotTeacherUsernameComponent', () => { it('should show an email not found message', () => { submitAndReceiveResponse('sendForgotUsernameEmail', 'failure', 'emailNotFound'); - expect(getErrorMessage()).toContain('We did not find a WISE account associated with that email'); + expect(getErrorMessage()).toContain( + 'We did not find a WISE account associated with that email' + ); }); it('should navigate to the success page', () => { const teacherService = TestBed.get(TeacherService); - const observableResponse = Observable.create(observer => { + const observableResponse = Observable.create((observer) => { const response = { status: 'success', messageCode: 'emailSent' diff --git a/src/main/webapp/site/src/app/forgot/teacher/forgot-teacher-username/forgot-teacher-username.component.ts b/src/main/webapp/site/src/app/forgot/teacher/forgot-teacher-username/forgot-teacher-username.component.ts index 46f6ab241a..46c7bb410a 100644 --- a/src/main/webapp/site/src/app/forgot/teacher/forgot-teacher-username/forgot-teacher-username.component.ts +++ b/src/main/webapp/site/src/app/forgot/teacher/forgot-teacher-username/forgot-teacher-username.component.ts @@ -10,19 +10,19 @@ import { finalize } from 'rxjs/operators'; styleUrls: ['./forgot-teacher-username.component.scss'] }) export class ForgotTeacherUsernameComponent implements OnInit { - forgotTeacherUsernameFormGroup: FormGroup = this.fb.group({ email: new FormControl('', [Validators.required, Validators.email]) }); message: string = ''; processing: boolean = false; - constructor(private fb: FormBuilder, - private router: Router, - private teacherService: TeacherService) { } + constructor( + private fb: FormBuilder, + private router: Router, + private teacherService: TeacherService + ) {} - ngOnInit() { - } + ngOnInit() {} getControlFieldValue(fieldName) { return this.forgotTeacherUsernameFormGroup.get(fieldName).value; @@ -40,7 +40,8 @@ export class ForgotTeacherUsernameComponent implements OnInit { this.processing = true; this.clearMessage(); const email = this.getEmail(); - this.teacherService.sendForgotUsernameEmail(email) + this.teacherService + .sendForgotUsernameEmail(email) .pipe( finalize(() => { this.processing = false; diff --git a/src/main/webapp/site/src/app/forgot/teacher/forgot-teacher/forgot-teacher.component.spec.ts b/src/main/webapp/site/src/app/forgot/teacher/forgot-teacher/forgot-teacher.component.spec.ts index 02bdd1086a..4d9b85c1ab 100644 --- a/src/main/webapp/site/src/app/forgot/teacher/forgot-teacher/forgot-teacher.component.spec.ts +++ b/src/main/webapp/site/src/app/forgot/teacher/forgot-teacher/forgot-teacher.component.spec.ts @@ -9,8 +9,8 @@ describe('ForgotTeacherComponent', () => { configureTestSuite(() => { TestBed.configureTestingModule({ - declarations: [ ForgotTeacherComponent ], - schemas: [ NO_ERRORS_SCHEMA ] + declarations: [ForgotTeacherComponent], + schemas: [NO_ERRORS_SCHEMA] }); }); diff --git a/src/main/webapp/site/src/app/forgot/teacher/forgot-teacher/forgot-teacher.component.ts b/src/main/webapp/site/src/app/forgot/teacher/forgot-teacher/forgot-teacher.component.ts index 4ade5b2b99..8b6979b2c1 100644 --- a/src/main/webapp/site/src/app/forgot/teacher/forgot-teacher/forgot-teacher.component.ts +++ b/src/main/webapp/site/src/app/forgot/teacher/forgot-teacher/forgot-teacher.component.ts @@ -6,10 +6,7 @@ import { Component, OnInit } from '@angular/core'; styleUrls: ['./forgot-teacher.component.scss'] }) export class ForgotTeacherComponent implements OnInit { + constructor() {} - constructor() { } - - ngOnInit() { - } - + ngOnInit() {} } diff --git a/src/main/webapp/site/src/app/help/getting-started/getting-started.component.spec.ts b/src/main/webapp/site/src/app/help/getting-started/getting-started.component.spec.ts index a813de865c..a46fd8dc43 100644 --- a/src/main/webapp/site/src/app/help/getting-started/getting-started.component.spec.ts +++ b/src/main/webapp/site/src/app/help/getting-started/getting-started.component.spec.ts @@ -1,19 +1,19 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { GettingStartedComponent } from './getting-started.component'; -import { NO_ERRORS_SCHEMA } from "@angular/core"; -import { ConfigService } from "../../services/config.service"; -import { Config } from "../../domain/config"; +import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { ConfigService } from '../../services/config.service'; +import { Config } from '../../domain/config'; import { Observable } from 'rxjs'; export class MockConfigService { getConfig(): Observable { const config: Config = { - contextPath: "/wise", - logOutURL: "/logout", - currentTime: new Date("2018-10-24T15:05:40.214").getTime() + contextPath: '/wise', + logOutURL: '/logout', + currentTime: new Date('2018-10-24T15:05:40.214').getTime() }; - return Observable.create(observer => { + return Observable.create((observer) => { observer.next(config); observer.complete(); }); @@ -26,13 +26,10 @@ describe('GettingStartedComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ GettingStartedComponent ], - providers: [ - { provide: ConfigService, useClass: MockConfigService } - ], - schemas: [ NO_ERRORS_SCHEMA ] - }) - .compileComponents(); + declarations: [GettingStartedComponent], + providers: [{ provide: ConfigService, useClass: MockConfigService }], + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); })); beforeEach(() => { diff --git a/src/main/webapp/site/src/app/help/getting-started/getting-started.component.ts b/src/main/webapp/site/src/app/help/getting-started/getting-started.component.ts index 930044ebfa..c2af5ccd98 100644 --- a/src/main/webapp/site/src/app/help/getting-started/getting-started.component.ts +++ b/src/main/webapp/site/src/app/help/getting-started/getting-started.component.ts @@ -1,5 +1,5 @@ import { Component, OnInit } from '@angular/core'; -import { ConfigService } from "../../services/config.service"; +import { ConfigService } from '../../services/config.service'; @Component({ selector: 'app-getting-started', @@ -7,7 +7,6 @@ import { ConfigService } from "../../services/config.service"; styleUrls: ['./getting-started.component.scss'] }) export class GettingStartedComponent implements OnInit { - contextPath: string; constructor(private configService: ConfigService) { @@ -18,9 +17,7 @@ export class GettingStartedComponent implements OnInit { }); } - ngOnInit() { - - } + ngOnInit() {} ngAfterViewInit() { const appHelpElements = document.getElementsByTagName('app-help'); @@ -28,5 +25,4 @@ export class GettingStartedComponent implements OnInit { appHelpElements[0].scrollIntoView(); } } - } diff --git a/src/main/webapp/site/src/app/help/help-home/help-home.component.spec.ts b/src/main/webapp/site/src/app/help/help-home/help-home.component.spec.ts index 3b634a412e..762b75143c 100644 --- a/src/main/webapp/site/src/app/help/help-home/help-home.component.spec.ts +++ b/src/main/webapp/site/src/app/help/help-home/help-home.component.spec.ts @@ -1,7 +1,7 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { HelpHomeComponent } from './help-home.component'; -import { NO_ERRORS_SCHEMA } from "@angular/core"; +import { NO_ERRORS_SCHEMA } from '@angular/core'; describe('HelpHomeComponent', () => { let component: HelpHomeComponent; @@ -9,10 +9,9 @@ describe('HelpHomeComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ HelpHomeComponent ], - schemas: [ NO_ERRORS_SCHEMA ] - }) - .compileComponents(); + declarations: [HelpHomeComponent], + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); })); beforeEach(() => { diff --git a/src/main/webapp/site/src/app/help/help-home/help-home.component.ts b/src/main/webapp/site/src/app/help/help-home/help-home.component.ts index ed70d685d1..cfa48329ab 100644 --- a/src/main/webapp/site/src/app/help/help-home/help-home.component.ts +++ b/src/main/webapp/site/src/app/help/help-home/help-home.component.ts @@ -6,10 +6,7 @@ import { Component, OnInit } from '@angular/core'; styleUrls: ['./help-home.component.scss'] }) export class HelpHomeComponent implements OnInit { + constructor() {} - constructor() { } - - ngOnInit() { - } - + ngOnInit() {} } diff --git a/src/main/webapp/site/src/app/help/help-routing.module.ts b/src/main/webapp/site/src/app/help/help-routing.module.ts index f0b4da1e41..2424f0c9b7 100644 --- a/src/main/webapp/site/src/app/help/help-routing.module.ts +++ b/src/main/webapp/site/src/app/help/help-routing.module.ts @@ -1,10 +1,10 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; -import { HelpComponent } from "./help.component"; -import { HelpHomeComponent } from "./help-home/help-home.component"; -import { GettingStartedComponent } from "./getting-started/getting-started.component"; -import { TeacherFaqComponent } from "./teacher-faq/teacher-faq.component"; -import { StudentFaqComponent } from "./student-faq/student-faq.component"; +import { HelpComponent } from './help.component'; +import { HelpHomeComponent } from './help-home/help-home.component'; +import { GettingStartedComponent } from './getting-started/getting-started.component'; +import { TeacherFaqComponent } from './teacher-faq/teacher-faq.component'; +import { StudentFaqComponent } from './student-faq/student-faq.component'; const helpRoutes: Routes = [ { @@ -20,11 +20,7 @@ const helpRoutes: Routes = [ ]; @NgModule({ - imports: [ - RouterModule.forChild(helpRoutes) - ], - exports: [ - RouterModule - ] + imports: [RouterModule.forChild(helpRoutes)], + exports: [RouterModule] }) export class HelpRoutingModule {} diff --git a/src/main/webapp/site/src/app/help/help.component.spec.ts b/src/main/webapp/site/src/app/help/help.component.spec.ts index da72be7756..1bdf6fd375 100644 --- a/src/main/webapp/site/src/app/help/help.component.spec.ts +++ b/src/main/webapp/site/src/app/help/help.component.spec.ts @@ -1,7 +1,7 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { HelpComponent } from './help.component'; -import { NO_ERRORS_SCHEMA } from "@angular/core"; +import { NO_ERRORS_SCHEMA } from '@angular/core'; describe('HelpComponent', () => { let component: HelpComponent; @@ -9,10 +9,9 @@ describe('HelpComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ HelpComponent ], - schemas: [ NO_ERRORS_SCHEMA ] - }) - .compileComponents(); + declarations: [HelpComponent], + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); })); beforeEach(() => { diff --git a/src/main/webapp/site/src/app/help/help.component.ts b/src/main/webapp/site/src/app/help/help.component.ts index 80a9e23680..91273708e5 100644 --- a/src/main/webapp/site/src/app/help/help.component.ts +++ b/src/main/webapp/site/src/app/help/help.component.ts @@ -6,10 +6,7 @@ import { Component, OnInit } from '@angular/core'; styleUrls: ['./help.component.scss'] }) export class HelpComponent implements OnInit { + constructor() {} - constructor() { } - - ngOnInit() { - } - + ngOnInit() {} } diff --git a/src/main/webapp/site/src/app/help/help.module.ts b/src/main/webapp/site/src/app/help/help.module.ts index 6e60ac206d..13dfb61198 100644 --- a/src/main/webapp/site/src/app/help/help.module.ts +++ b/src/main/webapp/site/src/app/help/help.module.ts @@ -1,19 +1,15 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { HelpComponent } from './help.component'; -import { HelpRoutingModule } from "./help-routing.module"; -import { SharedModule } from "../modules/shared/shared.module"; +import { HelpRoutingModule } from './help-routing.module'; +import { SharedModule } from '../modules/shared/shared.module'; import { GettingStartedComponent } from './getting-started/getting-started.component'; import { TeacherFaqComponent } from './teacher-faq/teacher-faq.component'; import { StudentFaqComponent } from './student-faq/student-faq.component'; import { HelpHomeComponent } from './help-home/help-home.component'; @NgModule({ - imports: [ - CommonModule, - HelpRoutingModule, - SharedModule - ], + imports: [CommonModule, HelpRoutingModule, SharedModule], declarations: [ HelpComponent, GettingStartedComponent, @@ -22,4 +18,4 @@ import { HelpHomeComponent } from './help-home/help-home.component'; HelpHomeComponent ] }) -export class HelpModule { } +export class HelpModule {} diff --git a/src/main/webapp/site/src/app/help/student-faq/student-faq.component.spec.ts b/src/main/webapp/site/src/app/help/student-faq/student-faq.component.spec.ts index c535b1e61b..6ba704f95b 100644 --- a/src/main/webapp/site/src/app/help/student-faq/student-faq.component.spec.ts +++ b/src/main/webapp/site/src/app/help/student-faq/student-faq.component.spec.ts @@ -1,19 +1,19 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { StudentFaqComponent } from './student-faq.component'; -import { NO_ERRORS_SCHEMA } from "@angular/core"; -import { ConfigService } from "../../services/config.service"; +import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { ConfigService } from '../../services/config.service'; import { Observable } from 'rxjs'; -import { Config } from "../../domain/config"; +import { Config } from '../../domain/config'; export class MockConfigService { getConfig(): Observable { const config: Config = { - contextPath: "/wise", - logOutURL: "/logout", - currentTime: new Date("2018-10-24T15:05:40.214").getTime() + contextPath: '/wise', + logOutURL: '/logout', + currentTime: new Date('2018-10-24T15:05:40.214').getTime() }; - return Observable.create(observer => { + return Observable.create((observer) => { observer.next(config); observer.complete(); }); @@ -26,13 +26,10 @@ describe('StudentFaqComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ StudentFaqComponent ], - providers: [ - { provide: ConfigService, useClass: MockConfigService } - ], - schemas: [ NO_ERRORS_SCHEMA ] - }) - .compileComponents(); + declarations: [StudentFaqComponent], + providers: [{ provide: ConfigService, useClass: MockConfigService }], + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); })); beforeEach(() => { diff --git a/src/main/webapp/site/src/app/help/student-faq/student-faq.component.ts b/src/main/webapp/site/src/app/help/student-faq/student-faq.component.ts index 58a8cc56aa..ef648921a4 100644 --- a/src/main/webapp/site/src/app/help/student-faq/student-faq.component.ts +++ b/src/main/webapp/site/src/app/help/student-faq/student-faq.component.ts @@ -1,5 +1,5 @@ import { Component, OnInit } from '@angular/core'; -import { ConfigService } from "../../services/config.service"; +import { ConfigService } from '../../services/config.service'; @Component({ selector: 'app-student-faq', @@ -7,7 +7,6 @@ import { ConfigService } from "../../services/config.service"; styleUrls: ['./student-faq.component.scss'] }) export class StudentFaqComponent implements OnInit { - contextPath: string; constructor(private configService: ConfigService) { @@ -18,9 +17,7 @@ export class StudentFaqComponent implements OnInit { }); } - ngOnInit() { - - } + ngOnInit() {} ngAfterViewInit() { const appHelpElements = document.getElementsByTagName('app-help'); @@ -32,5 +29,4 @@ export class StudentFaqComponent implements OnInit { scrollTo(id) { document.getElementById(id).scrollIntoView(); } - } diff --git a/src/main/webapp/site/src/app/help/teacher-faq/teacher-faq.component.spec.ts b/src/main/webapp/site/src/app/help/teacher-faq/teacher-faq.component.spec.ts index ed420c9f0c..5bcfa1494a 100644 --- a/src/main/webapp/site/src/app/help/teacher-faq/teacher-faq.component.spec.ts +++ b/src/main/webapp/site/src/app/help/teacher-faq/teacher-faq.component.spec.ts @@ -1,19 +1,19 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { TeacherFaqComponent } from './teacher-faq.component'; -import { NO_ERRORS_SCHEMA } from "@angular/core"; -import { ConfigService } from "../../services/config.service"; +import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { ConfigService } from '../../services/config.service'; import { Observable } from 'rxjs'; -import { Config } from "../../domain/config"; +import { Config } from '../../domain/config'; export class MockConfigService { getConfig(): Observable { const config: Config = { - contextPath: "/wise", - logOutURL: "/logout", - currentTime: new Date("2018-10-24T15:05:40.214").getTime() + contextPath: '/wise', + logOutURL: '/logout', + currentTime: new Date('2018-10-24T15:05:40.214').getTime() }; - return Observable.create(observer => { + return Observable.create((observer) => { observer.next(config); observer.complete(); }); @@ -26,13 +26,10 @@ describe('TeacherFaqComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ TeacherFaqComponent ], - providers: [ - { provide: ConfigService, useClass: MockConfigService } - ], - schemas: [ NO_ERRORS_SCHEMA ] - }) - .compileComponents(); + declarations: [TeacherFaqComponent], + providers: [{ provide: ConfigService, useClass: MockConfigService }], + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); })); beforeEach(() => { diff --git a/src/main/webapp/site/src/app/help/teacher-faq/teacher-faq.component.ts b/src/main/webapp/site/src/app/help/teacher-faq/teacher-faq.component.ts index f1f6189b62..2d75176800 100644 --- a/src/main/webapp/site/src/app/help/teacher-faq/teacher-faq.component.ts +++ b/src/main/webapp/site/src/app/help/teacher-faq/teacher-faq.component.ts @@ -1,5 +1,5 @@ import { Component, OnInit } from '@angular/core'; -import { ConfigService } from "../../services/config.service"; +import { ConfigService } from '../../services/config.service'; @Component({ selector: 'app-teacher-faq', @@ -7,7 +7,6 @@ import { ConfigService } from "../../services/config.service"; styleUrls: ['./teacher-faq.component.scss'] }) export class TeacherFaqComponent implements OnInit { - contextPath: string; constructor(private configService: ConfigService) { @@ -18,9 +17,7 @@ export class TeacherFaqComponent implements OnInit { }); } - ngOnInit() { - - } + ngOnInit() {} ngAfterViewInit() { const appHelpElements = document.getElementsByTagName('app-help'); diff --git a/src/main/webapp/site/src/app/home/home-routing.module.ts b/src/main/webapp/site/src/app/home/home-routing.module.ts index 5da0dd9214..50473608d5 100644 --- a/src/main/webapp/site/src/app/home/home-routing.module.ts +++ b/src/main/webapp/site/src/app/home/home-routing.module.ts @@ -1,7 +1,7 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; -import { HomeComponent } from "./home.component"; +import { HomeComponent } from './home.component'; const homeRoutes: Routes = [ { @@ -12,11 +12,7 @@ const homeRoutes: Routes = [ ]; @NgModule({ - imports: [ - RouterModule.forChild(homeRoutes) - ], - exports: [ - RouterModule - ] + imports: [RouterModule.forChild(homeRoutes)], + exports: [RouterModule] }) export class HomeRoutingModule {} diff --git a/src/main/webapp/site/src/app/home/home.component.spec.ts b/src/main/webapp/site/src/app/home/home.component.spec.ts index b6eaadee33..cfbe273ba2 100644 --- a/src/main/webapp/site/src/app/home/home.component.spec.ts +++ b/src/main/webapp/site/src/app/home/home.component.spec.ts @@ -1,6 +1,6 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { HomeComponent } from './home.component'; -import { NO_ERRORS_SCHEMA } from "@angular/core"; +import { NO_ERRORS_SCHEMA } from '@angular/core'; describe('HomeComponent', () => { let component: HomeComponent; @@ -8,11 +8,10 @@ describe('HomeComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ HomeComponent ], - imports: [ ], - schemas: [ NO_ERRORS_SCHEMA ] - }) - .compileComponents(); + declarations: [HomeComponent], + imports: [], + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); })); beforeEach(() => { diff --git a/src/main/webapp/site/src/app/home/home.component.ts b/src/main/webapp/site/src/app/home/home.component.ts index a6179c0c01..152fcf1a01 100644 --- a/src/main/webapp/site/src/app/home/home.component.ts +++ b/src/main/webapp/site/src/app/home/home.component.ts @@ -1,12 +1,5 @@ import { Component, OnInit, ViewEncapsulation, SecurityContext } from '@angular/core'; -import { - bounceIn, - flipInX, - flipInY, - jackInTheBox, - rotateIn, - zoomIn -} from "../animations"; +import { bounceIn, flipInX, flipInY, jackInTheBox, rotateIn, zoomIn } from '../animations'; import { DomSanitizer } from '@angular/platform-browser'; @Component({ @@ -14,17 +7,9 @@ import { DomSanitizer } from '@angular/platform-browser'; templateUrl: './home.component.html', styleUrls: ['./home.component.scss'], encapsulation: ViewEncapsulation.None, - animations: [ - bounceIn, - flipInX, - flipInY, - jackInTheBox, - rotateIn, - zoomIn - ] + animations: [bounceIn, flipInX, flipInY, jackInTheBox, rotateIn, zoomIn] }) export class HomeComponent implements OnInit { - loaded: boolean = false; hero = { imgSrc: 'assets/img/wise-students-hero.jpg', @@ -57,22 +42,26 @@ export class HomeComponent implements OnInit { imgDescription: $localize`WISE students building`, imgSources: [ { - type: 'image/webp', - srcset: 'assets/img/wise-students-building.webp, assets/img/wise-students-building@2x.webp 2x' + type: 'image/webp', + srcset: + 'assets/img/wise-students-building.webp, assets/img/wise-students-building@2x.webp 2x' }, { - srcset: 'assets/img/wise-students-building.jpg, assets/img/wise-students-building@2x.jpg 2x' + srcset: + 'assets/img/wise-students-building.jpg, assets/img/wise-students-building@2x.jpg 2x' } ], - contentTemplate: this.sanitizer.sanitize(SecurityContext.HTML, - $localize`Free, standards-aligned, and research-based inquiry curricula that address ${this.ngssLink.startTag}:START_LINK:NGSS 3D proficiency${this.ngssLink.closeTag}:CLOSE_LINK:`) + contentTemplate: this.sanitizer.sanitize( + SecurityContext.HTML, + $localize`Free, standards-aligned, and research-based inquiry curricula that address ${this.ngssLink.startTag}:START_LINK:NGSS 3D proficiency${this.ngssLink.closeTag}:CLOSE_LINK:` + ) }, { imgSrc: 'assets/img/wise-project-view@2x.jpg', imgDescription: $localize`WISE unit on laptop`, imgSources: [ { - type: 'image/webp', + type: 'image/webp', srcset: 'assets/img/wise-project-view.webp, assets/img/wise-project-view@2x.webp 2x' }, { @@ -86,19 +75,20 @@ export class HomeComponent implements OnInit { imgDescription: $localize`WISE students and teacher`, imgSources: [ { - type: 'image/webp', - srcset: 'assets/img/wise-students-and-teacher.webp, assets/img/wise-students-and-teacher@2x.webp 2x' + type: 'image/webp', + srcset: + 'assets/img/wise-students-and-teacher.webp, assets/img/wise-students-and-teacher@2x.webp 2x' }, { - srcset: 'assets/img/wise-students-and-teacher.jpg, assets/img/wise-students-and-teacher@2x.jpg 2x' + srcset: + 'assets/img/wise-students-and-teacher.jpg, assets/img/wise-students-and-teacher@2x.jpg 2x' } ], content: $localize`Robust teacher grading and management tools supporting individualized and customized learning` } ]; - constructor(private sanitizer: DomSanitizer) { } + constructor(private sanitizer: DomSanitizer) {} - ngOnInit() { - } + ngOnInit() {} } diff --git a/src/main/webapp/site/src/app/home/home.module.ts b/src/main/webapp/site/src/app/home/home.module.ts index 2c1f9fcc20..b442daaeaf 100644 --- a/src/main/webapp/site/src/app/home/home.module.ts +++ b/src/main/webapp/site/src/app/home/home.module.ts @@ -2,25 +2,14 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { RouterModule } from '@angular/router'; -import { HomeComponent } from "./home.component"; -import { HomeRoutingModule } from "./home-routing.module"; -import { LibraryModule } from "../modules/library/library.module"; -import { SharedModule } from "../modules/shared/shared.module"; +import { HomeComponent } from './home.component'; +import { HomeRoutingModule } from './home-routing.module'; +import { LibraryModule } from '../modules/library/library.module'; +import { SharedModule } from '../modules/shared/shared.module'; @NgModule({ - imports: [ - CommonModule, - HomeRoutingModule, - LibraryModule, - SharedModule, - RouterModule - ], - declarations: [ - HomeComponent - ], - exports: [ - HomeComponent, - SharedModule - ] + imports: [CommonModule, HomeRoutingModule, LibraryModule, SharedModule, RouterModule], + declarations: [HomeComponent], + exports: [HomeComponent, SharedModule] }) -export class HomeModule { } +export class HomeModule {} diff --git a/src/main/webapp/site/src/app/http-error.interceptor.ts b/src/main/webapp/site/src/app/http-error.interceptor.ts index 29e8c65c45..9b379a75e4 100644 --- a/src/main/webapp/site/src/app/http-error.interceptor.ts +++ b/src/main/webapp/site/src/app/http-error.interceptor.ts @@ -1,13 +1,17 @@ import { Injectable } from '@angular/core'; -import { HttpInterceptor, HttpRequest, HttpErrorResponse, HttpHandler, - HttpEvent } from '@angular/common/http'; +import { + HttpInterceptor, + HttpRequest, + HttpErrorResponse, + HttpHandler, + HttpEvent +} from '@angular/common/http'; import { Observable, throwError } from 'rxjs'; import { catchError } from 'rxjs/operators'; import { MatSnackBar } from '@angular/material/snack-bar'; @Injectable() export class HttpErrorInterceptor implements HttpInterceptor { - constructor(public snackBar: MatSnackBar) {} intercept(request: HttpRequest, next: HttpHandler): Observable> { @@ -19,15 +23,17 @@ export class HttpErrorInterceptor implements HttpInterceptor { } else { // The backend returned an unsuccessful response code. // The response body may contain clues as to what went wrong. - console.error($localize`Backend returned code ${err.status}:status:, body was: ${err.error}:error:`); + console.error( + $localize`Backend returned code ${err.status}:status:, body was: ${err.error}:error:` + ); } - + if (err.status >= 500) { this.snackBar.open($localize`An error occurred. Please refresh this page and try again.`); } return throwError(err); }) - ) + ); } } diff --git a/src/main/webapp/site/src/app/login/login-google-user-not-found/login-google-user-not-found.component.spec.ts b/src/main/webapp/site/src/app/login/login-google-user-not-found/login-google-user-not-found.component.spec.ts index cb22acfec0..53ab96bc64 100644 --- a/src/main/webapp/site/src/app/login/login-google-user-not-found/login-google-user-not-found.component.spec.ts +++ b/src/main/webapp/site/src/app/login/login-google-user-not-found/login-google-user-not-found.component.spec.ts @@ -1,6 +1,6 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { LoginGoogleUserNotFoundComponent } from './login-google-user-not-found.component'; -import { NO_ERRORS_SCHEMA } from "@angular/core"; +import { NO_ERRORS_SCHEMA } from '@angular/core'; describe('LoginGoogleUserNotFoundComponent', () => { let component: LoginGoogleUserNotFoundComponent; @@ -8,11 +8,10 @@ describe('LoginGoogleUserNotFoundComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ LoginGoogleUserNotFoundComponent ], + declarations: [LoginGoogleUserNotFoundComponent], imports: [], - schemas: [ NO_ERRORS_SCHEMA ] - }) - .compileComponents(); + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); })); beforeEach(() => { diff --git a/src/main/webapp/site/src/app/login/login-google-user-not-found/login-google-user-not-found.component.ts b/src/main/webapp/site/src/app/login/login-google-user-not-found/login-google-user-not-found.component.ts index dbf4a7620d..ec5801115b 100644 --- a/src/main/webapp/site/src/app/login/login-google-user-not-found/login-google-user-not-found.component.ts +++ b/src/main/webapp/site/src/app/login/login-google-user-not-found/login-google-user-not-found.component.ts @@ -6,10 +6,7 @@ import { Component, OnInit } from '@angular/core'; styleUrls: ['./login-google-user-not-found.component.scss'] }) export class LoginGoogleUserNotFoundComponent implements OnInit { + constructor() {} - constructor() { } - - ngOnInit() { - } - + ngOnInit() {} } diff --git a/src/main/webapp/site/src/app/login/login-home/login-home.component.spec.ts b/src/main/webapp/site/src/app/login/login-home/login-home.component.spec.ts index e14dedf049..44e535a7a2 100644 --- a/src/main/webapp/site/src/app/login/login-home/login-home.component.spec.ts +++ b/src/main/webapp/site/src/app/login/login-home/login-home.component.spec.ts @@ -1,13 +1,13 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { LoginHomeComponent } from './login-home.component'; -import { UserService } from "../../services/user.service"; -import { Observable } from "rxjs"; -import { RouterTestingModule } from "@angular/router/testing"; +import { UserService } from '../../services/user.service'; +import { Observable } from 'rxjs'; +import { RouterTestingModule } from '@angular/router/testing'; import { ConfigService } from '../../services/config.service'; -import { Config } from "../../domain/config"; -import { NO_ERRORS_SCHEMA } from "@angular/core"; +import { Config } from '../../domain/config'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; import { HttpClientTestingModule } from '@angular/common/http/testing'; -import { RecaptchaModule } from "ng-recaptcha"; +import { RecaptchaModule } from 'ng-recaptcha'; export class MockUserService { isSignedIn(): boolean { @@ -18,11 +18,11 @@ export class MockUserService { export class MockConfigService { getConfig(): Observable { const config: Config = { - contextPath: "/wise", - logOutURL: "/logout", - currentTime: new Date("2018-10-17T00:00:00.0").getTime() + contextPath: '/wise', + logOutURL: '/logout', + currentTime: new Date('2018-10-17T00:00:00.0').getTime() }; - return Observable.create(observer => { + return Observable.create((observer) => { observer.next(config); observer.complete(); }); @@ -37,19 +37,14 @@ describe('LoginHomeComponent', () => { let fixture: ComponentFixture; beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ LoginHomeComponent ], - imports: [ - HttpClientTestingModule, - RouterTestingModule, - RecaptchaModule - ], + declarations: [LoginHomeComponent], + imports: [HttpClientTestingModule, RouterTestingModule, RecaptchaModule], providers: [ { provide: UserService, useClass: MockUserService }, { provide: ConfigService, useClass: MockConfigService } ], - schemas: [ NO_ERRORS_SCHEMA ] - }) - .compileComponents(); + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); })); beforeEach(() => { diff --git a/src/main/webapp/site/src/app/login/login-home/login-home.component.ts b/src/main/webapp/site/src/app/login/login-home/login-home.component.ts index f2817fa625..76e2526101 100644 --- a/src/main/webapp/site/src/app/login/login-home/login-home.component.ts +++ b/src/main/webapp/site/src/app/login/login-home/login-home.component.ts @@ -11,21 +11,23 @@ import { ConfigService } from '../../services/config.service'; styleUrls: ['./login-home.component.scss'] }) export class LoginHomeComponent implements OnInit { - - credentials: any = {username: '', password: '', recaptchaResponse: null}; + credentials: any = { username: '', password: '', recaptchaResponse: null }; passwordError: boolean = false; processing: boolean = false; isGoogleAuthenticationEnabled: boolean = false; isShowGoogleLogin: boolean = true; - recaptchaPublicKey: string = ""; + recaptchaPublicKey: string = ''; isRecaptchaRequired: boolean = false; - accessCode: string = ""; + accessCode: string = ''; @ViewChild('recaptchaRef', { static: false }) recaptchaRef: any; - constructor(private userService: UserService, private http: HttpClient, - private router: Router, private route: ActivatedRoute, - private configService: ConfigService) { - } + constructor( + private userService: UserService, + private http: HttpClient, + private router: Router, + private route: ActivatedRoute, + private configService: ConfigService + ) {} ngOnInit(): void { this.configService.getConfig().subscribe((config) => { @@ -37,13 +39,13 @@ export class LoginHomeComponent implements OnInit { this.router.navigateByUrl(this.getRedirectUrl('')); } }); - this.route.params.subscribe(params => { + this.route.params.subscribe((params) => { if (params['username'] != null) { this.credentials.username = params['username']; this.isShowGoogleLogin = false; } }); - this.route.queryParams.subscribe(params => { + this.route.queryParams.subscribe((params) => { if (params['username'] != null) { this.credentials.username = params['username']; } @@ -55,7 +57,7 @@ export class LoginHomeComponent implements OnInit { } }); } - + login(): boolean { this.processing = true; this.passwordError = false; @@ -82,7 +84,7 @@ export class LoginHomeComponent implements OnInit { this.router.navigate([], { relativeTo: this.route, queryParams: { - 'username': this.credentials.username, + username: this.credentials.username, 'is-recaptcha-required': true }, queryParamsHandling: 'merge' @@ -95,7 +97,7 @@ export class LoginHomeComponent implements OnInit { } } - public socialSignIn(socialPlatform : string) { + public socialSignIn(socialPlatform: string) { window.location.href = this.getRedirectUrl(socialPlatform); } diff --git a/src/main/webapp/site/src/app/login/login-routing.module.ts b/src/main/webapp/site/src/app/login/login-routing.module.ts index e634a42390..64d90b34e4 100644 --- a/src/main/webapp/site/src/app/login/login-routing.module.ts +++ b/src/main/webapp/site/src/app/login/login-routing.module.ts @@ -1,9 +1,9 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; -import { LoginComponent } from "./login.component"; -import { LoginHomeComponent } from "./login-home/login-home.component"; -import { LoginGoogleUserNotFoundComponent } from "./login-google-user-not-found/login-google-user-not-found.component"; +import { LoginComponent } from './login.component'; +import { LoginHomeComponent } from './login-home/login-home.component'; +import { LoginGoogleUserNotFoundComponent } from './login-google-user-not-found/login-google-user-not-found.component'; const loginRoutes: Routes = [ { @@ -17,11 +17,7 @@ const loginRoutes: Routes = [ ]; @NgModule({ - imports: [ - RouterModule.forChild(loginRoutes) - ], - exports: [ - RouterModule - ] + imports: [RouterModule.forChild(loginRoutes)], + exports: [RouterModule] }) export class LoginRoutingModule {} diff --git a/src/main/webapp/site/src/app/login/login.component.spec.ts b/src/main/webapp/site/src/app/login/login.component.spec.ts index 4194f42f3b..b9bc5ace17 100644 --- a/src/main/webapp/site/src/app/login/login.component.spec.ts +++ b/src/main/webapp/site/src/app/login/login.component.spec.ts @@ -1,7 +1,7 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { LoginComponent } from './login.component'; -import { RouterTestingModule } from "@angular/router/testing"; +import { RouterTestingModule } from '@angular/router/testing'; describe('LoginComponent', () => { let component: LoginComponent; @@ -9,10 +9,9 @@ describe('LoginComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ LoginComponent ], - imports: [ RouterTestingModule ] - }) - .compileComponents(); + declarations: [LoginComponent], + imports: [RouterTestingModule] + }).compileComponents(); })); beforeEach(() => { diff --git a/src/main/webapp/site/src/app/login/login.component.ts b/src/main/webapp/site/src/app/login/login.component.ts index 35fbce4eb1..b19bf8f9e4 100644 --- a/src/main/webapp/site/src/app/login/login.component.ts +++ b/src/main/webapp/site/src/app/login/login.component.ts @@ -6,10 +6,7 @@ import { Component, OnInit } from '@angular/core'; styleUrls: ['./login.component.scss'] }) export class LoginComponent implements OnInit { + constructor() {} - constructor() { } - - ngOnInit() { - } - + ngOnInit() {} } diff --git a/src/main/webapp/site/src/app/login/login.module.ts b/src/main/webapp/site/src/app/login/login.module.ts index b17c6d7fa0..e2cbd5d0ff 100644 --- a/src/main/webapp/site/src/app/login/login.module.ts +++ b/src/main/webapp/site/src/app/login/login.module.ts @@ -2,8 +2,8 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { LoginComponent } from './login.component'; import { LoginGoogleUserNotFoundComponent } from './login-google-user-not-found/login-google-user-not-found.component'; -import { LoginHomeComponent } from "./login-home/login-home.component"; -import { LoginRoutingModule } from "./login-routing.module"; +import { LoginHomeComponent } from './login-home/login-home.component'; +import { LoginRoutingModule } from './login-routing.module'; import { FlexLayoutModule } from '@angular/flex-layout'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { RecaptchaModule, RecaptchaFormsModule } from 'ng-recaptcha'; @@ -40,8 +40,6 @@ const materialModules = [ LoginGoogleUserNotFoundComponent, LoginComponent ], - exports: [ - LoginComponent - ] + exports: [LoginComponent] }) -export class LoginModule { } +export class LoginModule {} diff --git a/src/main/webapp/site/src/app/modules/footer/footer.component.spec.ts b/src/main/webapp/site/src/app/modules/footer/footer.component.spec.ts index 4674c254bd..61ea9598ff 100644 --- a/src/main/webapp/site/src/app/modules/footer/footer.component.spec.ts +++ b/src/main/webapp/site/src/app/modules/footer/footer.component.spec.ts @@ -1,8 +1,8 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { FooterComponent } from './footer.component'; -import { RouterTestingModule } from "@angular/router/testing"; +import { RouterTestingModule } from '@angular/router/testing'; import { NO_ERRORS_SCHEMA } from '@angular/core'; -import { MomentModule } from "ngx-moment"; +import { MomentModule } from 'ngx-moment'; describe('FooterComponent', () => { let component: FooterComponent; @@ -10,11 +10,10 @@ describe('FooterComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ FooterComponent ], - imports: [ RouterTestingModule, MomentModule ], - schemas: [ NO_ERRORS_SCHEMA ] - }) - .compileComponents(); + declarations: [FooterComponent], + imports: [RouterTestingModule, MomentModule], + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); })); beforeEach(() => { diff --git a/src/main/webapp/site/src/app/modules/footer/footer.component.ts b/src/main/webapp/site/src/app/modules/footer/footer.component.ts index 0a2bf7a6b9..4d0aad45d3 100644 --- a/src/main/webapp/site/src/app/modules/footer/footer.component.ts +++ b/src/main/webapp/site/src/app/modules/footer/footer.component.ts @@ -6,10 +6,9 @@ import { Component, OnInit } from '@angular/core'; styleUrls: ['./footer.component.scss'] }) export class FooterComponent implements OnInit { - time: Date; - constructor() { } + constructor() {} ngOnInit() { this.time = new Date(); diff --git a/src/main/webapp/site/src/app/modules/footer/footer.module.ts b/src/main/webapp/site/src/app/modules/footer/footer.module.ts index 5b8f5cd4dc..92b5e6b107 100644 --- a/src/main/webapp/site/src/app/modules/footer/footer.module.ts +++ b/src/main/webapp/site/src/app/modules/footer/footer.module.ts @@ -6,21 +6,13 @@ import { MatIconModule } from '@angular/material/icon'; import { MatToolbarModule } from '@angular/material/toolbar'; import { MomentModule } from 'ngx-moment'; import { FooterComponent } from './footer.component'; -import { AppRoutingModule } from "../../app-routing.module"; +import { AppRoutingModule } from '../../app-routing.module'; -const materialModules = [ - MatButtonModule, MatIconModule, MatToolbarModule -]; +const materialModules = [MatButtonModule, MatIconModule, MatToolbarModule]; @NgModule({ - imports: [ - CommonModule, - FlexLayoutModule, - AppRoutingModule, - materialModules, - MomentModule - ], + imports: [CommonModule, FlexLayoutModule, AppRoutingModule, materialModules, MomentModule], declarations: [FooterComponent], exports: [FooterComponent] }) -export class FooterModule { } +export class FooterModule {} diff --git a/src/main/webapp/site/src/app/modules/header/header-account-menu/header-account-menu.component.spec.ts b/src/main/webapp/site/src/app/modules/header/header-account-menu/header-account-menu.component.spec.ts index c23e9ddb8d..104039f2d0 100644 --- a/src/main/webapp/site/src/app/modules/header/header-account-menu/header-account-menu.component.spec.ts +++ b/src/main/webapp/site/src/app/modules/header/header-account-menu/header-account-menu.component.spec.ts @@ -1,21 +1,21 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { HeaderAccountMenuComponent } from './header-account-menu.component'; -import { User } from "../../../domain/user"; -import { MatMenuModule } from "@angular/material/menu"; -import { ConfigService } from "../../../services/config.service"; -import { Observable } from "rxjs"; +import { User } from '../../../domain/user'; +import { MatMenuModule } from '@angular/material/menu'; +import { ConfigService } from '../../../services/config.service'; +import { Observable } from 'rxjs'; import { HttpClientTestingModule } from '@angular/common/http/testing'; -import { NO_ERRORS_SCHEMA } from "@angular/core"; -import { Config } from "../../../domain/config"; +import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { Config } from '../../../domain/config'; export class MockConfigService { getConfig(): Observable { const config: Config = { - contextPath: "/wise", - logOutURL: "/logout", - currentTime: new Date("2018-10-17T00:00:00.0").getTime() + contextPath: '/wise', + logOutURL: '/logout', + currentTime: new Date('2018-10-17T00:00:00.0').getTime() }; - return Observable.create(observer => { + return Observable.create((observer) => { observer.next(config); observer.complete(); }); @@ -28,17 +28,11 @@ describe('HeaderAccountMenuComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ HeaderAccountMenuComponent ], - imports: [ - HttpClientTestingModule, - MatMenuModule - ], - providers: [ - { provide: ConfigService, useClass: MockConfigService } - ], - schemas: [ NO_ERRORS_SCHEMA ] - }) - .compileComponents(); + declarations: [HeaderAccountMenuComponent], + imports: [HttpClientTestingModule, MatMenuModule], + providers: [{ provide: ConfigService, useClass: MockConfigService }], + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); })); beforeEach(() => { @@ -46,10 +40,10 @@ describe('HeaderAccountMenuComponent', () => { component = fixture.componentInstance; const user: User = new User(); user.id = 1; - user.firstName = "Amanda"; - user.lastName = "Panda"; - user.role = "student"; - user.username = "AmandaP0101"; + user.firstName = 'Amanda'; + user.lastName = 'Panda'; + user.role = 'student'; + user.username = 'AmandaP0101'; component.user = user; fixture.detectChanges(); }); diff --git a/src/main/webapp/site/src/app/modules/header/header-account-menu/header-account-menu.component.ts b/src/main/webapp/site/src/app/modules/header/header-account-menu/header-account-menu.component.ts index 628757944c..59ce99ff59 100644 --- a/src/main/webapp/site/src/app/modules/header/header-account-menu/header-account-menu.component.ts +++ b/src/main/webapp/site/src/app/modules/header/header-account-menu/header-account-menu.component.ts @@ -1,6 +1,6 @@ import { Component, OnInit, Input } from '@angular/core'; -import { ConfigService } from "../../../services/config.service"; -import { User } from "../../../domain/user"; +import { ConfigService } from '../../../services/config.service'; +import { User } from '../../../domain/user'; @Component({ selector: 'app-header-account-menu', @@ -8,13 +8,12 @@ import { User } from "../../../domain/user"; styleUrls: ['./header-account-menu.component.scss'] }) export class HeaderAccountMenuComponent implements OnInit { - @Input() user: User; - firstName: string = ""; - lastName: string = ""; - role: string = ""; + firstName: string = ''; + lastName: string = ''; + role: string = ''; isPreviousAdmin: boolean = false; logOutURL: string; @@ -23,7 +22,7 @@ export class HeaderAccountMenuComponent implements OnInit { } ngOnInit() { - this.configService.getConfig().subscribe(config => { + this.configService.getConfig().subscribe((config) => { if (config != null) { this.logOutURL = config.logOutURL; } @@ -47,7 +46,7 @@ export class HeaderAccountMenuComponent implements OnInit { } switchToOriginalUser() { - ( document.getElementById('switchBackToOriginalUserForm')).submit(); + (document.getElementById('switchBackToOriginalUserForm')).submit(); } logOut() { diff --git a/src/main/webapp/site/src/app/modules/header/header-links/header-links.component.spec.ts b/src/main/webapp/site/src/app/modules/header/header-links/header-links.component.spec.ts index 95cbb5559a..6580deb81e 100644 --- a/src/main/webapp/site/src/app/modules/header/header-links/header-links.component.spec.ts +++ b/src/main/webapp/site/src/app/modules/header/header-links/header-links.component.spec.ts @@ -1,11 +1,11 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { HeaderLinksComponent } from './header-links.component'; -import { User } from "../../../domain/user"; -import { Component } from "@angular/core"; +import { User } from '../../../domain/user'; +import { Component } from '@angular/core'; import { NO_ERRORS_SCHEMA } from '@angular/core'; import { configureTestSuite } from 'ng-bullet'; -@Component({selector: 'app-header-signin', template: ''}) +@Component({ selector: 'app-header-signin', template: '' }) class HeaderSignInStubComponent {} describe('HeaderLinksComponent', () => { @@ -14,9 +14,9 @@ describe('HeaderLinksComponent', () => { configureTestSuite(() => { TestBed.configureTestingModule({ - declarations: [ HeaderLinksComponent ], - imports: [ ], - schemas: [ NO_ERRORS_SCHEMA ] + declarations: [HeaderLinksComponent], + imports: [], + schemas: [NO_ERRORS_SCHEMA] }); }); @@ -25,10 +25,10 @@ describe('HeaderLinksComponent', () => { component = fixture.componentInstance; const user: User = new User(); user.id = 1; - user.firstName = "Amanda"; - user.lastName = "Panda"; - user.role = "student"; - user.username = "AmandaP0101"; + user.firstName = 'Amanda'; + user.lastName = 'Panda'; + user.role = 'student'; + user.username = 'AmandaP0101'; component.user = user; component.location = 'student'; fixture.detectChanges(); @@ -40,7 +40,6 @@ describe('HeaderLinksComponent', () => { it('should show user welcome message', () => { const compiled = fixture.debugElement.nativeElement; - expect(compiled.querySelector('.header__links').textContent) - .toContain('Welcome Amanda!'); + expect(compiled.querySelector('.header__links').textContent).toContain('Welcome Amanda!'); }); }); diff --git a/src/main/webapp/site/src/app/modules/header/header-links/header-links.component.ts b/src/main/webapp/site/src/app/modules/header/header-links/header-links.component.ts index 960b0f281a..f1ef61efba 100644 --- a/src/main/webapp/site/src/app/modules/header/header-links/header-links.component.ts +++ b/src/main/webapp/site/src/app/modules/header/header-links/header-links.component.ts @@ -1,24 +1,22 @@ import { Component, OnInit, Input } from '@angular/core'; -import { User } from "../../../domain/user"; +import { User } from '../../../domain/user'; @Component({ selector: 'app-header-links', templateUrl: './header-links.component.html', styleUrls: ['./header-links.component.scss'] }) export class HeaderLinksComponent implements OnInit { - @Input() user: User; @Input() location: string; - role: string = ""; + role: string = ''; - constructor() { } + constructor() {} - ngOnInit() { - } + ngOnInit() {} ngOnChanges(changes) { if (changes.user) { @@ -28,5 +26,4 @@ export class HeaderLinksComponent implements OnInit { } } } - } diff --git a/src/main/webapp/site/src/app/modules/header/header-signin/header-signin.component.spec.ts b/src/main/webapp/site/src/app/modules/header/header-signin/header-signin.component.spec.ts index a8af8bb462..48d581ba80 100644 --- a/src/main/webapp/site/src/app/modules/header/header-signin/header-signin.component.spec.ts +++ b/src/main/webapp/site/src/app/modules/header/header-signin/header-signin.component.spec.ts @@ -8,12 +8,11 @@ describe('HeaderSigninComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ HeaderSigninComponent ], + declarations: [HeaderSigninComponent], imports: [], providers: [], - schemas: [ NO_ERRORS_SCHEMA ] - }) - .compileComponents(); + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); })); beforeEach(() => { diff --git a/src/main/webapp/site/src/app/modules/header/header-signin/header-signin.component.ts b/src/main/webapp/site/src/app/modules/header/header-signin/header-signin.component.ts index 2b87a36914..e30c79b43d 100644 --- a/src/main/webapp/site/src/app/modules/header/header-signin/header-signin.component.ts +++ b/src/main/webapp/site/src/app/modules/header/header-signin/header-signin.component.ts @@ -6,10 +6,7 @@ import { Component, OnInit } from '@angular/core'; styleUrls: ['./header-signin.component.scss'] }) export class HeaderSigninComponent implements OnInit { + constructor() {} - constructor() { } - - ngOnInit() { - } - + ngOnInit() {} } diff --git a/src/main/webapp/site/src/app/modules/header/header.component.spec.ts b/src/main/webapp/site/src/app/modules/header/header.component.spec.ts index f9b8ead79c..b411b2f2c7 100644 --- a/src/main/webapp/site/src/app/modules/header/header.component.spec.ts +++ b/src/main/webapp/site/src/app/modules/header/header.component.spec.ts @@ -1,19 +1,19 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { HeaderComponent } from './header.component'; -import { Component, Input } from "@angular/core"; -import { User } from "../../domain/user"; -import { RouterTestingModule } from "@angular/router/testing"; +import { Component, Input } from '@angular/core'; +import { User } from '../../domain/user'; +import { RouterTestingModule } from '@angular/router/testing'; import { HttpClientTestingModule } from '@angular/common/http/testing'; -import { Observable } from "rxjs"; +import { Observable } from 'rxjs'; import { ConfigService } from '../../services/config.service'; -import { UserService } from "../../services/user.service"; -import { NO_ERRORS_SCHEMA } from "@angular/core"; -import { Config } from "../../domain/config"; -import { UtilService } from "../../services/util.service"; +import { UserService } from '../../services/user.service'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { Config } from '../../domain/config'; +import { UtilService } from '../../services/util.service'; export class MockUserService { getUser(): Observable { - return Observable.create(observer => { + return Observable.create((observer) => { const user: User = new User(); observer.next(user); observer.complete(); @@ -28,21 +28,21 @@ export class MockUtilService { export class MockConfigService { getConfig(): Observable { const config: Config = { - contextPath: "/wise", - logOutURL: "/logout", - currentTime: new Date("2018-10-17T00:00:00.0").getTime() + contextPath: '/wise', + logOutURL: '/logout', + currentTime: new Date('2018-10-17T00:00:00.0').getTime() }; - return Observable.create(observer => { + return Observable.create((observer) => { observer.next(config); observer.complete(); }); } } -@Component({selector: 'app-header-account-menu', template: ''}) +@Component({ selector: 'app-header-account-menu', template: '' }) class HeaderAccountMenuStubComponent { @Input() - user: User + user: User; } describe('HeaderComponent', () => { @@ -51,16 +51,15 @@ describe('HeaderComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - imports: [ HttpClientTestingModule, RouterTestingModule ], - declarations: [ HeaderComponent ], + imports: [HttpClientTestingModule, RouterTestingModule], + declarations: [HeaderComponent], providers: [ { provide: UserService, useClass: MockUserService }, { provide: ConfigService, useClass: MockConfigService }, - { provide: UtilService, useClass: MockUtilService }, + { provide: UtilService, useClass: MockUtilService } ], - schemas: [ NO_ERRORS_SCHEMA ] - }) - .compileComponents(); + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); })); beforeEach(() => { diff --git a/src/main/webapp/site/src/app/modules/header/header.component.ts b/src/main/webapp/site/src/app/modules/header/header.component.ts index 2ff5d01c17..fdcaff8d06 100644 --- a/src/main/webapp/site/src/app/modules/header/header.component.ts +++ b/src/main/webapp/site/src/app/modules/header/header.component.ts @@ -1,8 +1,8 @@ import { Component, OnInit } from '@angular/core'; import { Router } from '@angular/router'; import { User } from '../../domain/user'; -import { UserService } from "../../services/user.service"; -import { UtilService } from "../../services/util.service"; +import { UserService } from '../../services/user.service'; +import { UtilService } from '../../services/util.service'; @Component({ selector: 'app-header', @@ -10,16 +10,17 @@ import { UtilService } from "../../services/util.service"; styleUrls: ['./header.component.scss'] }) export class HeaderComponent implements OnInit { - user: User; location: string = ''; // current location role: string = ''; url: string = ''; - constructor(private router: Router, - private userService: UserService, - private utilService: UtilService) { + constructor( + private router: Router, + private userService: UserService, + private utilService: UtilService + ) { this.router = router; this.router.events.subscribe((event) => { this.setLocation(); @@ -32,13 +33,12 @@ export class HeaderComponent implements OnInit { } getUser() { - this.userService.getUser() - .subscribe(user => { - this.user = user; - if (user != null) { - this.role = user.role; - } - }); + this.userService.getUser().subscribe((user) => { + this.user = user; + if (user != null) { + this.role = user.role; + } + }); } setLocation() { diff --git a/src/main/webapp/site/src/app/modules/header/header.module.ts b/src/main/webapp/site/src/app/modules/header/header.module.ts index b5d536c6f7..206c9a771c 100644 --- a/src/main/webapp/site/src/app/modules/header/header.module.ts +++ b/src/main/webapp/site/src/app/modules/header/header.module.ts @@ -1,18 +1,18 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { FlexLayoutModule } from '@angular/flex-layout'; -import { AppRoutingModule } from "../../app-routing.module"; +import { AppRoutingModule } from '../../app-routing.module'; import { MatButtonModule } from '@angular/material/button'; import { MatDividerModule } from '@angular/material/divider'; import { MatIconModule } from '@angular/material/icon'; import { MatMenuModule } from '@angular/material/menu'; import { MatToolbarModule } from '@angular/material/toolbar'; -import { HeaderComponent } from "./header.component"; +import { HeaderComponent } from './header.component'; import { HeaderSigninComponent } from './header-signin/header-signin.component'; import { HeaderLinksComponent } from './header-links/header-links.component'; import { HeaderAccountMenuComponent } from './header-account-menu/header-account-menu.component'; -import { ConfigService } from "../../services/config.service"; -import { UserService } from "../../services/user.service"; +import { ConfigService } from '../../services/config.service'; +import { UserService } from '../../services/user.service'; const materialModules = [ MatButtonModule, @@ -23,24 +23,14 @@ const materialModules = [ ]; @NgModule({ - imports: [ - CommonModule, - FlexLayoutModule, - AppRoutingModule, - materialModules - ], + imports: [CommonModule, FlexLayoutModule, AppRoutingModule, materialModules], declarations: [ HeaderComponent, HeaderSigninComponent, HeaderLinksComponent, HeaderAccountMenuComponent ], - providers: [ - ConfigService, - UserService - ], - exports: [ - HeaderComponent - ] + providers: [ConfigService, UserService], + exports: [HeaderComponent] }) -export class HeaderModule { } +export class HeaderModule {} diff --git a/src/main/webapp/site/src/app/modules/library/community-library/community-library.component.spec.ts b/src/main/webapp/site/src/app/modules/library/community-library/community-library.component.spec.ts index c0030f1a29..cd1a3196f7 100644 --- a/src/main/webapp/site/src/app/modules/library/community-library/community-library.component.spec.ts +++ b/src/main/webapp/site/src/app/modules/library/community-library/community-library.component.spec.ts @@ -1,8 +1,8 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { CommunityLibraryComponent } from './community-library.component'; -import { fakeAsyncResponse } from "../../../student/student-run-list/student-run-list.component.spec"; -import { LibraryService } from "../../../services/library.service"; -import { NO_ERRORS_SCHEMA } from "@angular/core"; +import { fakeAsyncResponse } from '../../../student/student-run-list/student-run-list.component.spec'; +import { LibraryService } from '../../../services/library.service'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; import { MatDialog, MatDialogModule } from '@angular/material/dialog'; import { BehaviorSubject } from 'rxjs'; import { OverlayModule } from '@angular/cdk/overlay'; @@ -11,7 +11,7 @@ export class MockLibraryService { implementationModelOptions = []; communityLibraryProjectsSource$ = fakeAsyncResponse([]); projectFilterValuesSource$ = fakeAsyncResponse({ - searchValue: "", + searchValue: '', disciplineValue: [], dciArrangementValue: [], peValue: [] @@ -25,14 +25,11 @@ describe('CommunityLibraryComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - imports: [ OverlayModule, MatDialogModule ], - declarations: [ CommunityLibraryComponent ], - providers: [ - { provide: LibraryService, useClass: MockLibraryService }, - ], - schemas: [ NO_ERRORS_SCHEMA ] - }) - .compileComponents(); + imports: [OverlayModule, MatDialogModule], + declarations: [CommunityLibraryComponent], + providers: [{ provide: LibraryService, useClass: MockLibraryService }], + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); })); beforeEach(() => { diff --git a/src/main/webapp/site/src/app/modules/library/community-library/community-library.component.ts b/src/main/webapp/site/src/app/modules/library/community-library/community-library.component.ts index f9c2bff96d..4378e1c4c4 100644 --- a/src/main/webapp/site/src/app/modules/library/community-library/community-library.component.ts +++ b/src/main/webapp/site/src/app/modules/library/community-library/community-library.component.ts @@ -1,7 +1,7 @@ import { Component, Inject } from '@angular/core'; -import { LibraryService } from "../../../services/library.service"; -import { LibraryProject } from "../libraryProject"; -import { LibraryComponent } from "../library/library.component"; +import { LibraryService } from '../../../services/library.service'; +import { LibraryProject } from '../libraryProject'; +import { LibraryComponent } from '../library/library.component'; import { MatDialog, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; @Component({ @@ -10,7 +10,6 @@ import { MatDialog, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dial styleUrls: ['./community-library.component.scss'] }) export class CommunityLibraryComponent extends LibraryComponent { - projects: LibraryProject[] = []; filteredProjects: LibraryProject[] = []; @@ -22,8 +21,7 @@ export class CommunityLibraryComponent extends LibraryComponent { }); } - ngOnInit() { - } + ngOnInit() {} emitNumberOfProjectsVisible(numProjectsVisible: number = null) { if (numProjectsVisible) { @@ -42,12 +40,13 @@ export class CommunityLibraryComponent extends LibraryComponent { @Component({ selector: 'community-library-details', - templateUrl: 'community-library-details.html', + templateUrl: 'community-library-details.html' }) export class CommunityLibraryDetailsComponent { constructor( public dialogRef: MatDialogRef, - @Inject(MAT_DIALOG_DATA) public data: any) {} + @Inject(MAT_DIALOG_DATA) public data: any + ) {} close(): void { this.dialogRef.close(); diff --git a/src/main/webapp/site/src/app/modules/library/copy-project-dialog/copy-project-dialog.component.spec.ts b/src/main/webapp/site/src/app/modules/library/copy-project-dialog/copy-project-dialog.component.spec.ts index 06cab8e7db..ec993cbc10 100644 --- a/src/main/webapp/site/src/app/modules/library/copy-project-dialog/copy-project-dialog.component.spec.ts +++ b/src/main/webapp/site/src/app/modules/library/copy-project-dialog/copy-project-dialog.component.spec.ts @@ -1,11 +1,11 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { CopyProjectDialogComponent } from './copy-project-dialog.component'; -import { LibraryService } from "../../../services/library.service"; +import { LibraryService } from '../../../services/library.service'; import { MatDialog, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; -import { Project } from "../../../domain/project"; +import { Project } from '../../../domain/project'; import { Observable, Subject } from 'rxjs'; -import { NO_ERRORS_SCHEMA } from "@angular/core"; -import { LibraryProject } from "../libraryProject"; +import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { LibraryProject } from '../libraryProject'; import { configureTestSuite } from 'ng-bullet'; import { MatSnackBarModule } from '@angular/material/snack-bar'; @@ -14,7 +14,7 @@ export class MockLibraryService { newProjectSource$ = this.newProjectSource.asObservable(); copyProject() { - return Observable.create(observer => { + return Observable.create((observer) => { const project: Project = new Project(); observer.next(project); observer.complete(); @@ -31,47 +31,46 @@ describe('CopyProjectDialogComponent', () => { let fixture: ComponentFixture; const projectObj = { id: 1, - name: "Test", + name: 'Test', owner: { id: 123456, - displayName: "Spongebob Squarepants" + displayName: 'Spongebob Squarepants' }, sharedOwners: [] }; const getCopyButton = () => { - const buttons = fixture.debugElement.nativeElement.querySelectorAll('button'); + const buttons = fixture.debugElement.nativeElement.querySelectorAll('button'); return buttons[1]; }; configureTestSuite(() => { TestBed.configureTestingModule({ - declarations: [ CopyProjectDialogComponent ], - imports: [ MatSnackBarModule ], + declarations: [CopyProjectDialogComponent], + imports: [MatSnackBarModule], providers: [ { provide: LibraryService, useClass: MockLibraryService }, - { provide: MatDialog, useValue: { - closeAll: () => { - - } + { + provide: MatDialog, + useValue: { + closeAll: () => {} } }, { - provide: MatDialogRef, useValue: { + provide: MatDialogRef, + useValue: { afterClosed: () => { - return Observable.create(observer => { + return Observable.create((observer) => { observer.next({}); observer.complete(); }); }, - close: () => { - - } + close: () => {} } }, { provide: MAT_DIALOG_DATA, useValue: { project: projectObj } } ], - schemas: [ NO_ERRORS_SCHEMA ] + schemas: [NO_ERRORS_SCHEMA] }); }); @@ -87,7 +86,7 @@ describe('CopyProjectDialogComponent', () => { expect(component).toBeTruthy(); }); - it('should close the dialog when copy is successful', async() => { + it('should close the dialog when copy is successful', async () => { const copyButton = getCopyButton(); copyButton.click(); fixture.detectChanges(); diff --git a/src/main/webapp/site/src/app/modules/library/copy-project-dialog/copy-project-dialog.component.ts b/src/main/webapp/site/src/app/modules/library/copy-project-dialog/copy-project-dialog.component.ts index 054eb89ccc..8a2db347d5 100644 --- a/src/main/webapp/site/src/app/modules/library/copy-project-dialog/copy-project-dialog.component.ts +++ b/src/main/webapp/site/src/app/modules/library/copy-project-dialog/copy-project-dialog.component.ts @@ -1,9 +1,9 @@ import { Component, Inject } from '@angular/core'; -import { LibraryProjectDetailsComponent } from "../library-project-details/library-project-details.component"; -import { MatDialog, MatDialogRef, MAT_DIALOG_DATA} from '@angular/material/dialog'; +import { LibraryProjectDetailsComponent } from '../library-project-details/library-project-details.component'; +import { MatDialog, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; import { finalize } from 'rxjs/operators'; -import { LibraryProject } from "../libraryProject"; -import { LibraryService } from "../../../services/library.service"; +import { LibraryProject } from '../libraryProject'; +import { LibraryService } from '../../../services/library.service'; import { MatSnackBar } from '@angular/material/snack-bar'; import { Subscription } from 'rxjs'; @@ -13,14 +13,15 @@ import { Subscription } from 'rxjs'; styleUrls: ['./copy-project-dialog.component.scss'] }) export class CopyProjectDialogComponent { - isCopying: boolean = false; - constructor(public dialog: MatDialog, - public dialogRef: MatDialogRef, - @Inject(MAT_DIALOG_DATA) public data: any, - private libraryService: LibraryService, - private snackBar: MatSnackBar) { + constructor( + public dialog: MatDialog, + public dialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) public data: any, + private libraryService: LibraryService, + private snackBar: MatSnackBar + ) { this.libraryService.newProjectSource$.subscribe(() => { this.dialog.closeAll(); }); @@ -28,13 +29,15 @@ export class CopyProjectDialogComponent { copy() { this.isCopying = true; - this.libraryService.copyProject(this.data.project.id) - .pipe( - finalize(() => { - this.isCopying = false; - }) - ) - .subscribe((response: any) => { + this.libraryService + .copyProject(this.data.project.id) + .pipe( + finalize(() => { + this.isCopying = false; + }) + ) + .subscribe( + (response: any) => { if (response.status === 'error') { this.showErrorMessage(); } else { @@ -42,12 +45,16 @@ export class CopyProjectDialogComponent { newLibraryProject.visible = true; this.libraryService.addPersonalLibraryProject(newLibraryProject); } - }, (error) => { - this.showErrorMessage(); - }); + }, + (error) => { + this.showErrorMessage(); + } + ); } showErrorMessage() { - this.snackBar.open($localize`There was an error trying to copy the project. Please refresh the page and try again.`); + this.snackBar.open( + $localize`There was an error trying to copy the project. Please refresh the page and try again.` + ); } } diff --git a/src/main/webapp/site/src/app/modules/library/home-page-project-library/home-page-project-library.component.spec.ts b/src/main/webapp/site/src/app/modules/library/home-page-project-library/home-page-project-library.component.spec.ts index 919b6eb3f5..32e47dc9ad 100644 --- a/src/main/webapp/site/src/app/modules/library/home-page-project-library/home-page-project-library.component.spec.ts +++ b/src/main/webapp/site/src/app/modules/library/home-page-project-library/home-page-project-library.component.spec.ts @@ -1,11 +1,11 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { HomePageProjectLibraryComponent } from './home-page-project-library.component'; -import { NO_ERRORS_SCHEMA } from "@angular/core"; +import { NO_ERRORS_SCHEMA } from '@angular/core'; import { LibraryService } from '../../../services/library.service'; export class MockLibraryService { - getOfficialLibraryProjects(){} - clearAll(){} + getOfficialLibraryProjects() {} + clearAll() {} } describe('HomePageProjectLibraryComponent', () => { @@ -14,15 +14,10 @@ describe('HomePageProjectLibraryComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ imports: [], - declarations: [ - HomePageProjectLibraryComponent - ], - providers: [ - { provide: LibraryService, useClass: MockLibraryService } - ], - schemas: [ NO_ERRORS_SCHEMA ] - }) - .compileComponents(); + declarations: [HomePageProjectLibraryComponent], + providers: [{ provide: LibraryService, useClass: MockLibraryService }], + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); })); beforeEach(() => { diff --git a/src/main/webapp/site/src/app/modules/library/home-page-project-library/home-page-project-library.component.ts b/src/main/webapp/site/src/app/modules/library/home-page-project-library/home-page-project-library.component.ts index 07d2128952..ca796e1f9c 100644 --- a/src/main/webapp/site/src/app/modules/library/home-page-project-library/home-page-project-library.component.ts +++ b/src/main/webapp/site/src/app/modules/library/home-page-project-library/home-page-project-library.component.ts @@ -4,13 +4,9 @@ import { LibraryService } from '../../../services/library.service'; @Component({ selector: 'app-home-page-project-library', templateUrl: './home-page-project-library.component.html', - styleUrls: [ - './home-page-project-library.component.scss', - '../library/library.component.scss' - ] + styleUrls: ['./home-page-project-library.component.scss', '../library/library.component.scss'] }) export class HomePageProjectLibraryComponent { - constructor(private libraryService: LibraryService) { libraryService.getOfficialLibraryProjects(); } diff --git a/src/main/webapp/site/src/app/modules/library/library-filters/library-filters.component.spec.ts b/src/main/webapp/site/src/app/modules/library/library-filters/library-filters.component.spec.ts index 721def7313..5d1edd7f28 100644 --- a/src/main/webapp/site/src/app/modules/library/library-filters/library-filters.component.spec.ts +++ b/src/main/webapp/site/src/app/modules/library/library-filters/library-filters.component.spec.ts @@ -15,7 +15,7 @@ export class MockLibraryService { public personalLibraryProjectsSource$ = fakeAsyncResponse([]); setFilterValues(projectFilterValues: ProjectFilterValues) {} getFilterValues(): ProjectFilterValues { - return new ProjectFilterValues; + return new ProjectFilterValues(); } } @@ -26,11 +26,11 @@ describe('LibraryFiltersComponent', () => { configureTestSuite(() => { TestBed.configureTestingModule({ - imports: [ ], - declarations: [ LibraryFiltersComponent ], - providers: [ { provide: LibraryService, useClass: MockLibraryService } ], - schemas: [ NO_ERRORS_SCHEMA ] - }) + imports: [], + declarations: [LibraryFiltersComponent], + providers: [{ provide: LibraryService, useClass: MockLibraryService }], + schemas: [NO_ERRORS_SCHEMA] + }); }); beforeEach(() => { @@ -38,7 +38,7 @@ describe('LibraryFiltersComponent', () => { fixture = TestBed.createComponent(LibraryFiltersComponent); component = fixture.componentInstance; component.libraryProjects = projects; - component.ngOnChanges({projects: new SimpleChange(null, projects, true)}); + component.ngOnChanges({ projects: new SimpleChange(null, projects, true) }); fixture.detectChanges(); }); @@ -64,5 +64,4 @@ describe('LibraryFiltersComponent', () => { component.filterUpdated(['Earth Sciences', 'Physical Sciences'], 'discipline'); expect(libraryServiceFilterValuesSpy).toHaveBeenCalled(); })); - }); diff --git a/src/main/webapp/site/src/app/modules/library/library-filters/library-filters.component.ts b/src/main/webapp/site/src/app/modules/library/library-filters/library-filters.component.ts index 76d1c6ebfc..48cb7dd342 100644 --- a/src/main/webapp/site/src/app/modules/library/library-filters/library-filters.component.ts +++ b/src/main/webapp/site/src/app/modules/library/library-filters/library-filters.component.ts @@ -1,4 +1,12 @@ -import { Component, OnInit, Input, SimpleChanges, Output, EventEmitter, ViewEncapsulation } from '@angular/core'; +import { + Component, + OnInit, + Input, + SimpleChanges, + Output, + EventEmitter, + ViewEncapsulation +} from '@angular/core'; import { LibraryProject } from '../libraryProject'; import { LibraryService } from '../../../services/library.service'; import { NGSSStandards } from '../ngssStandards'; @@ -12,12 +20,10 @@ import { UtilService } from '../../../services/util.service'; styleUrls: ['./library-filters.component.scss'], encapsulation: ViewEncapsulation.None }) - export class LibraryFiltersComponent implements OnInit { - @Input() isSplitScreen: boolean = false; - + allProjects: LibraryProject[] = []; libraryProjects: LibraryProject[] = []; communityProjects: LibraryProject[] = []; @@ -32,24 +38,27 @@ export class LibraryFiltersComponent implements OnInit { peValue = []; showFilters: boolean = false; - constructor(private libraryService: LibraryService, - private utilService: UtilService) { + constructor(private libraryService: LibraryService, private utilService: UtilService) { libraryService.officialLibraryProjectsSource$.subscribe((libraryProjects: LibraryProject[]) => { - this.libraryProjects = libraryProjects; - this.populateFilterOptions(); - }); - libraryService.communityLibraryProjectsSource$.subscribe((communityProjects: LibraryProject[]) => { + this.libraryProjects = libraryProjects; + this.populateFilterOptions(); + }); + libraryService.communityLibraryProjectsSource$.subscribe( + (communityProjects: LibraryProject[]) => { this.communityProjects = communityProjects; this.populateFilterOptions(); - }); + } + ); libraryService.sharedLibraryProjectsSource$.subscribe((sharedProjects: LibraryProject[]) => { - this.sharedProjects = sharedProjects; - this.populateFilterOptions(); - }); - libraryService.personalLibraryProjectsSource$.subscribe((personalProjects: LibraryProject[]) => { + this.sharedProjects = sharedProjects; + this.populateFilterOptions(); + }); + libraryService.personalLibraryProjectsSource$.subscribe( + (personalProjects: LibraryProject[]) => { this.personalProjects = personalProjects; this.populateFilterOptions(); - }); + } + ); } ngOnInit() { @@ -122,18 +131,26 @@ export class LibraryFiltersComponent implements OnInit { } removeDuplicatesAndSortAlphabetically() { - this.dciArrangementOptions = - this.utilService.removeObjectArrayDuplicatesByProperty(this.dciArrangementOptions, 'id'); + this.dciArrangementOptions = this.utilService.removeObjectArrayDuplicatesByProperty( + this.dciArrangementOptions, + 'id' + ); this.utilService.sortObjectArrayByProperty(this.dciArrangementOptions, 'id'); - this.disciplineOptions = - this.utilService.removeObjectArrayDuplicatesByProperty(this.disciplineOptions, 'id'); + this.disciplineOptions = this.utilService.removeObjectArrayDuplicatesByProperty( + this.disciplineOptions, + 'id' + ); this.utilService.sortObjectArrayByProperty(this.disciplineOptions, 'name'); this.peOptions = this.utilService.removeObjectArrayDuplicatesByProperty(this.peOptions, 'id'); this.utilService.sortObjectArrayByProperty(this.peOptions, 'id'); } hasFilters(): boolean { - return this.dciArrangementValue.length > 0 || this.peValue.length > 0 || this.disciplineValue.length > 0; + return ( + this.dciArrangementValue.length > 0 || + this.peValue.length > 0 || + this.disciplineValue.length > 0 + ); } searchUpdated(value: string): void { @@ -142,7 +159,7 @@ export class LibraryFiltersComponent implements OnInit { } filterUpdated(value: string[] = [], context: string = ''): void { - switch(context) { + switch (context) { case 'discipline': this.disciplineValue = value; break; diff --git a/src/main/webapp/site/src/app/modules/library/library-group-thumbs/library-group-thumbs.component.spec.ts b/src/main/webapp/site/src/app/modules/library/library-group-thumbs/library-group-thumbs.component.spec.ts index c1a11f6487..b9fb924090 100644 --- a/src/main/webapp/site/src/app/modules/library/library-group-thumbs/library-group-thumbs.component.spec.ts +++ b/src/main/webapp/site/src/app/modules/library/library-group-thumbs/library-group-thumbs.component.spec.ts @@ -1,7 +1,7 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { LibraryGroupThumbsComponent } from './library-group-thumbs.component'; -import { LibraryGroup } from "../libraryGroup"; -import { NO_ERRORS_SCHEMA } from "@angular/core"; +import { LibraryGroup } from '../libraryGroup'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; describe('LibraryGroupThumbsComponent', () => { let component: LibraryGroupThumbsComponent; @@ -9,11 +9,10 @@ describe('LibraryGroupThumbsComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - imports: [ ], - declarations: [ LibraryGroupThumbsComponent ], - schemas: [ NO_ERRORS_SCHEMA ] - }) - .compileComponents(); + imports: [], + declarations: [LibraryGroupThumbsComponent], + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); })); beforeEach(() => { diff --git a/src/main/webapp/site/src/app/modules/library/library-group-thumbs/library-group-thumbs.component.ts b/src/main/webapp/site/src/app/modules/library/library-group-thumbs/library-group-thumbs.component.ts index 25395a43e5..da232de702 100644 --- a/src/main/webapp/site/src/app/modules/library/library-group-thumbs/library-group-thumbs.component.ts +++ b/src/main/webapp/site/src/app/modules/library/library-group-thumbs/library-group-thumbs.component.ts @@ -1,6 +1,6 @@ import { Component, Input, OnInit } from '@angular/core'; import { DomSanitizer } from '@angular/platform-browser'; -import { LibraryGroup } from "../libraryGroup"; +import { LibraryGroup } from '../libraryGroup'; @Component({ selector: 'app-library-group-thumbs', @@ -8,7 +8,6 @@ import { LibraryGroup } from "../libraryGroup"; styleUrls: ['./library-group-thumbs.component.scss'] }) export class LibraryGroupThumbsComponent implements OnInit { - @Input() group: LibraryGroup = new LibraryGroup(); diff --git a/src/main/webapp/site/src/app/modules/library/library-project-details/library-project-details.component.spec.ts b/src/main/webapp/site/src/app/modules/library/library-project-details/library-project-details.component.spec.ts index 3407387e8f..cd5a914262 100644 --- a/src/main/webapp/site/src/app/modules/library/library-project-details/library-project-details.component.spec.ts +++ b/src/main/webapp/site/src/app/modules/library/library-project-details/library-project-details.component.spec.ts @@ -1,15 +1,15 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { Component, Input } from '@angular/core'; -import { Observable } from "rxjs"; -import { MAT_DIALOG_DATA, MatDialog, MatDialogRef } from "@angular/material/dialog"; +import { Observable } from 'rxjs'; +import { MAT_DIALOG_DATA, MatDialog, MatDialogRef } from '@angular/material/dialog'; import { LibraryProjectDetailsComponent } from './library-project-details.component'; -import { UserService } from "../../../services/user.service"; -import { Project } from "../../../domain/project"; -import { NGSSStandards } from "../ngssStandards"; -import { NO_ERRORS_SCHEMA } from "@angular/core"; -import { LibraryService } from "../../../services/library.service"; -import { ConfigService } from "../../../services/config.service"; -import { ParentProject } from "../../../domain/parentProject"; +import { UserService } from '../../../services/user.service'; +import { Project } from '../../../domain/project'; +import { NGSSStandards } from '../ngssStandards'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { LibraryService } from '../../../services/library.service'; +import { ConfigService } from '../../../services/config.service'; +import { ParentProject } from '../../../domain/parentProject'; import { configureTestSuite } from 'ng-bullet'; // @Component({ selector: 'app-library-project-menu', template: '' }) @@ -18,18 +18,14 @@ import { configureTestSuite } from 'ng-bullet'; // project: Project; // } -export class MockMatDialog { +export class MockMatDialog {} -} - -export class MockLibraryService { - -} +export class MockLibraryService {} export class MockUserService { isTeacher(): Observable { const isTeacher: boolean = true; - return Observable.create( observer => { + return Observable.create((observer) => { observer.next(isTeacher); observer.complete(); }); @@ -38,17 +34,15 @@ export class MockUserService { export class MockConfigService { getContextPath(): string { - return ""; + return ''; } } const parentProject = new ParentProject({ - "id": 1000, - "title": "Photosynthesis", - "uri": "http://localhost:8080/project/1000", - "authors": [ - {"id": 6, "firstName": "Susie", "lastName": "Derkins", "username": "SusieDerkins"} - ] + id: 1000, + title: 'Photosynthesis', + uri: 'http://localhost:8080/project/1000', + authors: [{ id: 6, firstName: 'Susie', lastName: 'Derkins', username: 'SusieDerkins' }] }); describe('LibraryProjectDetailsComponent', () => { @@ -57,7 +51,7 @@ describe('LibraryProjectDetailsComponent', () => { configureTestSuite(() => { TestBed.configureTestingModule({ - declarations: [ LibraryProjectDetailsComponent ], + declarations: [LibraryProjectDetailsComponent], providers: [ { provide: LibraryService, useClass: MockLibraryService }, { provide: UserService, useClass: MockUserService }, @@ -66,8 +60,8 @@ describe('LibraryProjectDetailsComponent', () => { { provide: MAT_DIALOG_DATA, useValue: [] }, { provide: MatDialog, useClass: MockMatDialog } ], - schemas: [ NO_ERRORS_SCHEMA ] - }) + schemas: [NO_ERRORS_SCHEMA] + }); }); beforeEach(() => { @@ -75,34 +69,41 @@ describe('LibraryProjectDetailsComponent', () => { component = fixture.componentInstance; const project: Project = new Project(); project.id = 1; - project.name = "Photosynthesis & Cellular Respiration"; - project.projectThumb = "photo.png"; + project.name = 'Photosynthesis & Cellular Respiration'; + project.projectThumb = 'photo.png'; project.metadata = { - "grades": ["7"], - "title": "Photosynthesis & Cellular Respiration", - "summary": "A really great unit.", - "totalTime": "6-7 hours", - "authors": [ - {"id": 10, "firstName": "Spaceman", "lastName": "Spiff", "username": "SpacemanSpiff"}, - {"id": 12, "firstName": "Captain", "lastName": "Napalm", "username": "CaptainNapalm"} + grades: ['7'], + title: 'Photosynthesis & Cellular Respiration', + summary: 'A really great unit.', + totalTime: '6-7 hours', + authors: [ + { id: 10, firstName: 'Spaceman', lastName: 'Spiff', username: 'SpacemanSpiff' }, + { id: 12, firstName: 'Captain', lastName: 'Napalm', username: 'CaptainNapalm' } ] }; const ngssObject: any = { - "disciplines": [{ - "name": "Life Sciences", - "id": "LS" - }], - "dciArrangements": [{ - "children": [{ - "name": "Construct a scientific explanation...", - "id": "MS-LS1-6" - }, { - "name": "Develop a model...", - "id": "MS-LS1-7" - }], - "name": "From Molecules to Organisms: Structures and Processes", - "id": "MS-LS1" - }] + disciplines: [ + { + name: 'Life Sciences', + id: 'LS' + } + ], + dciArrangements: [ + { + children: [ + { + name: 'Construct a scientific explanation...', + id: 'MS-LS1-6' + }, + { + name: 'Develop a model...', + id: 'MS-LS1-7' + } + ], + name: 'From Molecules to Organisms: Structures and Processes', + id: 'MS-LS1' + } + ] }; const ngss: NGSSStandards = new NGSSStandards(); ngss.disciplines = ngssObject.disciplines; diff --git a/src/main/webapp/site/src/app/modules/library/library-project-details/library-project-details.component.ts b/src/main/webapp/site/src/app/modules/library/library-project-details/library-project-details.component.ts index bc3c900078..5072175de4 100644 --- a/src/main/webapp/site/src/app/modules/library/library-project-details/library-project-details.component.ts +++ b/src/main/webapp/site/src/app/modules/library/library-project-details/library-project-details.component.ts @@ -1,12 +1,12 @@ import { Component, Inject, OnInit } from '@angular/core'; -import { MatDialog, MatDialogRef, MAT_DIALOG_DATA} from '@angular/material/dialog'; -import { LibraryService } from "../../../services/library.service"; -import { UserService } from "../../../services/user.service"; -import { CreateRunDialogComponent } from "../../../teacher/create-run-dialog/create-run-dialog.component"; -import { UseWithClassWarningDialogComponent } from "../../../teacher/use-with-class-warning-dialog/use-with-class-warning-dialog.component"; -import { NGSSStandards } from "../ngssStandards"; -import { Project } from "../../../domain/project"; -import { ParentProject } from "../../../domain/parentProject"; +import { MatDialog, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; +import { LibraryService } from '../../../services/library.service'; +import { UserService } from '../../../services/user.service'; +import { CreateRunDialogComponent } from '../../../teacher/create-run-dialog/create-run-dialog.component'; +import { UseWithClassWarningDialogComponent } from '../../../teacher/use-with-class-warning-dialog/use-with-class-warning-dialog.component'; +import { NGSSStandards } from '../ngssStandards'; +import { Project } from '../../../domain/project'; +import { ParentProject } from '../../../domain/parentProject'; import { Router } from '@angular/router'; import { ConfigService } from '../../../services/config.service'; @@ -30,29 +30,32 @@ export class LibraryProjectDetailsComponent implements OnInit { more: boolean = false; isCopy: boolean = false; - constructor(public dialog: MatDialog, - public dialogRef: MatDialogRef, - @Inject(MAT_DIALOG_DATA) public data: any, - private configService: ConfigService, - private libraryService: LibraryService, - private userService: UserService) { + constructor( + public dialog: MatDialog, + public dialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) public data: any, + private configService: ConfigService, + private libraryService: LibraryService, + private userService: UserService + ) { this.isTeacher = userService.isTeacher(); this.isRunProject = data.isRunProject; if (this.data.project) { this.project = new Project(this.data.project); - const numParents = this.data.project.metadata.parentProjects ? - this.data.project.metadata.parentProjects.length : null; + const numParents = this.data.project.metadata.parentProjects + ? this.data.project.metadata.parentProjects.length + : null; if (numParents) { - this.parentProject = - new ParentProject(this.data.project.metadata.parentProjects[numParents-1]); + this.parentProject = new ParentProject( + this.data.project.metadata.parentProjects[numParents - 1] + ); } this.setNGSS(); this.setLicenseInfo(); } } - ngOnInit() { - } + ngOnInit() {} onClose(): void { this.dialogRef.close(); @@ -96,9 +99,11 @@ export class LibraryProjectDetailsComponent implements OnInit { if (!authors) { return ''; } - return authors.map((author) => { + return authors + .map((author) => { return `${author.firstName} ${author.lastName}`; - }).join(', '); + }) + .join(', '); } runProject() { @@ -119,7 +124,10 @@ export class LibraryProjectDetailsComponent implements OnInit { previewProject() { if (this.project.wiseVersion === 4) { - window.open(`${this.configService.getWISE4Hostname()}` + `/previewproject.html?projectId=${this.project.id}`); + window.open( + `${this.configService.getWISE4Hostname()}` + + `/previewproject.html?projectId=${this.project.id}` + ); } else { window.open(`/preview/unit/${this.project.id}`); } diff --git a/src/main/webapp/site/src/app/modules/library/library-project-disciplines/library-project-disciplines.component.spec.ts b/src/main/webapp/site/src/app/modules/library/library-project-disciplines/library-project-disciplines.component.spec.ts index 0f01ccdfc8..d523a713e1 100644 --- a/src/main/webapp/site/src/app/modules/library/library-project-disciplines/library-project-disciplines.component.spec.ts +++ b/src/main/webapp/site/src/app/modules/library/library-project-disciplines/library-project-disciplines.component.spec.ts @@ -1,7 +1,7 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { LibraryProjectDisciplinesComponent } from './library-project-disciplines.component'; -import { LibraryProject } from "../libraryProject"; -import { NO_ERRORS_SCHEMA } from "@angular/core"; +import { LibraryProject } from '../libraryProject'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; describe('LibraryProjectDisciplinesComponent', () => { let component: LibraryProjectDisciplinesComponent; @@ -9,11 +9,10 @@ describe('LibraryProjectDisciplinesComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ LibraryProjectDisciplinesComponent ], + declarations: [LibraryProjectDisciplinesComponent], imports: [], - schemas: [ NO_ERRORS_SCHEMA ] - }) - .compileComponents(); + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); })); beforeEach(() => { diff --git a/src/main/webapp/site/src/app/modules/library/library-project-disciplines/library-project-disciplines.component.ts b/src/main/webapp/site/src/app/modules/library/library-project-disciplines/library-project-disciplines.component.ts index 8e867a7ade..7f47f480b5 100644 --- a/src/main/webapp/site/src/app/modules/library/library-project-disciplines/library-project-disciplines.component.ts +++ b/src/main/webapp/site/src/app/modules/library/library-project-disciplines/library-project-disciplines.component.ts @@ -1,5 +1,5 @@ import { Component, Input, OnInit } from '@angular/core'; -import { LibraryProject } from "../libraryProject"; +import { LibraryProject } from '../libraryProject'; @Component({ selector: 'app-library-project-disciplines', @@ -7,14 +7,12 @@ import { LibraryProject } from "../libraryProject"; styleUrls: ['./library-project-disciplines.component.scss'] }) export class LibraryProjectDisciplinesComponent implements OnInit { - @Input() project: LibraryProject = new LibraryProject(); disciplines: any[]; - constructor() { - } + constructor() {} ngOnInit() { let standards = this.project.metadata.standardsAddressed; @@ -24,22 +22,22 @@ export class LibraryProjectDisciplinesComponent implements OnInit { this.disciplines = standards.ngss.disciplines; for (let discipline of this.disciplines) { // default color - discipline.color = "#000000"; + discipline.color = '#000000'; // assign color based on discipline id if (discipline.id) { - switch(discipline.id) { - case "ESS": - discipline.color = "#2E7D32"; + switch (discipline.id) { + case 'ESS': + discipline.color = '#2E7D32'; break; - case "ETS": - discipline.color = "#1565C0"; + case 'ETS': + discipline.color = '#1565C0'; break; - case "LS": - discipline.color = "#D81B60"; + case 'LS': + discipline.color = '#D81B60'; break; - case "PS": - discipline.color = "#8E24AA"; + case 'PS': + discipline.color = '#8E24AA'; break; } } @@ -47,5 +45,4 @@ export class LibraryProjectDisciplinesComponent implements OnInit { } } } - } diff --git a/src/main/webapp/site/src/app/modules/library/library-project-menu/library-project-menu.component.spec.ts b/src/main/webapp/site/src/app/modules/library/library-project-menu/library-project-menu.component.spec.ts index 23c036bb9a..aec60c8ecd 100644 --- a/src/main/webapp/site/src/app/modules/library/library-project-menu/library-project-menu.component.spec.ts +++ b/src/main/webapp/site/src/app/modules/library/library-project-menu/library-project-menu.component.spec.ts @@ -1,14 +1,14 @@ -import {async, ComponentFixture, fakeAsync, TestBed} from '@angular/core/testing'; -import { LibraryProjectMenuComponent } from "./library-project-menu.component"; -import { TeacherService } from "../../../teacher/teacher.service"; -import { Project } from "../../../domain/project"; -import { MatDialogModule } from "@angular/material/dialog"; +import { async, ComponentFixture, fakeAsync, TestBed } from '@angular/core/testing'; +import { LibraryProjectMenuComponent } from './library-project-menu.component'; +import { TeacherService } from '../../../teacher/teacher.service'; +import { Project } from '../../../domain/project'; +import { MatDialogModule } from '@angular/material/dialog'; import { MatMenuModule } from '@angular/material/menu'; -import { UserService } from "../../../services/user.service"; -import { User } from "../../../domain/user"; +import { UserService } from '../../../services/user.service'; +import { User } from '../../../domain/user'; import { Observable } from 'rxjs'; -import { NO_ERRORS_SCHEMA } from "@angular/core"; -import { ConfigService } from "../../../services/config.service"; +import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { ConfigService } from '../../../services/config.service'; export class MockUserService { getUser(): Observable { @@ -18,7 +18,7 @@ export class MockUserService { user.role = 'teacher'; user.username = 'DemoTeacher'; user.id = 123456; - return Observable.create( observer => { + return Observable.create((observer) => { observer.next(user); observer.complete(); }); @@ -30,7 +30,7 @@ export class MockUserService { export class MockTeacherService { getProjectUsage(projectId: number): Observable { - return Observable.create(observer => { + return Observable.create((observer) => { observer.next(projectId); observer.complete(); }); @@ -39,27 +39,25 @@ export class MockTeacherService { export class MockConfigService { getContextPath(): string { - return ""; + return ''; } } describe('LibraryProjectMenuComponent', () => { - let component: LibraryProjectMenuComponent; let fixture: ComponentFixture; beforeEach(async(() => { TestBed.configureTestingModule({ - imports: [ MatMenuModule, MatDialogModule ], - declarations: [ LibraryProjectMenuComponent ], + imports: [MatMenuModule, MatDialogModule], + declarations: [LibraryProjectMenuComponent], providers: [ { provide: TeacherService, useClass: MockTeacherService }, { provide: UserService, useClass: MockUserService }, { provide: ConfigService, useClass: MockConfigService } ], - schemas: [ NO_ERRORS_SCHEMA ] - }) - .compileComponents(); + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); })); beforeEach(() => { @@ -67,11 +65,11 @@ describe('LibraryProjectMenuComponent', () => { component = fixture.componentInstance; const project: Project = new Project(); project.id = 1; - project.name = "Photosynthesis"; + project.name = 'Photosynthesis'; const user = new User(); user.id = 123456; - user.username = "Spongebob Squarepants"; - user.displayName = "Spongebob Squarepants"; + user.username = 'Spongebob Squarepants'; + user.displayName = 'Spongebob Squarepants'; project.owner = user; component.project = project; fixture.detectChanges(); diff --git a/src/main/webapp/site/src/app/modules/library/library-project-menu/library-project-menu.component.ts b/src/main/webapp/site/src/app/modules/library/library-project-menu/library-project-menu.component.ts index 9202ac757b..d40702ee17 100644 --- a/src/main/webapp/site/src/app/modules/library/library-project-menu/library-project-menu.component.ts +++ b/src/main/webapp/site/src/app/modules/library/library-project-menu/library-project-menu.component.ts @@ -63,7 +63,7 @@ export class LibraryProjectMenuComponent { } editProject() { - this.teacherService.getProjectLastRun(this.project.id).subscribe(projectRun => { + this.teacherService.getProjectLastRun(this.project.id).subscribe((projectRun) => { if (projectRun != null) { projectRun.project = this.project; this.dialog.open(EditRunWarningDialogComponent, { diff --git a/src/main/webapp/site/src/app/modules/library/library-project/library-project.component.spec.ts b/src/main/webapp/site/src/app/modules/library/library-project/library-project.component.spec.ts index b224830dc0..cbdc75c4af 100644 --- a/src/main/webapp/site/src/app/modules/library/library-project/library-project.component.spec.ts +++ b/src/main/webapp/site/src/app/modules/library/library-project/library-project.component.spec.ts @@ -1,8 +1,8 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { LibraryProjectComponent } from './library-project.component'; -import { LibraryProject } from "../libraryProject"; -import { NO_ERRORS_SCHEMA } from "@angular/core"; -import { MatDialogModule } from "@angular/material/dialog"; +import { LibraryProject } from '../libraryProject'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { MatDialogModule } from '@angular/material/dialog'; import { Router } from '@angular/router'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { RouterTestingModule } from '@angular/router/testing'; @@ -14,11 +14,10 @@ describe('LibraryProjectComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ LibraryProjectComponent ], - imports: [ BrowserAnimationsModule, RouterTestingModule, OverlayModule, MatDialogModule ], - schemas: [ NO_ERRORS_SCHEMA ] - }) - .compileComponents(); + declarations: [LibraryProjectComponent], + imports: [BrowserAnimationsModule, RouterTestingModule, OverlayModule, MatDialogModule], + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); })); beforeEach(() => { diff --git a/src/main/webapp/site/src/app/modules/library/library-project/library-project.component.ts b/src/main/webapp/site/src/app/modules/library/library-project/library-project.component.ts index bf1fb279f1..765a932d9d 100644 --- a/src/main/webapp/site/src/app/modules/library/library-project/library-project.component.ts +++ b/src/main/webapp/site/src/app/modules/library/library-project/library-project.component.ts @@ -1,6 +1,6 @@ import { Component, Input, OnInit, ViewEncapsulation, ElementRef } from '@angular/core'; import { DomSanitizer } from '@angular/platform-browser'; -import { MatDialog} from '@angular/material/dialog'; +import { MatDialog } from '@angular/material/dialog'; import { LibraryProject } from '../libraryProject'; import { LibraryProjectDetailsComponent } from '../library-project-details/library-project-details.component'; import { flash } from '../../../animations'; @@ -10,20 +10,20 @@ import { flash } from '../../../animations'; templateUrl: './library-project.component.html', styleUrls: ['./library-project.component.scss'], encapsulation: ViewEncapsulation.None, - animations: [ flash ] + animations: [flash] }) export class LibraryProjectComponent implements OnInit { - @Input() project: LibraryProject = new LibraryProject(); animateDuration: string = '0s'; animateDelay: string = '0s'; - constructor(public dialog: MatDialog, - private sanitizer: DomSanitizer, - private elRef: ElementRef) { - } + constructor( + public dialog: MatDialog, + private sanitizer: DomSanitizer, + private elRef: ElementRef + ) {} ngOnInit() { this.project.thumbStyle = this.getThumbStyle(this.project.projectThumb); diff --git a/src/main/webapp/site/src/app/modules/library/library.module.ts b/src/main/webapp/site/src/app/modules/library/library.module.ts index 9860633b2c..c7911a5c46 100644 --- a/src/main/webapp/site/src/app/modules/library/library.module.ts +++ b/src/main/webapp/site/src/app/modules/library/library.module.ts @@ -4,12 +4,12 @@ import { FlexLayoutModule } from '@angular/flex-layout'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { LibraryGroupThumbsComponent } from './library-group-thumbs/library-group-thumbs.component'; import { LibraryProjectComponent } from './library-project/library-project.component'; -import { LibraryProjectDetailsComponent } from "./library-project-details/library-project-details.component"; +import { LibraryProjectDetailsComponent } from './library-project-details/library-project-details.component'; import { LibraryProjectDisciplinesComponent } from './library-project-disciplines/library-project-disciplines.component'; -import { LibraryProjectMenuComponent } from "./library-project-menu/library-project-menu.component"; -import { LibraryService } from "../../services/library.service"; +import { LibraryProjectMenuComponent } from './library-project-menu/library-project-menu.component'; +import { LibraryService } from '../../services/library.service'; import { RouterModule } from '@angular/router'; -import { SharedModule } from "../shared/shared.module"; +import { SharedModule } from '../shared/shared.module'; import { MatAutocompleteModule } from '@angular/material/autocomplete'; import { MatBadgeModule } from '@angular/material/badge'; import { MatButtonModule } from '@angular/material/button'; @@ -28,13 +28,22 @@ import { MatSelectModule } from '@angular/material/select'; import { MatTableModule } from '@angular/material/table'; import { MatTabsModule } from '@angular/material/tabs'; import { MatTooltipModule } from '@angular/material/tooltip'; -import { TimelineModule } from "../timeline/timeline.module"; +import { TimelineModule } from '../timeline/timeline.module'; import { LibraryFiltersComponent } from './library-filters/library-filters.component'; import { HomePageProjectLibraryComponent } from './home-page-project-library/home-page-project-library.component'; import { TeacherProjectLibraryComponent } from './teacher-project-library/teacher-project-library.component'; -import { OfficialLibraryComponent, OfficialLibraryDetailsComponent } from './official-library/official-library.component'; -import { CommunityLibraryComponent, CommunityLibraryDetailsComponent } from './community-library/community-library.component'; -import { PersonalLibraryComponent, PersonalLibraryDetailsComponent } from './personal-library/personal-library.component'; +import { + OfficialLibraryComponent, + OfficialLibraryDetailsComponent +} from './official-library/official-library.component'; +import { + CommunityLibraryComponent, + CommunityLibraryDetailsComponent +} from './community-library/community-library.component'; +import { + PersonalLibraryComponent, + PersonalLibraryDetailsComponent +} from './personal-library/personal-library.component'; import { ShareProjectDialogComponent } from './share-project-dialog/share-project-dialog.component'; import { CopyProjectDialogComponent } from './copy-project-dialog/copy-project-dialog.component'; import { LibraryPaginatorIntl } from './libraryPaginatorIntl'; @@ -95,9 +104,6 @@ const materialModules = [ TeacherProjectLibraryComponent, materialModules ], - providers: [ - LibraryService, - { provide: MatPaginatorIntl, useClass: LibraryPaginatorIntl } - ] + providers: [LibraryService, { provide: MatPaginatorIntl, useClass: LibraryPaginatorIntl }] }) -export class LibraryModule { } +export class LibraryModule {} diff --git a/src/main/webapp/site/src/app/modules/library/library/library.component.ts b/src/main/webapp/site/src/app/modules/library/library/library.component.ts index 98b542116f..e154a51711 100644 --- a/src/main/webapp/site/src/app/modules/library/library/library.component.ts +++ b/src/main/webapp/site/src/app/modules/library/library/library.component.ts @@ -7,7 +7,6 @@ import { PageEvent, MatPaginator } from '@angular/material/paginator'; @Directive() export abstract class LibraryComponent implements OnInit { - projects: LibraryProject[] = []; filteredProjects: LibraryProject[] = []; searchValue: string = ''; @@ -28,7 +27,7 @@ export abstract class LibraryComponent implements OnInit { @Output('update') update: EventEmitter = new EventEmitter(); - @ViewChildren(MatPaginator) paginators !: QueryList; + @ViewChildren(MatPaginator) paginators!: QueryList; constructor(protected libraryService: LibraryService) { libraryService.projectFilterValuesSource$.subscribe((projectFilterValues) => { @@ -36,10 +35,9 @@ export abstract class LibraryComponent implements OnInit { }); } - ngOnInit() { - } + ngOnInit() {} - pageChange(event?:PageEvent, scroll?:boolean): void { + pageChange(event?: PageEvent, scroll?: boolean): void { this.pageIndex = event.pageIndex; this.pageSize = event.pageSize; this.setPagination(); @@ -64,7 +62,7 @@ export abstract class LibraryComponent implements OnInit { } isOnPage(index: number): boolean { - return (index >= this.lowIndex && index < this.highIndex); + return index >= this.lowIndex && index < this.highIndex; } filterUpdated(filterValues: ProjectFilterValues = null): void { @@ -92,21 +90,30 @@ export abstract class LibraryComponent implements OnInit { this.setPagination(); } - emitNumberOfProjectsVisible(numProjectsVisible: number = null) { - } + emitNumberOfProjectsVisible(numProjectsVisible: number = null) {} hasFilters(): boolean { - return this.dciArrangementValue.length > 0 || this.peValue.length > 0 || this.disciplineValue.length > 0; + return ( + this.dciArrangementValue.length > 0 || + this.peValue.length > 0 || + this.disciplineValue.length > 0 + ); } isSearchMatch(project: LibraryProject, searchValue: string): boolean { if (searchValue) { let data: any = project.metadata; data.id = project.id; - return Object.keys(data).some(prop => { + return Object.keys(data).some((prop) => { // only check for match in specific metadata fields - if (prop != 'title' && prop != 'summary' && prop != 'keywords' && - prop != 'features' && prop != 'standardsAddressed' && prop != 'id') { + if ( + prop != 'title' && + prop != 'summary' && + prop != 'keywords' && + prop != 'features' && + prop != 'standardsAddressed' && + prop != 'id' + ) { return false; } else { let value = data[prop]; @@ -126,7 +133,7 @@ export abstract class LibraryComponent implements OnInit { } isFilterMatch(project: LibraryProject): boolean { - if (this.hasFilters()) { + if (this.hasFilters()) { const standardsAddressed = project.metadata.standardsAddressed; if (standardsAddressed.ngss) { const ngss = standardsAddressed.ngss; diff --git a/src/main/webapp/site/src/app/modules/library/libraryPaginatorIntl.ts b/src/main/webapp/site/src/app/modules/library/libraryPaginatorIntl.ts index 86b304c265..2d7d0a8f35 100644 --- a/src/main/webapp/site/src/app/modules/library/libraryPaginatorIntl.ts +++ b/src/main/webapp/site/src/app/modules/library/libraryPaginatorIntl.ts @@ -1,10 +1,10 @@ -import { MatPaginatorIntl } from "@angular/material/paginator"; -import { Injectable } from "@angular/core"; +import { MatPaginatorIntl } from '@angular/material/paginator'; +import { Injectable } from '@angular/core'; @Injectable() export class LibraryPaginatorIntl extends MatPaginatorIntl { itemsPerPageLabel = $localize`Show:`; - nextPageLabel = $localize`Next page`; + nextPageLabel = $localize`Next page`; previousPageLabel = $localize`Previous page`; getRangeLabel = function (page, pageSize, length) { @@ -12,7 +12,8 @@ export class LibraryPaginatorIntl extends MatPaginatorIntl { return $localize`0 of ${length}:total:`; } const startIndex = page * pageSize + 1; - const endIndex = startIndex < length ? Math.min(startIndex + pageSize - 1, length) : startIndex + pageSize - 1; + const endIndex = + startIndex < length ? Math.min(startIndex + pageSize - 1, length) : startIndex + pageSize - 1; return $localize`${startIndex}:start: - ${endIndex}:end: of ${length}:total:`; }; } diff --git a/src/main/webapp/site/src/app/modules/library/libraryProject.ts b/src/main/webapp/site/src/app/modules/library/libraryProject.ts index 5524410581..f2027d343e 100644 --- a/src/main/webapp/site/src/app/modules/library/libraryProject.ts +++ b/src/main/webapp/site/src/app/modules/library/libraryProject.ts @@ -1,4 +1,4 @@ -import { Project } from "../../domain/project"; +import { Project } from '../../domain/project'; export class LibraryProject extends Project { notes: string; diff --git a/src/main/webapp/site/src/app/modules/library/ngssStandards.ts b/src/main/webapp/site/src/app/modules/library/ngssStandards.ts index ba1c4061c0..4411492de6 100644 --- a/src/main/webapp/site/src/app/modules/library/ngssStandards.ts +++ b/src/main/webapp/site/src/app/modules/library/ngssStandards.ts @@ -1,4 +1,4 @@ -import { Standard } from "./standard"; +import { Standard } from './standard'; export class NGSSStandards { disciplines: Standard[] = []; diff --git a/src/main/webapp/site/src/app/modules/library/official-library/official-library.component.spec.ts b/src/main/webapp/site/src/app/modules/library/official-library/official-library.component.spec.ts index 26625d4e3f..3eae5e4d90 100644 --- a/src/main/webapp/site/src/app/modules/library/official-library/official-library.component.spec.ts +++ b/src/main/webapp/site/src/app/modules/library/official-library/official-library.component.spec.ts @@ -1,9 +1,9 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { OfficialLibraryComponent } from './official-library.component'; -import { fakeAsyncResponse } from "../../../student/student-run-list/student-run-list.component.spec"; -import { LibraryService } from "../../../services/library.service"; -import { NO_ERRORS_SCHEMA } from "@angular/core"; -import { LibraryGroup } from "../libraryGroup"; +import { fakeAsyncResponse } from '../../../student/student-run-list/student-run-list.component.spec'; +import { LibraryService } from '../../../services/library.service'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { LibraryGroup } from '../libraryGroup'; import { MatDialog, MatDialogModule } from '@angular/material/dialog'; import { BehaviorSubject } from 'rxjs'; import { OverlayModule } from '@angular/cdk/overlay'; @@ -12,7 +12,7 @@ export class MockLibraryService { libraryGroupsSource$ = fakeAsyncResponse({}); officialLibraryProjectsSource$ = fakeAsyncResponse([]); projectFilterValuesSource$ = fakeAsyncResponse({ - searchValue: "", + searchValue: '', disciplineValue: [], dciArrangementValue: [], peValue: [] @@ -27,14 +27,11 @@ describe('OfficialLibraryComponent', () => { let fixture: ComponentFixture; beforeEach(async(() => { TestBed.configureTestingModule({ - imports: [ OverlayModule, MatDialogModule ], - declarations: [ OfficialLibraryComponent ], - providers: [ - { provide: LibraryService, useClass: MockLibraryService } - ], - schemas: [ NO_ERRORS_SCHEMA ] - }) - .compileComponents(); + imports: [OverlayModule, MatDialogModule], + declarations: [OfficialLibraryComponent], + providers: [{ provide: LibraryService, useClass: MockLibraryService }], + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); })); beforeEach(() => { diff --git a/src/main/webapp/site/src/app/modules/library/official-library/official-library.component.ts b/src/main/webapp/site/src/app/modules/library/official-library/official-library.component.ts index b406c9fbc6..93986ef578 100644 --- a/src/main/webapp/site/src/app/modules/library/official-library/official-library.component.ts +++ b/src/main/webapp/site/src/app/modules/library/official-library/official-library.component.ts @@ -30,8 +30,7 @@ export class OfficialLibraryComponent extends LibraryComponent { }); } - ngOnInit() { - } + ngOnInit() {} emitNumberOfProjectsVisible(numProjectsVisible: number = null) { if (numProjectsVisible) { @@ -50,12 +49,13 @@ export class OfficialLibraryComponent extends LibraryComponent { @Component({ selector: 'official-library-details', - templateUrl: 'official-library-details.html', + templateUrl: 'official-library-details.html' }) export class OfficialLibraryDetailsComponent { constructor( public dialogRef: MatDialogRef, - @Inject(MAT_DIALOG_DATA) public data: any) {} + @Inject(MAT_DIALOG_DATA) public data: any + ) {} close(): void { this.dialogRef.close(); diff --git a/src/main/webapp/site/src/app/modules/library/personal-library/personal-library.component.spec.ts b/src/main/webapp/site/src/app/modules/library/personal-library/personal-library.component.spec.ts index bd22f9f7bd..e98e6872c3 100644 --- a/src/main/webapp/site/src/app/modules/library/personal-library/personal-library.component.spec.ts +++ b/src/main/webapp/site/src/app/modules/library/personal-library/personal-library.component.spec.ts @@ -1,8 +1,8 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { PersonalLibraryComponent } from './personal-library.component'; -import { fakeAsyncResponse } from "../../../student/student-run-list/student-run-list.component.spec"; -import { LibraryService } from "../../../services/library.service"; -import { NO_ERRORS_SCHEMA } from "@angular/core"; +import { fakeAsyncResponse } from '../../../student/student-run-list/student-run-list.component.spec'; +import { LibraryService } from '../../../services/library.service'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; import { MatDialogModule } from '@angular/material/dialog'; import { BehaviorSubject } from 'rxjs'; import { OverlayModule } from '@angular/cdk/overlay'; @@ -12,7 +12,7 @@ export class MockLibraryService { personalLibraryProjectsSource$ = fakeAsyncResponse([]); sharedLibraryProjectsSource$ = fakeAsyncResponse([]); projectFilterValuesSource$ = fakeAsyncResponse({ - searchValue: "", + searchValue: '', disciplineValue: [], dciArrangementValue: [], peValue: [] @@ -27,16 +27,11 @@ describe('PersonalLibraryComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - imports: [ OverlayModule, MatDialogModule ], - declarations: [ - PersonalLibraryComponent - ], - providers: [ - { provide: LibraryService, useClass: MockLibraryService }, - ], - schemas: [ NO_ERRORS_SCHEMA ] - }) - .compileComponents(); + imports: [OverlayModule, MatDialogModule], + declarations: [PersonalLibraryComponent], + providers: [{ provide: LibraryService, useClass: MockLibraryService }], + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); })); beforeEach(() => { diff --git a/src/main/webapp/site/src/app/modules/library/personal-library/personal-library.component.ts b/src/main/webapp/site/src/app/modules/library/personal-library/personal-library.component.ts index 110582080f..6af14bb8da 100644 --- a/src/main/webapp/site/src/app/modules/library/personal-library/personal-library.component.ts +++ b/src/main/webapp/site/src/app/modules/library/personal-library/personal-library.component.ts @@ -1,7 +1,7 @@ import { Component, Inject } from '@angular/core'; -import { LibraryProject } from "../libraryProject"; -import { LibraryService } from "../../../services/library.service"; -import { LibraryComponent } from "../library/library.component"; +import { LibraryProject } from '../libraryProject'; +import { LibraryService } from '../../../services/library.service'; +import { LibraryComponent } from '../library/library.component'; import { MatDialog, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; import { Subscription } from 'rxjs'; @@ -11,7 +11,6 @@ import { Subscription } from 'rxjs'; styleUrls: ['./personal-library.component.scss'] }) export class PersonalLibraryComponent extends LibraryComponent { - projects: LibraryProject[] = []; filteredProjects: LibraryProject[] = []; personalProjects: LibraryProject[] = []; @@ -26,23 +25,27 @@ export class PersonalLibraryComponent extends LibraryComponent { } ngOnInit() { - this.personalLibraryProjectsSourceSubscription = - this.libraryService.personalLibraryProjectsSource$.subscribe((personalProjects: LibraryProject[]) => { - this.personalProjects = personalProjects; - this.updateProjects(); - }); - this.sharedLibraryProjectsSourceSubscription = - this.libraryService.sharedLibraryProjectsSource$.subscribe((sharedProjects: LibraryProject[]) => { - this.sharedProjects = sharedProjects; - this.updateProjects(); - }); - this.newProjectSourceSubscription = this.libraryService.newProjectSource$.subscribe(project => { - if (project) { - project.isHighlighted = true; - this.projects.unshift(project); - this.filterUpdated(); + this.personalLibraryProjectsSourceSubscription = this.libraryService.personalLibraryProjectsSource$.subscribe( + (personalProjects: LibraryProject[]) => { + this.personalProjects = personalProjects; + this.updateProjects(); } - }); + ); + this.sharedLibraryProjectsSourceSubscription = this.libraryService.sharedLibraryProjectsSource$.subscribe( + (sharedProjects: LibraryProject[]) => { + this.sharedProjects = sharedProjects; + this.updateProjects(); + } + ); + this.newProjectSourceSubscription = this.libraryService.newProjectSource$.subscribe( + (project) => { + if (project) { + project.isHighlighted = true; + this.projects.unshift(project); + this.filterUpdated(); + } + } + ); } ngOnDestroy() { @@ -89,12 +92,13 @@ export class PersonalLibraryComponent extends LibraryComponent { @Component({ selector: 'personal-library-details', - templateUrl: 'personal-library-details.html', + templateUrl: 'personal-library-details.html' }) export class PersonalLibraryDetailsComponent { constructor( public dialogRef: MatDialogRef, - @Inject(MAT_DIALOG_DATA) public data: any) {} + @Inject(MAT_DIALOG_DATA) public data: any + ) {} close(): void { this.dialogRef.close(); diff --git a/src/main/webapp/site/src/app/modules/library/sampleLibraryProjects.ts b/src/main/webapp/site/src/app/modules/library/sampleLibraryProjects.ts index cf56167a10..f32836f084 100644 --- a/src/main/webapp/site/src/app/modules/library/sampleLibraryProjects.ts +++ b/src/main/webapp/site/src/app/modules/library/sampleLibraryProjects.ts @@ -1,50 +1,52 @@ -import { LibraryProject } from "./libraryProject"; +import { LibraryProject } from './libraryProject'; const projects: LibraryProject[] = []; const project1 = new LibraryProject(); project1.id = 1; project1.name = 'Photosynthesis'; project1.metadata = { - "standardsAddressed": { - "ngss": { - "disciplines": [ + standardsAddressed: { + ngss: { + disciplines: [ { - "id": "LS", - "name": "Life Sciences" + id: 'LS', + name: 'Life Sciences' } ], - "dci": [ + dci: [ { - "id": "LS1.B", - "name": "Growth and Development of Organisms" + id: 'LS1.B', + name: 'Growth and Development of Organisms' } ], - "dciArrangements": [ + dciArrangements: [ { - "id": "MS-LS1", - "name": "From Molecules to Organisms: Structures and Processes", - "children": [ + id: 'MS-LS1', + name: 'From Molecules to Organisms: Structures and Processes', + children: [ { - "id": "MS-LS1-4", - "name": "Use argument based on empirical evidence and scientific reasoning to support an explanation for how characteristic animal behaviors and specialized plant structures affect the probability of successful reproduction of animals and plants respectively." + id: 'MS-LS1-4', + name: + 'Use argument based on empirical evidence and scientific reasoning to support an explanation for how characteristic animal behaviors and specialized plant structures affect the probability of successful reproduction of animals and plants respectively.' }, { - "id": "MS-LS1-5", - "name": "Construct a scientific explanation based on evidence for how environmental and genetic factors influence the growth of organisms." + id: 'MS-LS1-5', + name: + 'Construct a scientific explanation based on evidence for how environmental and genetic factors influence the growth of organisms.' } ] } ], - "ccc": [ + ccc: [ { - "id": "ce", - "name": "Cause and Effect" + id: 'ce', + name: 'Cause and Effect' } ], - "practices": [ + practices: [ { - "id": "eae", - "name": "Engaging in Argument from Evidence" + id: 'eae', + name: 'Engaging in Argument from Evidence' } ] } @@ -56,46 +58,48 @@ const project2 = new LibraryProject(); project2.id = 2; project2.name = 'Global Climate Change'; project2.metadata = { - "standardsAddressed": { - "ngss": { - "disciplines": [ + standardsAddressed: { + ngss: { + disciplines: [ { - "id": "PS", - "name": "Physical Sciences" + id: 'PS', + name: 'Physical Sciences' } ], - "dci": [ + dci: [ { - "id": "LS1.B", - "name": "Growth and Development of Organisms" + id: 'LS1.B', + name: 'Growth and Development of Organisms' } ], - "dciArrangements": [ + dciArrangements: [ { - "id": "MS-LS1", - "name": "From Molecules to Organisms: Structures and Processes", - "children": [ + id: 'MS-LS1', + name: 'From Molecules to Organisms: Structures and Processes', + children: [ { - "id": "MS-LS1-4", - "name": "Use argument based on empirical evidence and scientific reasoning to support an explanation for how characteristic animal behaviors and specialized plant structures affect the probability of successful reproduction of animals and plants respectively." + id: 'MS-LS1-4', + name: + 'Use argument based on empirical evidence and scientific reasoning to support an explanation for how characteristic animal behaviors and specialized plant structures affect the probability of successful reproduction of animals and plants respectively.' }, { - "id": "MS-LS1-6", - "name": "Construct a scientific explanation based on evidence for the role of photosynthesis in the cycling of matter and flow of energy into and out of organisms." + id: 'MS-LS1-6', + name: + 'Construct a scientific explanation based on evidence for the role of photosynthesis in the cycling of matter and flow of energy into and out of organisms.' } ] } ], - "ccc": [ + ccc: [ { - "id": "ce", - "name": "Cause and Effect" + id: 'ce', + name: 'Cause and Effect' } ], - "practices": [ + practices: [ { - "id": "ceds", - "name": "Constructing Explanations and Designing Solutions" + id: 'ceds', + name: 'Constructing Explanations and Designing Solutions' } ] } diff --git a/src/main/webapp/site/src/app/modules/library/select-menu-test.helper.ts b/src/main/webapp/site/src/app/modules/library/select-menu-test.helper.ts index 927cda6745..d407ce28d3 100644 --- a/src/main/webapp/site/src/app/modules/library/select-menu-test.helper.ts +++ b/src/main/webapp/site/src/app/modules/library/select-menu-test.helper.ts @@ -22,7 +22,9 @@ export class SelectMenuTestHelper { } public getOptions(): HTMLElement[] { - return Array.from(this._containerElement.querySelectorAll('mat-option') as NodeListOf); + return Array.from( + this._containerElement.querySelectorAll('mat-option') as NodeListOf + ); } public selectOption(option: HTMLElement) { @@ -44,5 +46,4 @@ export class SelectMenuTestHelper { public cleanup() { this._container.ngOnDestroy(); } - } diff --git a/src/main/webapp/site/src/app/modules/library/share-item-dialog/share-item-dialog.component.ts b/src/main/webapp/site/src/app/modules/library/share-item-dialog/share-item-dialog.component.ts index d02e8a259c..93ba999a87 100644 --- a/src/main/webapp/site/src/app/modules/library/share-item-dialog/share-item-dialog.component.ts +++ b/src/main/webapp/site/src/app/modules/library/share-item-dialog/share-item-dialog.component.ts @@ -1,15 +1,14 @@ import { OnInit, Inject, Directive } from '@angular/core'; import { FormControl } from '@angular/forms'; import { BehaviorSubject, Observable } from 'rxjs'; -import { TeacherService } from "../../../teacher/teacher.service"; -import { MAT_DIALOG_DATA, MatDialogRef } from "@angular/material/dialog"; +import { TeacherService } from '../../../teacher/teacher.service'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { MatSnackBar } from '@angular/material/snack-bar'; import { map, debounceTime } from 'rxjs/operators'; -import { Project } from "../../../domain/project"; +import { Project } from '../../../domain/project'; @Directive() export abstract class ShareItemDialogComponent implements OnInit { - project: Project; projectId: number; runId: number; @@ -20,19 +19,21 @@ export abstract class ShareItemDialogComponent implements OnInit { sharedOwners: any[] = []; private sharedOwners$: BehaviorSubject = new BehaviorSubject(this.sharedOwners); - constructor(public dialogRef: MatDialogRef, - @Inject(MAT_DIALOG_DATA) public data: any, - public teacherService: TeacherService, - public snackBar: MatSnackBar) { + constructor( + public dialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) public data: any, + public teacherService: TeacherService, + public snackBar: MatSnackBar + ) { this.teacherService.retrieveAllTeacherUsernames().subscribe((teacherUsernames) => { this.allTeacherUsernames = teacherUsernames; - }) + }); } ngOnInit() { this.filteredTeacherUsernames = this.teacherSearchControl.valueChanges.pipe( debounceTime(1000), - map(value => this._filter(value)) + map((value) => this._filter(value)) ); } @@ -45,7 +46,7 @@ export abstract class ShareItemDialogComponent implements OnInit { if (filterValue == '') { return []; } - return this.allTeacherUsernames.filter(option => option.toLowerCase().includes(filterValue)); + return this.allTeacherUsernames.filter((option) => option.toLowerCase().includes(filterValue)); } populateSharedOwners(sharedOwners) { @@ -89,19 +90,21 @@ export abstract class ShareItemDialogComponent implements OnInit { projectPermissionChanged(project, sharedOwnerId, permissionId, isAddingPermission) { if (isAddingPermission) { - this.teacherService.addSharedOwnerProjectPermission(project.id, sharedOwnerId, permissionId) + this.teacherService + .addSharedOwnerProjectPermission(project.id, sharedOwnerId, permissionId) .subscribe((response: any) => { - if (response.status == "success") { + if (response.status == 'success') { this.addProjectPermissionToSharedOwner(sharedOwnerId, permissionId); } - }) + }); } else { - this.teacherService.removeSharedOwnerProjectPermission(project.id, sharedOwnerId, permissionId) + this.teacherService + .removeSharedOwnerProjectPermission(project.id, sharedOwnerId, permissionId) .subscribe((response: any) => { - if (response.status == "success") { + if (response.status == 'success') { this.removeProjectPermissionFromSharedOwner(sharedOwnerId, permissionId); } - }) + }); } } @@ -119,7 +122,8 @@ export abstract class ShareItemDialogComponent implements OnInit { notifyRunPermissionChange(sharedOwner) { this.snackBar.open( - $localize`Sharing permissions updated for ${sharedOwner.firstName}:firstName: ${sharedOwner.lastName}:lastName:.`); + $localize`Sharing permissions updated for ${sharedOwner.firstName}:firstName: ${sharedOwner.lastName}:lastName:.` + ); } isSharedOwner(username) { @@ -135,7 +139,8 @@ export abstract class ShareItemDialogComponent implements OnInit { this.sharedOwners.push(sharedOwner); this.sharedOwners$.next(this.sharedOwners); this.snackBar.open( - $localize`Added shared teacher: ${sharedOwner.firstName}:firstName: ${sharedOwner.lastName}:lastName:.`); + $localize`Added shared teacher: ${sharedOwner.firstName}:firstName: ${sharedOwner.lastName}:lastName:.` + ); } removeSharedOwner(sharedOwner) { @@ -144,7 +149,8 @@ export abstract class ShareItemDialogComponent implements OnInit { this.sharedOwners.splice(i, 1); this.sharedOwners$.next(this.sharedOwners); this.snackBar.open( - $localize`Removed shared teacher: ${sharedOwner.firstName}:firstName: ${sharedOwner.lastName}:lastName:.`); + $localize`Removed shared teacher: ${sharedOwner.firstName}:firstName: ${sharedOwner.lastName}:lastName:.` + ); return; } } diff --git a/src/main/webapp/site/src/app/modules/library/share-project-dialog/share-project-dialog.component.spec.ts b/src/main/webapp/site/src/app/modules/library/share-project-dialog/share-project-dialog.component.spec.ts index 12a31bdc60..2b9d6998ef 100644 --- a/src/main/webapp/site/src/app/modules/library/share-project-dialog/share-project-dialog.component.spec.ts +++ b/src/main/webapp/site/src/app/modules/library/share-project-dialog/share-project-dialog.component.spec.ts @@ -1,23 +1,23 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { ShareProjectDialogComponent } from './share-project-dialog.component'; -import { TeacherService } from "../../../teacher/teacher.service"; +import { TeacherService } from '../../../teacher/teacher.service'; import { Observable } from 'rxjs'; import { MatAutocompleteModule } from '@angular/material/autocomplete'; import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; import { MatSnackBarModule } from '@angular/material/snack-bar'; import { MatTableModule } from '@angular/material/table'; -import { LibraryService } from "../../../services/library.service"; -import { NO_ERRORS_SCHEMA } from "@angular/core"; -import { Project } from "../../../domain/project"; -import { User } from "../../../domain/user"; +import { LibraryService } from '../../../services/library.service'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { Project } from '../../../domain/project'; +import { User } from '../../../domain/user'; export class MockLibraryService { getProjectInfo() { - return Observable.create(observer => { + return Observable.create((observer) => { const project = new Project(); project.id = 1; - project.name = "Test"; + project.name = 'Test'; project.owner = new User(); project.owner.id = 1; project.sharedOwners = []; @@ -29,7 +29,7 @@ export class MockLibraryService { export class MockTeacherService { retrieveAllTeacherUsernames() { - return Observable.create(observer => { + return Observable.create((observer) => { observer.next([]); observer.complete(); }); @@ -39,10 +39,10 @@ export class MockTeacherService { describe('ShareProjectDialogComponent', () => { const projectObj = { id: 1, - name: "Test", + name: 'Test', owner: { id: 123456, - displayName: "Spongebob Squarepants" + displayName: 'Spongebob Squarepants' }, sharedOwners: [] }; @@ -52,25 +52,21 @@ describe('ShareProjectDialogComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ ShareProjectDialogComponent ], - imports: [ - BrowserAnimationsModule, - MatAutocompleteModule, - MatSnackBarModule, - MatTableModule - ], + declarations: [ShareProjectDialogComponent], + imports: [BrowserAnimationsModule, MatAutocompleteModule, MatSnackBarModule, MatTableModule], providers: [ { provide: TeacherService, useClass: MockTeacherService }, { provide: LibraryService, useClass: MockLibraryService }, { provide: MatDialogRef, useValue: {} }, - { provide: MAT_DIALOG_DATA, useValue: { + { + provide: MAT_DIALOG_DATA, + useValue: { project: projectObj } } ], - schemas: [ NO_ERRORS_SCHEMA ] - }) - .compileComponents(); + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); })); beforeEach(() => { diff --git a/src/main/webapp/site/src/app/modules/library/share-project-dialog/share-project-dialog.component.ts b/src/main/webapp/site/src/app/modules/library/share-project-dialog/share-project-dialog.component.ts index 0d1b09c300..805cc43526 100644 --- a/src/main/webapp/site/src/app/modules/library/share-project-dialog/share-project-dialog.component.ts +++ b/src/main/webapp/site/src/app/modules/library/share-project-dialog/share-project-dialog.component.ts @@ -1,11 +1,11 @@ import { Component, Inject } from '@angular/core'; -import { MAT_DIALOG_DATA, MatDialogRef } from "@angular/material/dialog"; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { MatSnackBar } from '@angular/material/snack-bar'; -import { MatTableDataSource } from '@angular/material/table' -import { TeacherService } from "../../../teacher/teacher.service"; -import { LibraryService } from "../../../services/library.service"; -import { ShareItemDialogComponent } from "../share-item-dialog/share-item-dialog.component"; -import { Project } from "../../../domain/project"; +import { MatTableDataSource } from '@angular/material/table'; +import { TeacherService } from '../../../teacher/teacher.service'; +import { LibraryService } from '../../../services/library.service'; +import { ShareItemDialogComponent } from '../share-item-dialog/share-item-dialog.component'; +import { Project } from '../../../domain/project'; @Component({ selector: 'app-share-project-dialog', @@ -13,16 +13,17 @@ import { Project } from "../../../domain/project"; styleUrls: ['./share-project-dialog.component.scss'] }) export class ShareProjectDialogComponent extends ShareItemDialogComponent { - dataSource: MatTableDataSource = new MatTableDataSource(); displayedColumns: string[] = ['name', 'permissions']; duplicate: boolean = false; - constructor(public dialogRef: MatDialogRef, - @Inject(MAT_DIALOG_DATA) public data: any, - public libraryService: LibraryService, - public teacherService: TeacherService, - public snackBar: MatSnackBar) { + constructor( + public dialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) public data: any, + public libraryService: LibraryService, + public teacherService: TeacherService, + public snackBar: MatSnackBar + ) { super(dialogRef, data, teacherService, snackBar); this.project = data.project; this.projectId = data.project.id; @@ -34,7 +35,7 @@ export class ShareProjectDialogComponent extends ShareItemDialogComponent { ngOnInit() { super.ngOnInit(); - this.getSharedOwners().subscribe(sharedOwners => { + this.getSharedOwners().subscribe((sharedOwners) => { let owners = [...sharedOwners]; owners.reverse(); if (this.project.owner) { @@ -53,18 +54,21 @@ export class ShareProjectDialogComponent extends ShareItemDialogComponent { setDefaultProjectPermissions(sharedOwner) { sharedOwner.projectPermissions = { - 1: true, // View the project - 2: false, // Edit the project - 16: false // Admin (read, write, share) + 1: true, // View the project + 2: false, // Edit the project + 16: false // Admin (read, write, share) }; } shareProject() { this.duplicate = false; const sharedOwnerUsername = this.teacherSearchControl.value; - if (this.project.owner.username !== sharedOwnerUsername && - !this.isSharedOwner(sharedOwnerUsername)) { - this.teacherService.addSharedProjectOwner(this.project.id, sharedOwnerUsername) + if ( + this.project.owner.username !== sharedOwnerUsername && + !this.isSharedOwner(sharedOwnerUsername) + ) { + this.teacherService + .addSharedProjectOwner(this.project.id, sharedOwnerUsername) .subscribe((newSharedOwner) => { if (newSharedOwner != null) { this.setDefaultProjectPermissions(newSharedOwner); @@ -75,11 +79,12 @@ export class ShareProjectDialogComponent extends ShareItemDialogComponent { } else { this.duplicate = true; } - document.getElementById("share-project-dialog-search").blur(); + document.getElementById('share-project-dialog-search').blur(); } unshareProject(sharedOwner) { - this.teacherService.removeSharedProjectOwner(this.project.id, sharedOwner.username) + this.teacherService + .removeSharedProjectOwner(this.project.id, sharedOwner.username) .subscribe((response) => { this.removeSharedOwner(sharedOwner); }); diff --git a/src/main/webapp/site/src/app/modules/library/teacher-project-library/teacher-project-library.component.spec.ts b/src/main/webapp/site/src/app/modules/library/teacher-project-library/teacher-project-library.component.spec.ts index 564e90e649..27d80cb1c9 100644 --- a/src/main/webapp/site/src/app/modules/library/teacher-project-library/teacher-project-library.component.spec.ts +++ b/src/main/webapp/site/src/app/modules/library/teacher-project-library/teacher-project-library.component.spec.ts @@ -1,6 +1,6 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { TeacherProjectLibraryComponent } from './teacher-project-library.component'; -import { MatDialogModule } from "@angular/material/dialog"; +import { MatDialogModule } from '@angular/material/dialog'; import { MatMenuModule } from '@angular/material/menu'; import { NO_ERRORS_SCHEMA } from '@angular/core'; import { RouterTestingModule } from '@angular/router/testing'; @@ -12,14 +12,14 @@ export function fakeAsyncResponse(data: T) { } export class MockLibraryService { - numberOfOfficialProjectsVisible$ = fakeAsyncResponse(0) + numberOfOfficialProjectsVisible$ = fakeAsyncResponse(0); numberOfCommunityProjectsVisible$ = fakeAsyncResponse(0); numberOfPersonalProjectsVisible$ = fakeAsyncResponse(0); newProjectSource$ = fakeAsyncResponse(0); - getOfficialLibraryProjects(){} - getCommunityLibraryProjects(){} - getPersonalLibraryProjects(){} - getSharedLibraryProjects(){} + getOfficialLibraryProjects() {} + getCommunityLibraryProjects() {} + getPersonalLibraryProjects() {} + getSharedLibraryProjects() {} } describe('TeacherProjectLibraryComponent', () => { @@ -28,14 +28,11 @@ describe('TeacherProjectLibraryComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - imports: [ MatMenuModule, RouterTestingModule, MatDialogModule ], - declarations: [ TeacherProjectLibraryComponent ], - providers: [ - { provide: LibraryService, useClass: MockLibraryService } - ], - schemas: [ NO_ERRORS_SCHEMA ] - }) - .compileComponents(); + imports: [MatMenuModule, RouterTestingModule, MatDialogModule], + declarations: [TeacherProjectLibraryComponent], + providers: [{ provide: LibraryService, useClass: MockLibraryService }], + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); })); beforeEach(() => { diff --git a/src/main/webapp/site/src/app/modules/library/teacher-project-library/teacher-project-library.component.ts b/src/main/webapp/site/src/app/modules/library/teacher-project-library/teacher-project-library.component.ts index 230ed31343..f5f47f1739 100644 --- a/src/main/webapp/site/src/app/modules/library/teacher-project-library/teacher-project-library.component.ts +++ b/src/main/webapp/site/src/app/modules/library/teacher-project-library/teacher-project-library.component.ts @@ -1,6 +1,6 @@ import { Component, ViewEncapsulation, OnInit } from '@angular/core'; -import { LibraryProject } from "../libraryProject"; -import { LibraryService } from "../../../services/library.service"; +import { LibraryProject } from '../libraryProject'; +import { LibraryService } from '../../../services/library.service'; import { MatDialog } from '@angular/material/dialog'; import { OfficialLibraryDetailsComponent } from '../official-library/official-library.component'; import { Router } from '@angular/router'; @@ -8,14 +8,10 @@ import { Router } from '@angular/router'; @Component({ selector: 'app-teacher-project-library', templateUrl: './teacher-project-library.component.html', - styleUrls: [ - './teacher-project-library.component.scss', - '../library/library.component.scss' - ], + styleUrls: ['./teacher-project-library.component.scss', '../library/library.component.scss'], encapsulation: ViewEncapsulation.None }) export class TeacherProjectLibraryComponent implements OnInit { - projects: LibraryProject[] = []; numberOfOfficialProjectsVisible: number = 0; numberOfCommunityProjectsVisible: number = 0; @@ -27,9 +23,7 @@ export class TeacherProjectLibraryComponent implements OnInit { { path: 'library/personal', label: $localize`My Units`, numVisible: 0 } ]; - constructor(libraryService: LibraryService, - public dialog: MatDialog, - private router: Router) { + constructor(libraryService: LibraryService, public dialog: MatDialog, private router: Router) { libraryService.numberOfOfficialProjectsVisible$.subscribe((num) => { this.tabs[0].numVisible = num; }); @@ -46,15 +40,14 @@ export class TeacherProjectLibraryComponent implements OnInit { libraryService.getSharedLibraryProjects(); libraryService.hasLoaded = true; } - libraryService.newProjectSource$.subscribe(project => { + libraryService.newProjectSource$.subscribe((project) => { if (project) { document.querySelector('.library').scrollIntoView(); } }); } - ngOnInit() { - } + ngOnInit() {} isOfficialRoute(): boolean { return this.router.url === '/teacher/home/library/tested'; diff --git a/src/main/webapp/site/src/app/modules/mobile-menu/mobile-menu.component.spec.ts b/src/main/webapp/site/src/app/modules/mobile-menu/mobile-menu.component.spec.ts index bf0605ffb0..b59bcaba19 100644 --- a/src/main/webapp/site/src/app/modules/mobile-menu/mobile-menu.component.spec.ts +++ b/src/main/webapp/site/src/app/modules/mobile-menu/mobile-menu.component.spec.ts @@ -1,33 +1,31 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { RouterTestingModule } from "@angular/router/testing"; +import { RouterTestingModule } from '@angular/router/testing'; import { Observable } from 'rxjs'; import { NO_ERRORS_SCHEMA, Provider } from '@angular/core'; import { By } from '@angular/platform-browser'; import { MobileMenuComponent } from './mobile-menu.component'; -import { User } from "../../domain/user"; -import { UserService } from "../../services/user.service"; +import { User } from '../../domain/user'; +import { UserService } from '../../services/user.service'; import { configureTestSuite } from 'ng-bullet'; export class MockUserServiceLogIn { getUser(): Observable { const user: User = new User(); user.role = 'teacher'; - return Observable.create(observer => { - observer.next(user); - observer.complete(); - } - ); + return Observable.create((observer) => { + observer.next(user); + observer.complete(); + }); } } export class MockUserServiceLogOut { getUser(): Observable { const user: User = null; - return Observable.create(observer => { - observer.next(user); - observer.complete(); - } - ); + return Observable.create((observer) => { + observer.next(user); + observer.complete(); + }); } } @@ -37,14 +35,11 @@ describe('MobileMenuComponent', () => { function createComponent(providers: Provider[] = []) { TestBed.configureTestingModule({ - imports: [ RouterTestingModule ], - declarations: [ MobileMenuComponent ], - providers: [ - { provide: UserService, useValue: new MockUserServiceLogIn() } - ], - schemas: [ NO_ERRORS_SCHEMA ] - }) - .compileComponents(); + imports: [RouterTestingModule], + declarations: [MobileMenuComponent], + providers: [{ provide: UserService, useValue: new MockUserServiceLogIn() }], + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); fixture = TestBed.createComponent(MobileMenuComponent); component = fixture.componentInstance; diff --git a/src/main/webapp/site/src/app/modules/mobile-menu/mobile-menu.component.ts b/src/main/webapp/site/src/app/modules/mobile-menu/mobile-menu.component.ts index 7e859b3f2b..9784b67104 100644 --- a/src/main/webapp/site/src/app/modules/mobile-menu/mobile-menu.component.ts +++ b/src/main/webapp/site/src/app/modules/mobile-menu/mobile-menu.component.ts @@ -1,7 +1,7 @@ import { Component, OnInit } from '@angular/core'; -import { UserService } from "../../services/user.service"; -import { User } from "../../domain/user"; -import { UtilService } from "../../services/util.service"; +import { UserService } from '../../services/user.service'; +import { User } from '../../domain/user'; +import { UtilService } from '../../services/util.service'; @Component({ selector: 'app-mobile-menu', @@ -9,11 +9,9 @@ import { UtilService } from "../../services/util.service"; styleUrls: ['./mobile-menu.component.scss'] }) export class MobileMenuComponent implements OnInit { - signedIn: boolean = false; - constructor(private userService: UserService, - private utilService: UtilService) { + constructor(private userService: UserService, private utilService: UtilService) { this.userService = userService; this.utilService = utilService; } @@ -27,13 +25,12 @@ export class MobileMenuComponent implements OnInit { } getUser() { - this.userService.getUser() - .subscribe(user => { - if (user && user.role) { - this.signedIn = true; - } else { - this.signedIn = false; - } - }); + this.userService.getUser().subscribe((user) => { + if (user && user.role) { + this.signedIn = true; + } else { + this.signedIn = false; + } + }); } } diff --git a/src/main/webapp/site/src/app/modules/mobile-menu/mobile-menu.module.ts b/src/main/webapp/site/src/app/modules/mobile-menu/mobile-menu.module.ts index 7e0fdc1cb4..78c24b128d 100644 --- a/src/main/webapp/site/src/app/modules/mobile-menu/mobile-menu.module.ts +++ b/src/main/webapp/site/src/app/modules/mobile-menu/mobile-menu.module.ts @@ -5,7 +5,7 @@ import { MatButtonModule } from '@angular/material/button'; import { MatDividerModule } from '@angular/material/divider'; import { MatIconModule } from '@angular/material/icon'; -import { AppRoutingModule } from "../../app-routing.module"; +import { AppRoutingModule } from '../../app-routing.module'; import { MobileMenuComponent } from './mobile-menu.component'; @NgModule({ @@ -18,8 +18,6 @@ import { MobileMenuComponent } from './mobile-menu.component'; MatIconModule ], declarations: [MobileMenuComponent], - exports: [ - MobileMenuComponent - ] + exports: [MobileMenuComponent] }) -export class MobileMenuModule { } +export class MobileMenuModule {} diff --git a/src/main/webapp/site/src/app/modules/shared/blurb/blurb.component.spec.ts b/src/main/webapp/site/src/app/modules/shared/blurb/blurb.component.spec.ts index e26d4a2857..1471c0ade4 100644 --- a/src/main/webapp/site/src/app/modules/shared/blurb/blurb.component.spec.ts +++ b/src/main/webapp/site/src/app/modules/shared/blurb/blurb.component.spec.ts @@ -1,6 +1,6 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { BlurbComponent } from './blurb.component'; -import { NO_ERRORS_SCHEMA } from "@angular/core"; +import { NO_ERRORS_SCHEMA } from '@angular/core'; describe('BlurbComponent', () => { let component: BlurbComponent; @@ -8,11 +8,10 @@ describe('BlurbComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ BlurbComponent ], + declarations: [BlurbComponent], imports: [], - schemas: [ NO_ERRORS_SCHEMA ] - }) - .compileComponents(); + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); })); beforeEach(() => { diff --git a/src/main/webapp/site/src/app/modules/shared/blurb/blurb.component.ts b/src/main/webapp/site/src/app/modules/shared/blurb/blurb.component.ts index 3f7f5234b0..3af8a3ab5b 100644 --- a/src/main/webapp/site/src/app/modules/shared/blurb/blurb.component.ts +++ b/src/main/webapp/site/src/app/modules/shared/blurb/blurb.component.ts @@ -6,7 +6,6 @@ import { Component, ContentChild, Input, OnInit, TemplateRef } from '@angular/co styleUrls: ['./blurb.component.scss'] }) export class BlurbComponent implements OnInit { - @Input() imgSrc: string; @@ -19,16 +18,14 @@ export class BlurbComponent implements OnInit { @Input() headline: string; - @ContentChild('headlineTemplate', {static: false}) headlineRef: TemplateRef; + @ContentChild('headlineTemplate', { static: false }) headlineRef: TemplateRef; @Input() content: string; - @ContentChild('contentTemplate', {static: false}) contentRef: TemplateRef; - - constructor() { } + @ContentChild('contentTemplate', { static: false }) contentRef: TemplateRef; - ngOnInit() { - } + constructor() {} + ngOnInit() {} } diff --git a/src/main/webapp/site/src/app/modules/shared/call-to-action/call-to-action.component.spec.ts b/src/main/webapp/site/src/app/modules/shared/call-to-action/call-to-action.component.spec.ts index 671788debe..91bcb9aea9 100644 --- a/src/main/webapp/site/src/app/modules/shared/call-to-action/call-to-action.component.spec.ts +++ b/src/main/webapp/site/src/app/modules/shared/call-to-action/call-to-action.component.spec.ts @@ -1,6 +1,6 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { CallToActionComponent } from './call-to-action.component'; -import { NO_ERRORS_SCHEMA } from "@angular/core"; +import { NO_ERRORS_SCHEMA } from '@angular/core'; describe('CallToActionComponent', () => { let component: CallToActionComponent; @@ -8,11 +8,10 @@ describe('CallToActionComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ CallToActionComponent ], + declarations: [CallToActionComponent], imports: [], - schemas: [ NO_ERRORS_SCHEMA ] - }) - .compileComponents(); + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); })); beforeEach(() => { diff --git a/src/main/webapp/site/src/app/modules/shared/call-to-action/call-to-action.component.ts b/src/main/webapp/site/src/app/modules/shared/call-to-action/call-to-action.component.ts index 6b2d85cfe9..fd78f8d39c 100644 --- a/src/main/webapp/site/src/app/modules/shared/call-to-action/call-to-action.component.ts +++ b/src/main/webapp/site/src/app/modules/shared/call-to-action/call-to-action.component.ts @@ -1,4 +1,11 @@ -import { Component, ContentChild, Input, OnInit, TemplateRef, ViewEncapsulation } from '@angular/core'; +import { + Component, + ContentChild, + Input, + OnInit, + TemplateRef, + ViewEncapsulation +} from '@angular/core'; @Component({ selector: 'app-call-to-action', @@ -7,7 +14,6 @@ import { Component, ContentChild, Input, OnInit, TemplateRef, ViewEncapsulation encapsulation: ViewEncapsulation.None }) export class CallToActionComponent implements OnInit { - @Input() destination: string; @@ -29,16 +35,14 @@ export class CallToActionComponent implements OnInit { @Input() headline: string; - @ContentChild('headlineTemplate', {static:false}) headlineRef: TemplateRef; + @ContentChild('headlineTemplate', { static: false }) headlineRef: TemplateRef; @Input() content: string; - @ContentChild('contentTemplate', {static:false}) contentRef: TemplateRef; - - constructor() { } + @ContentChild('contentTemplate', { static: false }) contentRef: TemplateRef; - ngOnInit() { - } + constructor() {} + ngOnInit() {} } diff --git a/src/main/webapp/site/src/app/modules/shared/edit-password/edit-password.component.spec.ts b/src/main/webapp/site/src/app/modules/shared/edit-password/edit-password.component.spec.ts index 470fd13806..6d33a734a5 100644 --- a/src/main/webapp/site/src/app/modules/shared/edit-password/edit-password.component.spec.ts +++ b/src/main/webapp/site/src/app/modules/shared/edit-password/edit-password.component.spec.ts @@ -1,13 +1,13 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { EditPasswordComponent } from './edit-password.component'; -import { UserService } from "../../../services/user.service"; +import { UserService } from '../../../services/user.service'; import { BehaviorSubject, Observable } from 'rxjs'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { ReactiveFormsModule } from '@angular/forms'; import { NO_ERRORS_SCHEMA, Provider } from '@angular/core'; import { MatSnackBarModule } from '@angular/material/snack-bar'; import { By } from '@angular/platform-browser'; -import { User } from "../../../domain/user"; +import { User } from '../../../domain/user'; import { configureTestSuite } from 'ng-bullet'; export class MockUserService { @@ -23,12 +23,12 @@ export class MockUserService { changePassword(username, oldPassword, newPassword) { if (oldPassword === 'a') { - return Observable.create(observer => { + return Observable.create((observer) => { observer.next({ status: 'success', messageCode: 'passwordChanged' }); observer.complete(); }); } else { - return Observable.create(observer => { + return Observable.create((observer) => { observer.next({ status: 'error', messageCode: 'incorrectPassword' }); observer.complete(); }); @@ -50,14 +50,10 @@ describe('EditPasswordComponent', () => { configureTestSuite(() => { TestBed.configureTestingModule({ - declarations: [ EditPasswordComponent ], - imports: [ - BrowserAnimationsModule, ReactiveFormsModule, MatSnackBarModule - ], - providers: [ - { provide: UserService, useValue: new MockUserService() } - ], - schemas: [ NO_ERRORS_SCHEMA ] + declarations: [EditPasswordComponent], + imports: [BrowserAnimationsModule, ReactiveFormsModule, MatSnackBarModule], + providers: [{ provide: UserService, useValue: new MockUserService() }], + schemas: [NO_ERRORS_SCHEMA] }); }); @@ -106,7 +102,9 @@ describe('EditPasswordComponent', () => { fixture.detectChanges(); const submitButton = getSubmitButton(); expect(submitButton.disabled).toBe(true); - expect(component.changePasswordFormGroup.get('oldPassword').getError('incorrectPassword')).toBe(true); + expect(component.changePasswordFormGroup.get('oldPassword').getError('incorrectPassword')).toBe( + true + ); }); it('should disable submit button when form is successfully submitted', async () => { @@ -138,7 +136,8 @@ describe('EditPasswordComponent', () => { messageCode: 'incorrectPassword' }; component.handleChangePasswordResponse(response); - expect(component.changePasswordFormGroup.get('oldPassword').getError('incorrectPassword')) - .toBe(true); + expect(component.changePasswordFormGroup.get('oldPassword').getError('incorrectPassword')).toBe( + true + ); }); }); diff --git a/src/main/webapp/site/src/app/modules/shared/edit-password/edit-password.component.ts b/src/main/webapp/site/src/app/modules/shared/edit-password/edit-password.component.ts index 8cf1b59e9f..f22788cc5f 100644 --- a/src/main/webapp/site/src/app/modules/shared/edit-password/edit-password.component.ts +++ b/src/main/webapp/site/src/app/modules/shared/edit-password/edit-password.component.ts @@ -1,8 +1,8 @@ import { Component, OnInit, ViewChild } from '@angular/core'; -import { FormControl, FormGroup, Validators, FormBuilder } from "@angular/forms"; +import { FormControl, FormGroup, Validators, FormBuilder } from '@angular/forms'; import { finalize } from 'rxjs/operators'; import { MatSnackBar } from '@angular/material/snack-bar'; -import { UserService } from "../../../services/user.service"; +import { UserService } from '../../../services/user.service'; @Component({ selector: 'app-edit-password', @@ -10,27 +10,31 @@ import { UserService } from "../../../services/user.service"; styleUrls: ['./edit-password.component.scss'] }) export class EditPasswordComponent implements OnInit { - @ViewChild('changePasswordForm', { static: false }) changePasswordForm; isSaving: boolean = false; isGoogleUser: boolean = false; - newPasswordFormGroup: FormGroup = this.fb.group({ - newPassword: new FormControl('', [Validators.required]), - confirmNewPassword: new FormControl('', [Validators.required]) - }, { validator: this.passwordMatchValidator }); + newPasswordFormGroup: FormGroup = this.fb.group( + { + newPassword: new FormControl('', [Validators.required]), + confirmNewPassword: new FormControl('', [Validators.required]) + }, + { validator: this.passwordMatchValidator } + ); changePasswordFormGroup: FormGroup = this.fb.group({ oldPassword: new FormControl('', [Validators.required]), newPasswordFormGroup: this.newPasswordFormGroup }); - constructor(private fb: FormBuilder, - private userService: UserService, - public snackBar: MatSnackBar) { } + constructor( + private fb: FormBuilder, + private userService: UserService, + public snackBar: MatSnackBar + ) {} ngOnInit() { - this.userService.getUser().subscribe(user => { + this.userService.getUser().subscribe((user) => { this.isGoogleUser = user.isGoogleUser; }); } @@ -41,7 +45,7 @@ export class EditPasswordComponent implements OnInit { if (newPassword === confirmNewPassword) { return null; } else { - const error = { 'passwordDoesNotMatch': true }; + const error = { passwordDoesNotMatch: true }; passwordsFormGroup.controls['confirmNewPassword'].setErrors(error); return error; } @@ -51,15 +55,16 @@ export class EditPasswordComponent implements OnInit { this.isSaving = true; const oldPassword: string = this.getControlFieldValue('oldPassword'); const newPassword: string = this.getControlFieldValue('newPassword'); - this.userService.changePassword(oldPassword, newPassword) - .pipe( - finalize(() => { - this.isSaving = false; - }) - ) - .subscribe((response) => { - this.handleChangePasswordResponse(response); - }); + this.userService + .changePassword(oldPassword, newPassword) + .pipe( + finalize(() => { + this.isSaving = false; + }) + ) + .subscribe((response) => { + this.handleChangePasswordResponse(response); + }); } getControlFieldValue(fieldName) { @@ -79,7 +84,7 @@ export class EditPasswordComponent implements OnInit { this.resetForm(); this.snackBar.open($localize`Password changed.`); } else if (response.status === 'error' && response.messageCode === 'incorrectPassword') { - const error = { 'incorrectPassword': true }; + const error = { incorrectPassword: true }; const oldPasswordControl = this.changePasswordFormGroup.get('oldPassword'); oldPasswordControl.setErrors(error); } diff --git a/src/main/webapp/site/src/app/modules/shared/hero-section/hero-section.component.spec.ts b/src/main/webapp/site/src/app/modules/shared/hero-section/hero-section.component.spec.ts index 61032b5cdc..cea467d901 100644 --- a/src/main/webapp/site/src/app/modules/shared/hero-section/hero-section.component.spec.ts +++ b/src/main/webapp/site/src/app/modules/shared/hero-section/hero-section.component.spec.ts @@ -1,6 +1,6 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { HeroSectionComponent } from './hero-section.component'; -import { NO_ERRORS_SCHEMA } from "@angular/core"; +import { NO_ERRORS_SCHEMA } from '@angular/core'; describe('HeroSectionComponent', () => { let component: HeroSectionComponent; @@ -8,11 +8,10 @@ describe('HeroSectionComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ HeroSectionComponent ], - imports: [ ], - schemas: [ NO_ERRORS_SCHEMA ] - }) - .compileComponents(); + declarations: [HeroSectionComponent], + imports: [], + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); })); beforeEach(() => { diff --git a/src/main/webapp/site/src/app/modules/shared/hero-section/hero-section.component.ts b/src/main/webapp/site/src/app/modules/shared/hero-section/hero-section.component.ts index 5130639032..aa42e2dfc6 100644 --- a/src/main/webapp/site/src/app/modules/shared/hero-section/hero-section.component.ts +++ b/src/main/webapp/site/src/app/modules/shared/hero-section/hero-section.component.ts @@ -1,4 +1,13 @@ -import { Component, ContentChild, Input, OnInit, TemplateRef, ViewEncapsulation, ViewChild, ElementRef } from '@angular/core'; +import { + Component, + ContentChild, + Input, + OnInit, + TemplateRef, + ViewEncapsulation, + ViewChild, + ElementRef +} from '@angular/core'; import { DomSanitizer, SafeStyle } from '@angular/platform-browser'; @Component({ @@ -8,7 +17,6 @@ import { DomSanitizer, SafeStyle } from '@angular/platform-browser'; encapsulation: ViewEncapsulation.None }) export class HeroSectionComponent { - @Input() imgSrc: string; @@ -39,7 +47,7 @@ export class HeroSectionComponent { ngAfterViewInit() { this.bgRef.nativeElement.onload = () => { this.bgStyle = this.getBgStyle(); - } + }; } /** diff --git a/src/main/webapp/site/src/app/modules/shared/search-bar/search-bar.component.spec.ts b/src/main/webapp/site/src/app/modules/shared/search-bar/search-bar.component.spec.ts index 61d2cb13cb..e02a228c62 100644 --- a/src/main/webapp/site/src/app/modules/shared/search-bar/search-bar.component.spec.ts +++ b/src/main/webapp/site/src/app/modules/shared/search-bar/search-bar.component.spec.ts @@ -1,6 +1,6 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { SearchBarComponent } from './search-bar.component'; -import { NO_ERRORS_SCHEMA } from "@angular/core"; +import { NO_ERRORS_SCHEMA } from '@angular/core'; describe('SearchBarComponent', () => { let component: SearchBarComponent; @@ -8,11 +8,10 @@ describe('SearchBarComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ SearchBarComponent ], - imports: [ ], - schemas: [ NO_ERRORS_SCHEMA ] - }) - .compileComponents(); + declarations: [SearchBarComponent], + imports: [], + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); })); beforeEach(() => { diff --git a/src/main/webapp/site/src/app/modules/shared/search-bar/search-bar.component.ts b/src/main/webapp/site/src/app/modules/shared/search-bar/search-bar.component.ts index 888a78292e..ff9da1c4dc 100644 --- a/src/main/webapp/site/src/app/modules/shared/search-bar/search-bar.component.ts +++ b/src/main/webapp/site/src/app/modules/shared/search-bar/search-bar.component.ts @@ -1,6 +1,14 @@ -import { Component, EventEmitter, Input, OnInit, Output, SimpleChanges, ViewEncapsulation } from '@angular/core'; +import { + Component, + EventEmitter, + Input, + OnInit, + Output, + SimpleChanges, + ViewEncapsulation +} from '@angular/core'; import { FormControl } from '@angular/forms'; -import { debounceTime , distinctUntilChanged } from 'rxjs/operators'; +import { debounceTime, distinctUntilChanged } from 'rxjs/operators'; @Component({ selector: 'app-search-bar', @@ -9,7 +17,6 @@ import { debounceTime , distinctUntilChanged } from 'rxjs/operators'; encapsulation: ViewEncapsulation.None }) export class SearchBarComponent implements OnInit { - @Input() placeholderText: string = $localize`Search`; // placeholder text @@ -25,10 +32,9 @@ export class SearchBarComponent implements OnInit { @Output('update') change: EventEmitter = new EventEmitter(); // change event emitter - searchField = new FormControl(""); // form control for the search input + searchField = new FormControl(''); // form control for the search input - constructor() { - } + constructor() {} ngOnInit() { this.searchField = new FormControl({ @@ -38,7 +44,7 @@ export class SearchBarComponent implements OnInit { this.searchField.valueChanges .pipe(debounceTime(this.debounce)) // wait specified interval for any changes .pipe(distinctUntilChanged()) // only emit event if search string has changed - .subscribe(value => { + .subscribe((value) => { this.change.emit(this.searchField.value); }); } diff --git a/src/main/webapp/site/src/app/modules/shared/select-menu/select-menu.component.spec.ts b/src/main/webapp/site/src/app/modules/shared/select-menu/select-menu.component.spec.ts index 81553b8092..081ac5b330 100644 --- a/src/main/webapp/site/src/app/modules/shared/select-menu/select-menu.component.spec.ts +++ b/src/main/webapp/site/src/app/modules/shared/select-menu/select-menu.component.spec.ts @@ -1,6 +1,6 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { SelectMenuComponent } from './select-menu.component'; -import { NO_ERRORS_SCHEMA } from "@angular/core"; +import { NO_ERRORS_SCHEMA } from '@angular/core'; describe('SelectMenuComponent', () => { let component: SelectMenuComponent; @@ -8,10 +8,9 @@ describe('SelectMenuComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ SelectMenuComponent ], - schemas: [ NO_ERRORS_SCHEMA ] - }) - .compileComponents(); + declarations: [SelectMenuComponent], + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); })); beforeEach(() => { diff --git a/src/main/webapp/site/src/app/modules/shared/select-menu/select-menu.component.ts b/src/main/webapp/site/src/app/modules/shared/select-menu/select-menu.component.ts index 74af8c2e67..9158440011 100644 --- a/src/main/webapp/site/src/app/modules/shared/select-menu/select-menu.component.ts +++ b/src/main/webapp/site/src/app/modules/shared/select-menu/select-menu.component.ts @@ -1,4 +1,12 @@ -import { Component, EventEmitter, Input, OnInit, Output, SimpleChanges, ViewEncapsulation } from '@angular/core'; +import { + Component, + EventEmitter, + Input, + OnInit, + Output, + SimpleChanges, + ViewEncapsulation +} from '@angular/core'; import { FormControl } from '@angular/forms'; @Component({ @@ -8,7 +16,6 @@ import { FormControl } from '@angular/forms'; encapsulation: ViewEncapsulation.None }) export class SelectMenuComponent implements OnInit { - @Input() disable: boolean = false; // whether select is disabled @@ -33,10 +40,9 @@ export class SelectMenuComponent implements OnInit { @Output('update') change: EventEmitter = new EventEmitter(); // change event emitter - selectField = new FormControl(""); // form control for the search input + selectField = new FormControl(''); // form control for the search input - constructor() { - } + constructor() {} ngOnInit() { this.selectField = new FormControl({ @@ -44,10 +50,9 @@ export class SelectMenuComponent implements OnInit { disabled: this.disable }); // this.selectField.setValue(this.value); - this.selectField.valueChanges - .subscribe(value => { - this.change.emit(this.selectField.value); - }); + this.selectField.valueChanges.subscribe((value) => { + this.change.emit(this.selectField.value); + }); } ngOnChanges(changes: SimpleChanges) { diff --git a/src/main/webapp/site/src/app/modules/shared/shared.module.ts b/src/main/webapp/site/src/app/modules/shared/shared.module.ts index f59125c160..5ec6a150df 100644 --- a/src/main/webapp/site/src/app/modules/shared/shared.module.ts +++ b/src/main/webapp/site/src/app/modules/shared/shared.module.ts @@ -55,4 +55,4 @@ import { EditPasswordComponent } from './edit-password/edit-password.component'; EditPasswordComponent ] }) -export class SharedModule { } +export class SharedModule {} diff --git a/src/main/webapp/site/src/app/modules/timeline/timeline-item/timeline-item.component.spec.ts b/src/main/webapp/site/src/app/modules/timeline/timeline-item/timeline-item.component.spec.ts index 81bfe8e028..edb4b12db7 100644 --- a/src/main/webapp/site/src/app/modules/timeline/timeline-item/timeline-item.component.spec.ts +++ b/src/main/webapp/site/src/app/modules/timeline/timeline-item/timeline-item.component.spec.ts @@ -1,6 +1,6 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { TimelineItemComponent } from './timeline-item.component'; -import { NO_ERRORS_SCHEMA } from "@angular/core"; +import { NO_ERRORS_SCHEMA } from '@angular/core'; describe('TimelineItemComponent', () => { let component: TimelineItemComponent; @@ -8,10 +8,9 @@ describe('TimelineItemComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ TimelineItemComponent ], - schemas: [ NO_ERRORS_SCHEMA ] - }) - .compileComponents(); + declarations: [TimelineItemComponent], + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); })); beforeEach(() => { diff --git a/src/main/webapp/site/src/app/modules/timeline/timeline-item/timeline-item.component.ts b/src/main/webapp/site/src/app/modules/timeline/timeline-item/timeline-item.component.ts index c434de03bf..a634912cc0 100644 --- a/src/main/webapp/site/src/app/modules/timeline/timeline-item/timeline-item.component.ts +++ b/src/main/webapp/site/src/app/modules/timeline/timeline-item/timeline-item.component.ts @@ -2,13 +2,13 @@ import { Component, Input, OnInit, Directive, ViewEncapsulation } from '@angular @Directive({ selector: 'app-timeline-item-label', - host: {'class': 'timeline-item__label'} + host: { class: 'timeline-item__label' } }) export class TimelineItemLabel {} @Directive({ selector: 'app-timeline-item-content', - host: {'class': 'timeline-item__content'} + host: { class: 'timeline-item__content' } }) export class TimelineItemContent {} @@ -19,12 +19,10 @@ export class TimelineItemContent {} encapsulation: ViewEncapsulation.None }) export class TimelineItemComponent implements OnInit { - @Input() active: boolean = false; - constructor() { } + constructor() {} - ngOnInit() { - } + ngOnInit() {} } diff --git a/src/main/webapp/site/src/app/modules/timeline/timeline.module.ts b/src/main/webapp/site/src/app/modules/timeline/timeline.module.ts index acf7d1c135..89cf0fffc0 100644 --- a/src/main/webapp/site/src/app/modules/timeline/timeline.module.ts +++ b/src/main/webapp/site/src/app/modules/timeline/timeline.module.ts @@ -4,23 +4,12 @@ import { TimelineComponent } from './timeline/timeline.component'; import { TimelineItemComponent, TimelineItemContent, - TimelineItemLabel} from './timeline-item/timeline-item.component'; + TimelineItemLabel +} from './timeline-item/timeline-item.component'; @NgModule({ - imports: [ - CommonModule - ], - declarations: [ - TimelineComponent, - TimelineItemComponent, - TimelineItemContent, - TimelineItemLabel - ], - exports: [ - TimelineComponent, - TimelineItemComponent, - TimelineItemContent, - TimelineItemLabel - ] + imports: [CommonModule], + declarations: [TimelineComponent, TimelineItemComponent, TimelineItemContent, TimelineItemLabel], + exports: [TimelineComponent, TimelineItemComponent, TimelineItemContent, TimelineItemLabel] }) -export class TimelineModule { } +export class TimelineModule {} diff --git a/src/main/webapp/site/src/app/modules/timeline/timeline/timeline.component.spec.ts b/src/main/webapp/site/src/app/modules/timeline/timeline/timeline.component.spec.ts index 2ef591d21d..7e365395b1 100644 --- a/src/main/webapp/site/src/app/modules/timeline/timeline/timeline.component.spec.ts +++ b/src/main/webapp/site/src/app/modules/timeline/timeline/timeline.component.spec.ts @@ -1,6 +1,6 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { TimelineComponent } from './timeline.component'; -import { NO_ERRORS_SCHEMA } from "@angular/core"; +import { NO_ERRORS_SCHEMA } from '@angular/core'; describe('TimelineComponent', () => { let component: TimelineComponent; @@ -8,10 +8,9 @@ describe('TimelineComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ TimelineComponent ], - schemas: [ NO_ERRORS_SCHEMA ] - }) - .compileComponents(); + declarations: [TimelineComponent], + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); })); beforeEach(() => { diff --git a/src/main/webapp/site/src/app/modules/timeline/timeline/timeline.component.ts b/src/main/webapp/site/src/app/modules/timeline/timeline/timeline.component.ts index 20f1300965..aab0716163 100644 --- a/src/main/webapp/site/src/app/modules/timeline/timeline/timeline.component.ts +++ b/src/main/webapp/site/src/app/modules/timeline/timeline/timeline.component.ts @@ -7,13 +7,10 @@ export class TimelineItem {} templateUrl: './timeline.component.html', styleUrls: ['./timeline.component.scss'], encapsulation: ViewEncapsulation.None, - host: {'class': 'timeline'} + host: { class: 'timeline' } }) export class TimelineComponent implements OnInit { + constructor() {} - constructor() { } - - ngOnInit() { - } - + ngOnInit() {} } diff --git a/src/main/webapp/site/src/app/news/news-routing.module.ts b/src/main/webapp/site/src/app/news/news-routing.module.ts index 31d8bf7c05..c9217734b5 100644 --- a/src/main/webapp/site/src/app/news/news-routing.module.ts +++ b/src/main/webapp/site/src/app/news/news-routing.module.ts @@ -1,13 +1,11 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; -import { NewsComponent } from "./news.component"; +import { NewsComponent } from './news.component'; -const newsRoutes: Routes = [ - { path: '', component: NewsComponent } -]; +const newsRoutes: Routes = [{ path: '', component: NewsComponent }]; @NgModule({ - imports: [ RouterModule.forChild(newsRoutes) ], - exports: [ RouterModule ] + imports: [RouterModule.forChild(newsRoutes)], + exports: [RouterModule] }) -export class NewsRoutingModule { } +export class NewsRoutingModule {} diff --git a/src/main/webapp/site/src/app/news/news.component.spec.ts b/src/main/webapp/site/src/app/news/news.component.spec.ts index 8e4f392977..0efa5bceef 100644 --- a/src/main/webapp/site/src/app/news/news.component.spec.ts +++ b/src/main/webapp/site/src/app/news/news.component.spec.ts @@ -1,11 +1,11 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { NewsComponent } from './news.component'; -import { NO_ERRORS_SCHEMA } from "@angular/core"; -import { NewsService } from "../services/news.service"; +import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { NewsService } from '../services/news.service'; import { MomentModule } from 'ngx-moment'; -import { News } from "../domain/news"; +import { News } from '../domain/news'; import { Observable } from 'rxjs'; -import { User } from "../domain/user"; +import { User } from '../domain/user'; import * as moment from 'moment'; import { configureTestSuite } from 'ng-bullet'; @@ -31,15 +31,17 @@ const createUser = (id, firstName, lastName, displayName) => { const news1Date = '2018-10-16 18:45:38.0'; const news1Title = 'Testing new portal website'; -const news1Text = 'We have begun testing the new website with some of our users. Once that is completed, everyone will be able to use the new website.'; +const news1Text = + 'We have begun testing the new website with some of our users. Once that is completed, everyone will be able to use the new website.'; const news2Date = '2018-9-21 15:37:14.0'; const news2Title = 'Working on a new portal website'; -const news2Text = 'We have been working on a new portal website. The new website will have a more modern user interface.'; +const news2Text = + 'We have been working on a new portal website. The new website will have a more modern user interface.'; export class MockNewsService { getAllNews(): Observable { - return Observable.create(observer => { + return Observable.create((observer) => { const allNewsItems: News[] = []; const user1 = createUser(100, 'Spongebob', 'Squarepants', 'Spongebob Squarepants'); const news1 = createNewsItem(1, news1Date, 'public', news1Title, news1Text, user1); @@ -76,13 +78,11 @@ describe('NewsComponent', () => { configureTestSuite(() => { TestBed.configureTestingModule({ - declarations: [ NewsComponent ], - imports: [ MomentModule ], - providers: [ - { provide: NewsService, useClass: MockNewsService } - ], - schemas: [ NO_ERRORS_SCHEMA ] - }) + declarations: [NewsComponent], + imports: [MomentModule], + providers: [{ provide: NewsService, useClass: MockNewsService }], + schemas: [NO_ERRORS_SCHEMA] + }); }); beforeEach(() => { diff --git a/src/main/webapp/site/src/app/news/news.component.ts b/src/main/webapp/site/src/app/news/news.component.ts index aa3644d3b1..5520911c75 100644 --- a/src/main/webapp/site/src/app/news/news.component.ts +++ b/src/main/webapp/site/src/app/news/news.component.ts @@ -1,6 +1,6 @@ import { Component, OnInit } from '@angular/core'; -import { NewsService } from "../services/news.service"; -import { News } from "../domain/news"; +import { NewsService } from '../services/news.service'; +import { News } from '../domain/news'; import { DomSanitizer } from '@angular/platform-browser'; @Component({ @@ -9,12 +9,10 @@ import { DomSanitizer } from '@angular/platform-browser'; styleUrls: ['./news.component.scss'] }) export class NewsComponent implements OnInit { - allNewsItems: any = []; showAll: boolean = false; - constructor(private newsService: NewsService, - private sanitizer: DomSanitizer) { } + constructor(private newsService: NewsService, private sanitizer: DomSanitizer) {} ngOnInit() { this.newsService.getAllNews().subscribe((allNewsItems: News[]) => { diff --git a/src/main/webapp/site/src/app/news/news.module.ts b/src/main/webapp/site/src/app/news/news.module.ts index ac20715367..d08cd231dc 100644 --- a/src/main/webapp/site/src/app/news/news.module.ts +++ b/src/main/webapp/site/src/app/news/news.module.ts @@ -1,9 +1,9 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; -import { TimelineModule } from "../modules/timeline/timeline.module"; -import { NewsComponent } from "./news.component"; +import { TimelineModule } from '../modules/timeline/timeline.module'; +import { NewsComponent } from './news.component'; import { MomentModule } from 'ngx-moment'; -import { NewsRoutingModule } from "./news-routing.module"; +import { NewsRoutingModule } from './news-routing.module'; import { MatButtonModule } from '@angular/material/button'; import { MatCardModule } from '@angular/material/card'; import { MatIconModule } from '@angular/material/icon'; @@ -20,8 +20,6 @@ import { FlexLayoutModule } from '@angular/flex-layout'; NewsRoutingModule, TimelineModule ], - declarations: [ - NewsComponent - ] + declarations: [NewsComponent] }) -export class NewsModule { } +export class NewsModule {} diff --git a/src/main/webapp/site/src/app/possible-score/possible-score.component.ts b/src/main/webapp/site/src/app/possible-score/possible-score.component.ts index c201394121..93525de275 100644 --- a/src/main/webapp/site/src/app/possible-score/possible-score.component.ts +++ b/src/main/webapp/site/src/app/possible-score/possible-score.component.ts @@ -6,15 +6,13 @@ import { ProjectService } from '../../../../wise5/services/projectService'; templateUrl: 'possible-score.component.html' }) export class PossibleScoreComponent { - @Input() maxScore: any; themeSettings: any; hidePossibleScores: any; - constructor(private projectService: ProjectService) { - } + constructor(private projectService: ProjectService) {} ngOnInit() { this.themeSettings = this.projectService.getThemeSettings(); diff --git a/src/main/webapp/site/src/app/preview/modules/choose-branch-path-dialog/choose-branch-path-dialog.component.spec.ts b/src/main/webapp/site/src/app/preview/modules/choose-branch-path-dialog/choose-branch-path-dialog.component.spec.ts index 92770e70f9..b716c68aab 100644 --- a/src/main/webapp/site/src/app/preview/modules/choose-branch-path-dialog/choose-branch-path-dialog.component.spec.ts +++ b/src/main/webapp/site/src/app/preview/modules/choose-branch-path-dialog/choose-branch-path-dialog.component.spec.ts @@ -8,12 +8,9 @@ describe('ChooseBranchPathDialogComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - providers: [ MatDialogModule, - { provide: MAT_DIALOG_DATA, useValue: { } } - ], - declarations: [ ChooseBranchPathDialogComponent ] - }) - .compileComponents(); + providers: [MatDialogModule, { provide: MAT_DIALOG_DATA, useValue: {} }], + declarations: [ChooseBranchPathDialogComponent] + }).compileComponents(); })); beforeEach(() => { diff --git a/src/main/webapp/site/src/app/preview/modules/choose-branch-path-dialog/choose-branch-path-dialog.component.ts b/src/main/webapp/site/src/app/preview/modules/choose-branch-path-dialog/choose-branch-path-dialog.component.ts index fdf54177e2..ee4d17f34f 100644 --- a/src/main/webapp/site/src/app/preview/modules/choose-branch-path-dialog/choose-branch-path-dialog.component.ts +++ b/src/main/webapp/site/src/app/preview/modules/choose-branch-path-dialog/choose-branch-path-dialog.component.ts @@ -15,6 +15,5 @@ export class ChooseBranchPathDialogComponent implements OnInit { this.paths = data.paths; } - ngOnInit(): void { - } + ngOnInit(): void {} } diff --git a/src/main/webapp/site/src/app/preview/preview.module.ts b/src/main/webapp/site/src/app/preview/preview.module.ts index f6b7953b06..8ac3800a0b 100644 --- a/src/main/webapp/site/src/app/preview/preview.module.ts +++ b/src/main/webapp/site/src/app/preview/preview.module.ts @@ -7,16 +7,17 @@ import { RouterModule } from '@angular/router'; { path: 'preview', children: [ - {path: '', loadChildren: () => import('../student-hybrid-angular.module').then(m => m.PreviewAngularJSModule)} + { + path: '', + loadChildren: () => + import('../student-hybrid-angular.module').then((m) => m.PreviewAngularJSModule) + } ] } ]) ], - declarations: [ - ], - providers: [ - ], - exports: [ - ] + declarations: [], + providers: [], + exports: [] }) -export class PreviewModule { } +export class PreviewModule {} diff --git a/src/main/webapp/site/src/app/privacy/privacy.component.spec.ts b/src/main/webapp/site/src/app/privacy/privacy.component.spec.ts index 1944a89365..5adc746f2a 100644 --- a/src/main/webapp/site/src/app/privacy/privacy.component.spec.ts +++ b/src/main/webapp/site/src/app/privacy/privacy.component.spec.ts @@ -1,6 +1,6 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { PrivacyComponent } from './privacy.component'; -import { NO_ERRORS_SCHEMA } from "@angular/core"; +import { NO_ERRORS_SCHEMA } from '@angular/core'; describe('PrivacyComponent', () => { let component: PrivacyComponent; @@ -8,10 +8,9 @@ describe('PrivacyComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ PrivacyComponent ], - schemas: [ NO_ERRORS_SCHEMA ] - }) - .compileComponents(); + declarations: [PrivacyComponent], + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); })); beforeEach(() => { diff --git a/src/main/webapp/site/src/app/privacy/privacy.component.ts b/src/main/webapp/site/src/app/privacy/privacy.component.ts index d90640371b..a4c47603de 100644 --- a/src/main/webapp/site/src/app/privacy/privacy.component.ts +++ b/src/main/webapp/site/src/app/privacy/privacy.component.ts @@ -6,10 +6,7 @@ import { Component, OnInit } from '@angular/core'; styleUrls: ['./privacy.component.scss'] }) export class PrivacyComponent implements OnInit { + constructor() {} - constructor() { } - - ngOnInit() { - } - + ngOnInit() {} } diff --git a/src/main/webapp/site/src/app/register/register-google-user-already-exists/register-google-user-already-exists.component.spec.ts b/src/main/webapp/site/src/app/register/register-google-user-already-exists/register-google-user-already-exists.component.spec.ts index c0a50c104a..0231a47255 100644 --- a/src/main/webapp/site/src/app/register/register-google-user-already-exists/register-google-user-already-exists.component.spec.ts +++ b/src/main/webapp/site/src/app/register/register-google-user-already-exists/register-google-user-already-exists.component.spec.ts @@ -1,6 +1,6 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { RegisterGoogleUserAlreadyExistsComponent } from './register-google-user-already-exists.component'; -import { ConfigService } from "../../services/config.service"; +import { ConfigService } from '../../services/config.service'; import { HttpClientTestingModule } from '@angular/common/http/testing'; import { NO_ERRORS_SCHEMA } from '@angular/core'; @@ -10,12 +10,11 @@ describe('RegisterGoogleUserAlreadyExistsComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ RegisterGoogleUserAlreadyExistsComponent ], - imports: [ HttpClientTestingModule ], - providers: [ ConfigService ], - schemas: [ NO_ERRORS_SCHEMA ] - }) - .compileComponents(); + declarations: [RegisterGoogleUserAlreadyExistsComponent], + imports: [HttpClientTestingModule], + providers: [ConfigService], + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); })); beforeEach(() => { diff --git a/src/main/webapp/site/src/app/register/register-google-user-already-exists/register-google-user-already-exists.component.ts b/src/main/webapp/site/src/app/register/register-google-user-already-exists/register-google-user-already-exists.component.ts index ea4c5583bb..cd575eacd5 100644 --- a/src/main/webapp/site/src/app/register/register-google-user-already-exists/register-google-user-already-exists.component.ts +++ b/src/main/webapp/site/src/app/register/register-google-user-already-exists/register-google-user-already-exists.component.ts @@ -7,13 +7,11 @@ import { ConfigService } from '../../services/config.service'; styleUrls: ['./register-google-user-already-exists.component.scss'] }) export class RegisterGoogleUserAlreadyExistsComponent implements OnInit { + constructor(private configService: ConfigService) {} - constructor(private configService: ConfigService) { } + ngOnInit() {} - ngOnInit() { - } - - public socialSignIn(socialPlatform : string) { + public socialSignIn(socialPlatform: string) { window.location.href = `${this.configService.getContextPath()}/google-login`; } } diff --git a/src/main/webapp/site/src/app/register/register-home/register-home.component.spec.ts b/src/main/webapp/site/src/app/register/register-home/register-home.component.spec.ts index c9a3b815f8..0a8c291346 100644 --- a/src/main/webapp/site/src/app/register/register-home/register-home.component.spec.ts +++ b/src/main/webapp/site/src/app/register/register-home/register-home.component.spec.ts @@ -1,6 +1,6 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { RegisterHomeComponent } from './register-home.component'; -import { NO_ERRORS_SCHEMA } from "@angular/core"; +import { NO_ERRORS_SCHEMA } from '@angular/core'; describe('RegisterHomeComponent', () => { let component: RegisterHomeComponent; @@ -8,11 +8,10 @@ describe('RegisterHomeComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ RegisterHomeComponent ], + declarations: [RegisterHomeComponent], imports: [], - schemas: [ NO_ERRORS_SCHEMA ] - }) - .compileComponents(); + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); })); beforeEach(() => { diff --git a/src/main/webapp/site/src/app/register/register-home/register-home.component.ts b/src/main/webapp/site/src/app/register/register-home/register-home.component.ts index a68975471e..8ffd290c63 100644 --- a/src/main/webapp/site/src/app/register/register-home/register-home.component.ts +++ b/src/main/webapp/site/src/app/register/register-home/register-home.component.ts @@ -7,10 +7,7 @@ import { Component, OnInit, ViewEncapsulation } from '@angular/core'; encapsulation: ViewEncapsulation.None }) export class RegisterHomeComponent implements OnInit { + constructor() {} - constructor() { } - - ngOnInit() { - } - + ngOnInit() {} } diff --git a/src/main/webapp/site/src/app/register/register-routing.module.ts b/src/main/webapp/site/src/app/register/register-routing.module.ts index 2636cacdb7..cf29240769 100644 --- a/src/main/webapp/site/src/app/register/register-routing.module.ts +++ b/src/main/webapp/site/src/app/register/register-routing.module.ts @@ -1,13 +1,13 @@ import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from "@angular/router"; +import { RouterModule, Routes } from '@angular/router'; -import { RegisterComponent } from "./register.component"; -import { RegisterHomeComponent } from "./register-home/register-home.component"; -import { RegisterStudentFormComponent } from "./register-student-form/register-student-form.component"; -import { RegisterStudentCompleteComponent } from "./register-student-complete/register-student-complete.component"; -import { RegisterTeacherComponent } from "./register-teacher/register-teacher.component"; -import { RegisterTeacherFormComponent } from "./register-teacher-form/register-teacher-form.component"; -import { RegisterTeacherCompleteComponent } from "./register-teacher-complete/register-teacher-complete.component"; +import { RegisterComponent } from './register.component'; +import { RegisterHomeComponent } from './register-home/register-home.component'; +import { RegisterStudentFormComponent } from './register-student-form/register-student-form.component'; +import { RegisterStudentCompleteComponent } from './register-student-complete/register-student-complete.component'; +import { RegisterTeacherComponent } from './register-teacher/register-teacher.component'; +import { RegisterTeacherFormComponent } from './register-teacher-form/register-teacher-form.component'; +import { RegisterTeacherCompleteComponent } from './register-teacher-complete/register-teacher-complete.component'; import { RegisterStudentComponent } from './register-student/register-student.component'; import { RegisterGoogleUserAlreadyExistsComponent } from './register-google-user-already-exists/register-google-user-already-exists.component'; @@ -29,11 +29,7 @@ const registerRoutes: Routes = [ ]; @NgModule({ - imports: [ - RouterModule.forChild(registerRoutes) - ], - exports: [ - RouterModule - ] + imports: [RouterModule.forChild(registerRoutes)], + exports: [RouterModule] }) export class RegisterRoutingModule {} diff --git a/src/main/webapp/site/src/app/register/register-student-complete/register-student-complete.component.spec.ts b/src/main/webapp/site/src/app/register/register-student-complete/register-student-complete.component.spec.ts index e9bc3f4da4..dafb45ef13 100644 --- a/src/main/webapp/site/src/app/register/register-student-complete/register-student-complete.component.spec.ts +++ b/src/main/webapp/site/src/app/register/register-student-complete/register-student-complete.component.spec.ts @@ -1,8 +1,8 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { RegisterStudentCompleteComponent } from './register-student-complete.component'; -import { RouterTestingModule } from "@angular/router/testing"; -import { NO_ERRORS_SCHEMA } from "@angular/core"; -import { ConfigService } from "../../services/config.service"; +import { RouterTestingModule } from '@angular/router/testing'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { ConfigService } from '../../services/config.service'; export class MockConfigService { getContextPath(): string { @@ -16,14 +16,11 @@ describe('RegisterStudentCompleteComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ RegisterStudentCompleteComponent ], - imports: [ RouterTestingModule ], - providers: [ - { provide: ConfigService, useClass: MockConfigService } - ], - schemas: [ NO_ERRORS_SCHEMA ] - }) - .compileComponents(); + declarations: [RegisterStudentCompleteComponent], + imports: [RouterTestingModule], + providers: [{ provide: ConfigService, useClass: MockConfigService }], + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); })); beforeEach(() => { diff --git a/src/main/webapp/site/src/app/register/register-student-complete/register-student-complete.component.ts b/src/main/webapp/site/src/app/register/register-student-complete/register-student-complete.component.ts index ed7cfefe2d..13025848fb 100644 --- a/src/main/webapp/site/src/app/register/register-student-complete/register-student-complete.component.ts +++ b/src/main/webapp/site/src/app/register/register-student-complete/register-student-complete.component.ts @@ -1,6 +1,6 @@ import { Component, OnInit } from '@angular/core'; -import { ActivatedRoute, Router } from "@angular/router"; -import { ConfigService } from "../../services/config.service"; +import { ActivatedRoute, Router } from '@angular/router'; +import { ConfigService } from '../../services/config.service'; @Component({ selector: 'app-register-student-complete', @@ -8,18 +8,20 @@ import { ConfigService } from "../../services/config.service"; styleUrls: ['./register-student-complete.component.scss'] }) export class RegisterStudentCompleteComponent implements OnInit { - username: string; isUsingGoogleId: boolean; googleLogInURL = `${this.configService.getContextPath()}/google-login`; - constructor(private router: Router, private route: ActivatedRoute, - private configService: ConfigService) { } + constructor( + private router: Router, + private route: ActivatedRoute, + private configService: ConfigService + ) {} ngOnInit() { - this.route.params.subscribe(params => { + this.route.params.subscribe((params) => { this.username = params['username']; - this.isUsingGoogleId = params['isUsingGoogleId'] == "true"; + this.isUsingGoogleId = params['isUsingGoogleId'] == 'true'; }); } diff --git a/src/main/webapp/site/src/app/register/register-student-form/register-student-form.component.spec.ts b/src/main/webapp/site/src/app/register/register-student-form/register-student-form.component.spec.ts index b62f8de697..02a87ba630 100644 --- a/src/main/webapp/site/src/app/register/register-student-form/register-student-form.component.spec.ts +++ b/src/main/webapp/site/src/app/register/register-student-form/register-student-form.component.spec.ts @@ -1,17 +1,17 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { RegisterStudentFormComponent } from './register-student-form.component'; -import { BrowserAnimationsModule } from "@angular/platform-browser/animations"; -import { RouterTestingModule } from "@angular/router/testing"; -import { Observable, of, throwError } from "rxjs"; -import { StudentService } from "../../student/student.service"; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { RouterTestingModule } from '@angular/router/testing'; +import { Observable, of, throwError } from 'rxjs'; +import { StudentService } from '../../student/student.service'; import { UserService } from '../../services/user.service'; -import { ReactiveFormsModule } from "@angular/forms"; -import { MatInputModule } from "@angular/material/input"; +import { ReactiveFormsModule } from '@angular/forms'; +import { MatInputModule } from '@angular/material/input'; import { MatSelectModule } from '@angular/material/select'; -import { NO_ERRORS_SCHEMA } from "@angular/core"; +import { NO_ERRORS_SCHEMA } from '@angular/core'; import { Router } from '@angular/router'; import { MatSnackBar, MatSnackBarModule } from '@angular/material/snack-bar'; -import * as helpers from "../register-user-form/register-user-form-spec-helpers"; +import * as helpers from '../register-user-form/register-user-form-spec-helpers'; let router: Router; let component: RegisterStudentFormComponent; @@ -20,27 +20,22 @@ let studentService: StudentService; let snackBar: MatSnackBar; export class MockStudentService { - registerStudentAccount() { + registerStudentAccount() {} - } - retrieveSecurityQuestions() { - return Observable.create(observer => { + return Observable.create((observer) => { observer.next([]); observer.complete(); }); } } -export class MockUserService { - -} +export class MockUserService {} describe('RegisterStudentFormComponent', () => { - beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ RegisterStudentFormComponent ], + declarations: [RegisterStudentFormComponent], imports: [ BrowserAnimationsModule, RouterTestingModule, @@ -53,9 +48,8 @@ describe('RegisterStudentFormComponent', () => { { provide: StudentService, useClass: MockStudentService }, { provide: UserService, useClass: MockUserService } ], - schemas: [ NO_ERRORS_SCHEMA ] - }) - .compileComponents(); + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); })); beforeEach(() => { @@ -73,24 +67,27 @@ describe('RegisterStudentFormComponent', () => { function createAccount() { describe('createAccount()', () => { it('should create account with valid form fields', () => { - component.createStudentAccountFormGroup.setValue(createAccountFormValue( - 'Patrick', - 'Star', - 'Male', - '01', - '01', - 'Who lives in a pineapple under the sea?', - 'Spongebob Squarepants', - 'a', - 'a' - )); + component.createStudentAccountFormGroup.setValue( + createAccountFormValue( + 'Patrick', + 'Star', + 'Male', + '01', + '01', + 'Who lives in a pineapple under the sea?', + 'Spongebob Squarepants', + 'a', + 'a' + ) + ); const username = 'PatrickS0101'; const response: any = helpers.createAccountSuccessResponse(username); spyOn(studentService, 'registerStudentAccount').and.returnValue(of(response)); const routerNavigateSpy = spyOn(router, 'navigate').and.callFake( - (args: any[]): Promise => { - return of(true).toPromise(); - }); + (args: any[]): Promise => { + return of(true).toPromise(); + } + ); component.createAccount(); expect(routerNavigateSpy).toHaveBeenCalledWith([ 'join/student/complete', @@ -103,36 +100,38 @@ function createAccount() { 'invalidFirstName', 'Error: First Name must only contain characters A-Z' ); - }) + }); it('should show error when invalid last name is sent to server', () => { expectCreateAccountWithInvalidNameToShowError( 'invalidLastName', 'Error: Last Name must only contain characters A-Z' ); - }) + }); it('should show error when invalid first and last name is sent to server', () => { expectCreateAccountWithInvalidNameToShowError( 'invalidFirstAndLastName', 'Error: First Name and Last Name must only contain characters A-Z' ); - }) + }); }); } function expectCreateAccountWithInvalidNameToShowError(errorCode: string, errorMessage: string) { - component.createStudentAccountFormGroup.setValue(createAccountFormValue( - 'Patrick', - 'Star', - 'Male', - '01', - '01', - 'Who lives in a pineapple under the sea?', - 'Spongebob Squarepants', - 'a', - 'a' - )); + component.createStudentAccountFormGroup.setValue( + createAccountFormValue( + 'Patrick', + 'Star', + 'Male', + '01', + '01', + 'Who lives in a pineapple under the sea?', + 'Spongebob Squarepants', + 'a', + 'a' + ) + ); const response: any = helpers.createAccountErrorResponse(errorCode); spyOn(studentService, 'registerStudentAccount').and.returnValue(throwError(response)); const snackBarSpy = spyOn(snackBar, 'open'); @@ -140,9 +139,17 @@ function expectCreateAccountWithInvalidNameToShowError(errorCode: string, errorM expect(snackBarSpy).toHaveBeenCalledWith(errorMessage); } -function createAccountFormValue(firstName: string, lastName: string, gender: string, - birthMonth: string, birthDay: string, securityQuestion: string, securityQuestionAnswer: string, - password: string, confirmPassword: string) { +function createAccountFormValue( + firstName: string, + lastName: string, + gender: string, + birthMonth: string, + birthDay: string, + securityQuestion: string, + securityQuestionAnswer: string, + password: string, + confirmPassword: string +) { return { firstName: firstName, lastName: lastName, @@ -155,5 +162,5 @@ function createAccountFormValue(firstName: string, lastName: string, gender: str password: password, confirmPassword: confirmPassword } - } + }; } diff --git a/src/main/webapp/site/src/app/register/register-student-form/register-student-form.component.ts b/src/main/webapp/site/src/app/register/register-student-form/register-student-form.component.ts index d29afb90d3..0c5a6b1a14 100644 --- a/src/main/webapp/site/src/app/register/register-student-form/register-student-form.component.ts +++ b/src/main/webapp/site/src/app/register/register-student-form/register-student-form.component.ts @@ -1,12 +1,12 @@ -import { ActivatedRoute, Router } from "@angular/router"; +import { ActivatedRoute, Router } from '@angular/router'; import { Component, OnInit } from '@angular/core'; -import { Student } from "../../domain/student"; -import { StudentService } from "../../student/student.service"; -import { FormControl, FormGroup, Validators, FormBuilder } from "@angular/forms"; -import { UtilService } from "../../services/util.service"; -import { MatSnackBar } from "@angular/material/snack-bar"; -import { RegisterUserFormComponent } from "../register-user-form/register-user-form.component"; -import { HttpErrorResponse } from "@angular/common/http"; +import { Student } from '../../domain/student'; +import { StudentService } from '../../student/student.service'; +import { FormControl, FormGroup, Validators, FormBuilder } from '@angular/forms'; +import { UtilService } from '../../services/util.service'; +import { MatSnackBar } from '@angular/material/snack-bar'; +import { RegisterUserFormComponent } from '../register-user-form/register-user-form.component'; +import { HttpErrorResponse } from '@angular/common/http'; @Component({ selector: 'app-register-student-form', @@ -14,72 +14,83 @@ import { HttpErrorResponse } from "@angular/common/http"; styleUrls: ['./register-student-form.component.scss'] }) export class RegisterStudentFormComponent extends RegisterUserFormComponent implements OnInit { - studentUser: Student = new Student(); genders: any[] = [ - { code: "FEMALE", label: $localize`Female` }, - { code: "MALE", label: $localize`Male` }, - { code: "NO_ANSWER", label: $localize`No Answer/Other` } + { code: 'FEMALE', label: $localize`Female` }, + { code: 'MALE', label: $localize`Male` }, + { code: 'NO_ANSWER', label: $localize`No Answer/Other` } ]; months: any[] = [ - { code: "1", label: $localize`01 (Jan)` }, - { code: "2", label: $localize`02 (Feb)` }, - { code: "3", label: $localize`03 (Mar)` }, - { code: "4", label: $localize`04 (Apr)` }, - { code: "5", label: $localize`05 (May)` }, - { code: "6", label: $localize`06 (Jun)` }, - { code: "7", label: $localize`07 (Jul)` }, - { code: "8", label: $localize`08 (Aug)` }, - { code: "9", label: $localize`09 (Sep)` }, - { code: "10", label: $localize`10 (Oct)` }, - { code: "11", label: $localize`11 (Nov)` }, - { code: "12", label: $localize`12 (Dec)` } + { code: '1', label: $localize`01 (Jan)` }, + { code: '2', label: $localize`02 (Feb)` }, + { code: '3', label: $localize`03 (Mar)` }, + { code: '4', label: $localize`04 (Apr)` }, + { code: '5', label: $localize`05 (May)` }, + { code: '6', label: $localize`06 (Jun)` }, + { code: '7', label: $localize`07 (Jul)` }, + { code: '8', label: $localize`08 (Aug)` }, + { code: '9', label: $localize`09 (Sep)` }, + { code: '10', label: $localize`10 (Oct)` }, + { code: '11', label: $localize`11 (Nov)` }, + { code: '12', label: $localize`12 (Dec)` } ]; days: string[] = []; securityQuestions: object; - passwordsFormGroup = this.fb.group({ - password: ['', [Validators.required]], - confirmPassword: ['', [Validators.required]] - }, { validator: this.passwordMatchValidator }); + passwordsFormGroup = this.fb.group( + { + password: ['', [Validators.required]], + confirmPassword: ['', [Validators.required]] + }, + { validator: this.passwordMatchValidator } + ); createStudentAccountFormGroup: FormGroup = this.fb.group({ firstName: new FormControl('', [Validators.required, Validators.pattern('[a-zA-Z]+')]), lastName: new FormControl('', [Validators.required, Validators.pattern('[a-zA-Z]+')]), gender: new FormControl('', [Validators.required]), birthMonth: new FormControl('', [Validators.required]), - birthDay: new FormControl({value: '', disabled: true}, [Validators.required]) + birthDay: new FormControl({ value: '', disabled: true }, [Validators.required]) }); processing: boolean = false; - constructor(private router: Router, private route: ActivatedRoute, - private studentService: StudentService, - private utilService: UtilService, - private fb: FormBuilder, - private snackBar: MatSnackBar) { + constructor( + private router: Router, + private route: ActivatedRoute, + private studentService: StudentService, + private utilService: UtilService, + private fb: FormBuilder, + private snackBar: MatSnackBar + ) { super(); - this.studentService.retrieveSecurityQuestions().subscribe(response => { + this.studentService.retrieveSecurityQuestions().subscribe((response) => { this.securityQuestions = response; }); } ngOnInit() { - this.route.params.subscribe(params => { + this.route.params.subscribe((params) => { this.studentUser.googleUserId = params['gID']; if (!this.isUsingGoogleId()) { this.createStudentAccountFormGroup.addControl('passwords', this.passwordsFormGroup); - this.createStudentAccountFormGroup.addControl('securityQuestion', new FormControl('', [Validators.required])); - this.createStudentAccountFormGroup.addControl('securityQuestionAnswer', new FormControl('', [Validators.required])); + this.createStudentAccountFormGroup.addControl( + 'securityQuestion', + new FormControl('', [Validators.required]) + ); + this.createStudentAccountFormGroup.addControl( + 'securityQuestionAnswer', + new FormControl('', [Validators.required]) + ); } const name = params['name']; if (name != null) { this.setControlFieldValue('firstName', this.utilService.getFirstName(name)); this.setControlFieldValue('lastName', this.utilService.getLastName(name)); } else { - this.setControlFieldValue("firstName", params['firstName']); - this.setControlFieldValue("lastName", params['lastName']); + this.setControlFieldValue('firstName', params['firstName']); + this.setControlFieldValue('lastName', params['lastName']); } }); - this.createStudentAccountFormGroup.controls['birthMonth'].valueChanges.subscribe(value => { + this.createStudentAccountFormGroup.controls['birthMonth'].valueChanges.subscribe((value) => { this.setBirthDayOptions(); }); } @@ -92,19 +103,23 @@ export class RegisterStudentFormComponent extends RegisterUserFormComponent impl if (this.createStudentAccountFormGroup.valid) { this.processing = true; this.populateStudentUser(); - this.studentService.registerStudentAccount(this.studentUser).subscribe((response: any) => { - if (response.status === 'success') { - this.router.navigate(['join/student/complete', - { username: response.username, isUsingGoogleId: this.isUsingGoogleId() } - ]); - } else { - this.snackBar.open(this.translateCreateAccountErrorMessageCode(response.messageCode)); + this.studentService.registerStudentAccount(this.studentUser).subscribe( + (response: any) => { + if (response.status === 'success') { + this.router.navigate([ + 'join/student/complete', + { username: response.username, isUsingGoogleId: this.isUsingGoogleId() } + ]); + } else { + this.snackBar.open(this.translateCreateAccountErrorMessageCode(response.messageCode)); + } + this.processing = false; + }, + (error: HttpErrorResponse) => { + this.snackBar.open(this.translateCreateAccountErrorMessageCode(error.error.messageCode)); + this.processing = false; } - this.processing = false; - }, (error: HttpErrorResponse) => { - this.snackBar.open(this.translateCreateAccountErrorMessageCode(error.error.messageCode)); - this.processing = false; - }); + ); } } @@ -133,7 +148,7 @@ export class RegisterStudentFormComponent extends RegisterUserFormComponent impl if (password == confirmPassword) { return null; } else { - const error = { 'passwordDoesNotMatch': true }; + const error = { passwordDoesNotMatch: true }; passwordsFormGroup.controls['confirmPassword'].setErrors(error); return error; } diff --git a/src/main/webapp/site/src/app/register/register-student/register-student.component.spec.ts b/src/main/webapp/site/src/app/register/register-student/register-student.component.spec.ts index f7b656a3ef..a7b7e5d330 100644 --- a/src/main/webapp/site/src/app/register/register-student/register-student.component.spec.ts +++ b/src/main/webapp/site/src/app/register/register-student/register-student.component.spec.ts @@ -1,34 +1,28 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { RegisterStudentComponent } from './register-student.component'; -import { Observable } from "rxjs"; -import { StudentService } from "../../student/student.service"; -import { UserService } from "../../services/user.service"; -import { AuthService } from "angularx-social-login"; -import { RouterTestingModule } from "@angular/router/testing"; -import { Config } from "../../domain/config"; -import { ConfigService } from "../../services/config.service"; -import { NO_ERRORS_SCHEMA } from "@angular/core"; +import { Observable } from 'rxjs'; +import { StudentService } from '../../student/student.service'; +import { UserService } from '../../services/user.service'; +import { AuthService } from 'angularx-social-login'; +import { RouterTestingModule } from '@angular/router/testing'; +import { Config } from '../../domain/config'; +import { ConfigService } from '../../services/config.service'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; -export class MockStudentService { +export class MockStudentService {} -} - -export class MockUserService { - -} - -export class MockAuthService { +export class MockUserService {} -} +export class MockAuthService {} export class MockConfigService { getConfig(): Observable { const config: Config = { - contextPath: "/wise", - logOutURL: "/logout", - currentTime: new Date("2018-10-17T00:00:00.0").getTime() + contextPath: '/wise', + logOutURL: '/logout', + currentTime: new Date('2018-10-17T00:00:00.0').getTime() }; - return Observable.create(observer => { + return Observable.create((observer) => { observer.next(config); observer.complete(); }); @@ -40,17 +34,16 @@ describe('RegisterStudentComponent', () => { let fixture: ComponentFixture; beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ RegisterStudentComponent ], - imports: [ RouterTestingModule ], + declarations: [RegisterStudentComponent], + imports: [RouterTestingModule], providers: [ { provide: StudentService, useClass: MockStudentService }, { provide: UserService, useClass: MockUserService }, { provide: AuthService, useClass: MockAuthService }, { provide: ConfigService, useClass: MockConfigService } ], - schemas: [ NO_ERRORS_SCHEMA ] - }) - .compileComponents(); + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); })); beforeEach(() => { diff --git a/src/main/webapp/site/src/app/register/register-student/register-student.component.ts b/src/main/webapp/site/src/app/register/register-student/register-student.component.ts index 5cea08b6f0..c39705c0c4 100644 --- a/src/main/webapp/site/src/app/register/register-student/register-student.component.ts +++ b/src/main/webapp/site/src/app/register/register-student/register-student.component.ts @@ -1,9 +1,9 @@ import { Component, OnInit } from '@angular/core'; -import { AuthService, GoogleLoginProvider } from "angularx-social-login"; -import { Router } from "@angular/router"; +import { AuthService, GoogleLoginProvider } from 'angularx-social-login'; +import { Router } from '@angular/router'; import { StudentService } from '../../student/student.service'; import { UserService } from '../../services/user.service'; -import { ConfigService } from "../../services/config.service"; +import { ConfigService } from '../../services/config.service'; @Component({ selector: 'app-register-student', @@ -11,14 +11,17 @@ import { ConfigService } from "../../services/config.service"; styleUrls: ['./register-student.component.scss'] }) export class RegisterStudentComponent implements OnInit { - - firstName: string = ""; - lastName: string = ""; + firstName: string = ''; + lastName: string = ''; isGoogleAuthenticationEnabled: boolean = false; - constructor(private socialAuthService: AuthService, - private studentService: StudentService, private userService: UserService, - private configService: ConfigService, private router: Router) {} + constructor( + private socialAuthService: AuthService, + private studentService: StudentService, + private userService: UserService, + private configService: ConfigService, + private router: Router + ) {} ngOnInit() { this.configService.getConfig().subscribe((config) => { @@ -29,32 +32,27 @@ export class RegisterStudentComponent implements OnInit { } public signUp() { - this.router.navigate(['join/student/form', { firstName: this.firstName, lastName: this.lastName } ]); + this.router.navigate([ + 'join/student/form', + { firstName: this.firstName, lastName: this.lastName } + ]); } - public socialSignIn(socialPlatform : string) { + public socialSignIn(socialPlatform: string) { let socialPlatformProvider; - if (socialPlatform == "google"){ + if (socialPlatform == 'google') { socialPlatformProvider = GoogleLoginProvider.PROVIDER_ID; } - this.socialAuthService.signIn(socialPlatformProvider).then( - (userData) => { - const googleUserID = userData.id; - this.userService.isGoogleIdExists(googleUserID).subscribe((isExists) => { - if (isExists) { - this.router.navigate(['join/googleUserAlreadyExists']); - } else { - this.router.navigate(['join/student/form', - { gID: googleUserID, - name: userData.name - } - ]); - - } - }); - } - ); + this.socialAuthService.signIn(socialPlatformProvider).then((userData) => { + const googleUserID = userData.id; + this.userService.isGoogleIdExists(googleUserID).subscribe((isExists) => { + if (isExists) { + this.router.navigate(['join/googleUserAlreadyExists']); + } else { + this.router.navigate(['join/student/form', { gID: googleUserID, name: userData.name }]); + } + }); + }); } - } diff --git a/src/main/webapp/site/src/app/register/register-teacher-complete/register-teacher-complete.component.spec.ts b/src/main/webapp/site/src/app/register/register-teacher-complete/register-teacher-complete.component.spec.ts index d599e2745f..73fd27079b 100644 --- a/src/main/webapp/site/src/app/register/register-teacher-complete/register-teacher-complete.component.spec.ts +++ b/src/main/webapp/site/src/app/register/register-teacher-complete/register-teacher-complete.component.spec.ts @@ -1,10 +1,10 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { RegisterTeacherCompleteComponent } from './register-teacher-complete.component'; -import { RouterTestingModule } from "@angular/router/testing"; -import { NO_ERRORS_SCHEMA } from "@angular/core"; -import { Observable } from "rxjs"; -import { Config } from "../../domain/config"; -import { ConfigService } from "../../services/config.service"; +import { RouterTestingModule } from '@angular/router/testing'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { Observable } from 'rxjs'; +import { Config } from '../../domain/config'; +import { ConfigService } from '../../services/config.service'; export class MockConfigService { getContextPath(): string { @@ -18,14 +18,11 @@ describe('RegisterTeacherCompleteComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ RegisterTeacherCompleteComponent ], - imports: [ RouterTestingModule ], - providers: [ - { provide: ConfigService, useClass: MockConfigService } - ], - schemas: [ NO_ERRORS_SCHEMA ] - }) - .compileComponents(); + declarations: [RegisterTeacherCompleteComponent], + imports: [RouterTestingModule], + providers: [{ provide: ConfigService, useClass: MockConfigService }], + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); })); beforeEach(() => { diff --git a/src/main/webapp/site/src/app/register/register-teacher-complete/register-teacher-complete.component.ts b/src/main/webapp/site/src/app/register/register-teacher-complete/register-teacher-complete.component.ts index 7214ccb51d..d994442cbf 100644 --- a/src/main/webapp/site/src/app/register/register-teacher-complete/register-teacher-complete.component.ts +++ b/src/main/webapp/site/src/app/register/register-teacher-complete/register-teacher-complete.component.ts @@ -1,6 +1,6 @@ import { Component, OnInit } from '@angular/core'; -import { ActivatedRoute, Router } from "@angular/router"; -import { ConfigService } from "../../services/config.service"; +import { ActivatedRoute, Router } from '@angular/router'; +import { ConfigService } from '../../services/config.service'; @Component({ selector: 'app-register-teacher-complete', @@ -8,18 +8,20 @@ import { ConfigService } from "../../services/config.service"; styleUrls: ['./register-teacher-complete.component.scss'] }) export class RegisterTeacherCompleteComponent implements OnInit { - username: string; isUsingGoogleId: boolean; googleLogInURL = `${this.configService.getContextPath()}/google-login`; - constructor(private router: Router, private route: ActivatedRoute, - private configService: ConfigService) { } + constructor( + private router: Router, + private route: ActivatedRoute, + private configService: ConfigService + ) {} ngOnInit() { - this.route.params.subscribe(params => { + this.route.params.subscribe((params) => { this.username = params['username']; - this.isUsingGoogleId = params['isUsingGoogleId'] == "true"; + this.isUsingGoogleId = params['isUsingGoogleId'] == 'true'; }); } diff --git a/src/main/webapp/site/src/app/register/register-teacher-form/register-teacher-form.component.spec.ts b/src/main/webapp/site/src/app/register/register-teacher-form/register-teacher-form.component.spec.ts index 8ed6b83899..ebc12ffc0d 100644 --- a/src/main/webapp/site/src/app/register/register-teacher-form/register-teacher-form.component.spec.ts +++ b/src/main/webapp/site/src/app/register/register-teacher-form/register-teacher-form.component.spec.ts @@ -1,28 +1,24 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { RegisterTeacherFormComponent } from './register-teacher-form.component'; -import { RouterTestingModule } from "@angular/router/testing"; -import { TeacherService } from "../../teacher/teacher.service"; -import { BrowserAnimationsModule } from "@angular/platform-browser/animations"; +import { RouterTestingModule } from '@angular/router/testing'; +import { TeacherService } from '../../teacher/teacher.service'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { UserService } from '../../services/user.service'; -import { ReactiveFormsModule } from "@angular/forms"; +import { ReactiveFormsModule } from '@angular/forms'; import { MatCheckboxModule } from '@angular/material/checkbox'; import { MatInputModule } from '@angular/material/input'; import { MatSelectModule } from '@angular/material/select'; -import { NO_ERRORS_SCHEMA } from "@angular/core"; +import { NO_ERRORS_SCHEMA } from '@angular/core'; import { MatSnackBarModule, MatSnackBar } from '@angular/material/snack-bar'; import { of, throwError } from 'rxjs'; import { Router } from '@angular/router'; -import * as helpers from "../register-user-form/register-user-form-spec-helpers"; +import * as helpers from '../register-user-form/register-user-form-spec-helpers'; class MockTeacherService { - registerTeacherAccount() { - - } + registerTeacherAccount() {} } -class MockUserService { - -} +class MockUserService {} let component: RegisterTeacherFormComponent; let fixture: ComponentFixture; @@ -31,10 +27,9 @@ let router: Router; let snackBar: MatSnackBar; describe('RegisterTeacherFormComponent', () => { - beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ RegisterTeacherFormComponent ], + declarations: [RegisterTeacherFormComponent], imports: [ BrowserAnimationsModule, RouterTestingModule, @@ -48,9 +43,8 @@ describe('RegisterTeacherFormComponent', () => { { provide: TeacherService, useClass: MockTeacherService }, { provide: UserService, useClass: MockUserService } ], - schemas: [ NO_ERRORS_SCHEMA ] - }) - .compileComponents(); + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); })); beforeEach(() => { @@ -68,27 +62,30 @@ describe('RegisterTeacherFormComponent', () => { function createAccount() { describe('createAccount()', () => { it('should create account with valid form fields', () => { - component.createTeacherAccountFormGroup.setValue(createAccountFormValue( - 'Spongebob', - 'Squarepants', - 'spongebob@bikinibottom.com', - 'Bikini Bottom', - 'Ocean', - 'Pacific Ocean', - 'Boating School', - 'Other', - '', - 'a', - 'a', - true - )); + component.createTeacherAccountFormGroup.setValue( + createAccountFormValue( + 'Spongebob', + 'Squarepants', + 'spongebob@bikinibottom.com', + 'Bikini Bottom', + 'Ocean', + 'Pacific Ocean', + 'Boating School', + 'Other', + '', + 'a', + 'a', + true + ) + ); const username = 'SpongebobSquarepants'; const response: any = helpers.createAccountSuccessResponse(username); spyOn(teacherService, 'registerTeacherAccount').and.returnValue(of(response)); const routerNavigateSpy = spyOn(router, 'navigate').and.callFake( - (args: any[]): Promise => { - return of(true).toPromise(); - }); + (args: any[]): Promise => { + return of(true).toPromise(); + } + ); component.createAccount(); expect(routerNavigateSpy).toHaveBeenCalledWith([ 'join/teacher/complete', @@ -120,20 +117,22 @@ function createAccount() { } function expectCreateAccountWithInvalidNameToShowError(errorCode: string, errorMessage: string) { - component.createTeacherAccountFormGroup.setValue(createAccountFormValue( - 'Spongebob', - 'Squarepants', - 'spongebob@bikinibottom.com', - 'Bikini Bottom', - 'Ocean', - 'Pacific Ocean', - 'Boating School', - 'Other', - '', - 'a', - 'a', - true - )); + component.createTeacherAccountFormGroup.setValue( + createAccountFormValue( + 'Spongebob', + 'Squarepants', + 'spongebob@bikinibottom.com', + 'Bikini Bottom', + 'Ocean', + 'Pacific Ocean', + 'Boating School', + 'Other', + '', + 'a', + 'a', + true + ) + ); const response: any = helpers.createAccountErrorResponse(errorCode); spyOn(teacherService, 'registerTeacherAccount').and.returnValue(throwError(response)); const snackBarSpy = spyOn(snackBar, 'open'); @@ -141,9 +140,20 @@ function expectCreateAccountWithInvalidNameToShowError(errorCode: string, errorM expect(snackBarSpy).toHaveBeenCalledWith(errorMessage); } -function createAccountFormValue(firstName: string, lastName: string, email: string, city: string, - state: string, country: string, schoolName: string, schoolLevel: string, - howDidYouHearAboutUs: string, password: string, confirmPassword: string, agree: boolean) { +function createAccountFormValue( + firstName: string, + lastName: string, + email: string, + city: string, + state: string, + country: string, + schoolName: string, + schoolLevel: string, + howDidYouHearAboutUs: string, + password: string, + confirmPassword: string, + agree: boolean +) { return { firstName: firstName, lastName: lastName, diff --git a/src/main/webapp/site/src/app/register/register-teacher-form/register-teacher-form.component.ts b/src/main/webapp/site/src/app/register/register-teacher-form/register-teacher-form.component.ts index e7c9be9796..e3757427e0 100644 --- a/src/main/webapp/site/src/app/register/register-teacher-form/register-teacher-form.component.ts +++ b/src/main/webapp/site/src/app/register/register-teacher-form/register-teacher-form.component.ts @@ -1,8 +1,8 @@ import { Component, OnInit } from '@angular/core'; -import { ActivatedRoute, Router } from "@angular/router"; -import { Teacher } from "../../domain/teacher"; -import { TeacherService } from "../../teacher/teacher.service"; -import { FormControl, FormGroup, Validators, FormBuilder } from "@angular/forms"; +import { ActivatedRoute, Router } from '@angular/router'; +import { Teacher } from '../../domain/teacher'; +import { TeacherService } from '../../teacher/teacher.service'; +import { FormControl, FormGroup, Validators, FormBuilder } from '@angular/forms'; import { UtilService } from '../../services/util.service'; import { MatSnackBar } from '@angular/material/snack-bar'; import { RegisterUserFormComponent } from '../register-user-form/register-user-form.component'; @@ -14,44 +14,52 @@ import { HttpErrorResponse } from '@angular/common/http'; styleUrls: ['./register-teacher-form.component.scss'] }) export class RegisterTeacherFormComponent extends RegisterUserFormComponent implements OnInit { - teacherUser: Teacher = new Teacher(); schoolLevels: any[] = [ - { code: "ELEMENTARY_SCHOOL", label: $localize`Elementary School` }, - { code: "MIDDLE_SCHOOL", label: $localize`Middle School` }, - { code: "HIGH_SCHOOL", label: $localize`High School` }, - { code: "COLLEGE", label: $localize`College` }, - { code: "OTHER", label: $localize`Other` } + { code: 'ELEMENTARY_SCHOOL', label: $localize`Elementary School` }, + { code: 'MIDDLE_SCHOOL', label: $localize`Middle School` }, + { code: 'HIGH_SCHOOL', label: $localize`High School` }, + { code: 'COLLEGE', label: $localize`College` }, + { code: 'OTHER', label: $localize`Other` } ]; - passwordsFormGroup = this.fb.group({ - password: ['', [Validators.required]], - confirmPassword: ['', [Validators.required]] - }, { validator: this.passwordMatchValidator }); - createTeacherAccountFormGroup: FormGroup = this.fb.group({ - firstName: new FormControl('', [Validators.required, Validators.pattern('[a-zA-Z]+')]), - lastName: new FormControl('', [Validators.required, Validators.pattern('[a-zA-Z]+')]), - email: new FormControl('', [Validators.required, Validators.email]), - city: new FormControl('', [Validators.required]), - state: new FormControl('', [Validators.required]), - country: new FormControl('', [Validators.required]), - schoolName: new FormControl('', [Validators.required]), - schoolLevel: new FormControl('', [Validators.required]), - howDidYouHearAboutUs: new FormControl(''), - agree: new FormControl('') - }, { validator: this.agreeCheckboxValidator }); + passwordsFormGroup = this.fb.group( + { + password: ['', [Validators.required]], + confirmPassword: ['', [Validators.required]] + }, + { validator: this.passwordMatchValidator } + ); + createTeacherAccountFormGroup: FormGroup = this.fb.group( + { + firstName: new FormControl('', [Validators.required, Validators.pattern('[a-zA-Z]+')]), + lastName: new FormControl('', [Validators.required, Validators.pattern('[a-zA-Z]+')]), + email: new FormControl('', [Validators.required, Validators.email]), + city: new FormControl('', [Validators.required]), + state: new FormControl('', [Validators.required]), + country: new FormControl('', [Validators.required]), + schoolName: new FormControl('', [Validators.required]), + schoolLevel: new FormControl('', [Validators.required]), + howDidYouHearAboutUs: new FormControl(''), + agree: new FormControl('') + }, + { validator: this.agreeCheckboxValidator } + ); isSubmitted = false; processing: boolean = false; - constructor(private router: Router, private route: ActivatedRoute, - private teacherService: TeacherService, - private utilService: UtilService, - private fb: FormBuilder, - private snackBar: MatSnackBar) { + constructor( + private router: Router, + private route: ActivatedRoute, + private teacherService: TeacherService, + private utilService: UtilService, + private fb: FormBuilder, + private snackBar: MatSnackBar + ) { super(); } ngOnInit() { - this.route.params.subscribe(params => { + this.route.params.subscribe((params) => { this.teacherUser.googleUserId = params['gID']; if (!this.isUsingGoogleId()) { this.createTeacherAccountFormGroup.addControl('passwords', this.passwordsFormGroup); @@ -78,19 +86,23 @@ export class RegisterTeacherFormComponent extends RegisterUserFormComponent impl if (this.createTeacherAccountFormGroup.valid) { this.processing = true; this.populateTeacherUser(); - this.teacherService.registerTeacherAccount(this.teacherUser).subscribe((response: any) => { - if (response.status === 'success') { - this.router.navigate(['join/teacher/complete', - { username: response.username, isUsingGoogleId: this.isUsingGoogleId() } - ]); - } else { - this.snackBar.open(this.translateCreateAccountErrorMessageCode(response.messageCode)); + this.teacherService.registerTeacherAccount(this.teacherUser).subscribe( + (response: any) => { + if (response.status === 'success') { + this.router.navigate([ + 'join/teacher/complete', + { username: response.username, isUsingGoogleId: this.isUsingGoogleId() } + ]); + } else { + this.snackBar.open(this.translateCreateAccountErrorMessageCode(response.messageCode)); + } + this.processing = false; + }, + (error: HttpErrorResponse) => { + this.snackBar.open(this.translateCreateAccountErrorMessageCode(error.error.messageCode)); + this.processing = false; } - this.processing = false; - }, (error: HttpErrorResponse) => { - this.snackBar.open(this.translateCreateAccountErrorMessageCode(error.error.messageCode)); - this.processing = false; - }); + ); } } @@ -115,7 +127,7 @@ export class RegisterTeacherFormComponent extends RegisterUserFormComponent impl if (password == confirmPassword) { return null; } else { - const error = { 'passwordDoesNotMatch': true }; + const error = { passwordDoesNotMatch: true }; passwordsFormGroup.controls['confirmPassword'].setErrors(error); return error; } @@ -124,7 +136,7 @@ export class RegisterTeacherFormComponent extends RegisterUserFormComponent impl agreeCheckboxValidator(createTeacherAccountFormGroup: FormGroup) { const agree = createTeacherAccountFormGroup.get('agree').value; if (!agree) { - const error = { 'agreeNotChecked': true }; + const error = { agreeNotChecked: true }; createTeacherAccountFormGroup.setErrors(error); return error; } diff --git a/src/main/webapp/site/src/app/register/register-teacher/register-teacher.component.spec.ts b/src/main/webapp/site/src/app/register/register-teacher/register-teacher.component.spec.ts index 68ee4e7522..14cc6ec346 100644 --- a/src/main/webapp/site/src/app/register/register-teacher/register-teacher.component.spec.ts +++ b/src/main/webapp/site/src/app/register/register-teacher/register-teacher.component.spec.ts @@ -1,39 +1,33 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { RegisterTeacherComponent } from './register-teacher.component'; -import { TeacherService } from "../../teacher/teacher.service"; -import { AuthService } from "angularx-social-login"; -import { RouterTestingModule } from "@angular/router/testing"; -import { UserService } from "../../services/user.service"; -import { Observable } from "rxjs"; -import { Config } from "../../domain/config"; -import { ConfigService } from "../../services/config.service"; -import { NO_ERRORS_SCHEMA } from "@angular/core"; +import { TeacherService } from '../../teacher/teacher.service'; +import { AuthService } from 'angularx-social-login'; +import { RouterTestingModule } from '@angular/router/testing'; +import { UserService } from '../../services/user.service'; +import { Observable } from 'rxjs'; +import { Config } from '../../domain/config'; +import { ConfigService } from '../../services/config.service'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; export class MockConfigService { getConfig(): Observable { const config: Config = { - contextPath: "/wise", - logOutURL: "/logout", - currentTime: new Date("2018-10-17T00:00:00.0").getTime() + contextPath: '/wise', + logOutURL: '/logout', + currentTime: new Date('2018-10-17T00:00:00.0').getTime() }; - return Observable.create(observer => { + return Observable.create((observer) => { observer.next(config); observer.complete(); }); } } -export class MockAuthService { +export class MockAuthService {} -} - -export class MockTeacherService { - -} - -export class MockUserService { +export class MockTeacherService {} -} +export class MockUserService {} describe('RegisterTeacherComponent', () => { let component: RegisterTeacherComponent; @@ -41,17 +35,16 @@ describe('RegisterTeacherComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ RegisterTeacherComponent ], - imports: [ RouterTestingModule ], + declarations: [RegisterTeacherComponent], + imports: [RouterTestingModule], providers: [ { provide: AuthService, useClass: MockAuthService }, { provide: TeacherService, useClass: MockTeacherService }, { provide: UserService, useClass: MockUserService }, { provide: ConfigService, useClass: MockConfigService } ], - schemas: [ NO_ERRORS_SCHEMA ] - }) - .compileComponents(); + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); })); beforeEach(() => { diff --git a/src/main/webapp/site/src/app/register/register-teacher/register-teacher.component.ts b/src/main/webapp/site/src/app/register/register-teacher/register-teacher.component.ts index 1d9e778819..1d237cfa9e 100644 --- a/src/main/webapp/site/src/app/register/register-teacher/register-teacher.component.ts +++ b/src/main/webapp/site/src/app/register/register-teacher/register-teacher.component.ts @@ -1,7 +1,7 @@ import { Component, OnInit } from '@angular/core'; -import { AuthService, GoogleLoginProvider } from "angularx-social-login"; -import { Router } from "@angular/router"; -import { TeacherService } from "../../teacher/teacher.service"; +import { AuthService, GoogleLoginProvider } from 'angularx-social-login'; +import { Router } from '@angular/router'; +import { TeacherService } from '../../teacher/teacher.service'; import { UserService } from '../../services/user.service'; import { ConfigService } from '../../services/config.service'; @@ -11,13 +11,16 @@ import { ConfigService } from '../../services/config.service'; styleUrls: ['./register-teacher.component.scss'] }) export class RegisterTeacherComponent implements OnInit { - - email: string = ""; + email: string = ''; isGoogleAuthenticationEnabled: boolean = false; - constructor(private socialAuthService: AuthService, - private teacherService: TeacherService, private userService: UserService, - private configService: ConfigService, private router: Router) {} + constructor( + private socialAuthService: AuthService, + private teacherService: TeacherService, + private userService: UserService, + private configService: ConfigService, + private router: Router + ) {} ngOnInit() { this.configService.getConfig().subscribe((config) => { @@ -28,32 +31,27 @@ export class RegisterTeacherComponent implements OnInit { } public signUp() { - this.router.navigate(['join/teacher/form', { email: this.email} ]); + this.router.navigate(['join/teacher/form', { email: this.email }]); } - public socialSignIn(socialPlatform : string) { + public socialSignIn(socialPlatform: string) { let socialPlatformProvider; - if (socialPlatform == "google"){ + if (socialPlatform == 'google') { socialPlatformProvider = GoogleLoginProvider.PROVIDER_ID; } - this.socialAuthService.signIn(socialPlatformProvider).then( - (userData) => { - const googleUserID = userData.id; - this.userService.isGoogleIdExists(googleUserID).subscribe((isExists) => { - if (isExists) { - this.router.navigate(['join/googleUserAlreadyExists']); - } else { - this.router.navigate(['join/teacher/form', - { gID: googleUserID, - name: userData.name, - email: userData.email - } - ]); - - } - }); - } - ); + this.socialAuthService.signIn(socialPlatformProvider).then((userData) => { + const googleUserID = userData.id; + this.userService.isGoogleIdExists(googleUserID).subscribe((isExists) => { + if (isExists) { + this.router.navigate(['join/googleUserAlreadyExists']); + } else { + this.router.navigate([ + 'join/teacher/form', + { gID: googleUserID, name: userData.name, email: userData.email } + ]); + } + }); + }); } } diff --git a/src/main/webapp/site/src/app/register/register-user-form/register-user-form-spec-helpers.ts b/src/main/webapp/site/src/app/register/register-user-form/register-user-form-spec-helpers.ts index a53c55ed21..1e0835a7d0 100644 --- a/src/main/webapp/site/src/app/register/register-user-form/register-user-form-spec-helpers.ts +++ b/src/main/webapp/site/src/app/register/register-user-form/register-user-form-spec-helpers.ts @@ -1,4 +1,3 @@ - export function createAccountSuccessResponse(username: string) { return { status: 'success', diff --git a/src/main/webapp/site/src/app/register/register-user-form/register-user-form.component.ts b/src/main/webapp/site/src/app/register/register-user-form/register-user-form.component.ts index 70da40a24e..ac3bb87baf 100644 --- a/src/main/webapp/site/src/app/register/register-user-form/register-user-form.component.ts +++ b/src/main/webapp/site/src/app/register/register-user-form/register-user-form.component.ts @@ -1,8 +1,6 @@ - - export class RegisterUserFormComponent { translateCreateAccountErrorMessageCode(messageCode: string) { - switch(messageCode) { + switch (messageCode) { case 'invalidFirstAndLastName': return $localize`Error: First Name and Last Name must only contain characters A-Z`; case 'invalidFirstName': @@ -12,4 +10,4 @@ export class RegisterUserFormComponent { } return messageCode; } -} \ No newline at end of file +} diff --git a/src/main/webapp/site/src/app/register/register.component.spec.ts b/src/main/webapp/site/src/app/register/register.component.spec.ts index 6864c1f7f9..86c7725619 100644 --- a/src/main/webapp/site/src/app/register/register.component.spec.ts +++ b/src/main/webapp/site/src/app/register/register.component.spec.ts @@ -1,6 +1,6 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { RegisterComponent } from './register.component'; -import { NO_ERRORS_SCHEMA } from "@angular/core"; +import { NO_ERRORS_SCHEMA } from '@angular/core'; describe('RegisterComponent', () => { let component: RegisterComponent; @@ -8,12 +8,11 @@ describe('RegisterComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ RegisterComponent ], + declarations: [RegisterComponent], providers: [], imports: [], - schemas: [ NO_ERRORS_SCHEMA ] - }) - .compileComponents(); + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); })); beforeEach(() => { diff --git a/src/main/webapp/site/src/app/register/register.component.ts b/src/main/webapp/site/src/app/register/register.component.ts index 8223a64225..9991df2dc3 100644 --- a/src/main/webapp/site/src/app/register/register.component.ts +++ b/src/main/webapp/site/src/app/register/register.component.ts @@ -1,5 +1,5 @@ import { Component, OnInit } from '@angular/core'; -import { Router } from "@angular/router"; +import { Router } from '@angular/router'; @Component({ selector: 'app-register', @@ -7,9 +7,7 @@ import { Router } from "@angular/router"; styleUrls: ['./register.component.scss'] }) export class RegisterComponent implements OnInit { + constructor() {} - constructor() { } - - ngOnInit(): void { - } + ngOnInit(): void {} } diff --git a/src/main/webapp/site/src/app/register/register.module.ts b/src/main/webapp/site/src/app/register/register.module.ts index c1a835db26..29b6f51eec 100644 --- a/src/main/webapp/site/src/app/register/register.module.ts +++ b/src/main/webapp/site/src/app/register/register.module.ts @@ -1,17 +1,17 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { RegisterComponent } from './register.component'; -import { RegisterRoutingModule } from "./register-routing.module"; +import { RegisterRoutingModule } from './register-routing.module'; import { RegisterTeacherComponent } from './register-teacher/register-teacher.component'; import { RegisterHomeComponent } from './register-home/register-home.component'; -import { FormsModule, ReactiveFormsModule } from "@angular/forms"; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { RegisterTeacherFormComponent } from './register-teacher-form/register-teacher-form.component'; import { RegisterTeacherCompleteComponent } from './register-teacher-complete/register-teacher-complete.component'; import { RegisterStudentFormComponent } from './register-student-form/register-student-form.component'; import { RegisterStudentCompleteComponent } from './register-student-complete/register-student-complete.component'; import { RegisterStudentComponent } from './register-student/register-student.component'; import { RegisterGoogleUserAlreadyExistsComponent } from './register-google-user-already-exists/register-google-user-already-exists.component'; -import { SharedModule } from "../modules/shared/shared.module"; +import { SharedModule } from '../modules/shared/shared.module'; import { MatButtonModule } from '@angular/material/button'; import { MatCardModule } from '@angular/material/card'; import { MatCheckboxModule } from '@angular/material/checkbox'; @@ -52,6 +52,6 @@ const materialModules = [ RegisterStudentComponent, RegisterGoogleUserAlreadyExistsComponent ], - exports: [ RegisterComponent ] + exports: [RegisterComponent] }) -export class RegisterModule { } +export class RegisterModule {} diff --git a/src/main/webapp/site/src/app/services/animationService.spec.ts b/src/main/webapp/site/src/app/services/animationService.spec.ts index af71160521..cf4630f17e 100644 --- a/src/main/webapp/site/src/app/services/animationService.spec.ts +++ b/src/main/webapp/site/src/app/services/animationService.spec.ts @@ -1,22 +1,22 @@ -import { TestBed } from "@angular/core/testing"; -import { HttpClientTestingModule } from "@angular/common/http/testing"; -import { UpgradeModule } from "@angular/upgrade/static"; -import { AnnotationService } from "../../../../wise5/services/annotationService"; -import { ConfigService } from "../../../../wise5/services/configService"; -import { ProjectService } from "../../../../wise5/services/projectService"; -import { StudentAssetService } from "../../../../wise5/services/studentAssetService"; -import { StudentDataService } from "../../../../wise5/services/studentDataService"; -import { TagService } from "../../../../wise5/services/tagService"; -import { UtilService } from "../../../../wise5/services/utilService"; -import { AnimationService } from "../../../../wise5/components/animation/animationService"; -import { SessionService } from "../../../../wise5/services/sessionService"; +import { TestBed } from '@angular/core/testing'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { UpgradeModule } from '@angular/upgrade/static'; +import { AnnotationService } from '../../../../wise5/services/annotationService'; +import { ConfigService } from '../../../../wise5/services/configService'; +import { ProjectService } from '../../../../wise5/services/projectService'; +import { StudentAssetService } from '../../../../wise5/services/studentAssetService'; +import { StudentDataService } from '../../../../wise5/services/studentDataService'; +import { TagService } from '../../../../wise5/services/tagService'; +import { UtilService } from '../../../../wise5/services/utilService'; +import { AnimationService } from '../../../../wise5/components/animation/animationService'; +import { SessionService } from '../../../../wise5/services/sessionService'; let service: AnimationService; describe('AnimationService', () => { beforeEach(() => { TestBed.configureTestingModule({ - imports: [ HttpClientTestingModule, UpgradeModule ], + imports: [HttpClientTestingModule, UpgradeModule], providers: [ AnimationService, AnnotationService, @@ -36,7 +36,6 @@ describe('AnimationService', () => { componentStateHasStudentWork(); }); - function createComponent() { it('should create an animation component', () => { const component = service.createComponent(); @@ -68,11 +67,10 @@ function componentStateHasStudentWork() { function expectComponentStateHasStudentWork(componentState: any, expectedResult: boolean) { expect(service.componentStateHasStudentWork(componentState, {})).toEqual(expectedResult); } - it('should check if a component state has student work when it does not have student work', - () => { + it('should check if a component state has student work when it does not have student work', () => { expectComponentStateHasStudentWork({}, false); - }) + }); it('should check if a component state has student work when it does have student work', () => { expectComponentStateHasStudentWork({ studentData: {} }, true); - }) -} \ No newline at end of file + }); +} diff --git a/src/main/webapp/site/src/app/services/audioOscillatorService.spec.ts b/src/main/webapp/site/src/app/services/audioOscillatorService.spec.ts index e86e48c17b..603b895da6 100644 --- a/src/main/webapp/site/src/app/services/audioOscillatorService.spec.ts +++ b/src/main/webapp/site/src/app/services/audioOscillatorService.spec.ts @@ -1,22 +1,22 @@ -import { TestBed } from "@angular/core/testing"; -import { HttpClientTestingModule } from "@angular/common/http/testing"; -import { UpgradeModule } from "@angular/upgrade/static"; -import { AnnotationService } from "../../../../wise5/services/annotationService"; -import { ConfigService } from "../../../../wise5/services/configService"; -import { ProjectService } from "../../../../wise5/services/projectService"; -import { StudentAssetService } from "../../../../wise5/services/studentAssetService"; -import { StudentDataService } from "../../../../wise5/services/studentDataService"; -import { TagService } from "../../../../wise5/services/tagService"; -import { UtilService } from "../../../../wise5/services/utilService"; -import { AudioOscillatorService } from "../../../../wise5/components/audioOscillator/audioOscillatorService"; -import { SessionService } from "../../../../wise5/services/sessionService"; +import { TestBed } from '@angular/core/testing'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { UpgradeModule } from '@angular/upgrade/static'; +import { AnnotationService } from '../../../../wise5/services/annotationService'; +import { ConfigService } from '../../../../wise5/services/configService'; +import { ProjectService } from '../../../../wise5/services/projectService'; +import { StudentAssetService } from '../../../../wise5/services/studentAssetService'; +import { StudentDataService } from '../../../../wise5/services/studentDataService'; +import { TagService } from '../../../../wise5/services/tagService'; +import { UtilService } from '../../../../wise5/services/utilService'; +import { AudioOscillatorService } from '../../../../wise5/components/audioOscillator/audioOscillatorService'; +import { SessionService } from '../../../../wise5/services/sessionService'; let service: AudioOscillatorService; describe('AudioOscillatorService', () => { beforeEach(() => { TestBed.configureTestingModule({ - imports: [ HttpClientTestingModule, UpgradeModule ], + imports: [HttpClientTestingModule, UpgradeModule], providers: [ AnnotationService, AudioOscillatorService, @@ -73,12 +73,10 @@ function componentHasStudentWork() { function expectComponentHasStudentWork(componentState: any, expectedResult: boolean) { expect(service.componentStateHasStudentWork(componentState, {})).toEqual(expectedResult); } - it('should check if a component state has student work when it does not have student work', - () => { + it('should check if a component state has student work when it does not have student work', () => { expectComponentHasStudentWork(createComponentState([]), false); - }) - it('should check if a component state has student work when it does have student work', - () => { + }); + it('should check if a component state has student work when it does have student work', () => { expectComponentHasStudentWork(createComponentState([440]), true); - }) -} \ No newline at end of file + }); +} diff --git a/src/main/webapp/site/src/app/services/cRaterService.spec.ts b/src/main/webapp/site/src/app/services/cRaterService.spec.ts index 26b3b0a801..27e3a905ff 100644 --- a/src/main/webapp/site/src/app/services/cRaterService.spec.ts +++ b/src/main/webapp/site/src/app/services/cRaterService.spec.ts @@ -10,8 +10,8 @@ let http: HttpTestingController; describe('CRaterService', () => { beforeEach(() => { TestBed.configureTestingModule({ - imports: [ HttpClientTestingModule, UpgradeModule ], - providers: [ ConfigService, CRaterService ] + imports: [HttpClientTestingModule, UpgradeModule], + providers: [ConfigService, CRaterService] }); http = TestBed.get(HttpTestingController); configService = TestBed.get(ConfigService); @@ -36,14 +36,15 @@ describe('CRaterService', () => { function makeCRaterScoringRequest() { describe('makeCRaterScoringRequest()', () => { it('should make a CRater scoring request', () => { - spyOn(configService, 'getCRaterRequestURL').and.returnValue('/c-rater') + spyOn(configService, 'getCRaterRequestURL').and.returnValue('/c-rater'); const itemId = 'ColdBeverage1Sub'; const responseId = 1; const studentData = 'Hello World.'; service.makeCRaterScoringRequest(itemId, responseId, studentData); http.expectOne({ - url: `/c-rater/score?itemId=${itemId}&responseId=${responseId}` + - `&studentData=${encodeURI(studentData)}`, + url: + `/c-rater/score?itemId=${itemId}&responseId=${responseId}` + + `&studentData=${encodeURI(studentData)}`, method: 'GET' }); }); @@ -178,7 +179,7 @@ function createScoringRule(score, feedbackText) { return { score: score, feedbackText: feedbackText - } + }; } function getCRaterScoringRuleByScore() { @@ -187,10 +188,7 @@ function getCRaterScoringRuleByScore() { const scoringRule2 = createScoringRule(2, 'You received a score of 2.'); const component = { cRater: { - scoringRules: [ - scoringRule1, - scoringRule2 - ] + scoringRules: [scoringRule1, scoringRule2] } }; it('should get CRater scoring rule by score 1', () => { @@ -211,10 +209,7 @@ function getCRaterFeedbackTextByScore() { const scoringRule2 = createScoringRule(2, feedbackText2); const component = { cRater: { - scoringRules: [ - scoringRule1, - scoringRule2 - ] + scoringRules: [scoringRule1, scoringRule2] } }; it('should get CRater feedback text by score 1', () => { @@ -247,13 +242,15 @@ function getMultipleAttemptCRaterFeedbackTextByScore() { }; it('should get multiple attempt CRater feedback text by score 1 then 2', () => { - expect(service.getMultipleAttemptCRaterFeedbackTextByScore(component, 1, 2)) - .toEqual(feedbackText1); + expect(service.getMultipleAttemptCRaterFeedbackTextByScore(component, 1, 2)).toEqual( + feedbackText1 + ); }); it('should get multiple attempt CRater feedback text by score 2 then 1', () => { - expect(service.getMultipleAttemptCRaterFeedbackTextByScore(component, 2, 1)) - .toEqual(feedbackText2); + expect(service.getMultipleAttemptCRaterFeedbackTextByScore(component, 2, 1)).toEqual( + feedbackText2 + ); }); }); } @@ -276,16 +273,17 @@ function getMultipleAttemptCRaterScoringRuleByScore() { ] } }; - expect(service.getMultipleAttemptCRaterScoringRuleByScore(component, 1, 2)) - .toEqual(multipleAttemptScoringRule1To2); + expect(service.getMultipleAttemptCRaterScoringRuleByScore(component, 1, 2)).toEqual( + multipleAttemptScoringRule1To2 + ); }); const multipleAttemptScoringRule1To45 = { - scoreSequence: [1, "4-5"], + scoreSequence: [1, '4-5'], feedbackText: 'You improved a lot.' }; const multipleAttemptScoringRule1To345 = { - scoreSequence: [1, "3,4,5"], + scoreSequence: [1, '3,4,5'], feedbackText: 'You improved a lot.' }; const multipleAttemptScoringRule2To1 = { @@ -302,8 +300,9 @@ function getMultipleAttemptCRaterScoringRuleByScore() { multipleAttemptScoringRule1To45, multipleAttemptScoringRule2To1 ]; - expect(service.getMultipleAttemptCRaterScoringRuleByScore(component, 1, 5)) - .toEqual(multipleAttemptScoringRule1To45); + expect(service.getMultipleAttemptCRaterScoringRuleByScore(component, 1, 5)).toEqual( + multipleAttemptScoringRule1To45 + ); }); it('should get multiple attempt CRater scoring rule by score with comma separated values', () => { @@ -311,15 +310,16 @@ function getMultipleAttemptCRaterScoringRuleByScore() { multipleAttemptScoringRule1To345, multipleAttemptScoringRule2To1 ]; - expect(service.getMultipleAttemptCRaterScoringRuleByScore(component, 1, 4)) - .toEqual(multipleAttemptScoringRule1To345); + expect(service.getMultipleAttemptCRaterScoringRuleByScore(component, 1, 4)).toEqual( + multipleAttemptScoringRule1To345 + ); }); } function makeCRaterVerifyRequest() { describe('makeCRaterVerifyRequest()', () => { it('should make a CRater verify request', () => { - spyOn(configService, 'getCRaterRequestURL').and.returnValue('/c-rater') + spyOn(configService, 'getCRaterRequestURL').and.returnValue('/c-rater'); const itemId = 'ColdBeverage1Sub'; service.makeCRaterVerifyRequest(itemId); http.expectOne({ diff --git a/src/main/webapp/site/src/app/services/conceptMapService.spec.ts b/src/main/webapp/site/src/app/services/conceptMapService.spec.ts index 3728a95c97..bf3a2f1f41 100644 --- a/src/main/webapp/site/src/app/services/conceptMapService.spec.ts +++ b/src/main/webapp/site/src/app/services/conceptMapService.spec.ts @@ -1,15 +1,15 @@ -import { TestBed } from "@angular/core/testing"; -import { HttpClientTestingModule } from "@angular/common/http/testing"; -import { UpgradeModule } from "@angular/upgrade/static"; -import { AnnotationService } from "../../../../wise5/services/annotationService"; -import { ProjectService } from "../../../../wise5/services/projectService"; -import { StudentAssetService } from "../../../../wise5/services/studentAssetService"; -import { StudentDataService } from "../../../../wise5/services/studentDataService"; -import { TagService } from "../../../../wise5/services/tagService"; -import { UtilService } from "../../../../wise5/services/utilService"; -import { ConceptMapService } from "../../../../wise5/components/conceptMap/conceptMapService"; -import { ConfigService } from "../../../../wise5/services/configService"; -import { SessionService } from "../../../../wise5/services/sessionService"; +import { TestBed } from '@angular/core/testing'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { UpgradeModule } from '@angular/upgrade/static'; +import { AnnotationService } from '../../../../wise5/services/annotationService'; +import { ProjectService } from '../../../../wise5/services/projectService'; +import { StudentAssetService } from '../../../../wise5/services/studentAssetService'; +import { StudentDataService } from '../../../../wise5/services/studentDataService'; +import { TagService } from '../../../../wise5/services/tagService'; +import { UtilService } from '../../../../wise5/services/utilService'; +import { ConceptMapService } from '../../../../wise5/components/conceptMap/conceptMapService'; +import { ConfigService } from '../../../../wise5/services/configService'; +import { SessionService } from '../../../../wise5/services/sessionService'; let service: ConceptMapService; let conceptMapNode1: any; @@ -44,7 +44,7 @@ const link2DestinationNodeInstanceId = 'studentNode2'; describe('ConceptMapService', () => { beforeEach(() => { TestBed.configureTestingModule({ - imports: [ HttpClientTestingModule, UpgradeModule ], + imports: [HttpClientTestingModule, UpgradeModule], providers: [ AnnotationService, ConceptMapService, @@ -58,16 +58,38 @@ describe('ConceptMapService', () => { ] }); service = TestBed.get(ConceptMapService); - conceptMapNode1 = createConceptMapNode(node1OriginalId, node1InstanceId, node1Label, - node1X, node1Y); - conceptMapNode2 = createConceptMapNode(node2OriginalId, node2InstanceId, node2Label, - node2X, node2Y); - conceptMapLink1 = createConceptMapLink(link1OriginalId, link1InstanceId, link1Label, - link1SourceNodeOriginalId, link1SourceNodeInstanceId, link1DestinationNodeOriginalId, - link1DestinationNodeInstanceId); - conceptMapLink2 = createConceptMapLink(link2OriginalId, link2InstanceId, link2Label, - link2SourceNodeOriginalId, link2SourceNodeInstanceId, link2DestinationNodeOriginalId, - link2DestinationNodeInstanceId); + conceptMapNode1 = createConceptMapNode( + node1OriginalId, + node1InstanceId, + node1Label, + node1X, + node1Y + ); + conceptMapNode2 = createConceptMapNode( + node2OriginalId, + node2InstanceId, + node2Label, + node2X, + node2Y + ); + conceptMapLink1 = createConceptMapLink( + link1OriginalId, + link1InstanceId, + link1Label, + link1SourceNodeOriginalId, + link1SourceNodeInstanceId, + link1DestinationNodeOriginalId, + link1DestinationNodeInstanceId + ); + conceptMapLink2 = createConceptMapLink( + link2OriginalId, + link2InstanceId, + link2Label, + link2SourceNodeOriginalId, + link2SourceNodeInstanceId, + link2DestinationNodeOriginalId, + link2DestinationNodeInstanceId + ); }); createComponent(); isCompleted(); @@ -115,8 +137,11 @@ function createAnnotation(type: string, displayToStudent: boolean) { }; } -function createComponentContent(nodes: any[] = [], links: any[] = [], - starterConceptMap: any = null) { +function createComponentContent( + nodes: any[] = [], + links: any[] = [], + starterConceptMap: any = null +) { return { nodes: nodes, links: links, @@ -131,8 +156,13 @@ function createConceptMapData(nodes: any[] = [], links: any[] = []) { }; } -function createConceptMapNode(originalId: string, instanceId: string, label: string, x: number, - y: number) { +function createConceptMapNode( + originalId: string, + instanceId: string, + label: string, + x: number, + y: number +) { return { originalId: originalId, instanceId: instanceId, @@ -142,9 +172,15 @@ function createConceptMapNode(originalId: string, instanceId: string, label: str }; } -function createConceptMapLink(originalId: string, instanceId: string, label: string, - sourceNodeOriginalId: string, sourceNodeInstanceId: string, destinationNodeOriginalId: string, - destinationNodeInstanceId: string) { +function createConceptMapLink( + originalId: string, + instanceId: string, + label: string, + sourceNodeOriginalId: string, + sourceNodeInstanceId: string, + destinationNodeOriginalId: string, + destinationNodeInstanceId: string +) { return { originalId: originalId, instanceId: instanceId, @@ -192,10 +228,15 @@ function isCompleted() { node = {}; component = {}; }); - function expectIsCompleted(component: any, componentStates: any[], node: any, - expectedResult: boolean) { - expect(service.isCompleted(component, componentStates, null, null, node)) - .toEqual(expectedResult); + function expectIsCompleted( + component: any, + componentStates: any[], + node: any, + expectedResult: boolean + ) { + expect(service.isCompleted(component, componentStates, null, null, node)).toEqual( + expectedResult + ); } it(`should check if is completed when submit is required and there are no submit component states`, () => { @@ -209,12 +250,10 @@ function isCompleted() { componentStates.push(createComponentState([], [], true)); expectIsCompleted(component, componentStates, node, true); }); - it(`should check if is completed when submit is not required and there are no component states`, - () => { + it(`should check if is completed when submit is not required and there are no component states`, () => { expectIsCompleted(component, componentStates, node, false); }); - it(`should check if is completed when submit is not required and there are component states`, - () => { + it(`should check if is completed when submit is not required and there are component states`, () => { componentStates.push(createComponentState()); expectIsCompleted(component, componentStates, node, true); }); @@ -337,38 +376,47 @@ function getLinksByLabels() { } function spyOnEvaluateRuleByRuleName(ruleToResult: any) { - spyOn(service, 'evaluateRuleByRuleName').and.callFake((componentContent: any, - conceptMapData: any, ruleName: string) => { + spyOn(service, 'evaluateRuleByRuleName').and.callFake( + (componentContent: any, conceptMapData: any, ruleName: string) => { return ruleToResult[ruleName]; - }); + } + ); } function any() { - function expectAny(componentContent: any, conceptMapData: any, ruleNames: string[], - expectedResult: boolean) { + function expectAny( + componentContent: any, + conceptMapData: any, + ruleNames: string[], + expectedResult: boolean + ) { expect(service.any(componentContent, conceptMapData, ruleNames)).toEqual(expectedResult); } it('should check if any rule is satisfied when none are satisified', () => { - spyOnEvaluateRuleByRuleName({ 'rule1': false, 'rule2': false }); + spyOnEvaluateRuleByRuleName({ rule1: false, rule2: false }); expectAny({}, {}, ['rule1', 'rule2'], false); }); it('should check if any rule is satisfied when one is satisified', () => { - spyOnEvaluateRuleByRuleName({ 'rule1': false, 'rule2': true }); + spyOnEvaluateRuleByRuleName({ rule1: false, rule2: true }); expectAny({}, {}, ['rule1', 'rule2'], true); }); } function all() { - function expectAll(componentContent: any, conceptMapData: any, ruleNames: string[], - expectedResult: boolean) { + function expectAll( + componentContent: any, + conceptMapData: any, + ruleNames: string[], + expectedResult: boolean + ) { expect(service.all(componentContent, conceptMapData, ruleNames)).toEqual(expectedResult); } it('should check if all rules are satisfied when only one is satisified', () => { - spyOnEvaluateRuleByRuleName({ 'rule1': false, 'rule2': true }); + spyOnEvaluateRuleByRuleName({ rule1: false, rule2: true }); expectAll({}, {}, ['rule1', 'rule2'], false); }); it('should check if all rules are satisfied when all are satisified', () => { - spyOnEvaluateRuleByRuleName({ 'rule1': true, 'rule2': true }); + spyOnEvaluateRuleByRuleName({ rule1: true, rule2: true }); expectAll({}, {}, ['rule1', 'rule2'], true); }); } @@ -425,10 +473,14 @@ function componentStateHasStudentWork() { componentState = createComponentState(); componentContent = createComponentContent(); }); - function expectComponentStateHasStudentWork(componentState: any, componentContent: any, - expectedResult: boolean) { - expect(service.componentStateHasStudentWork(componentState, componentContent)) - .toEqual(expectedResult); + function expectComponentStateHasStudentWork( + componentState: any, + componentContent: any, + expectedResult: boolean + ) { + expect(service.componentStateHasStudentWork(componentState, componentContent)).toEqual( + expectedResult + ); } it('should check if component state has student work when it does not have work', () => { expectComponentStateHasStudentWork(componentState, componentContent, false); @@ -440,47 +492,86 @@ function componentStateHasStudentWork() { } function isStudentConceptMapDifferentThanStarterConceptMap() { - function expectIsStudentConceptMapDifferentThanStarterConceptMap(studentNodes: any[], - studentLinks: any[], starterNodes: any[], starterLinks: any[], expectedResult: boolean) { + function expectIsStudentConceptMapDifferentThanStarterConceptMap( + studentNodes: any[], + studentLinks: any[], + starterNodes: any[], + starterLinks: any[], + expectedResult: boolean + ) { const studentConceptMap = createConceptMapData(studentNodes, studentLinks); const starterConceptMap = createConceptMapData(starterNodes, starterLinks); - expect(service.isStudentConceptMapDifferentThanStarterConceptMap(studentConceptMap, - starterConceptMap)).toEqual(expectedResult); + expect( + service.isStudentConceptMapDifferentThanStarterConceptMap( + studentConceptMap, + starterConceptMap + ) + ).toEqual(expectedResult); } it(`should check if student concept map is different than starter concept map when nodes are not different`, () => { - expectIsStudentConceptMapDifferentThanStarterConceptMap([conceptMapNode1], [], - [conceptMapNode1], [], false); + expectIsStudentConceptMapDifferentThanStarterConceptMap( + [conceptMapNode1], + [], + [conceptMapNode1], + [], + false + ); }); it(`should check if student concept map is different than starter concept map when nodes are different`, () => { - expectIsStudentConceptMapDifferentThanStarterConceptMap([conceptMapNode1], [], - [conceptMapNode2], [], true); + expectIsStudentConceptMapDifferentThanStarterConceptMap( + [conceptMapNode1], + [], + [conceptMapNode2], + [], + true + ); }); it(`should check if student concept map is different than starter concept map when there are different number of nodes`, () => { - expectIsStudentConceptMapDifferentThanStarterConceptMap([conceptMapNode1], [], - [conceptMapNode1, conceptMapNode2], [], true); + expectIsStudentConceptMapDifferentThanStarterConceptMap( + [conceptMapNode1], + [], + [conceptMapNode1, conceptMapNode2], + [], + true + ); }); it(`should check if student concept map is different than starter concept map when links are not different`, () => { - expectIsStudentConceptMapDifferentThanStarterConceptMap([], [conceptMapLink1], [], - [conceptMapLink1], false); + expectIsStudentConceptMapDifferentThanStarterConceptMap( + [], + [conceptMapLink1], + [], + [conceptMapLink1], + false + ); }); it(`should check if student concept map is different than starter concept map when links are different`, () => { - expectIsStudentConceptMapDifferentThanStarterConceptMap([], [conceptMapLink1], [], - [conceptMapLink2], true); + expectIsStudentConceptMapDifferentThanStarterConceptMap( + [], + [conceptMapLink1], + [], + [conceptMapLink2], + true + ); }); it(`should check if student concept map is different than starter concept map when there are different number of links`, () => { - expectIsStudentConceptMapDifferentThanStarterConceptMap([], [conceptMapLink1], [], - [conceptMapLink1, conceptMapLink2], true); + expectIsStudentConceptMapDifferentThanStarterConceptMap( + [], + [conceptMapLink1], + [], + [conceptMapLink1, conceptMapLink2], + true + ); }); } function generateImageFromRenderedComponentState() { - // TODO + // TODO } function getNextAvailableId() { @@ -495,8 +586,11 @@ function displayAnnotation() { beforeEach(() => { componentContent = createComponentContent([], []); }); - function expectDisplayAnnotation(componentContent: any, annotation: any, - expectedResult: boolean) { + function expectDisplayAnnotation( + componentContent: any, + annotation: any, + expectedResult: boolean + ) { expect(service.displayAnnotation(componentContent, annotation)).toEqual(expectedResult); } it(`should check if we should display the annotation to the student when display to student is @@ -505,12 +599,12 @@ function displayAnnotation() { }); it(`should check if we should display the annotation to the student when type is auto score`, () => { - componentContent.showAutoScore = true + componentContent.showAutoScore = true; expectDisplayAnnotation(componentContent, createAnnotation('autoScore', true), true); }); it(`should check if we should display the annotation to the student when type is auto comment`, () => { - componentContent.showAutoFeedback = true + componentContent.showAutoFeedback = true; expectDisplayAnnotation(componentContent, createAnnotation('autoComment', true), true); }); -} \ No newline at end of file +} diff --git a/src/main/webapp/site/src/app/services/config.service.spec.ts b/src/main/webapp/site/src/app/services/config.service.spec.ts index 954b71d65a..07699e10df 100644 --- a/src/main/webapp/site/src/app/services/config.service.spec.ts +++ b/src/main/webapp/site/src/app/services/config.service.spec.ts @@ -5,8 +5,8 @@ import { HttpClientTestingModule } from '@angular/common/http/testing'; describe('ConfigService', () => { beforeEach(() => { TestBed.configureTestingModule({ - providers: [ ConfigService ], - imports: [ HttpClientTestingModule ] + providers: [ConfigService], + imports: [HttpClientTestingModule] }); }); diff --git a/src/main/webapp/site/src/app/services/config.service.ts b/src/main/webapp/site/src/app/services/config.service.ts index 1a1f82719e..8aa8fba2bc 100644 --- a/src/main/webapp/site/src/app/services/config.service.ts +++ b/src/main/webapp/site/src/app/services/config.service.ts @@ -1,19 +1,17 @@ import { Injectable } from '@angular/core'; -import { HttpClient, HttpHeaders } from "@angular/common/http"; -import { BehaviorSubject, Observable } from "rxjs"; -import { Config } from "../domain/config"; +import { HttpClient, HttpHeaders } from '@angular/common/http'; +import { BehaviorSubject, Observable } from 'rxjs'; +import { Config } from '../domain/config'; import { Announcement } from '../domain/announcement'; @Injectable() export class ConfigService { - private userConfigUrl = '/api/user/config'; private announcementUrl = '/announcement'; private config$: BehaviorSubject = new BehaviorSubject(null); private timeDiff: number = 0; - constructor(private http: HttpClient) { - } + constructor(private http: HttpClient) {} getConfig(): Observable { return this.config$; @@ -21,8 +19,9 @@ export class ConfigService { retrieveConfig() { const headers = new HttpHeaders({ 'Cache-Control': 'no-cache' }); - this.http.get(this.userConfigUrl, { headers: headers }) - .subscribe(config => { + this.http + .get(this.userConfigUrl, { headers: headers }) + .subscribe((config) => { this.config$.next(config); this.timeDiff = Date.now() - config.currentTime; }); diff --git a/src/main/webapp/site/src/app/services/configService.spec.ts b/src/main/webapp/site/src/app/services/configService.spec.ts index bb047d3acc..341cbcf9bd 100644 --- a/src/main/webapp/site/src/app/services/configService.spec.ts +++ b/src/main/webapp/site/src/app/services/configService.spec.ts @@ -11,8 +11,8 @@ let configJSON; describe('ConfigService', () => { beforeEach(() => { TestBed.configureTestingModule({ - imports: [ HttpClientTestingModule, UpgradeModule ], - providers: [ ConfigService ] + imports: [HttpClientTestingModule, UpgradeModule], + providers: [ConfigService] }); http = TestBed.get(HttpTestingController); service = TestBed.get(ConfigService); @@ -60,7 +60,7 @@ function sortTheClassmatesAlphabeticallyByNameWhenSettingConfig() { function getLocale() { it('should get locale in config', () => { - service.setConfig({locale: 'ja'}); + service.setConfig({ locale: 'ja' }); expect(service.getLocale()).toEqual('ja'); }); it('should get default locale if config does not specify', () => { @@ -71,11 +71,11 @@ function getLocale() { function getMode() { it('should get modes', () => { - service.setConfig({mode: 'run'}); + service.setConfig({ mode: 'run' }); expect(service.getMode()).toEqual('run'); expect(service.isPreview()).toEqual(false); - service.setConfig({mode: 'preview'}); + service.setConfig({ mode: 'preview' }); expect(service.getMode()).toEqual('preview'); expect(service.isPreview()).toEqual(true); }); @@ -83,7 +83,7 @@ function getMode() { function getPeriodIdOfStudent() { it('should get period id of student', () => { - service.setConfig({userInfo:{myUserInfo:{periodId: 1}}}); + service.setConfig({ userInfo: { myUserInfo: { periodId: 1 } } }); expect(service.getPeriodId()).toEqual(1); }); } @@ -129,7 +129,7 @@ function getPeriodIdGivenWorkgroupId() { expect(service.getUserInfoByWorkgroupId).toHaveBeenCalledWith(-1); }); - it('should return workgroup\'s period id', () => { + it("should return workgroup's period id", () => { expect(service.getPeriodIdByWorkgroupId(8)).toEqual(1); expect(service.getUserInfoByWorkgroupId).toHaveBeenCalledWith(8); }); @@ -140,7 +140,7 @@ function calculateIsRunActive() { describe('calculateIsRunActive', () => { calculateIsRunActive_RunOnlyHasAStartTime_ReturnWhetherRunIsActive(); calculateIsRunActive_RunHasAStartTimeAndEndTimeAndIsNotLocked_ReturnWhetherRunIsActive(); - calculateIsRunActive_RunHasAStartTimeAndEndTimeAndIsLocked_ReturnWhetherRunIsActive + calculateIsRunActive_RunHasAStartTimeAndEndTimeAndIsLocked_ReturnWhetherRunIsActive; }); } diff --git a/src/main/webapp/site/src/app/services/data.service.spec.ts b/src/main/webapp/site/src/app/services/data.service.spec.ts index 6281663081..4886eb05cd 100644 --- a/src/main/webapp/site/src/app/services/data.service.spec.ts +++ b/src/main/webapp/site/src/app/services/data.service.spec.ts @@ -12,11 +12,10 @@ let service: DataService; let projectService: ProjectService; describe('DataService', () => { - beforeEach(() => { TestBed.configureTestingModule({ - imports: [ HttpClientTestingModule, UpgradeModule ], - providers: [ ConfigService, ProjectService, SessionService, UtilService ] + imports: [HttpClientTestingModule, UpgradeModule], + providers: [ConfigService, ProjectService, SessionService, UtilService] }); service = TestBed.inject(DataService); projectService = TestBed.inject(ProjectService); @@ -37,10 +36,12 @@ function setCurrentNode() { const node2 = { id: 'node2' }; service.setCurrentNode(node1); expect(service.currentNode).toEqual(node1); - spyOn(projectService, 'isGroupNode').and.callFake(() => { return false }); + spyOn(projectService, 'isGroupNode').and.callFake(() => { + return false; + }); spyOn(service, 'broadcastCurrentNodeChanged').and.callFake(() => {}); service.setCurrentNode(node2); expect(service.previousStep).toEqual(node1); expect(service.currentNode).toEqual(node2); - }) + }); } diff --git a/src/main/webapp/site/src/app/services/data.service.ts b/src/main/webapp/site/src/app/services/data.service.ts index 22eb7d2b90..b57f190081 100644 --- a/src/main/webapp/site/src/app/services/data.service.ts +++ b/src/main/webapp/site/src/app/services/data.service.ts @@ -7,7 +7,6 @@ import { ProjectService } from '../../../../wise5/services/projectService'; providedIn: 'root' }) export class DataService { - currentNode = null; previousStep = null; private currentNodeChangedSource: Subject = new Subject(); @@ -15,17 +14,11 @@ export class DataService { private studentWorkReceivedSource: Subject = new Subject(); public studentWorkReceived$ = this.studentWorkReceivedSource.asObservable(); - constructor( - protected upgrade: UpgradeModule, - protected ProjectService: ProjectService) { } - - isCompleted(nodeId, componentId) { + constructor(protected upgrade: UpgradeModule, protected ProjectService: ProjectService) {} - } + isCompleted(nodeId, componentId) {} - endCurrentNodeAndSetCurrentNodeByNodeId(nextNodeId) { - - } + endCurrentNodeAndSetCurrentNodeByNodeId(nextNodeId) {} getCurrentNode() { return this.currentNode; @@ -46,13 +39,9 @@ export class DataService { return []; } - evaluateCriterias(criteria) { + evaluateCriterias(criteria) {} - } - - saveVLEEvent(nodeId, componentId, componentType, category, event, eventData) { - - } + saveVLEEvent(nodeId, componentId, componentType, category, event, eventData) {} setCurrentNodeByNodeId(nodeId) { this.setCurrentNode(this.ProjectService.getNodeById(nodeId)); diff --git a/src/main/webapp/site/src/app/services/discussionService.spec.ts b/src/main/webapp/site/src/app/services/discussionService.spec.ts index 686be545e8..fd86cd2694 100644 --- a/src/main/webapp/site/src/app/services/discussionService.spec.ts +++ b/src/main/webapp/site/src/app/services/discussionService.spec.ts @@ -1,15 +1,15 @@ -import { DiscussionService } from "../../../../wise5/components/discussion/discussionService"; -import { TestBed } from "@angular/core/testing"; -import { HttpClientTestingModule, HttpTestingController } from "@angular/common/http/testing"; -import { UpgradeModule } from "@angular/upgrade/static"; -import { AnnotationService } from "../../../../wise5/services/annotationService"; -import { ConfigService } from "../../../../wise5/services/configService"; -import { ProjectService } from "../../../../wise5/services/projectService"; -import { StudentAssetService } from "../../../../wise5/services/studentAssetService"; -import { StudentDataService } from "../../../../wise5/services/studentDataService"; -import { TagService } from "../../../../wise5/services/tagService"; -import { UtilService } from "../../../../wise5/services/utilService"; -import { SessionService } from "../../../../wise5/services/sessionService"; +import { DiscussionService } from '../../../../wise5/components/discussion/discussionService'; +import { TestBed } from '@angular/core/testing'; +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; +import { UpgradeModule } from '@angular/upgrade/static'; +import { AnnotationService } from '../../../../wise5/services/annotationService'; +import { ConfigService } from '../../../../wise5/services/configService'; +import { ProjectService } from '../../../../wise5/services/projectService'; +import { StudentAssetService } from '../../../../wise5/services/studentAssetService'; +import { StudentDataService } from '../../../../wise5/services/studentDataService'; +import { TagService } from '../../../../wise5/services/tagService'; +import { UtilService } from '../../../../wise5/services/utilService'; +import { SessionService } from '../../../../wise5/services/sessionService'; let service: DiscussionService; let http: HttpTestingController; @@ -19,7 +19,7 @@ let studentDataService: StudentDataService; describe('DiscussionService', () => { beforeEach(() => { TestBed.configureTestingModule({ - imports: [ HttpClientTestingModule, UpgradeModule ], + imports: [HttpClientTestingModule, UpgradeModule], providers: [ AnnotationService, ConfigService, @@ -107,8 +107,12 @@ function isCompleted() { componentStates = []; nodeEvents = []; }); - function expectIsCompleted(component: any, componentStates: any[], nodeEvents: any[], - expectedResult: boolean) { + function expectIsCompleted( + component: any, + componentStates: any[], + nodeEvents: any[], + expectedResult: boolean + ) { expect(service.isCompleted(component, componentStates, [], nodeEvents)).toEqual(expectedResult); } it(`should check if a component is completed when it does not have a show work connected component @@ -116,17 +120,14 @@ function isCompleted() { expectIsCompleted(component, componentStates, nodeEvents, false); }); it('should check if a component is completed when it has a show work connected component', () => { - spyOn(studentDataService, 'getComponentStatesByNodeIdAndComponentId').and.returnValue( - [createComponentState('Hello World')] - ); - component.connectedComponents = [ - createConnectedComponent('node1', 'component1', 'showWork') - ]; + spyOn(studentDataService, 'getComponentStatesByNodeIdAndComponentId').and.returnValue([ + createComponentState('Hello World') + ]); + component.connectedComponents = [createConnectedComponent('node1', 'component1', 'showWork')]; nodeEvents.push(createNodeEvent('nodeEntered')); expectIsCompleted(component, componentStates, nodeEvents, true); }); - it(`should check if a component is completed when it has a component state with a response`, - () => { + it(`should check if a component is completed when it has a component state with a response`, () => { componentStates.push(createComponentState('Hello World')); expectIsCompleted(component, componentStates, nodeEvents, true); }); @@ -139,10 +140,13 @@ function hasShowWorkConnectedComponentThatHasWork() { connectedComponents: [] }; }); - function expectHasShowWorkConnectedComponentThatHasWork(componentContent: any, - expectedResult: boolean) { - expect(service.hasShowWorkConnectedComponentThatHasWork(componentContent)) - .toEqual(expectedResult); + function expectHasShowWorkConnectedComponentThatHasWork( + componentContent: any, + expectedResult: boolean + ) { + expect(service.hasShowWorkConnectedComponentThatHasWork(componentContent)).toEqual( + expectedResult + ); } it(`should check if there is a show work connected component when there are no connected components`, () => { @@ -152,9 +156,9 @@ function hasShowWorkConnectedComponentThatHasWork() { component`, () => { const connectedComponent = createConnectedComponent('node1', 'component1', 'showWork'); componentContent.connectedComponents.push(connectedComponent); - spyOn(studentDataService, 'getComponentStatesByNodeIdAndComponentId').and.returnValue( - [createComponentState('Hello World')] - ); + spyOn(studentDataService, 'getComponentStatesByNodeIdAndComponentId').and.returnValue([ + createComponentState('Hello World') + ]); expectHasShowWorkConnectedComponentThatHasWork(componentContent, true); }); } @@ -174,14 +178,17 @@ function hasNodeEnteredEvent() { function getClassmateResponses() { it('should get classmate responses', () => { spyOn(configService, 'getConfigParam').and.returnValue('/student/data'); - service.getClassmateResponses(1, 2, [{ nodeId: 'node1', componentId: 'component1' }]) - .then((data: any) => { - expect(data.studentWorkList[0].studentData.response).toEqual('Hello World'); - }); - const expectedRequest = '/student/data?runId=1&periodId=2&getStudentWork=true&getAnnotations=' + - 'true&components=%7B%22nodeId%22:%22node1%22,%22componentId%22:%22component1%22%7D'; - http.expectOne(expectedRequest) - .flush({studentWorkList: [{ studentData: { response: 'Hello World' }}]}); + service + .getClassmateResponses(1, 2, [{ nodeId: 'node1', componentId: 'component1' }]) + .then((data: any) => { + expect(data.studentWorkList[0].studentData.response).toEqual('Hello World'); + }); + const expectedRequest = + '/student/data?runId=1&periodId=2&getStudentWork=true&getAnnotations=' + + 'true&components=%7B%22nodeId%22:%22node1%22,%22componentId%22:%22component1%22%7D'; + http + .expectOne(expectedRequest) + .flush({ studentWorkList: [{ studentData: { response: 'Hello World' } }] }); }); } @@ -232,10 +239,14 @@ function componentStateHasStudentWork() { componentState = createComponentState(''); componentContent = createDiscussionComponent(''); }); - function expectComponentStatehasStudentWork(componentState: any, componentContent: any, - expectedResult: boolean) { - expect(service.componentStateHasStudentWork(componentState, componentContent)) - .toEqual(expectedResult); + function expectComponentStatehasStudentWork( + componentState: any, + componentContent: any, + expectedResult: boolean + ) { + expect(service.componentStateHasStudentWork(componentState, componentContent)).toEqual( + expectedResult + ); } it('should check if a component state has work when it has an attachment', () => { componentState.studentData.attachments.push({}); @@ -288,17 +299,19 @@ function isStudentResponseDifferentFromStarterSentence() { componentState = createComponentState(''); componentContent = createDiscussionComponent(''); }); - function expectIsStudentResponseDifferentFromStarterSentence(componentState: any, - componentContent: any, expectedResult: boolean) { - expect(service.isStudentResponseDifferentFromStarterSentence(componentState, componentContent)) - .toEqual(expectedResult); + function expectIsStudentResponseDifferentFromStarterSentence( + componentState: any, + componentContent: any, + expectedResult: boolean + ) { + expect( + service.isStudentResponseDifferentFromStarterSentence(componentState, componentContent) + ).toEqual(expectedResult); } - it('should check if student response is different from starter sentence when it is the same', - () => { + it('should check if student response is different from starter sentence when it is the same', () => { expectIsStudentResponseDifferentFromStarterSentence(componentState, componentContent, false); }); - it('should check if student response is different from starter sentence when it is different', - () => { + it('should check if student response is different from starter sentence when it is different', () => { componentState.studentData.response = 'Hello World'; expectIsStudentResponseDifferentFromStarterSentence(componentState, componentContent, true); }); @@ -336,4 +349,4 @@ function isStudentWorkHasAttachment() { componentState.studentData.attachments.push({}); expectIsStudentWorkHasAttachment(componentState, true); }); -} \ No newline at end of file +} diff --git a/src/main/webapp/site/src/app/services/drawService.spec.ts b/src/main/webapp/site/src/app/services/drawService.spec.ts index b1caa96fc7..3e2134794e 100644 --- a/src/main/webapp/site/src/app/services/drawService.spec.ts +++ b/src/main/webapp/site/src/app/services/drawService.spec.ts @@ -1,15 +1,15 @@ -import { TestBed } from "@angular/core/testing"; -import { HttpClientTestingModule } from "@angular/common/http/testing"; -import { UpgradeModule } from "@angular/upgrade/static"; -import { AnnotationService } from "../../../../wise5/services/annotationService"; -import { ConfigService } from "../../../../wise5/services/configService"; -import { ProjectService } from "../../../../wise5/services/projectService"; -import { StudentAssetService } from "../../../../wise5/services/studentAssetService"; -import { StudentDataService } from "../../../../wise5/services/studentDataService"; -import { TagService } from "../../../../wise5/services/tagService"; -import { UtilService } from "../../../../wise5/services/utilService"; -import { DrawService } from "../../../../wise5/components/draw/drawService"; -import { SessionService } from "../../../../wise5/services/sessionService"; +import { TestBed } from '@angular/core/testing'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { UpgradeModule } from '@angular/upgrade/static'; +import { AnnotationService } from '../../../../wise5/services/annotationService'; +import { ConfigService } from '../../../../wise5/services/configService'; +import { ProjectService } from '../../../../wise5/services/projectService'; +import { StudentAssetService } from '../../../../wise5/services/studentAssetService'; +import { StudentDataService } from '../../../../wise5/services/studentDataService'; +import { TagService } from '../../../../wise5/services/tagService'; +import { UtilService } from '../../../../wise5/services/utilService'; +import { DrawService } from '../../../../wise5/components/draw/drawService'; +import { SessionService } from '../../../../wise5/services/sessionService'; let service: DrawService; let defaultDrawDataWithNoObjectsField: string = '{"canvas":{}}'; @@ -19,7 +19,7 @@ let drawDataWithObjects: string = '{"canvas":{"objects":[{}]}}'; describe('DrawService', () => { beforeEach(() => { TestBed.configureTestingModule({ - imports: [ HttpClientTestingModule, UpgradeModule ], + imports: [HttpClientTestingModule, UpgradeModule], providers: [ AnnotationService, ConfigService, @@ -113,8 +113,10 @@ function isCompleted() { } function hasComponentStateWithIsSubmitTrue() { - function expectHasComponentStateWithIsSubmitTrue(componentStates: any[], - expectedResult: boolean) { + function expectHasComponentStateWithIsSubmitTrue( + componentStates: any[], + expectedResult: boolean + ) { expect(service.hasComponentStateWithIsSubmitTrue(componentStates)).toEqual(expectedResult); } it('should check if any component state has is submit true when there are none', () => { @@ -131,8 +133,7 @@ function hasComponentStateWithDrawData() { function expectHasComponentStateWithDrawData(componentStates: any[], expectedResult: boolean) { expect(service.hasComponentStateWithDrawData(componentStates)).toEqual(expectedResult); } - it('should check if there is a component state with draw data when there are no component states', - () => { + it('should check if there is a component state with draw data when there are no component states', () => { const componentStates = []; expectHasComponentStateWithDrawData(componentStates, false); }); @@ -159,10 +160,14 @@ function componentStateHasStudentWork() { componentState = createComponentState(drawDataWithEmptyObjects); componentContent = createDrawComponent(null); }); - function expectComponentStateHasStudentWork(componentState: any, componentContent: any, - expectedResult: boolean) { - expect(service.componentStateHasStudentWork(componentState, componentContent)) - .toEqual(expectedResult); + function expectComponentStateHasStudentWork( + componentState: any, + componentContent: any, + expectedResult: boolean + ) { + expect(service.componentStateHasStudentWork(componentState, componentContent)).toEqual( + expectedResult + ); } it(`should check if component state has student work when component content is not provided and component state does not have any objects`, () => { @@ -232,18 +237,28 @@ function isStarterDrawDataExists() { } function isStudentDrawDataDifferentFromStarterData() { - function expectIsStudentDrawDataDifferentFromStarterData(drawDataString: string, - starterDrawData: string, expectedResult: boolean) { - expect(service.isStudentDrawDataDifferentFromStarterData(drawDataString, starterDrawData)) - .toEqual(expectedResult); + function expectIsStudentDrawDataDifferentFromStarterData( + drawDataString: string, + starterDrawData: string, + expectedResult: boolean + ) { + expect( + service.isStudentDrawDataDifferentFromStarterData(drawDataString, starterDrawData) + ).toEqual(expectedResult); } it('should check when student draw data is the same as starter draw data', () => { - expectIsStudentDrawDataDifferentFromStarterData(defaultDrawDataWithNoObjectsField, - defaultDrawDataWithNoObjectsField, false); + expectIsStudentDrawDataDifferentFromStarterData( + defaultDrawDataWithNoObjectsField, + defaultDrawDataWithNoObjectsField, + false + ); }); it('should check when student draw data is different from starter draw data', () => { const studentDrawData = '{"canvas":{"objects":[]}}'; - expectIsStudentDrawDataDifferentFromStarterData(studentDrawData, - defaultDrawDataWithNoObjectsField, true); + expectIsStudentDrawDataDifferentFromStarterData( + studentDrawData, + defaultDrawDataWithNoObjectsField, + true + ); }); -} \ No newline at end of file +} diff --git a/src/main/webapp/site/src/app/services/embeddedService.spec.ts b/src/main/webapp/site/src/app/services/embeddedService.spec.ts index e4a056aded..e19f3f80b9 100644 --- a/src/main/webapp/site/src/app/services/embeddedService.spec.ts +++ b/src/main/webapp/site/src/app/services/embeddedService.spec.ts @@ -1,22 +1,22 @@ -import { HttpClientTestingModule } from "@angular/common/http/testing"; -import { TestBed } from "@angular/core/testing"; -import { UpgradeModule } from "@angular/upgrade/static"; -import { AnnotationService } from "../../../../wise5/services/annotationService"; -import { ConfigService } from "../../../../wise5/services/configService"; -import { ProjectService } from "../../../../wise5/services/projectService"; -import { StudentAssetService } from "../../../../wise5/services/studentAssetService"; -import { StudentDataService } from "../../../../wise5/services/studentDataService"; -import { TagService } from "../../../../wise5/services/tagService"; -import { UtilService } from "../../../../wise5/services/utilService"; -import { EmbeddedService } from "../../../../wise5/components/embedded/embeddedService"; -import { SessionService } from "../../../../wise5/services/sessionService"; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { TestBed } from '@angular/core/testing'; +import { UpgradeModule } from '@angular/upgrade/static'; +import { AnnotationService } from '../../../../wise5/services/annotationService'; +import { ConfigService } from '../../../../wise5/services/configService'; +import { ProjectService } from '../../../../wise5/services/projectService'; +import { StudentAssetService } from '../../../../wise5/services/studentAssetService'; +import { StudentDataService } from '../../../../wise5/services/studentDataService'; +import { TagService } from '../../../../wise5/services/tagService'; +import { UtilService } from '../../../../wise5/services/utilService'; +import { EmbeddedService } from '../../../../wise5/components/embedded/embeddedService'; +import { SessionService } from '../../../../wise5/services/sessionService'; let service: EmbeddedService; describe('EmbeddedService', () => { beforeEach(() => { TestBed.configureTestingModule({ - imports: [ HttpClientTestingModule, UpgradeModule ], + imports: [HttpClientTestingModule, UpgradeModule], providers: [ AnnotationService, ConfigService, @@ -62,12 +62,10 @@ function isCompleted() { function expectIsCompleted(componentStates: any[], nodeEvents: any[], expectedResult: boolean) { expect(service.isCompleted({}, componentStates, [], nodeEvents)).toEqual(expectedResult); } - it('should check is completed when there are no node entered events and no component states', - () => { + it('should check is completed when there are no node entered events and no component states', () => { expectIsCompleted(componentStates, nodeEvents, false); }); - it('should check is completed when there is a node entered event and no component states', - () => { + it('should check is completed when there is a node entered event and no component states', () => { nodeEvents.push({ event: 'nodeEntered' }); expectIsCompleted(componentStates, nodeEvents, true); }); diff --git a/src/main/webapp/site/src/app/services/graphService.spec.ts b/src/main/webapp/site/src/app/services/graphService.spec.ts index b6a792c444..f24b9fdb17 100644 --- a/src/main/webapp/site/src/app/services/graphService.spec.ts +++ b/src/main/webapp/site/src/app/services/graphService.spec.ts @@ -1,22 +1,22 @@ -import { TestBed } from "@angular/core/testing"; -import { HttpClientTestingModule } from "@angular/common/http/testing"; -import { UpgradeModule } from "@angular/upgrade/static"; -import { AnnotationService } from "../../../../wise5/services/annotationService"; -import { ConfigService } from "../../../../wise5/services/configService"; -import { ProjectService } from "../../../../wise5/services/projectService"; -import { StudentAssetService } from "../../../../wise5/services/studentAssetService"; -import { StudentDataService } from "../../../../wise5/services/studentDataService"; -import { TagService } from "../../../../wise5/services/tagService"; -import { UtilService } from "../../../../wise5/services/utilService"; -import { GraphService } from "../../../../wise5/components/graph/graphService"; -import { SessionService } from "../../../../wise5/services/sessionService"; +import { TestBed } from '@angular/core/testing'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { UpgradeModule } from '@angular/upgrade/static'; +import { AnnotationService } from '../../../../wise5/services/annotationService'; +import { ConfigService } from '../../../../wise5/services/configService'; +import { ProjectService } from '../../../../wise5/services/projectService'; +import { StudentAssetService } from '../../../../wise5/services/studentAssetService'; +import { StudentDataService } from '../../../../wise5/services/studentDataService'; +import { TagService } from '../../../../wise5/services/tagService'; +import { UtilService } from '../../../../wise5/services/utilService'; +import { GraphService } from '../../../../wise5/components/graph/graphService'; +import { SessionService } from '../../../../wise5/services/sessionService'; let service: GraphService; describe('GraphService', () => { beforeEach(() => { TestBed.configureTestingModule({ - imports: [ HttpClientTestingModule, UpgradeModule ], + imports: [HttpClientTestingModule, UpgradeModule], providers: [ AnnotationService, ConfigService, @@ -142,10 +142,16 @@ function isCompleted() { nodeEvents = []; node = {}; }); - function expectIsCompleted(component: any, componentStates: any[], nodeEvents: any[], node: any, - expectedResult: boolean) { - expect(service.isCompleted(component, componentStates, [], nodeEvents, node)) - .toEqual(expectedResult); + function expectIsCompleted( + component: any, + componentStates: any[], + nodeEvents: any[], + node: any, + expectedResult: boolean + ) { + expect(service.isCompleted(component, componentStates, [], nodeEvents, node)).toEqual( + expectedResult + ); } it(`should check is completed when component when component is not editable and has no node entered event`, () => { @@ -177,10 +183,10 @@ function isCompleted() { function hasComponentStates() { it('should check if there are component states when there are none', () => { expect(service.hasComponentStates([])).toEqual(false); - }) + }); it('should check if there are component states when there is one', () => { expect(service.hasComponentStates([{}])).toEqual(true); - }) + }); } function hasSubmitComponentState() { @@ -194,16 +200,14 @@ function hasSubmitComponentState() { function expectHasSubmitComponentState(componentStates: any[], expectedResult: boolean) { expect(service.hasSubmitComponentState(componentStates)).toEqual(expectedResult); } - it('should check if there is a submit component state when there is no submit component state', - () => { + it('should check if there is a submit component state when there is no submit component state', () => { expectHasSubmitComponentState(componentStates, false); - }) - it('should check if there is a submit component state when there is a submit component state', - () => { + }); + it('should check if there is a submit component state when there is a submit component state', () => { componentStates[0].studentData.trials[0].series[0].data.push({}); componentStates[0].isSubmit = true; expectHasSubmitComponentState(componentStates, true); - }) + }); } function canEdit() { @@ -245,7 +249,14 @@ function hasTrialData() { expect(service.hasTrialData(studentData)).toBeFalsy(); }); it('should return true when there is a series in a trial with data', () => { - const trials = [createTrial([createSingleSeries([[1, 5], [2, 10]])])]; + const trials = [ + createTrial([ + createSingleSeries([ + [1, 5], + [2, 10] + ]) + ]) + ]; const studentData = createStudentDataWithTrials(trials); expect(service.hasTrialData(studentData)).toBeTruthy(); }); @@ -278,14 +289,18 @@ function isStudentChangedAxisLimit() { const studentData = { xAxis: xAxis, yAxis: yAxis - } + }; componentState = createComponentState(studentData); componentContent = createComponentContent([], xAxis, yAxis); }); - function expectIsStudentChangedAxisLimit(componentState: any, componentContent: any, - expectedResult: boolean) { - expect(service.isStudentChangedAxisLimit(componentState, componentContent)) - .toEqual(expectedResult); + function expectIsStudentChangedAxisLimit( + componentState: any, + componentContent: any, + expectedResult: boolean + ) { + expect(service.isStudentChangedAxisLimit(componentState, componentContent)).toEqual( + expectedResult + ); } it('should return false when the student has not changed the axis limit', () => { expectIsStudentChangedAxisLimit(componentState, componentContent, false); @@ -411,4 +426,4 @@ function generateImageFromRenderedComponentState() { function getHighchartsDiv() { // TODO -} \ No newline at end of file +} diff --git a/src/main/webapp/site/src/app/services/htmlService.spec.ts b/src/main/webapp/site/src/app/services/htmlService.spec.ts index ef8b884f8e..88e2950a36 100644 --- a/src/main/webapp/site/src/app/services/htmlService.spec.ts +++ b/src/main/webapp/site/src/app/services/htmlService.spec.ts @@ -1,23 +1,22 @@ - -import { TestBed } from "@angular/core/testing"; -import { UpgradeModule } from "@angular/upgrade/static"; -import { HttpClientTestingModule } from "@angular/common/http/testing"; -import { StudentDataService } from "../../../../wise5/services/studentDataService"; -import { UtilService } from "../../../../wise5/services/utilService"; -import { HTMLService } from "../../../../wise5/components/html/htmlService"; -import { AnnotationService } from "../../../../wise5/services/annotationService"; -import { ConfigService } from "../../../../wise5/services/configService"; -import { ProjectService } from "../../../../wise5/services/projectService"; -import { StudentAssetService } from "../../../../wise5/services/studentAssetService"; -import { TagService } from "../../../../wise5/services/tagService"; -import { SessionService } from "../../../../wise5/services/sessionService"; +import { TestBed } from '@angular/core/testing'; +import { UpgradeModule } from '@angular/upgrade/static'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { StudentDataService } from '../../../../wise5/services/studentDataService'; +import { UtilService } from '../../../../wise5/services/utilService'; +import { HTMLService } from '../../../../wise5/components/html/htmlService'; +import { AnnotationService } from '../../../../wise5/services/annotationService'; +import { ConfigService } from '../../../../wise5/services/configService'; +import { ProjectService } from '../../../../wise5/services/projectService'; +import { StudentAssetService } from '../../../../wise5/services/studentAssetService'; +import { TagService } from '../../../../wise5/services/tagService'; +import { SessionService } from '../../../../wise5/services/sessionService'; let service: HTMLService; describe('TableService', () => { beforeEach(() => { TestBed.configureTestingModule({ - imports: [ HttpClientTestingModule, UpgradeModule ], + imports: [HttpClientTestingModule, UpgradeModule], providers: [ AnnotationService, ConfigService, @@ -61,4 +60,4 @@ function isCompleted() { it('should check if is compeleted when there is a node entered event', () => { expectIsCompleted([{ event: 'nodeEntered' }], true); }); -} \ No newline at end of file +} diff --git a/src/main/webapp/site/src/app/services/labelService.spec.ts b/src/main/webapp/site/src/app/services/labelService.spec.ts index 8443a50cee..c9e0688ad2 100644 --- a/src/main/webapp/site/src/app/services/labelService.spec.ts +++ b/src/main/webapp/site/src/app/services/labelService.spec.ts @@ -1,15 +1,15 @@ -import { HttpClientTestingModule } from "@angular/common/http/testing"; -import { UpgradeModule } from "@angular/upgrade/static"; -import { AnnotationService } from "../../../../wise5/services/annotationService"; -import { ConfigService } from "../../../../wise5/services/configService"; -import { ProjectService } from "../../../../wise5/services/projectService"; -import { StudentAssetService } from "../../../../wise5/services/studentAssetService"; -import { StudentDataService } from "../../../../wise5/services/studentDataService"; -import { TagService } from "../../../../wise5/services/tagService"; -import { UtilService } from "../../../../wise5/services/utilService"; -import { LabelService } from "../../../../wise5/components/label/labelService"; -import { TestBed } from "@angular/core/testing"; -import { SessionService } from "../../../../wise5/services/sessionService"; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { UpgradeModule } from '@angular/upgrade/static'; +import { AnnotationService } from '../../../../wise5/services/annotationService'; +import { ConfigService } from '../../../../wise5/services/configService'; +import { ProjectService } from '../../../../wise5/services/projectService'; +import { StudentAssetService } from '../../../../wise5/services/studentAssetService'; +import { StudentDataService } from '../../../../wise5/services/studentDataService'; +import { TagService } from '../../../../wise5/services/tagService'; +import { UtilService } from '../../../../wise5/services/utilService'; +import { LabelService } from '../../../../wise5/components/label/labelService'; +import { TestBed } from '@angular/core/testing'; +import { SessionService } from '../../../../wise5/services/sessionService'; let service: LabelService; let utilService: UtilService; @@ -19,7 +19,7 @@ let label2: any; describe('LabelServiceService', () => { beforeEach(() => { TestBed.configureTestingModule({ - imports: [ HttpClientTestingModule, UpgradeModule ], + imports: [HttpClientTestingModule, UpgradeModule], providers: [ AnnotationService, ConfigService, @@ -67,8 +67,14 @@ function createObjectWithLabels(labels: any[]) { }; } -function createLabel(text: string, pointX: number, pointY: number, textX: number, textY: number, - color: string) { +function createLabel( + text: string, + pointX: number, + pointY: number, + textX: number, + textY: number, + color: string +) { return { text: text, pointX: pointX, @@ -106,8 +112,12 @@ function isCompleted() { componentStates = []; node = {}; }); - function expectIsCompleted(component: any, componentStates: any[], node: any, - expectedResult: boolean) { + function expectIsCompleted( + component: any, + componentStates: any[], + node: any, + expectedResult: boolean + ) { expect(service.isCompleted(component, componentStates, [], [], node)).toEqual(expectedResult); } it('should check if is completed when submit is not required and there are no labels', () => { @@ -117,14 +127,12 @@ function isCompleted() { componentStates.push(createComponentState([label1])); expectIsCompleted(component, componentStates, node, true); }); - it(`should check if is completed when submit is required and there are labels but not submitted`, - () => { + it(`should check if is completed when submit is required and there are labels but not submitted`, () => { node.showSubmitButton = true; componentStates.push(createComponentState([label1])); expectIsCompleted(component, componentStates, node, false); }); - it(`should check if is completed when submit is required and there are labels submitted`, - () => { + it(`should check if is completed when submit is required and there are labels submitted`, () => { node.showSubmitButton = true; componentStates.push(createComponentState([label1], true)); expectIsCompleted(component, componentStates, node, true); @@ -136,17 +144,14 @@ function componentStateHasSubmitWithLabel() { beforeEach(() => { componentState = createComponentState([]); }); - it('should check if a component state has a submit with label when it does not have any labels', - () => { + it('should check if a component state has a submit with label when it does not have any labels', () => { expect(service.componentStateHasSubmitWithLabel(componentState)).toEqual(false); }); - it('should check if a component state has a submit with label when it has a label but no submit', - () => { + it('should check if a component state has a submit with label when it has a label but no submit', () => { componentState.studentData.labels.push(label1); expect(service.componentStateHasSubmitWithLabel(componentState)).toEqual(false); }); - it('should check if a component state has a submit with label when it has a label and submit', - () => { + it('should check if a component state has a submit with label when it has a label and submit', () => { componentState.studentData.labels.push(label1); componentState.isSubmit = true; expect(service.componentStateHasSubmitWithLabel(componentState)).toEqual(true); @@ -187,10 +192,14 @@ function componentStateHasStudentWork() { componentState = createComponentState([]); componentContent = createComponentContent([]); }); - function expectComponentStateHasStudentWork(componentState: any, componentContent: any, - expectedResult: boolean) { - expect(service.componentStateHasStudentWork(componentState, componentContent)) - .toEqual(expectedResult); + function expectComponentStateHasStudentWork( + componentState: any, + componentContent: any, + expectedResult: boolean + ) { + expect(service.componentStateHasStudentWork(componentState, componentContent)).toEqual( + expectedResult + ); } it('should check if a component state has work when it has no labels', () => { expectComponentStateHasStudentWork(componentState, componentContent, false); @@ -216,10 +225,14 @@ function componentStateIsSameAsStarter() { componentState = createComponentState([]); componentContent = createComponentContent([]); }); - function expectComponentStateIsSameAsStarter(componentState: any, componentContent: any, - expectedResult: boolean) { - expect(service.componentStateIsSameAsStarter(componentState, componentContent)) - .toEqual(expectedResult); + function expectComponentStateIsSameAsStarter( + componentState: any, + componentContent: any, + expectedResult: boolean + ) { + expect(service.componentStateIsSameAsStarter(componentState, componentContent)).toEqual( + expectedResult + ); } it(`should check if component state is the same as starter when component state has no labels and there are no starter labels`, () => { @@ -279,12 +292,10 @@ function labelsAreTheSame() { it('should check if labels are the same when one is null and one is not null', () => { expectLabelsAreTheSame({}, null, false); }); - it(`should check if labels are the same when both are not null and do not have the same values`, - () => { + it(`should check if labels are the same when both are not null and do not have the same values`, () => { expectLabelsAreTheSame(label1, label2, false); }); - it(`should check if labels are the same when both are not null and do have the same values`, - () => { + it(`should check if labels are the same when both are not null and do have the same values`, () => { const label3 = createLabel('Label 1', 1, 11, 111, 1111, 'blue'); expectLabelsAreTheSame(label1, label3, true); }); @@ -297,9 +308,9 @@ function getTSpans() { const spaceInbetweenLines = 40; const tspans = service.getTSpans(textWrapped, xPositionOfText, spaceInbetweenLines); const expectedResult = - `The quick brown fox` + - `jumps over` + - `the lazy dog.`; + `The quick brown fox` + + `jumps over` + + `the lazy dog.`; expect(tspans).toEqual(expectedResult); }); } @@ -309,8 +320,9 @@ function getSVGTextElementString() { const fontSize = 16; const tspans = 'The quick brown fox'; const textElementString = service.getSVGTextElementString(fontSize, tspans); - const expectedResult = `${tspans}`; + const expectedResult = + `${tspans}`; expect(textElementString).toEqual(expectedResult); }); -} \ No newline at end of file +} diff --git a/src/main/webapp/site/src/app/services/library.service.spec.ts b/src/main/webapp/site/src/app/services/library.service.spec.ts index 601ae89e98..b3f442c626 100644 --- a/src/main/webapp/site/src/app/services/library.service.spec.ts +++ b/src/main/webapp/site/src/app/services/library.service.spec.ts @@ -8,10 +8,8 @@ import { RouterTestingModule } from '@angular/router/testing'; describe('LibraryService', () => { configureTestSuite(() => { TestBed.configureTestingModule({ - providers: [ - LibraryService - ], - imports: [ HttpClientTestingModule, RouterTestingModule ] + providers: [LibraryService], + imports: [HttpClientTestingModule, RouterTestingModule] }); }); @@ -19,23 +17,30 @@ describe('LibraryService', () => { expect(service).toBeTruthy(); })); - it('should convert LibraryGroup JSON object into LibraryGroup object', inject([LibraryService], (service: LibraryService) => { - const libraryGroupsJSON = [{ - 'id': '6thGrade', - 'name': '6th grade projects', - 'type': 'group', - 'children': [{ - 'id': 2, - 'dateCreated': '2019-02-21', - 'name': 'sample project', - 'owner': {}, - 'sharedOwners': [], - 'projectThumb': '17/assets/projectThumb.png', - 'type': 'project', - 'wiseVersion': 5 - }] - }]; - const convertedLibraryGroups = service.convertLibraryGroups(libraryGroupsJSON); - expect(convertedLibraryGroups[0].children[0].constructor.name).toEqual('LibraryProject'); - })); + it('should convert LibraryGroup JSON object into LibraryGroup object', inject( + [LibraryService], + (service: LibraryService) => { + const libraryGroupsJSON = [ + { + id: '6thGrade', + name: '6th grade projects', + type: 'group', + children: [ + { + id: 2, + dateCreated: '2019-02-21', + name: 'sample project', + owner: {}, + sharedOwners: [], + projectThumb: '17/assets/projectThumb.png', + type: 'project', + wiseVersion: 5 + } + ] + } + ]; + const convertedLibraryGroups = service.convertLibraryGroups(libraryGroupsJSON); + expect(convertedLibraryGroups[0].children[0].constructor.name).toEqual('LibraryProject'); + } + )); }); diff --git a/src/main/webapp/site/src/app/services/library.service.ts b/src/main/webapp/site/src/app/services/library.service.ts index fbd4d5d175..821c783528 100644 --- a/src/main/webapp/site/src/app/services/library.service.ts +++ b/src/main/webapp/site/src/app/services/library.service.ts @@ -1,15 +1,14 @@ import { Injectable } from '@angular/core'; import { Observable, BehaviorSubject } from 'rxjs'; import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http'; -import { LibraryGroup } from "../modules/library/libraryGroup"; -import { LibraryProject } from "../modules/library/libraryProject"; +import { LibraryGroup } from '../modules/library/libraryGroup'; +import { LibraryProject } from '../modules/library/libraryProject'; import { ProjectFilterValues } from '../domain/projectFilterValues'; -import { Project } from "../domain/project"; +import { Project } from '../domain/project'; import { Router } from '@angular/router'; @Injectable() export class LibraryService { - private libraryGroupsUrl = '/api/project/library'; private communityProjectsUrl = '/api/project/community'; private personalProjectsUrl = '/api/project/personal'; @@ -27,7 +26,9 @@ export class LibraryService { public personalLibraryProjectsSource$ = this.personalLibraryProjectsSource.asObservable(); private sharedLibraryProjectsSource = new BehaviorSubject([]); public sharedLibraryProjectsSource$ = this.sharedLibraryProjectsSource.asObservable(); - private projectFilterValuesSource = new BehaviorSubject(new ProjectFilterValues); + private projectFilterValuesSource = new BehaviorSubject( + new ProjectFilterValues() + ); public projectFilterValuesSource$ = this.projectFilterValuesSource.asObservable(); private newProjectSource = new BehaviorSubject(null); public newProjectSource$ = this.newProjectSource.asObservable(); @@ -125,7 +126,7 @@ export class LibraryService { copyProject(projectId) { const headers = new HttpHeaders().set('Content-Type', 'application/x-www-form-urlencoded'); let body = new HttpParams(); - body = body.set('projectId', projectId + ""); + body = body.set('projectId', projectId + ''); return this.http.post(this.copyProjectUrl, body, { headers: headers }); } @@ -143,7 +144,7 @@ export class LibraryService { } getProjectInfo(projectId): Observable { - return this.http.get(this.projectInfoUrl + "/" + projectId); + return this.http.get(this.projectInfoUrl + '/' + projectId); } updateNumberOfOfficialProjectsVisible(count) { @@ -164,7 +165,7 @@ export class LibraryService { this.communityLibraryProjectsSource.next([]); this.personalLibraryProjectsSource.next([]); this.sharedLibraryProjectsSource.next([]); - this.projectFilterValuesSource.next(new ProjectFilterValues); + this.projectFilterValuesSource.next(new ProjectFilterValues()); this.hasLoaded = false; } } diff --git a/src/main/webapp/site/src/app/services/matchService.spec.ts b/src/main/webapp/site/src/app/services/matchService.spec.ts index 9f6ac9ebbb..93f6b6c4e1 100644 --- a/src/main/webapp/site/src/app/services/matchService.spec.ts +++ b/src/main/webapp/site/src/app/services/matchService.spec.ts @@ -1,15 +1,15 @@ -import { MatchService } from "../../../../wise5/components/match/matchService"; -import { TestBed } from "@angular/core/testing"; -import { HttpClientTestingModule } from "@angular/common/http/testing"; -import { UpgradeModule } from "@angular/upgrade/static"; -import { AnnotationService } from "../../../../wise5/services/annotationService"; -import { ConfigService } from "../../../../wise5/services/configService"; -import { ProjectService } from "../../../../wise5/services/projectService"; -import { StudentAssetService } from "../../../../wise5/services/studentAssetService"; -import { StudentDataService } from "../../../../wise5/services/studentDataService"; -import { TagService } from "../../../../wise5/services/tagService"; -import { UtilService } from "../../../../wise5/services/utilService"; -import { SessionService } from "../../../../wise5/services/sessionService"; +import { MatchService } from '../../../../wise5/components/match/matchService'; +import { TestBed } from '@angular/core/testing'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { UpgradeModule } from '@angular/upgrade/static'; +import { AnnotationService } from '../../../../wise5/services/annotationService'; +import { ConfigService } from '../../../../wise5/services/configService'; +import { ProjectService } from '../../../../wise5/services/projectService'; +import { StudentAssetService } from '../../../../wise5/services/studentAssetService'; +import { StudentDataService } from '../../../../wise5/services/studentDataService'; +import { TagService } from '../../../../wise5/services/tagService'; +import { UtilService } from '../../../../wise5/services/utilService'; +import { SessionService } from '../../../../wise5/services/sessionService'; let service: MatchService; let componentStateBucketWithItem: any; @@ -17,7 +17,7 @@ let componentStateBucketWithItem: any; describe('MatchService', () => { beforeEach(() => { TestBed.configureTestingModule({ - imports: [ HttpClientTestingModule, UpgradeModule ], + imports: [HttpClientTestingModule, UpgradeModule], providers: [ AnnotationService, ConfigService, @@ -31,8 +31,9 @@ describe('MatchService', () => { ] }); service = TestBed.get(MatchService); - componentStateBucketWithItem = createComponentStateBucket('bucket1', 'Bucket 1', - [createChoice('choice1', 'Choice 1')]); + componentStateBucketWithItem = createComponentStateBucket('bucket1', 'Bucket 1', [ + createChoice('choice1', 'Choice 1') + ]); }); createComponent(); isCompleted(); @@ -117,21 +118,22 @@ function isCompleted() { componentStates = []; node = {}; }); - function expectIsCompleted(component: any, componentStates: any[], node: any, - expectedResult: boolean) { + function expectIsCompleted( + component: any, + componentStates: any[], + node: any, + expectedResult: boolean + ) { expect(service.isCompleted(component, componentStates, [], [], node)).toEqual(expectedResult); } - it(`should check if is completed when submit is not required and there are no component states`, - () => { + it(`should check if is completed when submit is not required and there are no component states`, () => { expectIsCompleted(component, componentStates, node, false); }); - it(`should check if is completed when submit is not required and there are component states`, - () => { + it(`should check if is completed when submit is not required and there are component states`, () => { componentStates.push(createComponentState([componentStateBucketWithItem])); expectIsCompleted(component, componentStates, node, true); }); - it(`should check if is completed when submit is required and there are no component states`, - () => { + it(`should check if is completed when submit is required and there are no component states`, () => { node.showSubmitButton = true; expectIsCompleted(component, componentStates, node, false); }); @@ -191,14 +193,11 @@ function hasCorrectAnswer() { function getItemById() { const item1 = createChoice('item1', 'Item 1'); const item2 = createChoice('item2', 'Item 2'); - const items: any[] = [ - item1, - item2 - ]; + const items: any[] = [item1, item2]; it('should get the item by id when the id exists', () => { expect(service.getItemById('item1', items)).toEqual(item1); }); it('should return null when the item id does not exist', () => { expect(service.getItemById('item3', items)).toEqual(null); }); -} \ No newline at end of file +} diff --git a/src/main/webapp/site/src/app/services/milestoneService.spec.ts b/src/main/webapp/site/src/app/services/milestoneService.spec.ts index 9c4871ae24..1b604bcc80 100644 --- a/src/main/webapp/site/src/app/services/milestoneService.spec.ts +++ b/src/main/webapp/site/src/app/services/milestoneService.spec.ts @@ -1,21 +1,21 @@ import * as angular from 'angular'; import * as moment from 'moment'; -import { MilestoneService } from "../../../../wise5/services/milestoneService"; -import { TestBed } from "@angular/core/testing"; -import { HttpClientTestingModule } from "@angular/common/http/testing"; -import { UpgradeModule } from "@angular/upgrade/static"; -import { AchievementService } from "../../../../wise5/services/achievementService"; -import { AnnotationService } from "../../../../wise5/services/annotationService"; -import { ConfigService } from "../../../../wise5/services/configService"; -import { ProjectService } from "../../../../wise5/services/projectService"; -import { TeacherDataService } from "../../../../wise5/services/teacherDataService"; -import { UtilService } from "../../../../wise5/services/utilService"; -import { StudentDataService } from "../../../../wise5/services/studentDataService"; -import { TagService } from "../../../../wise5/services/tagService"; -import { TeacherProjectService } from "../../../../wise5/services/teacherProjectService"; -import { TeacherWebSocketService } from "../../../../wise5/services/teacherWebSocketService"; -import { NotificationService } from "../../../../wise5/services/notificationService"; -import { StudentStatusService } from "../../../../wise5/services/studentStatusService"; +import { MilestoneService } from '../../../../wise5/services/milestoneService'; +import { TestBed } from '@angular/core/testing'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { UpgradeModule } from '@angular/upgrade/static'; +import { AchievementService } from '../../../../wise5/services/achievementService'; +import { AnnotationService } from '../../../../wise5/services/annotationService'; +import { ConfigService } from '../../../../wise5/services/configService'; +import { ProjectService } from '../../../../wise5/services/projectService'; +import { TeacherDataService } from '../../../../wise5/services/teacherDataService'; +import { UtilService } from '../../../../wise5/services/utilService'; +import { StudentDataService } from '../../../../wise5/services/studentDataService'; +import { TagService } from '../../../../wise5/services/tagService'; +import { TeacherProjectService } from '../../../../wise5/services/teacherProjectService'; +import { TeacherWebSocketService } from '../../../../wise5/services/teacherWebSocketService'; +import { NotificationService } from '../../../../wise5/services/notificationService'; +import { StudentStatusService } from '../../../../wise5/services/studentStatusService'; import { SessionService } from '../../../../wise5/services/sessionService'; let service: MilestoneService; @@ -58,7 +58,7 @@ const reportSettingsCustomScoreValuesSample = { describe('MilestoneService', () => { beforeEach(() => { TestBed.configureTestingModule({ - imports: [ HttpClientTestingModule, UpgradeModule ], + imports: [HttpClientTestingModule, UpgradeModule], providers: [ AchievementService, AnnotationService, @@ -133,8 +133,14 @@ function createScoreCounts(counts: any[]) { return countsObject; } -function createSatisfyCriteria(nodeId: string, componentId: string, targetVariable: string = null, - func: string = null, value: number = null, percentThreshold: number = null) { +function createSatisfyCriteria( + nodeId: string, + componentId: string, + targetVariable: string = null, + func: string = null, + value: number = null, + percentThreshold: number = null +) { return { nodeId: nodeId, componentId: componentId, @@ -367,8 +373,11 @@ function insertMilestoneCompletion() { }); } -function createStudentAchievement(achievementId: string, achievementTime: number, - workgroupId: number) { +function createStudentAchievement( + achievementId: string, + achievementTime: number, + workgroupId: number +) { return { achievementId: achievementId, achievementTime: achievementTime, @@ -679,9 +688,9 @@ function isTemplateCriterionSatisfied() { } } }; - expect( - service.isTemplateCriterionSatisfied(satisfyCriterion, aggregateAutoScores) - ).toEqual(false); + expect(service.isTemplateCriterionSatisfied(satisfyCriterion, aggregateAutoScores)).toEqual( + false + ); }); it('should check is template criterion satisfied true', () => { const satisfyCriterion = createSatisfyCriteria( @@ -700,9 +709,9 @@ function isTemplateCriterionSatisfied() { } } }; - expect( - service.isTemplateCriterionSatisfied(satisfyCriterion, aggregateAutoScores) - ).toEqual(true); + expect(service.isTemplateCriterionSatisfied(satisfyCriterion, aggregateAutoScores)).toEqual( + true + ); }); } @@ -725,9 +734,9 @@ function isPercentOfScoresGreaterThan() { } } }; - expect( - service.isPercentOfScoresGreaterThan(satisfyCriterion, aggregateAutoScores) - ).toEqual(false); + expect(service.isPercentOfScoresGreaterThan(satisfyCriterion, aggregateAutoScores)).toEqual( + false + ); }); it('should check is percent of scores greater than true', () => { const satisfyCriterion = createSatisfyCriteria( @@ -746,9 +755,9 @@ function isPercentOfScoresGreaterThan() { } } }; - expect( - service.isPercentOfScoresGreaterThan(satisfyCriterion, aggregateAutoScores) - ).toEqual(true); + expect(service.isPercentOfScoresGreaterThan(satisfyCriterion, aggregateAutoScores)).toEqual( + true + ); }); }); } @@ -761,9 +770,9 @@ function getGreaterThanSum() { counts: createScoreCounts([10, 20, 30, 40, 50]) }; const possibleScores = [1, 2, 3, 4, 5]; - expect( - service.getGreaterThanSum(satisfyCriterion, aggregateData, possibleScores) - ).toEqual(140); + expect(service.getGreaterThanSum(satisfyCriterion, aggregateData, possibleScores)).toEqual( + 140 + ); }); it('should get greater than sum with score 2', () => { const satisfyCriterion = { value: 2 }; @@ -771,9 +780,9 @@ function getGreaterThanSum() { counts: createScoreCounts([10, 20, 30, 40, 50]) }; const possibleScores = [1, 2, 3, 4, 5]; - expect( - service.getGreaterThanSum(satisfyCriterion, aggregateData, possibleScores) - ).toEqual(120); + expect(service.getGreaterThanSum(satisfyCriterion, aggregateData, possibleScores)).toEqual( + 120 + ); }); it('should get greater than sum with score 3', () => { const satisfyCriterion = { value: 3 }; @@ -781,9 +790,9 @@ function getGreaterThanSum() { counts: createScoreCounts([10, 20, 30, 40, 50]) }; const possibleScores = [1, 2, 3, 4, 5]; - expect( - service.getGreaterThanSum(satisfyCriterion, aggregateData, possibleScores) - ).toEqual(90); + expect(service.getGreaterThanSum(satisfyCriterion, aggregateData, possibleScores)).toEqual( + 90 + ); }); it('should get greater than sum with score 4', () => { const satisfyCriterion = { value: 4 }; @@ -791,9 +800,9 @@ function getGreaterThanSum() { counts: createScoreCounts([10, 20, 30, 40, 50]) }; const possibleScores = [1, 2, 3, 4, 5]; - expect( - service.getGreaterThanSum(satisfyCriterion, aggregateData, possibleScores) - ).toEqual(50); + expect(service.getGreaterThanSum(satisfyCriterion, aggregateData, possibleScores)).toEqual( + 50 + ); }); }); } @@ -818,10 +827,7 @@ function isPercentOfScoresGreaterThanOrEqualTo() { } }; expect( - service.isPercentOfScoresGreaterThanOrEqualTo( - satisfyCriterion, - aggregateAutoScores - ) + service.isPercentOfScoresGreaterThanOrEqualTo(satisfyCriterion, aggregateAutoScores) ).toEqual(false); }); it('should check is percent of scores greater than or equal to true', () => { @@ -842,10 +848,7 @@ function isPercentOfScoresGreaterThanOrEqualTo() { } }; expect( - service.isPercentOfScoresGreaterThanOrEqualTo( - satisfyCriterion, - aggregateAutoScores - ) + service.isPercentOfScoresGreaterThanOrEqualTo(satisfyCriterion, aggregateAutoScores) ).toEqual(true); }); }); @@ -925,9 +928,9 @@ function isPercentOfScoresLessThan() { } } }; - expect( - service.isPercentOfScoresLessThan(satisfyCriterion, aggregateAutoScores) - ).toEqual(false); + expect(service.isPercentOfScoresLessThan(satisfyCriterion, aggregateAutoScores)).toEqual( + false + ); }); it('should check is percent of scores less than true', () => { const satisfyCriterion = createSatisfyCriteria( @@ -946,9 +949,9 @@ function isPercentOfScoresLessThan() { } } }; - expect( - service.isPercentOfScoresLessThan(satisfyCriterion, aggregateAutoScores) - ).toEqual(true); + expect(service.isPercentOfScoresLessThan(satisfyCriterion, aggregateAutoScores)).toEqual( + true + ); }); }); } @@ -961,9 +964,7 @@ function getLessThanSum() { counts: createScoreCounts([10, 20, 30, 40, 50]) }; const possibleScores = [1, 2, 3, 4, 5]; - expect( - service.getLessThanSum(satisfyCriterion, aggregateData, possibleScores) - ).toEqual(10); + expect(service.getLessThanSum(satisfyCriterion, aggregateData, possibleScores)).toEqual(10); }); it('should get less than sum with score 3', () => { const satisfyCriterion = { value: 3 }; @@ -971,9 +972,7 @@ function getLessThanSum() { counts: createScoreCounts([10, 20, 30, 40, 50]) }; const possibleScores = [1, 2, 3, 4, 5]; - expect( - service.getLessThanSum(satisfyCriterion, aggregateData, possibleScores) - ).toEqual(30); + expect(service.getLessThanSum(satisfyCriterion, aggregateData, possibleScores)).toEqual(30); }); it('should get less than sum with score 4', () => { const satisfyCriterion = { value: 4 }; @@ -981,9 +980,7 @@ function getLessThanSum() { counts: createScoreCounts([10, 20, 30, 40, 50]) }; const possibleScores = [1, 2, 3, 4, 5]; - expect( - service.getLessThanSum(satisfyCriterion, aggregateData, possibleScores) - ).toEqual(60); + expect(service.getLessThanSum(satisfyCriterion, aggregateData, possibleScores)).toEqual(60); }); it('should get less than sum with score 5', () => { const satisfyCriterion = { value: 5 }; @@ -991,9 +988,7 @@ function getLessThanSum() { counts: createScoreCounts([10, 20, 30, 40, 50]) }; const possibleScores = [1, 2, 3, 4, 5]; - expect( - service.getLessThanSum(satisfyCriterion, aggregateData, possibleScores) - ).toEqual(100); + expect(service.getLessThanSum(satisfyCriterion, aggregateData, possibleScores)).toEqual(100); }); }); } @@ -1119,9 +1114,9 @@ function isPercentOfScoresEqualTo() { } } }; - expect( - service.isPercentOfScoresEqualTo(satisfyCriterion, aggregateAutoScores) - ).toEqual(false); + expect(service.isPercentOfScoresEqualTo(satisfyCriterion, aggregateAutoScores)).toEqual( + false + ); }); it('should check is percent of scores equal to true', () => { const satisfyCriterion = createSatisfyCriteria( @@ -1140,9 +1135,7 @@ function isPercentOfScoresEqualTo() { } } }; - expect( - service.isPercentOfScoresEqualTo(satisfyCriterion, aggregateAutoScores) - ).toEqual(true); + expect(service.isPercentOfScoresEqualTo(satisfyCriterion, aggregateAutoScores)).toEqual(true); }); }); } @@ -1155,9 +1148,7 @@ function getEqualToSum() { counts: createScoreCounts([10, 20, 30, 40, 50]) }; const possibleScores = [1, 2, 3, 4, 5]; - expect( - service.getEqualToSum(satisfyCriterion, aggregateData, possibleScores) - ).toEqual(30); + expect(service.getEqualToSum(satisfyCriterion, aggregateData, possibleScores)).toEqual(30); }); }); } @@ -1203,10 +1194,7 @@ function getNotEqualToSum() { function getAggregateData() { describe('getAggregateData()', () => { it('should return the aggregate data', () => { - const result = service.getAggregateData( - satisfyCriterionSample, - aggregateAutoScoresSample - ); + const result = service.getAggregateData(satisfyCriterionSample, aggregateAutoScoresSample); expect(result).toEqual({ counts: { 1: 2, 2: 0, 3: 1, 4: 0, 5: 0 }, scoreCount: 3, @@ -1236,15 +1224,8 @@ function isPercentThresholdSatisfied() { ki: { counts: { 1: 1, 2: 0, 3: 2, 4: 0, 5: 0 }, scoreCount: 3 } } }; - const aggregateData = service.getAggregateData( - satisfyCriterionSample, - aggregateAutoScores - ); - const sum = service.getEqualToSum( - satisfyCriterionSample, - aggregateData, - possibleScoresKi - ); + const aggregateData = service.getAggregateData(satisfyCriterionSample, aggregateAutoScores); + const sum = service.getEqualToSum(satisfyCriterionSample, aggregateData, possibleScoresKi); const result = service.isPercentThresholdSatisfied( satisfyCriterionSample, aggregateData, @@ -1257,11 +1238,7 @@ function isPercentThresholdSatisfied() { satisfyCriterionSample, aggregateAutoScoresSample ); - const sum = service.getEqualToSum( - satisfyCriterionSample, - aggregateData, - possibleScoresKi - ); + const sum = service.getEqualToSum(satisfyCriterionSample, aggregateData, possibleScoresKi); const result = service.isPercentThresholdSatisfied( satisfyCriterionSample, aggregateData, @@ -1311,9 +1288,7 @@ function adjustKIScore() { describe('adjustKIScore()', () => { it('should return the adjusted KI score', () => { const value = 5; - expect(service.adjustKIScore(value, reportSettingsCustomScoreValuesSample)).toEqual( - 4 - ); + expect(service.adjustKIScore(value, reportSettingsCustomScoreValuesSample)).toEqual(4); }); }); } diff --git a/src/main/webapp/site/src/app/services/multipleChoiceService.spec.ts b/src/main/webapp/site/src/app/services/multipleChoiceService.spec.ts index ee6eac9ae2..a952d88d06 100644 --- a/src/main/webapp/site/src/app/services/multipleChoiceService.spec.ts +++ b/src/main/webapp/site/src/app/services/multipleChoiceService.spec.ts @@ -1,15 +1,15 @@ -import { MultipleChoiceService } from "../../../../wise5/components/multipleChoice/multipleChoiceService"; -import { HttpClientTestingModule } from "@angular/common/http/testing"; -import { UpgradeModule } from "@angular/upgrade/static"; -import { TestBed } from "@angular/core/testing"; -import { AnnotationService } from "../../../../wise5/services/annotationService"; -import { ConfigService } from "../../../../wise5/services/configService"; -import { ProjectService } from "../../../../wise5/services/projectService"; -import { StudentAssetService } from "../../../../wise5/services/studentAssetService"; -import { StudentDataService } from "../../../../wise5/services/studentDataService"; -import { TagService } from "../../../../wise5/services/tagService"; -import { UtilService } from "../../../../wise5/services/utilService"; -import { SessionService } from "../../../../wise5/services/sessionService"; +import { MultipleChoiceService } from '../../../../wise5/components/multipleChoice/multipleChoiceService'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { UpgradeModule } from '@angular/upgrade/static'; +import { TestBed } from '@angular/core/testing'; +import { AnnotationService } from '../../../../wise5/services/annotationService'; +import { ConfigService } from '../../../../wise5/services/configService'; +import { ProjectService } from '../../../../wise5/services/projectService'; +import { StudentAssetService } from '../../../../wise5/services/studentAssetService'; +import { StudentDataService } from '../../../../wise5/services/studentDataService'; +import { TagService } from '../../../../wise5/services/tagService'; +import { UtilService } from '../../../../wise5/services/utilService'; +import { SessionService } from '../../../../wise5/services/sessionService'; let service: MultipleChoiceService; let studentDataService: StudentDataService; @@ -26,7 +26,7 @@ let componentId1: string = 'abcdefghij'; describe('MultipleChoiceService', () => { beforeEach(() => { TestBed.configureTestingModule({ - imports: [ HttpClientTestingModule, UpgradeModule ], + imports: [HttpClientTestingModule, UpgradeModule], providers: [ AnnotationService, ConfigService, @@ -100,8 +100,9 @@ function createChoice(id: string, text: string, feedback: string, isCorrect: boo function choiceChosen() { function expectChoiceChosen(criteria: any, componentState: any, expectedResult: boolean) { - spyOn(studentDataService, 'getLatestComponentStateByNodeIdAndComponentId').and - .returnValue(componentState); + spyOn(studentDataService, 'getLatestComponentStateByNodeIdAndComponentId').and.returnValue( + componentState + ); expect(service.choiceChosen(criteria)).toEqual(expectedResult); } it(`should check if the student chose the choice in the criteria when the student does not have @@ -131,10 +132,14 @@ function choiceChosen() { } function isChoicesSelected() { - function expectIsChoicesSelected(studentChoiceIds: any[], constraintChoiceIds: any, - expectedResult: boolean) { - expect(service.isChoicesSelected(studentChoiceIds, constraintChoiceIds)) - .toEqual(expectedResult); + function expectIsChoicesSelected( + studentChoiceIds: any[], + constraintChoiceIds: any, + expectedResult: boolean + ) { + expect(service.isChoicesSelected(studentChoiceIds, constraintChoiceIds)).toEqual( + expectedResult + ); } it(`should check if choices are selected when constraint choice ids is a string and the constraint choice id is not selected`, () => { @@ -167,10 +172,13 @@ function isChoicesSelected() { } function getStudentChoiceIdsFromStudentChoiceObjects() { - function expectGetStudentChoiceIdsFromStudentChoiceObjects(studentChoices: any[], - expectedResult: any[]) { - expect(service.getStudentChoiceIdsFromStudentChoiceObjects(studentChoices)) - .toEqual(expectedResult); + function expectGetStudentChoiceIdsFromStudentChoiceObjects( + studentChoices: any[], + expectedResult: any[] + ) { + expect(service.getStudentChoiceIdsFromStudentChoiceObjects(studentChoices)).toEqual( + expectedResult + ); } it('should get student choice ids when there are none', () => { const studentChoices = []; @@ -183,8 +191,12 @@ function getStudentChoiceIdsFromStudentChoiceObjects() { } function isCompleted() { - function expectIsCompleted(component: any, componentStates: any[], node: any, - expectedResult: boolean) { + function expectIsCompleted( + component: any, + componentStates: any[], + node: any, + expectedResult: boolean + ) { expect(service.isCompleted(component, componentStates, [], [], node)).toEqual(expectedResult); } it('should check if a component is completed when there are no component states', () => { @@ -239,8 +251,9 @@ function componentStateHasStudentWork() { function componentHasCorrectAnswer() { function expectComponentHasCorrectAnswer(expectedResult: boolean) { - const component = createMultipleChoiceComponent( - [createChoice(choiceId1, choiceText1, '', expectedResult)]); + const component = createMultipleChoiceComponent([ + createChoice(choiceId1, choiceText1, '', expectedResult) + ]); expect(service.componentHasCorrectAnswer(component)).toEqual(expectedResult); } it('should check if component has correct answer when it is false', () => { @@ -249,4 +262,4 @@ function componentHasCorrectAnswer() { it('should check if component has correct answer when it is true', () => { expectComponentHasCorrectAnswer(true); }); -} \ No newline at end of file +} diff --git a/src/main/webapp/site/src/app/services/news.service.spec.ts b/src/main/webapp/site/src/app/services/news.service.spec.ts index b716007e34..5ca72854d8 100644 --- a/src/main/webapp/site/src/app/services/news.service.spec.ts +++ b/src/main/webapp/site/src/app/services/news.service.spec.ts @@ -4,9 +4,11 @@ import { NewsService } from './news.service'; import { HttpClientTestingModule } from '@angular/common/http/testing'; describe('NewsService', () => { - beforeEach(() => TestBed.configureTestingModule({ - imports: [ HttpClientTestingModule ] - })); + beforeEach(() => + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule] + }) + ); it('should be created', () => { const service: NewsService = TestBed.get(NewsService); diff --git a/src/main/webapp/site/src/app/services/news.service.ts b/src/main/webapp/site/src/app/services/news.service.ts index 7ee0f64e0f..5c984fe7b6 100644 --- a/src/main/webapp/site/src/app/services/news.service.ts +++ b/src/main/webapp/site/src/app/services/news.service.ts @@ -1,16 +1,15 @@ import { Injectable } from '@angular/core'; import { HttpClient, HttpHeaders } from '@angular/common/http'; -import { News } from "../domain/news"; +import { News } from '../domain/news'; import { Observable } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class NewsService { - private newsUrl = '/api/news'; - constructor(private http: HttpClient) { } + constructor(private http: HttpClient) {} getAllNews(): Observable { const headers = new HttpHeaders({ 'Cache-Control': 'no-cache' }); diff --git a/src/main/webapp/site/src/app/services/notebookService.spec.ts b/src/main/webapp/site/src/app/services/notebookService.spec.ts index c7794c970e..aa80c1600f 100644 --- a/src/main/webapp/site/src/app/services/notebookService.spec.ts +++ b/src/main/webapp/site/src/app/services/notebookService.spec.ts @@ -33,9 +33,18 @@ const teacherNotebookURL = 'http://localhost:8080/teacher/notebook/run/1'; describe('NotebookService', () => { beforeEach(() => { TestBed.configureTestingModule({ - imports: [ HttpClientTestingModule, UpgradeModule ], - providers: [ NotebookService, AnnotationService, ConfigService, ProjectService, SessionService, - StudentAssetService, StudentDataService, TagService, UtilService ] + imports: [HttpClientTestingModule, UpgradeModule], + providers: [ + NotebookService, + AnnotationService, + ConfigService, + ProjectService, + SessionService, + StudentAssetService, + StudentDataService, + TagService, + UtilService + ] }); http = TestBed.get(HttpTestingController); configService = TestBed.get(ConfigService); @@ -53,13 +62,13 @@ describe('NotebookService', () => { editNoteData = { runId: 1, workgroupId: 2, - type: "note", + type: 'note', localNotebookItemId: localNotebookItemId, - content: { text: "some new text", attachments: [], clientSaveTime: 1500000100000 }, + content: { text: 'some new text', attachments: [], clientSaveTime: 1500000100000 }, clientSaveTime: 1500000100000, periodId: 1, - nodeId: "node1", - title: "Note from Step #1" + nodeId: 'node1', + title: 'Note from Step #1' }; }); @@ -195,9 +204,10 @@ function shouldHandleRetrieveNotebookItems() { it('should group retrieved notebook items by workgroup id', () => { service.handleRetrieveNotebookItems(demoNotebookItems); expect(service.notebooksByWorkgroup['2'].deletedItems['stb6er46ad'].length).toBe(1); - expect(service.notebooksByWorkgroup['3'].items['finalReport'][0].content.content) - .toContain('This is a paragraph with some new text.'); - }); + expect(service.notebooksByWorkgroup['3'].items['finalReport'][0].content.content).toContain( + 'This is a paragraph with some new text.' + ); + }); } function shouldAddToNotebooksByWorgkroup() { @@ -235,8 +245,9 @@ function shouldGroupNotebookItems() { } service.groupNotebookItems(); expect(service.notebooksByWorkgroup['2'].deletedItems['stb6er46ad'].length).toBe(1); - expect(service.notebooksByWorkgroup['3'].items['finalReport'][0].content.content) - .toContain('This is a paragraph with some new text.'); + expect(service.notebooksByWorkgroup['3'].items['finalReport'][0].content.content).toContain( + 'This is a paragraph with some new text.' + ); }); } @@ -245,8 +256,9 @@ function shouldGetPrivateNotebookItems() { spyOn(configService, 'getWorkgroupId').and.returnValue(2); let privateNotebookItems = service.getPrivateNotebookItems(); expect(privateNotebookItems.length).toBe(3); - expect(service.getLatestNotebookReportItemByReportId('finalReport').content.content) - .toContain('This is a paragraph.'); + expect(service.getLatestNotebookReportItemByReportId('finalReport').content.content).toContain( + 'This is a paragraph.' + ); privateNotebookItems = service.getPrivateNotebookItems(4); expect(privateNotebookItems.length).toBe(0); }); @@ -275,7 +287,7 @@ function shouldRetrievePublicNotebookItems() { it('should retrieve all public notebook items in preview mode', () => { spyOn(configService, 'isPreview').and.returnValue(true); - service.retrievePublicNotebookItems('public').then(result => { + service.retrievePublicNotebookItems('public').then((result) => { expect(result).toEqual({}); }); }); @@ -290,9 +302,17 @@ function shouldHandleRetrievePublicNotebookItems() { } function saveNotebookItem(noteData) { - return service.saveNotebookItem(null, noteData.nodeId, noteData.localNotebookItemId, - noteData.type, noteData.title, noteData.content, [], noteData.clientSaveTime, - null); + return service.saveNotebookItem( + null, + noteData.nodeId, + noteData.localNotebookItemId, + noteData.type, + noteData.title, + noteData.content, + [], + noteData.clientSaveTime, + null + ); } function shouldSaveNotebookItem() { @@ -366,7 +386,7 @@ function shouldHandleNewNotebookItem() { spyOn(studentDataService, 'updateNodeStatuses'); service.handleRetrievePublicNotebookItems(demoPublicNotebookItems, 'public'); const newLocalNotebookItemId = 'tr46ba89tq'; - const copiedNote = {...service.publicNotebookItems['public'][0]}; + const copiedNote = { ...service.publicNotebookItems['public'][0] }; copiedNote.id = 8; copiedNote.localNotebookItemId = newLocalNotebookItemId; copiedNote.workgroupId = 3; @@ -379,4 +399,4 @@ function shouldHandleNewNotebookItem() { expect(newNote.localNotebookItemId).toBe(newLocalNotebookItemId); expect(studentDataService.updateNodeStatuses).toHaveBeenCalled(); }); -} \ No newline at end of file +} diff --git a/src/main/webapp/site/src/app/services/notificationService.spec.ts b/src/main/webapp/site/src/app/services/notificationService.spec.ts index 28672805f0..3bf20d32d0 100644 --- a/src/main/webapp/site/src/app/services/notificationService.spec.ts +++ b/src/main/webapp/site/src/app/services/notificationService.spec.ts @@ -1,27 +1,38 @@ -import { TestBed } from "@angular/core/testing"; -import { HttpClientTestingModule, HttpTestingController } from "@angular/common/http/testing"; -import { UpgradeModule } from "@angular/upgrade/static"; -import { NotificationService } from "../../../../wise5/services/notificationService"; -import { ConfigService } from "../../../../wise5/services/configService"; -import { ProjectService } from "../../../../wise5/services/projectService"; -import { UtilService } from "../../../../wise5/services/utilService"; -import { SessionService } from "../../../../wise5/services/sessionService"; +import { TestBed } from '@angular/core/testing'; +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; +import { UpgradeModule } from '@angular/upgrade/static'; +import { NotificationService } from '../../../../wise5/services/notificationService'; +import { ConfigService } from '../../../../wise5/services/configService'; +import { ProjectService } from '../../../../wise5/services/projectService'; +import { UtilService } from '../../../../wise5/services/utilService'; +import { SessionService } from '../../../../wise5/services/sessionService'; let service: NotificationService; let configService: ConfigService; let http: HttpTestingController; -const notification1 = {"id":56,"groupId":null,"nodeId":"node1","componentId":"lxa2e3w3ed", - "componentType":null,"type":"teacherToStudent", - "message":"You have new feedback from your teacher!", - "data":"{\"annotationId\":103}","timeGenerated":1589303714000, - "timeDismissed":1589474238000,"serverSaveTime":1589303714000, - "runId":303,"periodId":133,"toWorkgroupId":67,"fromWorkgroupId":66}; +const notification1 = { + id: 56, + groupId: null, + nodeId: 'node1', + componentId: 'lxa2e3w3ed', + componentType: null, + type: 'teacherToStudent', + message: 'You have new feedback from your teacher!', + data: '{"annotationId":103}', + timeGenerated: 1589303714000, + timeDismissed: 1589474238000, + serverSaveTime: 1589303714000, + runId: 303, + periodId: 133, + toWorkgroupId: 67, + fromWorkgroupId: 66 +}; describe('NotificationService', () => { beforeEach(() => { TestBed.configureTestingModule({ - imports: [ HttpClientTestingModule, UpgradeModule ], - providers: [ ConfigService, NotificationService, ProjectService, SessionService, UtilService ] + imports: [HttpClientTestingModule, UpgradeModule], + providers: [ConfigService, NotificationService, ProjectService, SessionService, UtilService] }); http = TestBed.get(HttpTestingController); service = TestBed.get(NotificationService); @@ -41,7 +52,7 @@ function retrieveNotifications_Teacher_ShouldReturnAndSetNotificaitons() { const currentRunId = 1; const retrieveNotificationsURL = `/notifications/${currentRunId}`; spyOn(configService, 'getNotificationURL').and.returnValue(retrieveNotificationsURL); - const notificationsExpected = [ notification1 ]; + const notificationsExpected = [notification1]; service.retrieveNotifications().then((notifications: any) => { expect(notifications.length).toEqual(1); }); diff --git a/src/main/webapp/site/src/app/services/openResponseService.spec.ts b/src/main/webapp/site/src/app/services/openResponseService.spec.ts index 03f4ed30d1..debb3c7fee 100644 --- a/src/main/webapp/site/src/app/services/openResponseService.spec.ts +++ b/src/main/webapp/site/src/app/services/openResponseService.spec.ts @@ -1,15 +1,15 @@ -import { TestBed } from "@angular/core/testing"; -import { HttpClientTestingModule } from "@angular/common/http/testing"; -import { UpgradeModule } from "@angular/upgrade/static"; -import { OpenResponseService } from "../../../../wise5/components/openResponse/openResponseService"; -import { AnnotationService } from "../../../../wise5/services/annotationService"; -import { ConfigService } from "../../../../wise5/services/configService"; -import { ProjectService } from "../../../../wise5/services/projectService"; -import { StudentAssetService } from "../../../../wise5/services/studentAssetService"; -import { StudentDataService } from "../../../../wise5/services/studentDataService"; -import { TagService } from "../../../../wise5/services/tagService"; -import { UtilService } from "../../../../wise5/services/utilService"; -import { SessionService } from "../../../../wise5/services/sessionService"; +import { TestBed } from '@angular/core/testing'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { UpgradeModule } from '@angular/upgrade/static'; +import { OpenResponseService } from '../../../../wise5/components/openResponse/openResponseService'; +import { AnnotationService } from '../../../../wise5/services/annotationService'; +import { ConfigService } from '../../../../wise5/services/configService'; +import { ProjectService } from '../../../../wise5/services/projectService'; +import { StudentAssetService } from '../../../../wise5/services/studentAssetService'; +import { StudentDataService } from '../../../../wise5/services/studentDataService'; +import { TagService } from '../../../../wise5/services/tagService'; +import { UtilService } from '../../../../wise5/services/utilService'; +import { SessionService } from '../../../../wise5/services/sessionService'; let service: OpenResponseService; let studentDataService: StudentDataService; @@ -17,7 +17,7 @@ let studentDataService: StudentDataService; describe('OpenResponseService', () => { beforeEach(() => { TestBed.configureTestingModule({ - imports: [ HttpClientTestingModule, UpgradeModule ], + imports: [HttpClientTestingModule, UpgradeModule], providers: [ AnnotationService, ConfigService, @@ -98,10 +98,15 @@ function isCompleted() { componentStates = []; node = createNode(); }); - function expectIsCompleted(component: any, componentStates: any, node: any, - expectedResult: boolean) { - expect(service.isCompleted(component, componentStates, componentEvents, nodeEvents, node)) - .toEqual(expectedResult); + function expectIsCompleted( + component: any, + componentStates: any, + node: any, + expectedResult: boolean + ) { + expect( + service.isCompleted(component, componentStates, componentEvents, nodeEvents, node) + ).toEqual(expectedResult); } it('should check if a component is completed when there are no component states', () => { expectIsCompleted(component, componentStates, node, false); @@ -212,8 +217,11 @@ function componentStatehasStudentWork() { component = createComponentContent(); component.starterSentence = 'I think...'; }); - function expectComponentStateHasStudentWork(componentState: any, component: any, - expectedResult: boolean) { + function expectComponentStateHasStudentWork( + componentState: any, + component: any, + expectedResult: boolean + ) { expect(service.componentStateHasStudentWork(componentState, component)).toEqual(expectedResult); } it('should check if a component state has student work when it does not have work', () => { @@ -277,8 +285,9 @@ function isAnyComponentStateHasResponse() { expect(service.isAnyComponentStateHasResponse([createComponentState('')])).toEqual(false); }); it('should check if any component state has a response when there is a response', () => { - expect(service.isAnyComponentStateHasResponse([createComponentState('Hello World')])) - .toEqual(true); + expect(service.isAnyComponentStateHasResponse([createComponentState('Hello World')])).toEqual( + true + ); }); } @@ -286,10 +295,10 @@ function isAnyComponentStateHasResponseAndIsSubmit() { it('should check if any component state has a response and submit when there are none', () => { const componentStates = [createComponentState('Hello World', false)]; expect(service.isAnyComponentStateHasResponseAndIsSubmit(componentStates)).toEqual(false); - }) + }); it(`should check if any component state has a response and submit when there is a submit response`, () => { const componentStates = [createComponentState('Hello World', true)]; expect(service.isAnyComponentStateHasResponseAndIsSubmit(componentStates)).toEqual(true); - }) -} \ No newline at end of file + }); +} diff --git a/src/main/webapp/site/src/app/services/outsideURLService.spec.ts b/src/main/webapp/site/src/app/services/outsideURLService.spec.ts index f529c8fc2e..905e9d2f5e 100644 --- a/src/main/webapp/site/src/app/services/outsideURLService.spec.ts +++ b/src/main/webapp/site/src/app/services/outsideURLService.spec.ts @@ -1,14 +1,14 @@ -import { OutsideURLService } from "../../../../wise5/components/outsideURL/outsideURLService"; -import { TestBed } from "@angular/core/testing"; -import { StudentDataService } from "../../../../wise5/services/studentDataService"; -import { UtilService } from "../../../../wise5/services/utilService"; -import { HttpClientTestingModule, HttpTestingController } from "@angular/common/http/testing"; -import { UpgradeModule } from "@angular/upgrade/static"; -import { AnnotationService } from "../../../../wise5/services/annotationService"; -import { ConfigService } from "../../../../wise5/services/configService"; -import { ProjectService } from "../../../../wise5/services/projectService"; -import { TagService } from "../../../../wise5/services/tagService"; -import { SessionService } from "../../../../wise5/services/sessionService"; +import { OutsideURLService } from '../../../../wise5/components/outsideURL/outsideURLService'; +import { TestBed } from '@angular/core/testing'; +import { StudentDataService } from '../../../../wise5/services/studentDataService'; +import { UtilService } from '../../../../wise5/services/utilService'; +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; +import { UpgradeModule } from '@angular/upgrade/static'; +import { AnnotationService } from '../../../../wise5/services/annotationService'; +import { ConfigService } from '../../../../wise5/services/configService'; +import { ProjectService } from '../../../../wise5/services/projectService'; +import { TagService } from '../../../../wise5/services/tagService'; +import { SessionService } from '../../../../wise5/services/sessionService'; let service: OutsideURLService; let http: HttpTestingController; @@ -16,10 +16,7 @@ let http: HttpTestingController; describe('OutsideURLService', () => { beforeEach(() => { TestBed.configureTestingModule({ - imports: [ - HttpClientTestingModule, - UpgradeModule - ], + imports: [HttpClientTestingModule, UpgradeModule], providers: [ AnnotationService, ConfigService, @@ -54,6 +51,6 @@ function getOpenEducationalResources() { expect(resources.length).toEqual(1); expect(resources[0].url).toEqual(url); }); - http.expectOne('wise5/components/outsideURL/resources.json').flush([ { url: url } ]); + http.expectOne('wise5/components/outsideURL/resources.json').flush([{ url: url }]); }); } diff --git a/src/main/webapp/site/src/app/services/projectAssetService.spec.ts b/src/main/webapp/site/src/app/services/projectAssetService.spec.ts index c508787091..e25e197c08 100644 --- a/src/main/webapp/site/src/app/services/projectAssetService.spec.ts +++ b/src/main/webapp/site/src/app/services/projectAssetService.spec.ts @@ -20,7 +20,7 @@ describe('ProjectAssetService', () => { http = TestBed.get(HttpTestingController); configService = TestBed.get(ConfigService); service = TestBed.get(ProjectAssetService); - spyOn(configService, 'getConfigParam').and.callFake(param => { + spyOn(configService, 'getConfigParam').and.callFake((param) => { if (param === 'projectAssetURL') { return '/author/project/asset/1'; } else if (param === 'projectAssetTotalSizeMax') { @@ -71,7 +71,7 @@ function uploadAssets() { success: [], error: [] }; - service.uploadAssets(files).subscribe(data => { + service.uploadAssets(files).subscribe((data) => { expect(data).toEqual(result); }); const request = http.expectOne({ url: '/author/project/asset/1', method: 'POST' }); @@ -279,7 +279,7 @@ function getTextFiles() { const text1 = 'text from model1'; const text2 = 'text from model2'; const textFileNames = ['model1.html', 'model2.html']; - service.getTextFiles(textFileNames).subscribe(textFiles => { + service.getTextFiles(textFileNames).subscribe((textFiles) => { expect(textFiles[0].url).toEqual(url1); expect(textFiles[1].url).toEqual(url2); expect(textFiles[0].body).toEqual(text1); @@ -305,5 +305,5 @@ function injectFileTypeValues() { expect(files[0].fileType).toEqual('image'); expect(files[1].fileType).toEqual('video'); expect(files[2].fileType).toEqual('other'); - }) + }); } diff --git a/src/main/webapp/site/src/app/services/projectAssetService.ts b/src/main/webapp/site/src/app/services/projectAssetService.ts index a8861d75f3..8404e6baf3 100644 --- a/src/main/webapp/site/src/app/services/projectAssetService.ts +++ b/src/main/webapp/site/src/app/services/projectAssetService.ts @@ -24,7 +24,7 @@ export class ProjectAssetService { protected ProjectService: ProjectService, protected UtilService: UtilService ) { - this.getProjectAssets().subscribe(projectAssets => { + this.getProjectAssets().subscribe((projectAssets) => { if (projectAssets != null) { this.calculateAssetUsage(projectAssets); } diff --git a/src/main/webapp/site/src/app/services/projectService.spec.ts b/src/main/webapp/site/src/app/services/projectService.spec.ts index bdb4cc0ffd..c1f8627b9f 100644 --- a/src/main/webapp/site/src/app/services/projectService.spec.ts +++ b/src/main/webapp/site/src/app/services/projectService.spec.ts @@ -24,12 +24,12 @@ let scootersProjectJSON: any; describe('ProjectService', () => { beforeEach(() => { TestBed.configureTestingModule({ - imports: [ HttpClientTestingModule, UpgradeModule ], - providers: [ ProjectService, ConfigService, SessionService, UtilService ] + imports: [HttpClientTestingModule, UpgradeModule], + providers: [ProjectService, ConfigService, SessionService, UtilService] }); http = TestBed.get(HttpTestingController); configService = TestBed.get(ConfigService); - sessionService = TestBed.get(SessionService) + sessionService = TestBed.get(SessionService); utilService = TestBed.get(UtilService); spyOn(utilService, 'broadcastEventInRootScope').and.callFake(() => {}); service = TestBed.get(ProjectService); @@ -134,7 +134,7 @@ describe('ProjectService', () => { }); function createNormalSpy() { - spyOn(configService, 'getConfigParam').and.callFake(param => { + spyOn(configService, 'getConfigParam').and.callFake((param) => { if (param === 'projectBaseURL') { return projectBaseURL; } else if (param === 'projectURL') { @@ -188,8 +188,7 @@ function shouldNotReplaceAssetPathsInHtmlComponentContent() { function shouldRetrieveProjectWhenConfigProjectURLIsValid() { it('should retrieve project when Config.projectURL is valid', () => { - spyOn(configService, 'getConfigParam').withArgs('projectURL') - .and.returnValue(projectURL); + spyOn(configService, 'getConfigParam').withArgs('projectURL').and.returnValue(projectURL); service.retrieveProject().then((response) => { expect(response).toEqual(scootersProjectJSON); }); @@ -209,12 +208,16 @@ function shouldNotRetrieveProjectWhenConfigProjectURLIsUndefined() { function shouldSaveProject() { it('should save project', () => { spyOn(configService, 'getConfigParam') - .withArgs('canEditProject').and.returnValue(true) - .withArgs('saveProjectURL').and.returnValue(saveProjectURL) - .withArgs('mode').and.returnValue('authoring') - .withArgs('userInfo').and.returnValue({}); + .withArgs('canEditProject') + .and.returnValue(true) + .withArgs('saveProjectURL') + .and.returnValue(saveProjectURL) + .withArgs('mode') + .and.returnValue('authoring') + .withArgs('userInfo') + .and.returnValue({}); spyOn(configService, 'getProjectId').and.returnValue(projectIdDefault); - spyOn(configService, 'getMyUserInfo').and.returnValue({id:1}); + spyOn(configService, 'getMyUserInfo').and.returnValue({ id: 1 }); service.setProject(scootersProjectJSON); service.saveProject(); expect(configService.getConfigParam).toHaveBeenCalledWith('saveProjectURL'); @@ -227,12 +230,16 @@ function shouldHandleSaveProjectResponse() { shouldHandleSaveProjectResponseSuccessHelper('broadcastProjectSaved'); }); it('should broadcast not logged in project not saved', () => { - shouldHandleSaveProjectResponseErrorHelper('notSignedIn', - 'broadcastNotLoggedInProjectNotSaved'); + shouldHandleSaveProjectResponseErrorHelper( + 'notSignedIn', + 'broadcastNotLoggedInProjectNotSaved' + ); }); it('should broadcast not allowed to edit this project', () => { - shouldHandleSaveProjectResponseErrorHelper('notAllowedToEditThisProject', - 'broadcastNotAllowedToEditThisProject'); + shouldHandleSaveProjectResponseErrorHelper( + 'notAllowedToEditThisProject', + 'broadcastNotAllowedToEditThisProject' + ); }); it('should broadcast error saving project', () => { shouldHandleSaveProjectResponseErrorHelper('errorSavingProject', 'broadcastErrorSavingProject'); @@ -247,8 +254,11 @@ function shouldHandleSaveProjectResponseErrorHelper(messageCode: string, functio shouldHandleSaveProjectResponseHelper('error', messageCode, functionName); } -function shouldHandleSaveProjectResponseHelper(status: string, messageCode: string, - functionName: any) { +function shouldHandleSaveProjectResponseHelper( + status: string, + messageCode: string, + functionName: any +) { const response = { status: status, messageCode: messageCode @@ -361,40 +371,40 @@ function getNodeIds() { it('should return the node ids in the project', () => { service.setProject(scootersProjectJSON); const nodeIdsExpected = [ - 'node1', - 'node2', - 'node3', - 'node4', - 'node5', - 'node6', - 'node7', - 'node9', - 'node12', - 'node13', - 'node14', - 'node18', - 'node19', - 'node21', - 'node22', - 'node23', - 'node24', - 'node25', - 'node26', - 'node27', - 'node28', - 'node29', - 'node30', - 'node31', - 'node40', - 'node32', - 'node33', - 'node34', - 'node35', - 'node36', - 'node37', - 'node38', - 'node39', - 'nodeWithNoComponents' + 'node1', + 'node2', + 'node3', + 'node4', + 'node5', + 'node6', + 'node7', + 'node9', + 'node12', + 'node13', + 'node14', + 'node18', + 'node19', + 'node21', + 'node22', + 'node23', + 'node24', + 'node25', + 'node26', + 'node27', + 'node28', + 'node29', + 'node30', + 'node31', + 'node40', + 'node32', + 'node33', + 'node34', + 'node35', + 'node36', + 'node37', + 'node38', + 'node39', + 'nodeWithNoComponents' ]; const nodeIdsActual = service.getNodeIds(); expect(nodeIdsActual).toEqual(nodeIdsExpected); @@ -420,29 +430,20 @@ function shouldGetTheComponentByNodeIdAndComponentId() { const nullComponentIdResult = service.getComponentByNodeIdAndComponentId('node13', null); expect(nullComponentIdResult).toBeNull(); - const nodeIdDNEResult = service.getComponentByNodeIdAndComponentId( - 'badNodeId', - '57lxhwfp5r' - ); + const nodeIdDNEResult = service.getComponentByNodeIdAndComponentId('badNodeId', '57lxhwfp5r'); expect(nodeIdDNEResult).toBeNull(); const componentIdDNEResult = service.getComponentByNodeIdAndComponentId( - 'node13', - 'badComponentId' + 'node13', + 'badComponentId' ); expect(componentIdDNEResult).toBeNull(); - const componentExists = service.getComponentByNodeIdAndComponentId( - 'node13', - '57lxhwfp5r' - ); + const componentExists = service.getComponentByNodeIdAndComponentId('node13', '57lxhwfp5r'); expect(componentExists).not.toBe(null); expect(componentExists.type).toEqual('HTML'); - const componentExists2 = service.getComponentByNodeIdAndComponentId( - 'node9', - 'mnzx68ix8h' - ); + const componentExists2 = service.getComponentByNodeIdAndComponentId('node9', 'mnzx68ix8h'); expect(componentExists2).not.toBe(null); expect(componentExists2.type).toEqual('embedded'); expect(componentExists2.url).toEqual('NewtonScooters-potential-kinetic.html'); @@ -452,39 +453,36 @@ function shouldGetTheComponentByNodeIdAndComponentId() { function shouldGetTheComponentPositionByNodeIdAndComonentId() { it('should get the component position by node id and comonent id', () => { service.setProject(scootersProjectJSON); - const nullNodeIdResult = service.getComponentPositionByNodeIdAndComponentId( - null, - '57lxhwfp5r' - ); + const nullNodeIdResult = service.getComponentPositionByNodeIdAndComponentId(null, '57lxhwfp5r'); expect(nullNodeIdResult).toEqual(-1); const nullComponentIdResult = service.getComponentPositionByNodeIdAndComponentId( - 'node13', - null + 'node13', + null ); expect(nullComponentIdResult).toEqual(-1); const nodeIdDNEResult = service.getComponentPositionByNodeIdAndComponentId( - 'badNodeId', - '57lxhwfp5r' + 'badNodeId', + '57lxhwfp5r' ); expect(nodeIdDNEResult).toEqual(-1); const componentIdDNEResult = service.getComponentPositionByNodeIdAndComponentId( - 'node13', - 'badComponentId' + 'node13', + 'badComponentId' ); expect(componentIdDNEResult).toEqual(-1); const componentExists = service.getComponentPositionByNodeIdAndComponentId( - 'node13', - '57lxhwfp5r' + 'node13', + '57lxhwfp5r' ); expect(componentExists).toEqual(0); const componentExists2 = service.getComponentPositionByNodeIdAndComponentId( - 'node9', - 'mnzx68ix8h' + 'node9', + 'mnzx68ix8h' ); expect(componentExists2).toEqual(1); }); @@ -497,9 +495,7 @@ function shouldGetTheComponentsByNodeId() { expect(nullNodeIdResult).toEqual([]); const nodeIdDNEResult = service.getComponentsByNodeId('badNodeId'); expect(nodeIdDNEResult).toEqual([]); - const nodeWithNullComponentResult = service.getComponentsByNodeId( - 'nodeWithNoComponents' - ); + const nodeWithNullComponentResult = service.getComponentsByNodeId('nodeWithNoComponents'); expect(nodeWithNullComponentResult).toEqual([]); const nodeExistsResult = service.getComponentsByNodeId('node13'); expect(nodeExistsResult).not.toBe(null); @@ -618,7 +614,7 @@ function expectGroupStartId(groupId, expectedStartNodeId) { function removeNodeFromGroup() { it('should remove node from group', () => { service.setProject(demoProjectJSON); - expectChildNodeIdLength('group1', 19) + expectChildNodeIdLength('group1', 19); const group1 = service.getNodeById('group1'); service.removeNodeIdFromGroup(group1, 'node3'); expectChildNodeIdLength('group1', 18); @@ -645,7 +641,7 @@ function shouldIdentifyBranchStartAndMergePoints() { } function expectFunctionCallToReturnValue(func, nodeIdArray, expectedValue) { - nodeIdArray.forEach(nodeId => { + nodeIdArray.forEach((nodeId) => { expect(service[func](nodeId)).toEqual(expectedValue); }); } @@ -686,10 +682,16 @@ function insertNodeAfterInTransitions() { function expectInsertNodeAfterInTransition(nodeIdBefore, nodeIdAfter) { service.setProject(demoProjectJSON); - expect(service.nodeHasTransitionToNodeId(service.getNodeById(nodeIdBefore), nodeIdAfter)).toBeTruthy(); + expect( + service.nodeHasTransitionToNodeId(service.getNodeById(nodeIdBefore), nodeIdAfter) + ).toBeTruthy(); service.insertNodeAfterInTransitions(service.getNodeById(nodeIdBefore), nodeIdAfter); - expect(service.nodeHasTransitionToNodeId(service.getNodeById(nodeIdBefore), nodeIdAfter)).toBeFalsy(); - expect(service.nodeHasTransitionToNodeId(service.getNodeById(nodeIdAfter), nodeIdBefore)).toBeTruthy(); + expect( + service.nodeHasTransitionToNodeId(service.getNodeById(nodeIdBefore), nodeIdAfter) + ).toBeFalsy(); + expect( + service.nodeHasTransitionToNodeId(service.getNodeById(nodeIdAfter), nodeIdBefore) + ).toBeTruthy(); } function shouldNotBeAbleToInsertANodeAfterAnotherNodeWhenTheyAreDifferentTypes() { @@ -749,19 +751,13 @@ function shouldDeleteAStepFromTheProject() { service.setProject(demoProjectJSON); expect(service.getNodes().length).toEqual(54); expect(service.getNodeById('node5')).not.toBeNull(); - expect( - service.nodeHasTransitionToNodeId(service.getNodeById('node4'), 'node5') - ).toBeTruthy(); - expect( - service.nodeHasTransitionToNodeId(service.getNodeById('node5'), 'node6') - ).toBeTruthy(); + expect(service.nodeHasTransitionToNodeId(service.getNodeById('node4'), 'node5')).toBeTruthy(); + expect(service.nodeHasTransitionToNodeId(service.getNodeById('node5'), 'node6')).toBeTruthy(); expect(service.getNodesWithTransitionToNodeId('node6').length).toEqual(1); service.deleteNode('node5'); expect(service.getNodes().length).toEqual(53); expect(service.getNodeById('node5')).toBeNull(); - expect( - service.nodeHasTransitionToNodeId(service.getNodeById('node4'), 'node6') - ).toBeTruthy(); + expect(service.nodeHasTransitionToNodeId(service.getNodeById('node4'), 'node6')).toBeTruthy(); expect(service.getNodesWithTransitionToNodeId('node6').length).toEqual(1); }); } @@ -804,21 +800,14 @@ function shouldDeleteAStepThatIsTheLastStepOfTheProject() { } function shouldDeleteAStepThatIsTheStartIdOfAnAactivityThatIsNotTheFirstActivity() { - it('should delete a step that is the start id of an activity that is not the first activity', - () => { + it('should delete a step that is the start id of an activity that is not the first activity', () => { service.setProject(demoProjectJSON); expect(service.getGroupStartId('group2')).toEqual('node20'); - expect( - service.nodeHasTransitionToNodeId(service.getNodeById('node19'), 'node20') - ).toBeTruthy(); - expect( - service.nodeHasTransitionToNodeId(service.getNodeById('node20'), 'node21') - ).toBeTruthy(); + expect(service.nodeHasTransitionToNodeId(service.getNodeById('node19'), 'node20')).toBeTruthy(); + expect(service.nodeHasTransitionToNodeId(service.getNodeById('node20'), 'node21')).toBeTruthy(); service.deleteNode('node20'); expect(service.getGroupStartId('group2')).toEqual('node21'); - expect( - service.nodeHasTransitionToNodeId(service.getNodeById('node19'), 'node21') - ).toBeTruthy(); + expect(service.nodeHasTransitionToNodeId(service.getNodeById('node19'), 'node21')).toBeTruthy(); }); } @@ -841,17 +830,11 @@ function shouldDeleteTheFirstActivityFromTheProject() { function shouldDeleteAnActivityInTheMiddleOfTheProject() { it('should delete an activity that is in the middle of the project', () => { service.setProject(demoProjectJSON); - expect( - service.nodeHasTransitionToNodeId(service.getNodeById('group2'), 'group3') - ).toBeTruthy(); + expect(service.nodeHasTransitionToNodeId(service.getNodeById('group2'), 'group3')).toBeTruthy(); expect(service.getNodes().length).toEqual(54); service.deleteNode('group3'); - expect( - service.nodeHasTransitionToNodeId(service.getNodeById('group2'), 'group3') - ).toBeFalsy(); - expect( - service.nodeHasTransitionToNodeId(service.getNodeById('group2'), 'group4') - ).toBeTruthy(); + expect(service.nodeHasTransitionToNodeId(service.getNodeById('group2'), 'group3')).toBeFalsy(); + expect(service.nodeHasTransitionToNodeId(service.getNodeById('group2'), 'group4')).toBeTruthy(); expect(service.getNodes().length).toEqual(51); }); } @@ -859,15 +842,11 @@ function shouldDeleteAnActivityInTheMiddleOfTheProject() { function shouldDeleteTheLastActivityFromTheProject() { it('should delete the last activity from the project', () => { service.setProject(demoProjectJSON); - expect( - service.nodeHasTransitionToNodeId(service.getNodeById('group4'), 'group5') - ).toBeTruthy(); + expect(service.nodeHasTransitionToNodeId(service.getNodeById('group4'), 'group5')).toBeTruthy(); expect(service.getTransitionsByFromNodeId('group4').length).toEqual(1); expect(service.getNodes().length).toEqual(54); service.deleteNode('group5'); - expect( - service.nodeHasTransitionToNodeId(service.getNodeById('group4'), 'group5') - ).toBeFalsy(); + expect(service.nodeHasTransitionToNodeId(service.getNodeById('group4'), 'group5')).toBeFalsy(); expect(service.getTransitionsByFromNodeId('group4').length).toEqual(0); expect(service.getNodes().length).toEqual(48); }); @@ -918,9 +897,7 @@ function getUniqueAuthors() { }); it('should get unique authors when there is one author', () => { - const authors = [ - { id: 1, firstName: 'a', lastName: 'a' } - ]; + const authors = [{ id: 1, firstName: 'a', lastName: 'a' }]; const uniqueAuthors = service.getUniqueAuthors(authors); expect(uniqueAuthors.length).toEqual(1); expect(uniqueAuthors[0].id).toEqual(1); @@ -1037,7 +1014,7 @@ function deleteAllStepsInAnActivity() { } function getTags() { - it ('should get tags from the project', () => { + it('should get tags from the project', () => { service.setProject(demoProjectJSON); const tags = service.getTags(); expect(tags.length).toEqual(2); diff --git a/src/main/webapp/site/src/app/services/sessionService.spec.ts b/src/main/webapp/site/src/app/services/sessionService.spec.ts index 164fa49c6e..9be0bb1838 100644 --- a/src/main/webapp/site/src/app/services/sessionService.spec.ts +++ b/src/main/webapp/site/src/app/services/sessionService.spec.ts @@ -10,8 +10,8 @@ let configService: ConfigService; describe('SessionService', () => { beforeEach(() => { TestBed.configureTestingModule({ - imports: [ HttpClientTestingModule, UpgradeModule ], - providers: [ ConfigService, SessionService ] + imports: [HttpClientTestingModule, UpgradeModule], + providers: [ConfigService, SessionService] }); configService = TestBed.get(ConfigService); service = TestBed.get(SessionService); @@ -121,24 +121,20 @@ function checkForLogout() { function renewSession() { describe('renewSession()', () => { - it('should renew the session', fakeAsync( - () => { - spyOn(service, 'checkIfSessionIsActive').and.returnValue(of(true)); - const logOutSpy = spyOn(service, 'logOut') - service.renewSession(); - tick(); - expect(logOutSpy).not.toHaveBeenCalled(); - }) - ); + it('should renew the session', fakeAsync(() => { + spyOn(service, 'checkIfSessionIsActive').and.returnValue(of(true)); + const logOutSpy = spyOn(service, 'logOut'); + service.renewSession(); + tick(); + expect(logOutSpy).not.toHaveBeenCalled(); + })); - it('should log the user out when renew session fails', fakeAsync( - () => { - spyOn(service, 'checkIfSessionIsActive').and.returnValue(of(false)); - const logOutSpy = spyOn(service, 'logOut'); - service.renewSession(); - tick(); - expect(logOutSpy).toHaveBeenCalled(); - }) - ); + it('should log the user out when renew session fails', fakeAsync(() => { + spyOn(service, 'checkIfSessionIsActive').and.returnValue(of(false)); + const logOutSpy = spyOn(service, 'logOut'); + service.renewSession(); + tick(); + expect(logOutSpy).toHaveBeenCalled(); + })); }); } diff --git a/src/main/webapp/site/src/app/services/spaceService.spec.ts b/src/main/webapp/site/src/app/services/spaceService.spec.ts index fdb8fbbd74..6111bcc4c2 100644 --- a/src/main/webapp/site/src/app/services/spaceService.spec.ts +++ b/src/main/webapp/site/src/app/services/spaceService.spec.ts @@ -1,6 +1,6 @@ import { TestBed } from '@angular/core/testing'; import { UpgradeModule } from '@angular/upgrade/static'; -import { HttpClientTestingModule, HttpTestingController } from "@angular/common/http/testing"; +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; import { ConfigService } from '../../../../wise5/services/configService'; import { ProjectService } from '../../../../wise5/services/projectService'; import { SpaceService } from '../../../../wise5/services/spaceService'; @@ -12,11 +12,11 @@ let projectService: ProjectService; describe('SpaceService', () => { beforeEach(() => { TestBed.configureTestingModule({ - imports: [ HttpClientTestingModule, UpgradeModule ], - providers: [ ConfigService, ProjectService, SessionService, SpaceService, UtilService ] + imports: [HttpClientTestingModule, UpgradeModule], + providers: [ConfigService, ProjectService, SessionService, SpaceService, UtilService] }); projectService = TestBed.get(ProjectService); - service = TestBed.get(SpaceService) + service = TestBed.get(SpaceService); }); createSpace(); addSpace(); @@ -36,7 +36,7 @@ function createSpace() { name: name, isPublic: isPublic, isShowInNotebook: isShowInNotebook - }) + }); }); }); } @@ -55,7 +55,7 @@ function addSpace() { name: name, isPublic: isPublic, isShowInNotebook: isShowInNotebook - }) + }); }); }); } @@ -68,4 +68,4 @@ function removeSpace() { expect(projectService.removeSpace).toHaveBeenCalledWith('public'); }); }); -} \ No newline at end of file +} diff --git a/src/main/webapp/site/src/app/services/studentAssetService.spec.ts b/src/main/webapp/site/src/app/services/studentAssetService.spec.ts index 1c1224553d..935ef2f8d8 100644 --- a/src/main/webapp/site/src/app/services/studentAssetService.spec.ts +++ b/src/main/webapp/site/src/app/services/studentAssetService.spec.ts @@ -15,8 +15,8 @@ let asset1, asset2; describe('StudentAssetService', () => { beforeEach(() => { TestBed.configureTestingModule({ - imports: [ HttpClientTestingModule, UpgradeModule ], - providers: [ StudentAssetService, ConfigService ] + imports: [HttpClientTestingModule, UpgradeModule], + providers: [StudentAssetService, ConfigService] }); http = TestBed.get(HttpTestingController); service = TestBed.get(StudentAssetService); @@ -28,17 +28,19 @@ describe('StudentAssetService', () => { }); function initialize() { - asset1 = {id:1, fileName:'wise.png', url:'/curriculum/1/wise.png'}; - asset2 = {id:2, fileName:'wiser.png', url:'/curriculum/1/wiser.png'}; + asset1 = { id: 1, fileName: 'wise.png', url: '/curriculum/1/wise.png' }; + asset2 = { id: 2, fileName: 'wiser.png', url: '/curriculum/1/wiser.png' }; } function retrieveAssets() { describe('retrieveAssets', () => { beforeEach(() => { spyOn(configService, 'getWorkgroupId').and.returnValue(workgroupId); - spyOn(configService, 'getConfigParam').withArgs('mode').and - .returnValue('studentRun').withArgs('studentAssetsURL').and - .returnValue(studentAssetURL); + spyOn(configService, 'getConfigParam') + .withArgs('mode') + .and.returnValue('studentRun') + .withArgs('studentAssetsURL') + .and.returnValue(studentAssetURL); }); retrieveAssets_StudentMode_FetchAssetsAndSetAttributes(); }); @@ -49,9 +51,11 @@ function deleteAsset() { beforeEach(() => { spyOn(configService, 'getWorkgroupId').and.returnValue(workgroupId); spyOn(configService, 'getPeriodId').and.returnValue(periodId); - spyOn(configService, 'getConfigParam').withArgs('mode').and - .returnValue('studentRun').withArgs('studentAssetsURL').and - .returnValue(studentAssetURL); + spyOn(configService, 'getConfigParam') + .withArgs('mode') + .and.returnValue('studentRun') + .withArgs('studentAssetsURL') + .and.returnValue(studentAssetURL); }); deleteAsset_StudentMode_DeleteAsset(); }); diff --git a/src/main/webapp/site/src/app/services/studentDataService.spec.ts b/src/main/webapp/site/src/app/services/studentDataService.spec.ts index 9665bb99c9..8181ce2eb5 100644 --- a/src/main/webapp/site/src/app/services/studentDataService.spec.ts +++ b/src/main/webapp/site/src/app/services/studentDataService.spec.ts @@ -25,9 +25,16 @@ let criteria2: any; describe('StudentDataService', () => { beforeEach(() => { TestBed.configureTestingModule({ - imports: [ HttpClientTestingModule, UpgradeModule ], - providers: [ AnnotationService, ConfigService, ProjectService, SessionService, StudentDataService, - TagService, UtilService ] + imports: [HttpClientTestingModule, UpgradeModule], + providers: [ + AnnotationService, + ConfigService, + ProjectService, + SessionService, + StudentDataService, + TagService, + UtilService + ] }); http = TestBed.get(HttpTestingController); service = TestBed.get(StudentDataService); @@ -48,7 +55,7 @@ describe('StudentDataService', () => { params: { nodeId: 'node2' } - } + }; }); shouldHandleNodeStatusesChanged(); @@ -152,7 +159,7 @@ function shouldHandleNodeStatusesChanged() { spyOn(annotationService, 'getActiveGlobalAnnotationGroups').and.returnValue( globalAnnotationGroups ); - spyOn(service, 'saveAnnotations').and.callFake(annotations => { + spyOn(service, 'saveAnnotations').and.callFake((annotations) => { return annotations; }); const componentState1 = { @@ -210,7 +217,7 @@ function shouldHandleNodeStatusesChanged() { spyOn(annotationService, 'getActiveGlobalAnnotationGroups').and.returnValue( globalAnnotationGroups ); - spyOn(service, 'saveAnnotations').and.callFake(annotations => { + spyOn(service, 'saveAnnotations').and.callFake((annotations) => { return annotations; }); const componentState1 = { @@ -268,7 +275,7 @@ function shouldHandleNodeStatusesChanged() { spyOn(annotationService, 'getActiveGlobalAnnotationGroups').and.returnValue( globalAnnotationGroups ); - spyOn(service, 'saveAnnotations').and.callFake(annotations => { + spyOn(service, 'saveAnnotations').and.callFake((annotations) => { return annotations; }); const componentState1 = { @@ -326,7 +333,7 @@ function shouldHandleNodeStatusesChanged() { spyOn(annotationService, 'getActiveGlobalAnnotationGroups').and.returnValue( globalAnnotationGroups ); - spyOn(service, 'saveAnnotations').and.callFake(annotations => { + spyOn(service, 'saveAnnotations').and.callFake((annotations) => { return annotations; }); const componentState1 = { @@ -399,7 +406,7 @@ function shouldProcessGlobalAnnotationAnyConditional() { ] }; const annotation = createAnnotationGroupAnnotation(1, 'node1', 'component1', data); - spyOn(service, 'saveAnnotations').and.callFake(annotations => { + spyOn(service, 'saveAnnotations').and.callFake((annotations) => { return annotations; }); const componentState1 = { @@ -453,7 +460,7 @@ function shouldProcessGlobalAnnotationAllConditional() { ] }; const annotation = createAnnotationGroupAnnotation(1, 'node1', 'component1', data); - spyOn(service, 'saveAnnotations').and.callFake(annotations => { + spyOn(service, 'saveAnnotations').and.callFake((annotations) => { return annotations; }); const componentState1 = { @@ -517,7 +524,7 @@ function shouldEvaluateNodeConstraintWithTwoRemovalCriteriaRequiringAll() { nodeId: 'node2' } }; - spyOn(service, 'evaluateCriteria').and.callFake(criteria => { + spyOn(service, 'evaluateCriteria').and.callFake((criteria) => { if (criteria == removalCriteria1) { return true; } else if (criteria == removalCriteria2) { @@ -549,7 +556,7 @@ function shouldEvaluateNodeConstraintWithTwoRemovalCriteriaRequiringAny() { nodeId: 'node2' } }; - spyOn(service, 'evaluateCriteria').and.callFake(criteria => { + spyOn(service, 'evaluateCriteria').and.callFake((criteria) => { if (criteria == removalCriteria1) { return true; } else if (criteria == removalCriteria2) { @@ -570,9 +577,7 @@ function shouldEvaluateNodeConstraintWithTwoRemovalCriteriaRequiringAny() { function shouldEvaluateIsCorrectCriteriaFalseWhenNoComponentStates() { it('should evaluate is correct criteria false when no component states', () => { const componentStates = []; - spyOn(service, 'getComponentStatesByNodeIdAndComponentId').and.returnValue( - componentStates - ); + spyOn(service, 'getComponentStatesByNodeIdAndComponentId').and.returnValue(componentStates); const criteria = { params: { nodeId: 'node1', @@ -586,9 +591,7 @@ function shouldEvaluateIsCorrectCriteriaFalseWhenNoComponentStates() { function shouldEvaluateIsCorrectCriteriaFalse() { it('should evaluate is correct criteria false', () => { const componentStates = [{ studentData: { isCorrect: false } }]; - spyOn(service, 'getComponentStatesByNodeIdAndComponentId').and.returnValue( - componentStates - ); + spyOn(service, 'getComponentStatesByNodeIdAndComponentId').and.returnValue(componentStates); const criteria = { params: { nodeId: 'node1', @@ -602,9 +605,7 @@ function shouldEvaluateIsCorrectCriteriaFalse() { function shouldEvaluateIsCorrectCriteriaTrue() { it('should evaluate is correct criteria true', () => { const componentStates = [{ studentData: { isCorrect: true } }]; - spyOn(service, 'getComponentStatesByNodeIdAndComponentId').and.returnValue( - componentStates - ); + spyOn(service, 'getComponentStatesByNodeIdAndComponentId').and.returnValue(componentStates); const criteria = { params: { nodeId: 'node1', @@ -618,9 +619,7 @@ function shouldEvaluateIsCorrectCriteriaTrue() { function shouldEvaluateBranchPathTakenWhenNoPathsTaken() { it('should evaluate branch path taken', () => { const branchPathTakenEvents = []; - spyOn(service, 'getBranchPathTakenEventsByNodeId').and.returnValue( - branchPathTakenEvents - ); + spyOn(service, 'getBranchPathTakenEventsByNodeId').and.returnValue(branchPathTakenEvents); const criteria = { params: { fromNodeId: 'node1', @@ -634,9 +633,7 @@ function shouldEvaluateBranchPathTakenWhenNoPathsTaken() { function shouldEvaluateBranchPathTakenFalse() { it('should evaluate branch path taken false', () => { const branchPathTakenEvents = [{ data: { fromNodeId: 'node1', toNodeId: 'node3' } }]; - spyOn(service, 'getBranchPathTakenEventsByNodeId').and.returnValue( - branchPathTakenEvents - ); + spyOn(service, 'getBranchPathTakenEventsByNodeId').and.returnValue(branchPathTakenEvents); const criteria = { params: { fromNodeId: 'node1', @@ -650,9 +647,7 @@ function shouldEvaluateBranchPathTakenFalse() { function shouldEvaluateBranchPathTakenTrue() { it('should evaluate branch path taken true', () => { const branchPathTakenEvents = [{ data: { fromNodeId: 'node1', toNodeId: 'node2' } }]; - spyOn(service, 'getBranchPathTakenEventsByNodeId').and.returnValue( - branchPathTakenEvents - ); + spyOn(service, 'getBranchPathTakenEventsByNodeId').and.returnValue(branchPathTakenEvents); const criteria = { params: { fromNodeId: 'node1', @@ -744,9 +739,7 @@ function shouldEvaluateIsVisitedAfterCriteriaTrue() { function shouldEvaluateIsRevisedAfterCriteriaFalseWithNoComponentStates() { it('should evaluate is revised after criteria false', () => { const componentState = null; - spyOn(service, 'getLatestComponentStateByNodeIdAndComponentId').and.returnValue( - componentState - ); + spyOn(service, 'getLatestComponentStateByNodeIdAndComponentId').and.returnValue(componentState); const criteria = { params: { isRevisedAfterNodeId: 'node1', @@ -761,9 +754,7 @@ function shouldEvaluateIsRevisedAfterCriteriaFalseWithNoComponentStates() { function shouldEvaluateIsRevisedAfterCriteriaFalse() { it('should evaluate is revised after criteria false', () => { const componentState = { nodeId: 'node1', componentId: 'component1', clientSaveTime: 1000 }; - spyOn(service, 'getLatestComponentStateByNodeIdAndComponentId').and.returnValue( - componentState - ); + spyOn(service, 'getLatestComponentStateByNodeIdAndComponentId').and.returnValue(componentState); const criteria = { params: { isRevisedAfterNodeId: 'node1', @@ -778,9 +769,7 @@ function shouldEvaluateIsRevisedAfterCriteriaFalse() { function shouldEvaluateIsRevisedAfterCriteriaTrue() { it('should evaluate is revised after criteria true', () => { const componentState = { nodeId: 'node1', componentId: 'component1', clientSaveTime: 3000 }; - spyOn(service, 'getLatestComponentStateByNodeIdAndComponentId').and.returnValue( - componentState - ); + spyOn(service, 'getLatestComponentStateByNodeIdAndComponentId').and.returnValue(componentState); const criteria = { params: { isRevisedAfterNodeId: 'node1', @@ -797,9 +786,7 @@ function shouldEvaluateIsVisitedAndRevisedAfterCriteriaFalseWithNoComponentState const events = [{ nodeId: 'node1', event: 'nodeEntered', clientSaveTime: 2000 }]; spyOn(service, 'getEvents').and.returnValue(events); const componentState = null; - spyOn(service, 'getLatestComponentStateByNodeIdAndComponentId').and.returnValue( - componentState - ); + spyOn(service, 'getLatestComponentStateByNodeIdAndComponentId').and.returnValue(componentState); const criteria = { params: { isVisitedAfterNodeId: 'node1', @@ -817,9 +804,7 @@ function shouldEvaluateIsVisitedAndRevisedAfterCriteriaFalse() { const events = [{ nodeId: 'node1', event: 'nodeEntered', clientSaveTime: 1000 }]; spyOn(service, 'getEvents').and.returnValue(events); const componentState = { clientSaveTime: 2000 }; - spyOn(service, 'getLatestComponentStateByNodeIdAndComponentId').and.returnValue( - componentState - ); + spyOn(service, 'getLatestComponentStateByNodeIdAndComponentId').and.returnValue(componentState); const criteria = { params: { isVisitedAfterNodeId: 'node1', @@ -837,9 +822,7 @@ function shouldEvaluateIsVisitedAndRevisedAfterCriteriaTrue() { const events = [{ nodeId: 'node1', event: 'nodeEntered', clientSaveTime: 4000 }]; spyOn(service, 'getEvents').and.returnValue(events); const componentState = { nodeId: 'node2', componentId: 'component2', clientSaveTime: 5000 }; - spyOn(service, 'getLatestComponentStateByNodeIdAndComponentId').and.returnValue( - componentState - ); + spyOn(service, 'getLatestComponentStateByNodeIdAndComponentId').and.returnValue(componentState); const criteria = { params: { isVisitedAfterNodeId: 'node1', @@ -857,9 +840,7 @@ function shouldEvaluateIsVisitedAndRevisedAfterCriteriaFalseNoVisitAfter() { const events = [{ nodeId: 'node1', event: 'nodeEntered', clientSaveTime: 1000 }]; spyOn(service, 'getEvents').and.returnValue(events); const componentState = { nodeId: 'node2', componentId: 'component2', clientSaveTime: 2000 }; - spyOn(service, 'getLatestComponentStateByNodeIdAndComponentId').and.returnValue( - componentState - ); + spyOn(service, 'getLatestComponentStateByNodeIdAndComponentId').and.returnValue(componentState); const criteria = { params: { isVisitedAfterNodeId: 'node1', @@ -877,9 +858,7 @@ function shouldEvaluateIsVisitedAndRevisedAfterCriteriaFalseVisitAfterNoRevise() const events = [{ nodeId: 'node1', event: 'nodeEntered', clientSaveTime: 4000 }]; spyOn(service, 'getEvents').and.returnValue(events); const componentState = { nodeId: 'node2', componentId: 'component2', clientSaveTime: 2000 }; - spyOn(service, 'getLatestComponentStateByNodeIdAndComponentId').and.returnValue( - componentState - ); + spyOn(service, 'getLatestComponentStateByNodeIdAndComponentId').and.returnValue(componentState); const criteria = { params: { isVisitedAfterNodeId: 'node1', @@ -909,24 +888,16 @@ function shouldEvaluateIsNodeVisitedAfterTimestampTrue() { function shouldEvaluateHasWorkCreatedAfterTimestampFalse() { it('should evaluate has work created after timestamp false', () => { const componentState = { nodeId: 'node1', componentId: 'component1', clientSaveTime: 4000 }; - spyOn(service, 'getLatestComponentStateByNodeIdAndComponentId').and.returnValue( - componentState - ); - expect(service.hasWorkCreatedAfterTimestamp('node1', 'component1', 5000)).toEqual( - false - ); + spyOn(service, 'getLatestComponentStateByNodeIdAndComponentId').and.returnValue(componentState); + expect(service.hasWorkCreatedAfterTimestamp('node1', 'component1', 5000)).toEqual(false); }); } function shouldEvaluateHasWorkCreatedAfterTimestampTrue() { it('should evaluate has work created after timestamp true', () => { const componentState = { nodeId: 'node2', componentId: 'component2', clientSaveTime: 6000 }; - spyOn(service, 'getLatestComponentStateByNodeIdAndComponentId').and.returnValue( - componentState - ); - expect(service.hasWorkCreatedAfterTimestamp('node1', 'component1', 5000)).toEqual( - true - ); + spyOn(service, 'getLatestComponentStateByNodeIdAndComponentId').and.returnValue(componentState); + expect(service.hasWorkCreatedAfterTimestamp('node1', 'component1', 5000)).toEqual(true); }); } @@ -988,9 +959,7 @@ function shouldEvaluateUsedXSubmitsCriteriaFalse() { } }; const componentStates = [{ studentData: { submitCounter: 1 } }]; - spyOn(service, 'getComponentStatesByNodeIdAndComponentId').and.returnValue( - componentStates - ); + spyOn(service, 'getComponentStatesByNodeIdAndComponentId').and.returnValue(componentStates); expect(service.evaluateUsedXSubmitsCriteria(criteria)).toEqual(false); }); } @@ -1005,9 +974,7 @@ function shouldEvaluateUsedXSubmitsCriteriaTrue() { } }; const componentStates = [{ studentData: { submitCounter: 2 } }]; - spyOn(service, 'getComponentStatesByNodeIdAndComponentId').and.returnValue( - componentStates - ); + spyOn(service, 'getComponentStatesByNodeIdAndComponentId').and.returnValue(componentStates); expect(service.evaluateUsedXSubmitsCriteria(criteria)).toEqual(true); }); } @@ -1022,9 +989,7 @@ function shouldEvaluateNumberOfWordsWrittenCriteriaFalse() { } }; const componentState = { studentData: { response: 'one two three four five' } }; - spyOn(service, 'getLatestComponentStateByNodeIdAndComponentId').and.returnValue( - componentState - ); + spyOn(service, 'getLatestComponentStateByNodeIdAndComponentId').and.returnValue(componentState); spyOn(utilService, 'wordCount').and.returnValue(5); expect(service.evaluateNumberOfWordsWrittenCriteria(criteria)).toEqual(false); }); @@ -1040,9 +1005,7 @@ function shouldEvaluateNumberOfWordsWrittenCriteriaTrue() { } }; const componentState = { studentData: { response: '1 2 3 4 5 6 7 8 9 0' } }; - spyOn(service, 'getLatestComponentStateByNodeIdAndComponentId').and.returnValue( - componentState - ); + spyOn(service, 'getLatestComponentStateByNodeIdAndComponentId').and.returnValue(componentState); spyOn(utilService, 'wordCount').and.returnValue(10); expect(service.evaluateNumberOfWordsWrittenCriteria(criteria)).toEqual(true); }); @@ -1177,7 +1140,10 @@ function shouldHandleSaveStudentWorkToServerSuccess() { spyOn($rootScope, '$broadcast'); spyOn(service, 'updateNodeStatuses').and.callFake(() => {}); spyOn(service, 'saveStudentStatus').and.callFake(() => { - return new Promise(() => { return true })}); + return new Promise(() => { + return true; + }); + }); service.saveToServerRequestCount = 1; service.handleSaveToServerSuccess(savedStudentDataResponse); expect(service.studentData.componentStates[0].serverSaveTime).toBeDefined(); @@ -1199,8 +1165,14 @@ function shouldHandleSaveStudentWorkToServerSuccess() { }); } -function createComponentState(id, nodeId, componentId, isSubmit = true, requestToken = 'abc', - serverSaveTime = 123) { +function createComponentState( + id, + nodeId, + componentId, + isSubmit = true, + requestToken = 'abc', + serverSaveTime = 123 +) { return { id: id, nodeId: nodeId, @@ -1230,7 +1202,10 @@ function shouldHandleSaveEventsToServerSuccess() { spyOn($rootScope, '$broadcast'); spyOn(service, 'updateNodeStatuses').and.callFake(() => {}); spyOn(service, 'saveStudentStatus').and.callFake(() => { - return new Promise(() => { return true })}); + return new Promise(() => { + return true; + }); + }); service.saveToServerRequestCount = 1; service.handleSaveToServerSuccess(savedStudentDataResponse); expect(service.studentData.events[0].serverSaveTime).toEqual(1000); @@ -1245,7 +1220,14 @@ function shouldHandleSaveEventsToServerSuccess() { }); } -function createEvent(id, nodeId, componentId, event = '', requestToken = 'abc', serverSaveTime = 123) { +function createEvent( + id, + nodeId, + componentId, + event = '', + requestToken = 'abc', + serverSaveTime = 123 +) { return { id: id, nodeId: nodeId, @@ -1271,7 +1253,10 @@ function shouldHandleSaveAnnotationsToServerSuccess() { spyOn(annotationService, 'broadcastAnnotationSavedToServer'); spyOn(service, 'updateNodeStatuses').and.callFake(() => {}); spyOn(service, 'saveStudentStatus').and.callFake(() => { - return new Promise(() => { return true })}); + return new Promise(() => { + return true; + }); + }); service.saveToServerRequestCount = 1; service.handleSaveToServerSuccess(savedStudentDataResponse); expect(service.studentData.annotations[0].serverSaveTime).toEqual(1000); @@ -1411,9 +1396,7 @@ function shouldGetLatestComponentStateByNodeIdAndComponentId() { createComponentState(3, 'node2', 'component3') ] }; - const componentState = service.getLatestComponentStateByNodeIdAndComponentId( - 'node1' - ); + const componentState = service.getLatestComponentStateByNodeIdAndComponentId('node1'); expect(componentState.id).toEqual(2); expect(componentState.nodeId).toEqual('node1'); expect(componentState.componentId).toEqual('component2'); @@ -1527,10 +1510,7 @@ function shouldGetComponentStatesByNodeIdAndComponentId() { createComponentState(5, 'node1', 'component1') ] }; - const componentStates = service.getComponentStatesByNodeIdAndComponentId( - 'node1', - 'component1' - ); + const componentStates = service.getComponentStatesByNodeIdAndComponentId('node1', 'component1'); expect(componentStates.length).toEqual(2); expect(componentStates[0].id).toEqual(1); expect(componentStates[1].id).toEqual(5); @@ -1585,12 +1565,12 @@ function shouldGetLatestNodeEnteredEventNodeIdWithExistingNode() { createEvent(5, 'node1', 'component1', 'nodeEntered') ] }; - spyOn(projectService, 'getNodeById').and.callFake(nodeId => { + spyOn(projectService, 'getNodeById').and.callFake((nodeId) => { return { id: nodeId }; }); - spyOn(projectService, 'isActive').and.callFake(nodeId => { + spyOn(projectService, 'isActive').and.callFake((nodeId) => { return nodeId === 'node1'; }); const nodeId = service.getLatestNodeEnteredEventNodeIdWithExistingNode(); @@ -1637,7 +1617,7 @@ function shouldGetProgressById() { node1: { nodeId: 'node1', isVisible: true, isCompleted: true }, node2: { nodeId: 'node2', isVisible: true, isCompleted: false } }; - spyOn(projectService, 'isGroupNode').and.callFake(nodeId => { + spyOn(projectService, 'isGroupNode').and.callFake((nodeId) => { return nodeId.startsWith('group'); }); const childNodeIds = ['node1', 'node2']; @@ -1672,7 +1652,7 @@ function shouldGetProgressById() { } } }; - spyOn(projectService, 'isGroupNode').and.callFake(nodeId => { + spyOn(projectService, 'isGroupNode').and.callFake((nodeId) => { return nodeId.startsWith('group'); }); const childNodeIds = ['group1', 'group2']; @@ -1690,9 +1670,7 @@ function shouldGetProgressById() { function shouldCheckIsCompleted() { xit('should check a component is completed false', () => { const componentStates = []; - spyOn(service, 'getComponentStatesByNodeIdAndComponentId').and.returnValue( - componentStates - ); + spyOn(service, 'getComponentStatesByNodeIdAndComponentId').and.returnValue(componentStates); const componentEvents = []; spyOn(service, 'getEventsByNodeIdAndComponentId').and.returnValue(componentEvents); const nodeEvents = []; @@ -1705,9 +1683,7 @@ function shouldCheckIsCompleted() { }); xit('should check a component is completed true', () => { const componentStates = [{ studentData: { response: 'Hello World' } }]; - spyOn(service, 'getComponentStatesByNodeIdAndComponentId').and.returnValue( - componentStates - ); + spyOn(service, 'getComponentStatesByNodeIdAndComponentId').and.returnValue(componentStates); const componentEvents = []; spyOn(service, 'getEventsByNodeIdAndComponentId').and.returnValue(componentEvents); const nodeEvents = []; @@ -1901,11 +1877,7 @@ function shouldGetComponentStateSavedAfter() { createComponentState(3, 'node1', 'component1', false, null, 3000) ] }; - const componentState = service.getComponentStateSavedAfter( - 'node1', - 'component1', - 4000 - ); + const componentState = service.getComponentStateSavedAfter('node1', 'component1', 4000); expect(componentState).toBeNull(); }); it('should get component state saved after true', () => { @@ -1916,11 +1888,7 @@ function shouldGetComponentStateSavedAfter() { createComponentState(3, 'node1', 'component1', false, null, 3000) ] }; - const componentState = service.getComponentStateSavedAfter( - 'node1', - 'component1', - 1500 - ); + const componentState = service.getComponentStateSavedAfter('node1', 'component1', 1500); expect(componentState.id).toEqual(2); }); } @@ -1934,11 +1902,7 @@ function shouldGetComponentStateSubmittedAfter() { createComponentState(3, 'node1', 'component1', false, null, 3000) ] }; - const componentState = service.getComponentStateSubmittedAfter( - 'node1', - 'component1', - 1500 - ); + const componentState = service.getComponentStateSubmittedAfter('node1', 'component1', 1500); expect(componentState).toBeNull(); }); it('should get component state submitted after true', () => { @@ -1949,11 +1913,7 @@ function shouldGetComponentStateSubmittedAfter() { createComponentState(3, 'node1', 'component1', true, null, 3000) ] }; - const componentState = service.getComponentStateSubmittedAfter( - 'node1', - 'component1', - 1500 - ); + const componentState = service.getComponentStateSubmittedAfter('node1', 'component1', 1500); expect(componentState.id).toEqual(3); }); } @@ -1986,36 +1946,42 @@ function shouldGetVisitEventAfter() { function shouldGetClassmateStudentWork() { it('should get classmate student work', () => { spyOn(configService, 'getRunId').and.returnValue(1); - spyOn(configService, 'getConfigParam') - .withArgs('studentDataURL') - .and.returnValue('/student'); + spyOn(configService, 'getConfigParam').withArgs('studentDataURL').and.returnValue('/student'); service.getClassmateStudentWork('node1', 'component1', 10); - http.expectOne('/student?runId=1&nodeId=node1&componentId=component1&getStudentWork=true&' + - 'getEvents=false&getAnnotations=false&onlyGetLatest=true&periodId=10').flush({}); + http + .expectOne( + '/student?runId=1&nodeId=node1&componentId=component1&getStudentWork=true&' + + 'getEvents=false&getAnnotations=false&onlyGetLatest=true&periodId=10' + ) + .flush({}); }); } function shouldGetClassmateScores() { it('should get classmate scores', () => { spyOn(configService, 'getRunId').and.returnValue(1); - spyOn(configService, 'getConfigParam') - .withArgs('studentDataURL') - .and.returnValue('/student'); + spyOn(configService, 'getConfigParam').withArgs('studentDataURL').and.returnValue('/student'); service.getClassmateScores('node1', 'component1', 10); - http.expectOne('/student?runId=1&nodeId=node1&componentId=component1&getStudentWork=false&' + - 'getEvents=false&getAnnotations=true&onlyGetLatest=false&periodId=10').flush({}); + http + .expectOne( + '/student?runId=1&nodeId=node1&componentId=component1&getStudentWork=false&' + + 'getEvents=false&getAnnotations=true&onlyGetLatest=false&periodId=10' + ) + .flush({}); }); } function shouldGetStudentWorkById() { it('should get student work by id', () => { spyOn(configService, 'getRunId').and.returnValue(1); - spyOn(configService, 'getConfigParam') - .withArgs('studentDataURL') - .and.returnValue('/student'); + spyOn(configService, 'getConfigParam').withArgs('studentDataURL').and.returnValue('/student'); service.getStudentWorkById(1000); - http.expectOne('/student?runId=1&id=1000&getStudentWork=true&getEvents=false&' + - 'getAnnotations=false&onlyGetLatest=true').flush({ studentWorkList: [] }); + http + .expectOne( + '/student?runId=1&id=1000&getStudentWork=true&getEvents=false&' + + 'getAnnotations=false&onlyGetLatest=true' + ) + .flush({ studentWorkList: [] }); }); } @@ -2035,10 +2001,10 @@ function shouldGetMaxScore() { isVisible: true } }; - spyOn(projectService, 'isGroupNode').and.callFake(nodeId => { + spyOn(projectService, 'isGroupNode').and.callFake((nodeId) => { return nodeId.startsWith('group'); }); - spyOn(projectService, 'getMaxScoreForNode').and.callFake(nodeId => { + spyOn(projectService, 'getMaxScoreForNode').and.callFake((nodeId) => { if (nodeId === 'node1') { return 1; } else if (nodeId === 'node2') { diff --git a/src/main/webapp/site/src/app/services/studentStatusService.spec.ts b/src/main/webapp/site/src/app/services/studentStatusService.spec.ts index 641364dd44..b14274b48d 100644 --- a/src/main/webapp/site/src/app/services/studentStatusService.spec.ts +++ b/src/main/webapp/site/src/app/services/studentStatusService.spec.ts @@ -15,15 +15,22 @@ let http: HttpTestingController; describe('StudentStatusService', () => { beforeEach(() => { TestBed.configureTestingModule({ - imports: [ HttpClientTestingModule, UpgradeModule ], - providers: [AnnotationService, ConfigService, ProjectService, SessionService, StudentStatusService, UtilService ] + imports: [HttpClientTestingModule, UpgradeModule], + providers: [ + AnnotationService, + ConfigService, + ProjectService, + SessionService, + StudentStatusService, + UtilService + ] }); http = TestBed.get(HttpTestingController); service = TestBed.get(StudentStatusService); configService = TestBed.get(ConfigService); }); retrieveStudentStatuses(); -}) +}); function retrieveStudentStatuses() { describe('retrieveStudentStatuses', () => { @@ -37,8 +44,9 @@ function retrieveStudentStatuses_SetStudentStatuses() { const statusPostTimestamp = 12345; spyOn(configService, 'getRunId').and.returnValue(currentRunId); const retrieveStudentStatusesURL = `/api/teacher/run/${currentRunId}/student-status`; - const statusesExpected = - [{timestamp: statusPostTimestamp, status: `{"runId":${currentRunId}}`}]; + const statusesExpected = [ + { timestamp: statusPostTimestamp, status: `{"runId":${currentRunId}}` } + ]; service.retrieveStudentStatuses().then((response) => { expect(response.length).toEqual(1); }); diff --git a/src/main/webapp/site/src/app/services/summaryService.spec.ts b/src/main/webapp/site/src/app/services/summaryService.spec.ts index caf5ac8b3e..ce7a909778 100644 --- a/src/main/webapp/site/src/app/services/summaryService.spec.ts +++ b/src/main/webapp/site/src/app/services/summaryService.spec.ts @@ -1,19 +1,30 @@ -import { TestBed } from "@angular/core/testing"; -import { SummaryService } from "../../../../wise5/components/summary/summaryService"; -import { UpgradeModule } from "@angular/upgrade/static"; -import { StudentDataService } from "../../../../wise5/services/studentDataService"; -import { UtilService } from "../../../../wise5/services/utilService"; -import { HttpClientTestingModule } from "@angular/common/http/testing"; -import { ConfigService } from "../../../../wise5/services/configService"; -import { AnnotationService } from "../../../../wise5/services/annotationService"; -import { ProjectService } from "../../../../wise5/services/projectService"; -import { TagService } from "../../../../wise5/services/tagService"; -import { SessionService } from "../../../../wise5/services/sessionService"; +import { TestBed } from '@angular/core/testing'; +import { SummaryService } from '../../../../wise5/components/summary/summaryService'; +import { UpgradeModule } from '@angular/upgrade/static'; +import { StudentDataService } from '../../../../wise5/services/studentDataService'; +import { UtilService } from '../../../../wise5/services/utilService'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { ConfigService } from '../../../../wise5/services/configService'; +import { AnnotationService } from '../../../../wise5/services/annotationService'; +import { ProjectService } from '../../../../wise5/services/projectService'; +import { TagService } from '../../../../wise5/services/tagService'; +import { SessionService } from '../../../../wise5/services/sessionService'; let service; -const summaryAllowedComponentTypes = ['Animation', 'AudioOscillator', 'ConceptMap', -'Discussion', 'Draw', 'Embedded', 'Graph', 'Label', 'Match', 'MultipleChoice', -'OpenResponse', 'Table']; +const summaryAllowedComponentTypes = [ + 'Animation', + 'AudioOscillator', + 'ConceptMap', + 'Discussion', + 'Draw', + 'Embedded', + 'Graph', + 'Label', + 'Match', + 'MultipleChoice', + 'OpenResponse', + 'Table' +]; const summaryDisallowedComponentTypes = ['HTML', 'OutsideURL', 'Summary']; const scoreSummaryAllowedComponentTypes = summaryAllowedComponentTypes; const scoreSummaryDisallowedComponentTypes = summaryDisallowedComponentTypes; @@ -21,7 +32,7 @@ const scoreSummaryDisallowedComponentTypes = summaryDisallowedComponentTypes; describe('SummaryService', () => { beforeEach(() => { TestBed.configureTestingModule({ - imports: [ HttpClientTestingModule, UpgradeModule ], + imports: [HttpClientTestingModule, UpgradeModule], providers: [ AnnotationService, ConfigService, @@ -68,19 +79,44 @@ function isComponentTypeAllowed() { function isScoresSummaryAvailableForComponentType() { it('should check if score summary is available', () => { - expectFunctionCall('isScoresSummaryAvailableForComponentType', - scoreSummaryAllowedComponentTypes, true); - expectFunctionCall('isScoresSummaryAvailableForComponentType', - scoreSummaryDisallowedComponentTypes, false); + expectFunctionCall( + 'isScoresSummaryAvailableForComponentType', + scoreSummaryAllowedComponentTypes, + true + ); + expectFunctionCall( + 'isScoresSummaryAvailableForComponentType', + scoreSummaryDisallowedComponentTypes, + false + ); }); } function isResponsesSummaryAvailableForComponentType() { it('should check if component types can be used with response summary', () => { - expectFunctionCall('isResponsesSummaryAvailableForComponentType', ['MultipleChoice', 'Table'], - true); - expectFunctionCall('isResponsesSummaryAvailableForComponentType', ['Animation', - 'AudioOscillator', 'ConceptMap', 'Discussion', 'Draw', 'Embedded', 'Graph', 'HTML', 'Label', - 'Match', 'OpenResponse', 'OutsideURL', 'Summary'], false); + expectFunctionCall( + 'isResponsesSummaryAvailableForComponentType', + ['MultipleChoice', 'Table'], + true + ); + expectFunctionCall( + 'isResponsesSummaryAvailableForComponentType', + [ + 'Animation', + 'AudioOscillator', + 'ConceptMap', + 'Discussion', + 'Draw', + 'Embedded', + 'Graph', + 'HTML', + 'Label', + 'Match', + 'OpenResponse', + 'OutsideURL', + 'Summary' + ], + false + ); }); } diff --git a/src/main/webapp/site/src/app/services/tableService.spec.ts b/src/main/webapp/site/src/app/services/tableService.spec.ts index c97e66ecf1..bbd6a4d12d 100644 --- a/src/main/webapp/site/src/app/services/tableService.spec.ts +++ b/src/main/webapp/site/src/app/services/tableService.spec.ts @@ -1,5 +1,5 @@ import { TestBed } from '@angular/core/testing'; -import { TableService } from "../../../../wise5/components/table/tableService"; +import { TableService } from '../../../../wise5/components/table/tableService'; import { UpgradeModule } from '@angular/upgrade/static'; import { StudentAssetService } from '../../../../wise5/services/studentAssetService'; import { HttpClientTestingModule } from '@angular/common/http/testing'; @@ -16,7 +16,7 @@ let service: TableService; describe('TableService', () => { beforeEach(() => { TestBed.configureTestingModule({ - imports: [ HttpClientTestingModule, UpgradeModule ], + imports: [HttpClientTestingModule, UpgradeModule], providers: [ AnnotationService, ConfigService, @@ -43,9 +43,10 @@ describe('TableService', () => { }); function createTableComponent( - tableData: any[], - showSaveButton: boolean, - showSubmitButton: boolean) { + tableData: any[], + showSaveButton: boolean, + showSubmitButton: boolean +) { return { tableData: tableData, showSaveButton: showSaveButton, @@ -89,7 +90,7 @@ function createComponent() { function isCompleted() { const node = createNode(false); - const authoredTableData = [ [ createCell('Item'), createCell('Count') ] ]; + const authoredTableData = [[createCell('Item'), createCell('Count')]]; const componentNotShowingSaveOrSubmit = createTableComponent(authoredTableData, false, false); const componentShowingSaveAndSubmit = createTableComponent(authoredTableData, true, true); const studentData = createStudentData([]); @@ -99,7 +100,7 @@ function isCompleted() { expect(service.isCompleted(component, componentStates, [], [], node)).toEqual(expectedResult); } it('should check if a component is completed when it has no editable cells', () => { - const component = { tableData: [ createCell('Item', false), createCell('Count', false) ] }; + const component = { tableData: [createCell('Item', false), createCell('Count', false)] }; expectIsCompleted(component, [], true); }); it('should check if a component is completed is false when submit button is not showing', () => { @@ -119,7 +120,7 @@ function isCompleted() { function componentHasEditableCells() { let component: any; beforeEach(() => { - const authoredTableData = [ [ createCell('Item', false), createCell('Count', false) ] ]; + const authoredTableData = [[createCell('Item', false), createCell('Count', false)]]; component = createTableComponent(authoredTableData, false, false); }); it('should calculate if a component has editable cells when it is false', () => { @@ -135,9 +136,9 @@ function componentStateHasStudentWork() { let componentState; let componentContent; beforeEach(() => { - const studentData = { tableData: [ [ createCell(''), createCell('') ] ] }; + const studentData = { tableData: [[createCell(''), createCell('')]] }; componentState = createComponentState(studentData, false); - const tableData = [ [ createCell(''), createCell('') ] ]; + const tableData = [[createCell(''), createCell('')]]; componentContent = createTableComponent(tableData, true, true); }); it('should check if component state has student work when it is false', () => { @@ -152,8 +153,8 @@ function componentStateHasStudentWork() { function getTableDataCellValue() { it('should get table data cell value', () => { const table = [ - [ createCell('Upper Left'), createCell('Upper Right'), ], - [ createCell('Bottom Left'), createCell('Bottom Right') ] + [createCell('Upper Left'), createCell('Upper Right')], + [createCell('Bottom Left'), createCell('Bottom Right')] ]; expect(service.getTableDataCellValue(0, 0, table)).toEqual('Upper Left'); expect(service.getTableDataCellValue(1, 0, table)).toEqual('Upper Right'); @@ -166,17 +167,27 @@ function hasRequiredNumberOfFilledRows() { let componentState: any; beforeEach(() => { const studentData = createStudentData([ - [ createCell('Object'), createCell('Count') ], - [ createCell('Computer'), createCell('') ], - [ createCell(''), createCell('') ] + [createCell('Object'), createCell('Count')], + [createCell('Computer'), createCell('')], + [createCell(''), createCell('')] ]); componentState = createComponentState(studentData, false); }); - function expectHasRequiredNumberOfFilledRows(componentState: any, - requiredNumberOfFilledRows: number, tableHasHeaderRow: boolean, - requireAllCellsInARowToBeFilled: boolean, expectedResult: boolean) { - expect(service.hasRequiredNumberOfFilledRows(componentState, requiredNumberOfFilledRows, - tableHasHeaderRow, requireAllCellsInARowToBeFilled)).toEqual(expectedResult); + function expectHasRequiredNumberOfFilledRows( + componentState: any, + requiredNumberOfFilledRows: number, + tableHasHeaderRow: boolean, + requireAllCellsInARowToBeFilled: boolean, + expectedResult: boolean + ) { + expect( + service.hasRequiredNumberOfFilledRows( + componentState, + requiredNumberOfFilledRows, + tableHasHeaderRow, + requireAllCellsInARowToBeFilled + ) + ).toEqual(expectedResult); } it(`should check if student data has the required number of filled rows when all cells in a row are not required to be filled and returns false`, () => { @@ -204,7 +215,7 @@ function hasRequiredNumberOfFilledRows() { function isRowFilled() { let row; beforeEach(() => { - row = [ createCell(''), createCell('') ]; + row = [createCell(''), createCell('')]; }); it('should check if row is filled when not all cells are required and returns false', () => { expect(service.isRowFilled(row, false)).toEqual(false); @@ -227,7 +238,7 @@ function isRowFilled() { function isAllCellsFilledInRow() { let row; beforeEach(() => { - row = [ createCell(''), createCell('') ]; + row = [createCell(''), createCell('')]; }); it('should check if all cells are filled in a row when it is false', () => { row[1].text = 'Hello World'; @@ -243,7 +254,7 @@ function isAllCellsFilledInRow() { function isAtLeastOneCellFilledInrow() { let row; beforeEach(() => { - row = [ createCell(''), createCell('') ]; + row = [createCell(''), createCell('')]; }); it('should check if at least one cell is filled in a row when it is false', () => { expect(service.isAtLeastOneCellFilledInRow(row)).toEqual(false); diff --git a/src/main/webapp/site/src/app/services/tagService.spec.ts b/src/main/webapp/site/src/app/services/tagService.spec.ts index 4df24b0303..46c98d6222 100644 --- a/src/main/webapp/site/src/app/services/tagService.spec.ts +++ b/src/main/webapp/site/src/app/services/tagService.spec.ts @@ -16,9 +16,9 @@ let service: TagService; describe('TagService', () => { beforeEach(() => { TestBed.configureTestingModule({ - imports: [ HttpClientTestingModule, UpgradeModule ], - providers: [ ConfigService, ProjectService, SessionService, TagService, UtilService ] - }) + imports: [HttpClientTestingModule, UpgradeModule], + providers: [ConfigService, ProjectService, SessionService, TagService, UtilService] + }); http = TestBed.get(HttpTestingController); configService = TestBed.get(ConfigService); projectService = TestBed.get(ProjectService); @@ -47,10 +47,7 @@ function retrieveRunTags() { function getNextAvailableTag() { it('should get the next available tag', () => { - const existingTags = [ - { name: 'Group 1' }, - { name: 'Group 2' } - ]; + const existingTags = [{ name: 'Group 1' }, { name: 'Group 2' }]; spyOn(projectService, 'getTags').and.returnValue(existingTags); expect(service.getNextAvailableTag()).toEqual('Group 3'); }); diff --git a/src/main/webapp/site/src/app/services/teacherProjectService.spec.ts b/src/main/webapp/site/src/app/services/teacherProjectService.spec.ts index d9c29300b6..f28443b587 100644 --- a/src/main/webapp/site/src/app/services/teacherProjectService.spec.ts +++ b/src/main/webapp/site/src/app/services/teacherProjectService.spec.ts @@ -44,8 +44,8 @@ const libraryProjects = [ describe('TeacherProjectService', () => { beforeEach(() => { TestBed.configureTestingModule({ - imports: [ HttpClientTestingModule, UpgradeModule ], - providers: [ TeacherProjectService, ConfigService, SessionService, UtilService ] + imports: [HttpClientTestingModule, UpgradeModule], + providers: [TeacherProjectService, ConfigService, SessionService, UtilService] }); http = TestBed.get(HttpTestingController); service = TestBed.get(TeacherProjectService); @@ -74,7 +74,7 @@ describe('TeacherProjectService', () => { }); function createConfigServiceGetConfigParamSpy() { - spyOn(configService, 'getConfigParam').and.callFake(param => { + spyOn(configService, 'getConfigParam').and.callFake((param) => { if (param === 'projectBaseURL') { return projectBaseURL; } else if (param === 'projectURL') { @@ -96,10 +96,12 @@ function registerNewProject() { it('should register new project', () => { createConfigServiceGetConfigParamSpy(); const newProjectIdExpected = projectIdDefault; - const newProjectIdActual = service.registerNewProject(scootersProjectName, - scootersProjectJSONString); + const newProjectIdActual = service.registerNewProject( + scootersProjectName, + scootersProjectJSONString + ); http.expectOne(registerNewProjectURL).flush(newProjectIdExpected); - newProjectIdActual.then(result => { + newProjectIdActual.then((result) => { expect(result).toEqual(newProjectIdExpected); }); }); @@ -145,9 +147,7 @@ function testDeleteComponent() { describe('deleteComponent', () => { it('should delete the component from the node', () => { service.setProject(demoProjectJSON); - expect( - service.getComponentByNodeIdAndComponentId('node1', 'zh4h1urdys') - ).not.toBeNull(); + expect(service.getComponentByNodeIdAndComponentId('node1', 'zh4h1urdys')).not.toBeNull(); service.deleteComponent('node1', 'zh4h1urdys'); expect(service.getComponentByNodeIdAndComponentId('node1', 'zh4h1urdys')).toBeNull(); }); @@ -203,7 +203,7 @@ function getLibraryProjects() { createConfigServiceGetConfigParamSpy(); const result = service.getLibraryProjects(); http.expectOne(getLibraryProjectsURL).flush(libraryProjects); - result.then(projects => { + result.then((projects) => { expect(projects).toEqual(libraryProjects); }); }); @@ -241,7 +241,6 @@ function filterUniqueProjects() { }); } - function shouldGetTheNodeIdAndComponentIdObjects() { it('should get the node id and component id objects', () => { const project = { diff --git a/src/main/webapp/site/src/app/services/test.config.service.ts b/src/main/webapp/site/src/app/services/test.config.service.ts index 7dda20af7a..eef092658c 100644 --- a/src/main/webapp/site/src/app/services/test.config.service.ts +++ b/src/main/webapp/site/src/app/services/test.config.service.ts @@ -1,6 +1,4 @@ import { Injectable } from '@angular/core'; @Injectable() -export class MockConfigService { - -} +export class MockConfigService {} diff --git a/src/main/webapp/site/src/app/services/user.service.spec.ts b/src/main/webapp/site/src/app/services/user.service.spec.ts index 4d5fbfa65e..5cb27e9c91 100644 --- a/src/main/webapp/site/src/app/services/user.service.spec.ts +++ b/src/main/webapp/site/src/app/services/user.service.spec.ts @@ -1,24 +1,19 @@ import { TestBed, inject } from '@angular/core/testing'; import { UserService } from './user.service'; import { HttpClientTestingModule } from '@angular/common/http/testing'; -import { ConfigService } from "./config.service"; +import { ConfigService } from './config.service'; -export class MockConfigService { - -} +export class MockConfigService {} describe('UserService', () => { beforeEach(() => { TestBed.configureTestingModule({ - providers: [ - UserService, - { provide: ConfigService, useClass: MockConfigService } - ], - imports: [ HttpClientTestingModule ] + providers: [UserService, { provide: ConfigService, useClass: MockConfigService }], + imports: [HttpClientTestingModule] }); }); - it('should be created', inject([UserService,ConfigService], (service: UserService) => { + it('should be created', inject([UserService, ConfigService], (service: UserService) => { expect(service).toBeTruthy(); })); }); diff --git a/src/main/webapp/site/src/app/services/user.service.ts b/src/main/webapp/site/src/app/services/user.service.ts index 0310f92488..65d7695e52 100644 --- a/src/main/webapp/site/src/app/services/user.service.ts +++ b/src/main/webapp/site/src/app/services/user.service.ts @@ -3,14 +3,13 @@ import { BehaviorSubject, Observable } from 'rxjs'; import { HttpClient, HttpHeaders } from '@angular/common/http'; import { tap } from 'rxjs/operators'; import { User } from '../domain/user'; -import { HttpParams } from "@angular/common/http"; +import { HttpParams } from '@angular/common/http'; import { ConfigService } from './config.service'; -import { Teacher } from "../domain/teacher"; -import { Student } from "../domain/student"; +import { Teacher } from '../domain/teacher'; +import { Student } from '../domain/student'; @Injectable() export class UserService { - private userUrl = '/api/user/info'; private user$: BehaviorSubject = new BehaviorSubject(null); private checkGoogleUserExistsUrl = '/api/user/check-google-user-exists'; @@ -24,8 +23,7 @@ export class UserService { isRecaptchaRequired = false; redirectUrl: string; // redirect here after logging in - constructor(private http: HttpClient, private configService: ConfigService) { - } + constructor(private http: HttpClient, private configService: ConfigService) {} getUser(): BehaviorSubject { return this.user$; @@ -40,14 +38,14 @@ export class UserService { } isStudent(): boolean { - return this.isAuthenticated && - this.user$.getValue().role === 'student'; + return this.isAuthenticated && this.user$.getValue().role === 'student'; } isTeacher(): boolean { const role = this.user$.getValue().role; - return this.isAuthenticated && - (role === 'teacher' || role === 'admin' || role === 'researcher'); + return ( + this.isAuthenticated && (role === 'teacher' || role === 'admin' || role === 'researcher') + ); } isGoogleUser(): boolean { @@ -60,16 +58,17 @@ export class UserService { retrieveUser(username?: string): Observable { const params = new HttpParams().set('username', username); - return this.http.get(this.userUrl, { params: params }) - .pipe( - tap((user) => { - if (user != null && user.id != null) { - this.isAuthenticated = true; - } - this.isRecaptchaRequired = user.isRecaptchaRequired; - this.user$.next(user); - }) - ); + return this.http + .get(this.userUrl, { params: params }) + .pipe( + tap((user) => { + if (user != null && user.id != null) { + this.isAuthenticated = true; + } + this.isRecaptchaRequired = user.isRecaptchaRequired; + this.user$.next(user); + }) + ); } checkAuthentication(username, password) { @@ -85,25 +84,22 @@ export class UserService { 'Content-Type': 'application/x-www-form-urlencoded' }; let httpParams = new HttpParams() - .set('username', credentials.username) - .set('password', credentials.password); + .set('username', credentials.username) + .set('password', credentials.password); if (credentials.recaptchaResponse != null) { httpParams = httpParams.set('g-recaptcha-response', credentials.recaptchaResponse); } const logInURL = `${this.configService.getContextPath()}/j_acegi_security_check`; - this.http.post(logInURL, - httpParams, - { headers: headers, responseType: 'text' }) - .subscribe((response) => { - try { - response = JSON.parse(response); - } catch(e) { - - } - this.retrieveUser(credentials.username).subscribe((user) => { - return callback && callback(response); - }); + this.http + .post(logInURL, httpParams, { headers: headers, responseType: 'text' }) + .subscribe((response) => { + try { + response = JSON.parse(response); + } catch (e) {} + this.retrieveUser(credentials.username).subscribe((user) => { + return callback && callback(response); }); + }); } getRedirectUrl(): string { @@ -119,20 +115,20 @@ export class UserService { } isGoogleIdExists(googleUserId: string) { - let params = new HttpParams().set("googleUserId", googleUserId); + let params = new HttpParams().set('googleUserId', googleUserId); return this.http.get(this.checkGoogleUserExistsUrl, { params: params }); } isGoogleIdCorrect(googleUserId: string, userId: string) { let params = new HttpParams(); - params = params.set("googleUserId", googleUserId); - params = params.set("userId", userId); + params = params.set('googleUserId', googleUserId); + params = params.set('userId', userId); return this.http.get(this.checkGoogleUserMatchesUrl, { params: params }); } getUserByGoogleId(googleUserId: string) { let params = new HttpParams(); - params = params.set("googleUserId", googleUserId); + params = params.set('googleUserId', googleUserId); return this.http.get(this.googleUserUrl, { params: params }); } @@ -166,16 +162,46 @@ export class UserService { user.language = language; } - sendContactMessage(name, email, teacherUsername, issueType, summary, description, runId, - projectId, userAgent, recaptchaResponse) { + sendContactMessage( + name, + email, + teacherUsername, + issueType, + summary, + description, + runId, + projectId, + userAgent, + recaptchaResponse + ) { const headers = new HttpHeaders().set('Content-Type', 'application/x-www-form-urlencoded'); - const body = this.generateContactMessageParams(name, email, teacherUsername, issueType, - summary, description, runId, projectId, userAgent, recaptchaResponse); + const body = this.generateContactMessageParams( + name, + email, + teacherUsername, + issueType, + summary, + description, + runId, + projectId, + userAgent, + recaptchaResponse + ); return this.http.post(this.contactUrl, body, { headers: headers }); } - generateContactMessageParams(name, email, teacherUsername, issueType, summary, description, runId, - projectId, userAgent, recaptchaResponse) { + generateContactMessageParams( + name, + email, + teacherUsername, + issueType, + summary, + description, + runId, + projectId, + userAgent, + recaptchaResponse + ) { let body = new HttpParams(); body = body.set('name', name); if (email != null) { diff --git a/src/main/webapp/site/src/app/services/util.service.spec.ts b/src/main/webapp/site/src/app/services/util.service.spec.ts index 4d7bd53436..d15403fdba 100644 --- a/src/main/webapp/site/src/app/services/util.service.spec.ts +++ b/src/main/webapp/site/src/app/services/util.service.spec.ts @@ -5,7 +5,7 @@ import { UtilService } from './util.service'; describe('UtilService', () => { beforeEach(() => { TestBed.configureTestingModule({ - providers: [ UtilService ] + providers: [UtilService] }); }); @@ -14,11 +14,11 @@ describe('UtilService', () => { })); it('should get the firstName', inject([UtilService], (service: UtilService) => { - expect(service.getFirstName("Spongebob Squarepants")).toEqual("Spongebob"); + expect(service.getFirstName('Spongebob Squarepants')).toEqual('Spongebob'); })); it('should get the lastName', inject([UtilService], (service: UtilService) => { - expect(service.getLastName("Spongebob Squarepants")).toEqual("Squarepants"); + expect(service.getLastName('Spongebob Squarepants')).toEqual('Squarepants'); })); it('should set and get the mobile menu state', inject([UtilService], (service: UtilService) => { diff --git a/src/main/webapp/site/src/app/services/util.service.ts b/src/main/webapp/site/src/app/services/util.service.ts index bc8ff64458..6ea071475e 100644 --- a/src/main/webapp/site/src/app/services/util.service.ts +++ b/src/main/webapp/site/src/app/services/util.service.ts @@ -5,17 +5,16 @@ import { BehaviorSubject } from 'rxjs'; providedIn: 'root' }) export class UtilService { - private mobileMenuState$: BehaviorSubject = new BehaviorSubject(false); - constructor() { } + constructor() {} getFirstName(fullName: string): string { - return fullName.substring(0, fullName.indexOf(" ")); + return fullName.substring(0, fullName.indexOf(' ')); } getLastName(fullName: string): string { - return fullName.substring(fullName.indexOf(" ") + 1); + return fullName.substring(fullName.indexOf(' ') + 1); } showMobileMenu(state: boolean = true): void { @@ -54,12 +53,12 @@ export class UtilService { removeObjectArrayDuplicatesByProperty(array: any[], prop: string): any[] { return array.filter((obj, pos, arr) => { - return arr.map(mapObj => mapObj[prop]).indexOf(obj[prop]) === pos; + return arr.map((mapObj) => mapObj[prop]).indexOf(obj[prop]) === pos; }); } sortObjectArrayByProperty(array: any[], prop: string): void { - array.sort( (a: any, b: any) => { + array.sort((a: any, b: any) => { const valA = a[prop].toLocaleLowerCase(); const valB = b[prop].toLocaleLowerCase(); if (valA < valB) { @@ -74,7 +73,7 @@ export class UtilService { sortByUsername(obj1: any, obj2: any) { const username1 = obj1.username.toLowerCase(); - const username2 = obj2.username.toLowerCase(); + const username2 = obj2.username.toLowerCase(); if (username1 < username2) { return -1; } else if (username1 > username2) { diff --git a/src/main/webapp/site/src/app/services/utilService.spec.ts b/src/main/webapp/site/src/app/services/utilService.spec.ts index d92fd07f26..974bc9f3af 100644 --- a/src/main/webapp/site/src/app/services/utilService.spec.ts +++ b/src/main/webapp/site/src/app/services/utilService.spec.ts @@ -6,8 +6,8 @@ let service: UtilService; describe('UtilService', () => { beforeEach(() => { TestBed.configureTestingModule({ - imports: [ UpgradeModule ], - providers: [ UtilService ] + imports: [UpgradeModule], + providers: [UtilService] }); service = TestBed.get(UtilService); }); @@ -55,7 +55,7 @@ function convertStringToNumberTests() { expect(service.convertStringToNumber('-100')).toEqual(-100); }); - it('should return null for null argument', () => { + it('should return null for null argument', () => { expect(service.convertStringToNumber(null)).toBeNull(); }); @@ -126,25 +126,25 @@ function expectArrayNameOrder(arr, nameOrder) { function moveObjectUpNotTopElement() { it('moveObjectUp should move an object up when the object is not the top element', () => { - expectMoveFunctionResult('moveObjectUp', 1, ['b','a','c']); + expectMoveFunctionResult('moveObjectUp', 1, ['b', 'a', 'c']); }); } function moveObjectUpIsTopElement() { it('moveObjectUp should not move an object up when the object is the top element', () => { - expectMoveFunctionResult('moveObjectUp', 0, ['a','b','c']); + expectMoveFunctionResult('moveObjectUp', 0, ['a', 'b', 'c']); }); } function moveObjectDownNotBottomElement() { it('moveObjectDown should move an object down when the object is not the bottom element', () => { - expectMoveFunctionResult('moveObjectDown', 1, ['a','c','b']); + expectMoveFunctionResult('moveObjectDown', 1, ['a', 'c', 'b']); }); } function moveObjectDownIsBottomElement() { it('moveObjectDown should not move an object down when the object is the bottom element', () => { - expectMoveFunctionResult('moveObjectDown', 2, ['a','b','c']); + expectMoveFunctionResult('moveObjectDown', 2, ['a', 'b', 'c']); }); } diff --git a/src/main/webapp/site/src/app/services/vleProjectService.spec.ts b/src/main/webapp/site/src/app/services/vleProjectService.spec.ts index 625bd47d4e..93cc690326 100644 --- a/src/main/webapp/site/src/app/services/vleProjectService.spec.ts +++ b/src/main/webapp/site/src/app/services/vleProjectService.spec.ts @@ -14,8 +14,8 @@ let http: HttpTestingController; describe('VLEProjectService', () => { beforeEach(() => { TestBed.configureTestingModule({ - imports: [ HttpClientTestingModule, UpgradeModule ], - providers: [ VLEProjectService, ConfigService, SessionService, UtilService ] + imports: [HttpClientTestingModule, UpgradeModule], + providers: [VLEProjectService, ConfigService, SessionService, UtilService] }); http = TestBed.get(HttpTestingController); service = TestBed.get(VLEProjectService); diff --git a/src/main/webapp/site/src/app/student-hybrid-angular.module.ts b/src/main/webapp/site/src/app/student-hybrid-angular.module.ts index f8a1913cb9..6ca7868980 100644 --- a/src/main/webapp/site/src/app/student-hybrid-angular.module.ts +++ b/src/main/webapp/site/src/app/student-hybrid-angular.module.ts @@ -1,6 +1,6 @@ import { NgModule } from '@angular/core'; -import { createStudentAngularJSModule} from '../../../wise5/vle/student-angular-js-module'; +import { createStudentAngularJSModule } from '../../../wise5/vle/student-angular-js-module'; import { UpgradeModule } from '@angular/upgrade/static'; import { setUpLocationSync } from '@angular/router/upgrade'; import { ProjectService } from '../../../wise5/services/projectService'; @@ -19,35 +19,20 @@ import { ComponentAnnotationsComponent } from '../../../wise5/directives/compone import { MomentModule } from 'ngx-moment'; @NgModule({ - declarations: [ - ComponentAnnotationsComponent, - NavItemComponent, - PossibleScoreComponent - ], - imports: [ - AngularJSModule - ], + declarations: [ComponentAnnotationsComponent, NavItemComponent, PossibleScoreComponent], + imports: [AngularJSModule], providers: [ { provide: DataService, useExisting: StudentDataService }, { provide: ProjectService, useExisting: VLEProjectService }, VLEProjectService ], - exports: [ - CommonModule, - MatButtonModule, - MatDialogModule, - MatListModule - ] + exports: [CommonModule, MatButtonModule, MatDialogModule, MatListModule] }) export class StudentAngularJSModule {} @NgModule({ - declarations: [ - ChooseBranchPathDialogComponent - ], - imports: [ - StudentAngularJSModule - ] + declarations: [ChooseBranchPathDialogComponent], + imports: [StudentAngularJSModule] }) export class StudentVLEAngularJSModule { constructor(upgrade: UpgradeModule) { @@ -56,9 +41,7 @@ export class StudentVLEAngularJSModule { } @NgModule({ - imports: [ - StudentAngularJSModule - ] + imports: [StudentAngularJSModule] }) export class PreviewAngularJSModule { constructor(upgrade: UpgradeModule) { diff --git a/src/main/webapp/site/src/app/student/account/edit-profile/edit-profile.component.spec.ts b/src/main/webapp/site/src/app/student/account/edit-profile/edit-profile.component.spec.ts index 8d7d8cc5ae..c338a4a343 100644 --- a/src/main/webapp/site/src/app/student/account/edit-profile/edit-profile.component.spec.ts +++ b/src/main/webapp/site/src/app/student/account/edit-profile/edit-profile.component.spec.ts @@ -1,21 +1,20 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { EditProfileComponent } from './edit-profile.component'; -import { User } from "../../../domain/user"; +import { User } from '../../../domain/user'; import { Observable, BehaviorSubject } from 'rxjs'; -import { UserService } from "../../../services/user.service"; +import { UserService } from '../../../services/user.service'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { ReactiveFormsModule } from '@angular/forms'; import { MatSelectModule } from '@angular/material/select'; import { MatSnackBarModule } from '@angular/material/snack-bar'; import { MatInputModule } from '@angular/material/input'; -import { StudentService } from "../../student.service"; -import { NO_ERRORS_SCHEMA } from "@angular/core"; +import { StudentService } from '../../student.service'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; import { By } from '@angular/platform-browser'; -import { Student } from "../../../domain/student"; +import { Student } from '../../../domain/student'; import { configureTestSuite } from 'ng-bullet'; export class MockUserService { - user: User; getUser(): BehaviorSubject { @@ -33,7 +32,7 @@ export class MockUserService { } getLanguages() { - return Observable.create( observer => { + return Observable.create((observer) => { observer.next([]); observer.complete(); }); @@ -47,7 +46,7 @@ export class MockUserService { export class MockStudentService { updateProfile() { - return Observable.create(observer => { + return Observable.create((observer) => { observer.next({}); observer.complete(); }); @@ -73,7 +72,7 @@ describe('EditProfileComponent', () => { configureTestSuite(() => { TestBed.configureTestingModule({ - declarations: [ EditProfileComponent ], + declarations: [EditProfileComponent], imports: [ BrowserAnimationsModule, ReactiveFormsModule, @@ -85,8 +84,8 @@ describe('EditProfileComponent', () => { { provide: StudentService, useClass: MockStudentService }, { provide: UserService, useClass: MockUserService } ], - schemas: [ NO_ERRORS_SCHEMA ] - }) + schemas: [NO_ERRORS_SCHEMA] + }); }); beforeEach(() => { @@ -117,7 +116,7 @@ describe('EditProfileComponent', () => { expect(submitButton.disabled).toBe(false); }); - it('should disable submit button when form is submitted', async() => { + it('should disable submit button when form is submitted', async () => { const submitButton = getSubmitButton(); const form = getForm(); form.triggerEventHandler('submit', null); @@ -125,7 +124,7 @@ describe('EditProfileComponent', () => { expect(submitButton.disabled).toBe(true); }); - it('should update the user', async() => { + it('should update the user', async () => { component.editProfileFormGroup.get('language').setValue('Spanish'); submitForm(); fixture.detectChanges(); diff --git a/src/main/webapp/site/src/app/student/account/edit-profile/edit-profile.component.ts b/src/main/webapp/site/src/app/student/account/edit-profile/edit-profile.component.ts index 04cc2cca31..ea256cd802 100644 --- a/src/main/webapp/site/src/app/student/account/edit-profile/edit-profile.component.ts +++ b/src/main/webapp/site/src/app/student/account/edit-profile/edit-profile.component.ts @@ -2,9 +2,9 @@ import { Component, OnInit } from '@angular/core'; import { FormGroup, FormControl, Validators, FormBuilder } from '@angular/forms'; import { finalize } from 'rxjs/operators'; import { MatSnackBar } from '@angular/material/snack-bar'; -import { Student } from "../../../domain/student"; -import { UserService } from "../../../services/user.service"; -import { StudentService } from "../../student.service"; +import { Student } from '../../../domain/student'; +import { UserService } from '../../../services/user.service'; +import { StudentService } from '../../student.service'; @Component({ selector: 'app-edit-profile', @@ -12,7 +12,6 @@ import { StudentService } from "../../student.service"; styleUrls: ['./edit-profile.component.scss'] }) export class EditProfileComponent implements OnInit { - user: Student; languages: object[]; changed: boolean = false; @@ -25,10 +24,12 @@ export class EditProfileComponent implements OnInit { language: new FormControl('', [Validators.required]) }); - constructor(private fb: FormBuilder, - private studentService: StudentService, - private userService: UserService, - public snackBar: MatSnackBar) { + constructor( + private fb: FormBuilder, + private studentService: StudentService, + private userService: UserService, + public snackBar: MatSnackBar + ) { this.user = this.getUser().getValue(); this.setControlFieldValue('firstName', this.user.firstName); this.setControlFieldValue('lastName', this.user.lastName); @@ -51,23 +52,23 @@ export class EditProfileComponent implements OnInit { this.editProfileFormGroup.controls[name].setValue(value); } - ngOnInit() { - } + ngOnInit() {} saveChanges() { this.isSaving = true; const username = this.user.username; const language = this.getControlFieldValue('language'); - this.studentService.updateProfile(username, language) - .pipe( - finalize(() => { - this.isSaving = false; - }) - ) - .subscribe((response) => { - this.handleUpdateProfileResponse(response); - this.userService.updateStudentUser(language); + this.studentService + .updateProfile(username, language) + .pipe( + finalize(() => { + this.isSaving = false; }) + ) + .subscribe((response) => { + this.handleUpdateProfileResponse(response); + this.userService.updateStudentUser(language); + }); } getControlFieldValue(fieldName) { diff --git a/src/main/webapp/site/src/app/student/account/edit/edit.component.spec.ts b/src/main/webapp/site/src/app/student/account/edit/edit.component.spec.ts index 70603baae2..9cd0d72929 100644 --- a/src/main/webapp/site/src/app/student/account/edit/edit.component.spec.ts +++ b/src/main/webapp/site/src/app/student/account/edit/edit.component.spec.ts @@ -3,13 +3,11 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { EditComponent } from './edit.component'; import { Component, NO_ERRORS_SCHEMA } from '@angular/core'; -@Component({selector: 'app-edit-password', template: ''}) -class EditPasswordComponent { -} +@Component({ selector: 'app-edit-password', template: '' }) +class EditPasswordComponent {} -@Component({selector: 'app-edit-profile', template: ''}) -class EditProfileComponent { -} +@Component({ selector: 'app-edit-profile', template: '' }) +class EditProfileComponent {} describe('EditComponent', () => { let component: EditComponent; @@ -17,11 +15,10 @@ describe('EditComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ EditComponent ], - providers: [ ], - schemas: [ NO_ERRORS_SCHEMA ] - }) - .compileComponents(); + declarations: [EditComponent], + providers: [], + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); })); beforeEach(() => { diff --git a/src/main/webapp/site/src/app/student/account/edit/edit.component.ts b/src/main/webapp/site/src/app/student/account/edit/edit.component.ts index 6e7cf4c24a..6f77a1e9b6 100644 --- a/src/main/webapp/site/src/app/student/account/edit/edit.component.ts +++ b/src/main/webapp/site/src/app/student/account/edit/edit.component.ts @@ -6,8 +6,7 @@ import { Component, OnInit } from '@angular/core'; styleUrls: ['./edit.component.scss'] }) export class EditComponent implements OnInit { - constructor() { } + constructor() {} ngOnInit() {} - } diff --git a/src/main/webapp/site/src/app/student/add-project-dialog/add-project-dialog.component.ts b/src/main/webapp/site/src/app/student/add-project-dialog/add-project-dialog.component.ts index 2307491908..dd7c3ddb5c 100644 --- a/src/main/webapp/site/src/app/student/add-project-dialog/add-project-dialog.component.ts +++ b/src/main/webapp/site/src/app/student/add-project-dialog/add-project-dialog.component.ts @@ -29,7 +29,7 @@ export class AddProjectDialogComponent implements OnInit { ) {} ngOnInit() { - this.route.queryParams.subscribe(params => { + this.route.queryParams.subscribe((params) => { if (params['accessCode'] != null) { this.accessCode = params['accessCode']; this.addProjectForm.controls['runCode'].setValue(params['accessCode']); @@ -42,7 +42,7 @@ export class AddProjectDialogComponent implements OnInit { this.isAdding = true; this.studentService .addRun(this.registerRunRunCode, this.selectedPeriod) - .subscribe(studentRun => { + .subscribe((studentRun) => { if (studentRun.status === 'error') { if (studentRun.messageCode === 'alreadyAddedRun') { this.addProjectForm.controls['runCode'].setErrors({ alreadyAddedRun: true }); @@ -70,7 +70,7 @@ export class AddProjectDialogComponent implements OnInit { const runCode = this.addProjectForm.controls['runCode'].value; this.registerRunRunCode = runCode; if (this.isValidRunCodeSyntax(runCode)) { - this.studentService.getRunInfo(runCode).subscribe(runInfo => { + this.studentService.getRunInfo(runCode).subscribe((runInfo) => { this.handleRunCodeResponse(runInfo); }); } else { diff --git a/src/main/webapp/site/src/app/student/auth.guard.spec.ts b/src/main/webapp/site/src/app/student/auth.guard.spec.ts index 04e76e9438..a8477f3660 100644 --- a/src/main/webapp/site/src/app/student/auth.guard.spec.ts +++ b/src/main/webapp/site/src/app/student/auth.guard.spec.ts @@ -1,29 +1,25 @@ import { TestBed, inject } from '@angular/core/testing'; import { HttpClientTestingModule } from '@angular/common/http/testing'; import { AuthGuard } from './auth.guard'; -import { UserService } from "../services/user.service"; -import { RouterTestingModule } from "@angular/router/testing"; -import { ConfigService } from "../services/config.service"; -import { NO_ERRORS_SCHEMA } from "@angular/core"; +import { UserService } from '../services/user.service'; +import { RouterTestingModule } from '@angular/router/testing'; +import { ConfigService } from '../services/config.service'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; -export class MockUserService { +export class MockUserService {} -} - -export class MockConfigService { - -} +export class MockConfigService {} describe('StudentAuthGuard', () => { beforeEach(() => { TestBed.configureTestingModule({ - imports: [ HttpClientTestingModule, RouterTestingModule ], + imports: [HttpClientTestingModule, RouterTestingModule], providers: [ AuthGuard, { provide: UserService, useClass: MockUserService }, { provide: ConfigService, useClass: MockConfigService } ], - schemas: [ NO_ERRORS_SCHEMA ] + schemas: [NO_ERRORS_SCHEMA] }); }); diff --git a/src/main/webapp/site/src/app/student/auth.guard.ts b/src/main/webapp/site/src/app/student/auth.guard.ts index caf2a45744..6d7a4f9506 100644 --- a/src/main/webapp/site/src/app/student/auth.guard.ts +++ b/src/main/webapp/site/src/app/student/auth.guard.ts @@ -1,15 +1,9 @@ import { Injectable } from '@angular/core'; -import { - CanActivate, - ActivatedRouteSnapshot, - RouterStateSnapshot, - Router -} from '@angular/router'; -import { UserService } from "../services/user.service"; +import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router } from '@angular/router'; +import { UserService } from '../services/user.service'; @Injectable() export class AuthGuard implements CanActivate { - constructor(private userService: UserService, private router: Router) {} canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean { diff --git a/src/main/webapp/site/src/app/student/student-home/student-home.component.spec.ts b/src/main/webapp/site/src/app/student/student-home/student-home.component.spec.ts index 28b06fa013..8d2c420c8f 100644 --- a/src/main/webapp/site/src/app/student/student-home/student-home.component.spec.ts +++ b/src/main/webapp/site/src/app/student/student-home/student-home.component.spec.ts @@ -1,10 +1,10 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; -import { Observable } from "rxjs"; -import { MatDialog } from "@angular/material/dialog"; -import { User } from "../../domain/user"; -import { UserService } from "../../services/user.service"; -import { StudentHomeComponent } from "./student-home.component"; -import { NO_ERRORS_SCHEMA } from "@angular/core"; +import { Observable } from 'rxjs'; +import { MatDialog } from '@angular/material/dialog'; +import { User } from '../../domain/user'; +import { UserService } from '../../services/user.service'; +import { StudentHomeComponent } from './student-home.component'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; export class MockUserService { getUser(): Observable { @@ -14,7 +14,7 @@ export class MockUserService { user.role = 'student'; user.username = 'DemoUser0101'; user.id = 123456; - return Observable.create( observer => { + return Observable.create((observer) => { observer.next(user); observer.complete(); }); @@ -27,15 +27,14 @@ describe('StudentHomeComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ StudentHomeComponent ], + declarations: [StudentHomeComponent], providers: [ { provide: UserService, useClass: MockUserService }, { provide: MatDialog, useValue: {} } ], imports: [], - schemas: [ NO_ERRORS_SCHEMA ] - }) - .compileComponents(); + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); })); beforeEach(() => { diff --git a/src/main/webapp/site/src/app/student/student-home/student-home.component.ts b/src/main/webapp/site/src/app/student/student-home/student-home.component.ts index 2ca0374431..1399d660ed 100644 --- a/src/main/webapp/site/src/app/student/student-home/student-home.component.ts +++ b/src/main/webapp/site/src/app/student/student-home/student-home.component.ts @@ -1,8 +1,8 @@ import { Component, OnInit } from '@angular/core'; -import { MatDialog } from "@angular/material/dialog" -import { UserService } from "../../services/user.service"; -import { User } from "../../domain/user"; -import { AddProjectDialogComponent } from "../add-project-dialog/add-project-dialog.component"; +import { MatDialog } from '@angular/material/dialog'; +import { UserService } from '../../services/user.service'; +import { User } from '../../domain/user'; +import { AddProjectDialogComponent } from '../add-project-dialog/add-project-dialog.component'; import { ActivatedRoute } from '@angular/router'; @Component({ @@ -11,20 +11,18 @@ import { ActivatedRoute } from '@angular/router'; styleUrls: ['./student-home.component.scss'] }) export class StudentHomeComponent implements OnInit { - user: User = new User(); - constructor(private userService: UserService, public dialog: MatDialog) { } + constructor(private userService: UserService, public dialog: MatDialog) {} ngOnInit() { this.getUser(); } getUser() { - this.userService.getUser() - .subscribe(user => { - this.user = user; - }); + this.userService.getUser().subscribe((user) => { + this.user = user; + }); } showAddRun() { diff --git a/src/main/webapp/site/src/app/student/student-routing.module.ts b/src/main/webapp/site/src/app/student/student-routing.module.ts index ef19ef67c0..73a3bb7638 100644 --- a/src/main/webapp/site/src/app/student/student-routing.module.ts +++ b/src/main/webapp/site/src/app/student/student-routing.module.ts @@ -3,8 +3,8 @@ import { RouterModule, Routes } from '@angular/router'; import { StudentComponent } from './student.component'; import { StudentHomeComponent } from './student-home/student-home.component'; -import { AuthGuard } from "./auth.guard"; -import { EditComponent } from "./account/edit/edit.component"; +import { AuthGuard } from './auth.guard'; +import { EditComponent } from './account/edit/edit.component'; const studentRoutes: Routes = [ { @@ -16,17 +16,17 @@ const studentRoutes: Routes = [ { path: 'home', component: StudentHomeComponent, pathMatch: 'full' }, { path: 'profile', redirectTo: '', pathMatch: 'full' }, { path: 'profile/edit', component: EditComponent }, - {path: '', loadChildren: () => import('../student-hybrid-angular.module').then(m => m.StudentVLEAngularJSModule)} + { + path: '', + loadChildren: () => + import('../student-hybrid-angular.module').then((m) => m.StudentVLEAngularJSModule) + } ] } ]; @NgModule({ - imports: [ - RouterModule.forChild(studentRoutes) - ], - exports: [ - RouterModule - ] + imports: [RouterModule.forChild(studentRoutes)], + exports: [RouterModule] }) export class StudentRoutingModule {} diff --git a/src/main/webapp/site/src/app/student/student-run-list-item/student-run-list-item.component.spec.ts b/src/main/webapp/site/src/app/student/student-run-list-item/student-run-list-item.component.spec.ts index 7bc5e8480f..3910106cad 100644 --- a/src/main/webapp/site/src/app/student/student-run-list-item/student-run-list-item.component.spec.ts +++ b/src/main/webapp/site/src/app/student/student-run-list-item/student-run-list-item.component.spec.ts @@ -1,27 +1,27 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { StudentRun } from '../student-run'; import { StudentRunListItemComponent } from './student-run-list-item.component'; -import { Observable } from "rxjs"; -import { Config } from "../../domain/config"; -import { ConfigService } from "../../services/config.service"; -import { MomentModule } from "ngx-moment"; -import { Project } from "../../domain/project"; -import { User } from "../../domain/user"; -import { NO_ERRORS_SCHEMA } from "@angular/core"; -import { MatDialog, MatDialogModule } from "@angular/material/dialog"; -import { StudentService } from "../student.service"; -import { UserService } from "../../services/user.service"; +import { Observable } from 'rxjs'; +import { Config } from '../../domain/config'; +import { ConfigService } from '../../services/config.service'; +import { MomentModule } from 'ngx-moment'; +import { Project } from '../../domain/project'; +import { User } from '../../domain/user'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { MatDialog, MatDialogModule } from '@angular/material/dialog'; +import { StudentService } from '../student.service'; +import { UserService } from '../../services/user.service'; import { configureTestSuite } from 'ng-bullet'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; export class MockConfigService { getConfig(): Observable { - const config : Config = { - "contextPath":"vle", - "logOutURL":"/logout", - "currentTime": new Date("2018-10-17T00:00:00.0").getTime() + const config: Config = { + contextPath: 'vle', + logOutURL: '/logout', + currentTime: new Date('2018-10-17T00:00:00.0').getTime() }; - return Observable.create( observer => { + return Observable.create((observer) => { observer.next(config); observer.complete(); }); @@ -34,13 +34,9 @@ export class MockConfigService { } } -export class MockStudentService { +export class MockStudentService {} -} - -export class MockUserService { - -} +export class MockUserService {} describe('StudentRunListItemComponent', () => { let component: StudentRunListItemComponent; @@ -48,14 +44,14 @@ describe('StudentRunListItemComponent', () => { configureTestSuite(() => { TestBed.configureTestingModule({ - imports: [ MomentModule, BrowserAnimationsModule, MatDialogModule ], - declarations: [ StudentRunListItemComponent ], + imports: [MomentModule, BrowserAnimationsModule, MatDialogModule], + declarations: [StudentRunListItemComponent], providers: [ { provide: ConfigService, useClass: MockConfigService }, { provide: StudentService, useClass: MockStudentService }, - { provide: UserService, useClass: MockUserService }, - ], - schemas: [ NO_ERRORS_SCHEMA ] + { provide: UserService, useClass: MockUserService } + ], + schemas: [NO_ERRORS_SCHEMA] }); }); @@ -64,15 +60,15 @@ describe('StudentRunListItemComponent', () => { component = fixture.componentInstance; const run: StudentRun = new StudentRun(); run.id = 1; - run.name = "Photosynthesis"; + run.name = 'Photosynthesis'; const owner = new User(); - owner.displayName = "Mr. Happy"; + owner.displayName = 'Mr. Happy'; run.owner = owner; - run.projectThumb = "Happy.png"; + run.projectThumb = 'Happy.png'; run.startTime = new Date('2018-10-17T00:00:00.0').getTime(); const project: Project = new Project(); project.id = 1; - project.name = "Test Project"; + project.name = 'Test Project'; run.project = project; component.run = run; fixture.detectChanges(); diff --git a/src/main/webapp/site/src/app/student/student-run-list-item/student-run-list-item.component.ts b/src/main/webapp/site/src/app/student/student-run-list-item/student-run-list-item.component.ts index 22c03a3faa..9730e66445 100644 --- a/src/main/webapp/site/src/app/student/student-run-list-item/student-run-list-item.component.ts +++ b/src/main/webapp/site/src/app/student/student-run-list-item/student-run-list-item.component.ts @@ -2,22 +2,21 @@ import { Component, OnInit, Input } from '@angular/core'; import { StudentRun } from '../student-run'; import { DomSanitizer } from '@angular/platform-browser'; import { SafeStyle } from '@angular/platform-browser'; -import { ConfigService } from "../../services/config.service"; -import { MatDialog } from "@angular/material/dialog"; -import { TeamSignInDialogComponent } from "../team-sign-in-dialog/team-sign-in-dialog.component"; -import { Student } from "../../domain/student"; -import { StudentService } from "../student.service"; -import { UserService } from "../../services/user.service"; +import { ConfigService } from '../../services/config.service'; +import { MatDialog } from '@angular/material/dialog'; +import { TeamSignInDialogComponent } from '../team-sign-in-dialog/team-sign-in-dialog.component'; +import { Student } from '../../domain/student'; +import { StudentService } from '../student.service'; +import { UserService } from '../../services/user.service'; import { flash } from '../../animations'; @Component({ selector: 'app-student-run-list-item', templateUrl: './student-run-list-item.component.html', styleUrls: ['./student-run-list-item.component.scss'], - animations: [ flash ] + animations: [flash] }) export class StudentRunListItemComponent implements OnInit { - @Input() run: StudentRun = new StudentRun(); @@ -26,11 +25,13 @@ export class StudentRunListItemComponent implements OnInit { animateDuration: string = '0s'; animateDelay: string = '0s'; - constructor(private sanitizer: DomSanitizer, - private configService: ConfigService, - public dialog: MatDialog, - private studentService: StudentService, - private userService: UserService) { + constructor( + private sanitizer: DomSanitizer, + private configService: ConfigService, + public dialog: MatDialog, + private studentService: StudentService, + private userService: UserService + ) { this.sanitizer = sanitizer; this.configService = configService; } @@ -49,7 +50,7 @@ export class StudentRunListItemComponent implements OnInit { this.animateDelay = '1s'; setTimeout(() => { this.run.isHighlighted = false; - }, 7000) + }, 7000); } } @@ -73,10 +74,11 @@ export class StudentRunListItemComponent implements OnInit { const user = this.userService.getUser().getValue(); const presentUserIds = [user.id]; const absentUserIds = []; - this.studentService.launchRun(this.run.id, this.run.workgroupId, presentUserIds, absentUserIds) - .subscribe((response: any) => { - window.location.href = response.startProjectUrl; - }); + this.studentService + .launchRun(this.run.id, this.run.workgroupId, presentUserIds, absentUserIds) + .subscribe((response: any) => { + window.location.href = response.startProjectUrl; + }); } isRunActive(run) { diff --git a/src/main/webapp/site/src/app/student/student-run-list/student-run-list.component.spec.ts b/src/main/webapp/site/src/app/student/student-run-list/student-run-list.component.spec.ts index 4350dab9b4..b7b23c4fe1 100644 --- a/src/main/webapp/site/src/app/student/student-run-list/student-run-list.component.spec.ts +++ b/src/main/webapp/site/src/app/student/student-run-list/student-run-list.component.spec.ts @@ -28,25 +28,25 @@ export class MockStudentService { projectThumb: '/wise/curriculum/360/assets/project_thumb.png' }); getRuns(): Observable { - const runs : Run[] = [ + const runs: Run[] = [ new Run({ - id:1, - name:'Photosynthesis', + id: 1, + name: 'Photosynthesis', startTime: new Date('2018-08-22T00:00:00.0').getTime() }), new Run({ - id:2, - name:'Plate Tectonics', + id: 2, + name: 'Plate Tectonics', startTime: new Date('2018-08-25T00:00:00.0').getTime() }), new Run({ - id:3, - name:'Chemical Reactions', + id: 3, + name: 'Chemical Reactions', startTime: new Date('2018-08-20T00:00:00.0').getTime(), endTime: new Date('2018-08-22T00:00:00.0').getTime() }) - ]; - return Observable.create( observer => { + ]; + return Observable.create((observer) => { observer.next(runs); observer.complete(); }); @@ -65,15 +65,15 @@ describe('StudentRunListComponent', () => { configureTestSuite(() => { TestBed.configureTestingModule({ - declarations: [ StudentRunListComponent ], - imports: [ MomentModule ], + declarations: [StudentRunListComponent], + imports: [MomentModule], providers: [ { provide: StudentService, useClass: MockStudentService }, { provide: ConfigService, useClass: MockConfigService }, { provide: ActivatedRoute, useValue: { queryParams: Observable.create() } }, { provide: MatDialog, useValue: {} } ], - schemas: [ NO_ERRORS_SCHEMA ] + schemas: [NO_ERRORS_SCHEMA] }); }); @@ -89,6 +89,8 @@ describe('StudentRunListComponent', () => { it('should show number of runs', () => { const compiled = fixture.debugElement.nativeElement; - expect(compiled.querySelector('#unitCount').textContent).toContain('My WISE units: 3 (1 scheduled, 1 active)'); - }) + expect(compiled.querySelector('#unitCount').textContent).toContain( + 'My WISE units: 3 (1 scheduled, 1 active)' + ); + }); }); diff --git a/src/main/webapp/site/src/app/student/student-run-list/student-run-list.component.ts b/src/main/webapp/site/src/app/student/student-run-list/student-run-list.component.ts index 3d562b1e5a..c3f550d918 100644 --- a/src/main/webapp/site/src/app/student/student-run-list/student-run-list.component.ts +++ b/src/main/webapp/site/src/app/student/student-run-list/student-run-list.component.ts @@ -13,18 +13,19 @@ import { AddProjectDialogComponent } from '../add-project-dialog/add-project-dia styleUrls: ['./student-run-list.component.scss'] }) export class StudentRunListComponent implements OnInit { - runs: StudentRun[] = []; filteredRuns: StudentRun[] = []; search: string = ''; loaded: boolean = false; showAll: boolean = false; - constructor(private studentService: StudentService, - private configService: ConfigService, - private route: ActivatedRoute, - private dialog: MatDialog) { - studentService.newRunSource$.subscribe(run => { + constructor( + private studentService: StudentService, + private configService: ConfigService, + private route: ActivatedRoute, + private dialog: MatDialog + ) { + studentService.newRunSource$.subscribe((run) => { run.isHighlighted = true; this.runs.unshift(new StudentRun(run)); if (!this.showAll) { @@ -34,7 +35,7 @@ export class StudentRunListComponent implements OnInit { } } setTimeout(() => { - document.getElementById(`run${run.id}`).scrollIntoView() + document.getElementById(`run${run.id}`).scrollIntoView(); }, 1000); }); } @@ -44,20 +45,19 @@ export class StudentRunListComponent implements OnInit { } getRuns() { - this.studentService.getRuns() - .subscribe(runs => { - for (let run of runs) { - this.runs.push(new StudentRun(run)); + this.studentService.getRuns().subscribe((runs) => { + for (let run of runs) { + this.runs.push(new StudentRun(run)); + } + this.filteredRuns = runs; + this.searchUpdated(this.search); + this.loaded = true; + this.route.queryParams.subscribe((params) => { + if (params['accessCode'] != null) { + this.handleClassroomAccessCode(params['accessCode']); } - this.filteredRuns = runs; - this.searchUpdated(this.search); - this.loaded = true; - this.route.queryParams.subscribe(params => { - if (params['accessCode'] != null) { - this.handleClassroomAccessCode(params['accessCode']); - } - }); }); + }); } sortByStartTimeDesc(a, b) { @@ -82,8 +82,8 @@ export class StudentRunListComponent implements OnInit { } runSpansYears(run: StudentRun) { - const startYear = (new DateFormatPipe()).transform(run.startTime, 'Y'); - const endYear = (new DateFormatPipe()).transform(run.endTime, 'Y'); + const startYear = new DateFormatPipe().transform(run.startTime, 'Y'); + const endYear = new DateFormatPipe().transform(run.endTime, 'Y'); return startYear != endYear; } @@ -91,8 +91,8 @@ export class StudentRunListComponent implements OnInit { if (this.runSpansYears(run)) { return true; } - const startMonth = (new DateFormatPipe()).transform(run.startTime, 'M'); - const endMonth = (new DateFormatPipe()).transform(run.endTime, 'M'); + const startMonth = new DateFormatPipe().transform(run.startTime, 'M'); + const endMonth = new DateFormatPipe().transform(run.endTime, 'M'); return startMonth != endMonth; } @@ -100,8 +100,8 @@ export class StudentRunListComponent implements OnInit { if (this.runSpansMonths(run)) { return true; } - const startDay = (new DateFormatPipe()).transform(run.startTime, 'D'); - const endDay = (new DateFormatPipe()).transform(run.endTime, 'D'); + const startDay = new DateFormatPipe().transform(run.startTime, 'D'); + const endDay = new DateFormatPipe().transform(run.endTime, 'D'); return startDay != endDay; } @@ -147,7 +147,7 @@ export class StudentRunListComponent implements OnInit { filterValue = this.search.toLocaleLowerCase(); // TODO: extract this for global use? return this.runs.filter((run: StudentRun) => - Object.keys(run).some(prop => { + Object.keys(run).some((prop) => { let value: any; if (prop === 'owner') { value = run[prop].displayName; diff --git a/src/main/webapp/site/src/app/student/student.component.spec.ts b/src/main/webapp/site/src/app/student/student.component.spec.ts index d68ce4954c..3b555b9cf5 100644 --- a/src/main/webapp/site/src/app/student/student.component.spec.ts +++ b/src/main/webapp/site/src/app/student/student.component.spec.ts @@ -2,7 +2,7 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { StudentComponent } from './student.component'; import { RouterTestingModule } from '@angular/router/testing'; import { Router } from '@angular/router'; -import { NO_ERRORS_SCHEMA } from "@angular/core"; +import { NO_ERRORS_SCHEMA } from '@angular/core'; describe('StudentComponent', () => { let component: StudentComponent; @@ -11,12 +11,11 @@ describe('StudentComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - imports: [ RouterTestingModule ], + imports: [RouterTestingModule], providers: [], - declarations: [ StudentComponent ], - schemas: [ NO_ERRORS_SCHEMA ] - }) - .compileComponents(); + declarations: [StudentComponent], + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); })); beforeEach(() => { diff --git a/src/main/webapp/site/src/app/student/student.component.ts b/src/main/webapp/site/src/app/student/student.component.ts index 1e1a25ee99..7f00a97395 100644 --- a/src/main/webapp/site/src/app/student/student.component.ts +++ b/src/main/webapp/site/src/app/student/student.component.ts @@ -7,12 +7,9 @@ import { Router } from '@angular/router'; styleUrls: ['./student.component.scss'] }) export class StudentComponent implements OnInit { + constructor(private router: Router) {} - constructor(private router: Router) { - } - - ngOnInit() { - } + ngOnInit() {} isShowingAngularJSApp() { return this.router.url.includes('/student/unit'); diff --git a/src/main/webapp/site/src/app/student/student.module.ts b/src/main/webapp/site/src/app/student/student.module.ts index fba90d67e8..03fff5da08 100644 --- a/src/main/webapp/site/src/app/student/student.module.ts +++ b/src/main/webapp/site/src/app/student/student.module.ts @@ -2,7 +2,7 @@ import { NgModule } from '@angular/core'; import { CommonModule, Location } from '@angular/common'; import { FlexLayoutModule } from '@angular/flex-layout'; import { MomentModule } from 'ngx-moment'; -import { FormsModule, ReactiveFormsModule } from "@angular/forms"; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { MatDialogModule } from '@angular/material/dialog'; import { MatButtonModule } from '@angular/material/button'; import { MatCardModule } from '@angular/material/card'; @@ -14,22 +14,28 @@ import { MatTabsModule } from '@angular/material/tabs'; import { MatTooltipModule } from '@angular/material/tooltip'; const materialModules = [ - MatButtonModule, MatCardModule, MatCheckboxModule, MatDialogModule, - MatDividerModule, MatIconModule, MatProgressBarModule, - MatTabsModule, MatTooltipModule + MatButtonModule, + MatCardModule, + MatCheckboxModule, + MatDialogModule, + MatDividerModule, + MatIconModule, + MatProgressBarModule, + MatTabsModule, + MatTooltipModule ]; -import { SharedModule } from "../modules/shared/shared.module"; +import { SharedModule } from '../modules/shared/shared.module'; import { StudentRoutingModule } from './student-routing.module'; import { StudentComponent } from './student.component'; import { StudentHomeComponent } from './student-home/student-home.component'; import { StudentRunListComponent } from './student-run-list/student-run-list.component'; import { StudentRunListItemComponent } from './student-run-list-item/student-run-list-item.component'; -import { AuthGuard } from "./auth.guard"; -import { AddProjectDialogComponent } from "./add-project-dialog/add-project-dialog.component"; +import { AuthGuard } from './auth.guard'; +import { AddProjectDialogComponent } from './add-project-dialog/add-project-dialog.component'; import { EditComponent } from './account/edit/edit.component'; import { EditProfileComponent } from './account/edit-profile/edit-profile.component'; -import { TimelineModule } from "../modules/timeline/timeline.module"; +import { TimelineModule } from '../modules/timeline/timeline.module'; import { TeamSignInDialogComponent } from './team-sign-in-dialog/team-sign-in-dialog.component'; @NgModule({ @@ -54,12 +60,7 @@ import { TeamSignInDialogComponent } from './team-sign-in-dialog/team-sign-in-di EditProfileComponent, TeamSignInDialogComponent ], - providers: [ - AuthGuard - ], - exports: [ - StudentComponent, - materialModules - ] + providers: [AuthGuard], + exports: [StudentComponent, materialModules] }) -export class StudentModule { } +export class StudentModule {} diff --git a/src/main/webapp/site/src/app/student/student.service.spec.ts b/src/main/webapp/site/src/app/student/student.service.spec.ts index e34c571d60..d5cb4bbccd 100644 --- a/src/main/webapp/site/src/app/student/student.service.spec.ts +++ b/src/main/webapp/site/src/app/student/student.service.spec.ts @@ -1,20 +1,23 @@ import { async, TestBed, inject } from '@angular/core/testing'; import { StudentService } from './student.service'; import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; -import { NO_ERRORS_SCHEMA } from "@angular/core"; +import { NO_ERRORS_SCHEMA } from '@angular/core'; describe('StudentService', () => { beforeEach(() => { TestBed.configureTestingModule({ - imports: [ HttpClientTestingModule ], - providers: [ StudentService ], - schemas: [ NO_ERRORS_SCHEMA ] + imports: [HttpClientTestingModule], + providers: [StudentService], + schemas: [NO_ERRORS_SCHEMA] }); }); - it('should be created', async(inject([StudentService, HttpTestingController], - (service: StudentService, backend: HttpTestingController) => { - expect(service).toBeTruthy(); - }))); - + it('should be created', async( + inject( + [StudentService, HttpTestingController], + (service: StudentService, backend: HttpTestingController) => { + expect(service).toBeTruthy(); + } + ) + )); }); diff --git a/src/main/webapp/site/src/app/student/student.service.ts b/src/main/webapp/site/src/app/student/student.service.ts index 91b13685dc..900d6ba828 100644 --- a/src/main/webapp/site/src/app/student/student.service.ts +++ b/src/main/webapp/site/src/app/student/student.service.ts @@ -1,16 +1,15 @@ import { Injectable } from '@angular/core'; -import { Observable , Subject } from 'rxjs'; +import { Observable, Subject } from 'rxjs'; import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http'; import { tap } from 'rxjs/operators'; import { RunInfo } from './run-info'; -import { Student } from "../domain/student"; +import { Student } from '../domain/student'; import { StudentRun } from './student-run'; -import {Run} from '../domain/run'; +import { Run } from '../domain/run'; @Injectable() export class StudentService { - private runsUrl = 'api/student/runs'; private runInfoUrl = 'api/student/run/info'; private runInfoByIdUrl = 'api/student/run/info-by-id'; @@ -29,23 +28,22 @@ export class StudentService { private newRunSource = new Subject(); newRunSource$ = this.newRunSource.asObservable(); - constructor(private http: HttpClient) { } + constructor(private http: HttpClient) {} getRuns(): Observable { const headers = new HttpHeaders({ 'Cache-Control': 'no-cache' }); - return this.http.get(this.runsUrl, { headers: headers }) - .pipe( - tap(runs => this.log(`fetched runs`)) - ); + return this.http + .get(this.runsUrl, { headers: headers }) + .pipe(tap((runs) => this.log(`fetched runs`))); } getRunInfo(runCode: string): Observable { - let params = new HttpParams().set("runCode", runCode); + let params = new HttpParams().set('runCode', runCode); return this.http.get(this.runInfoUrl, { params: params }); } getRunInfoById(runId: number): Observable { - let params = new HttpParams().set("runId", String(runId)); + let params = new HttpParams().set('runId', String(runId)); return this.http.get(this.runInfoByIdUrl, { params }); } @@ -57,8 +55,7 @@ export class StudentService { return this.http.post(this.addRunUrl, body, { headers: headers }); } - launchRun(runId: number, workgroupId: number, presentUserIds: number[], - absentUserIds: number[]) { + launchRun(runId: number, workgroupId: number, presentUserIds: number[], absentUserIds: number[]) { const headers = new HttpHeaders().set('Content-Type', 'application/x-www-form-urlencoded'); let body = new HttpParams(); body = body.set('runId', String(runId)); @@ -82,8 +79,10 @@ export class StudentService { const headers = { 'Content-Type': 'application/json' }; - return this.http.post(this.registerUrl, studentUser, - { headers: headers, responseType: 'json' }); + return this.http.post(this.registerUrl, studentUser, { + headers: headers, + responseType: 'json' + }); } retrieveSecurityQuestions(): Observable { diff --git a/src/main/webapp/site/src/app/student/team-sign-in-dialog/team-sign-in-dialog.component.spec.ts b/src/main/webapp/site/src/app/student/team-sign-in-dialog/team-sign-in-dialog.component.spec.ts index 2cae69454a..f8f5d9817e 100644 --- a/src/main/webapp/site/src/app/student/team-sign-in-dialog/team-sign-in-dialog.component.spec.ts +++ b/src/main/webapp/site/src/app/student/team-sign-in-dialog/team-sign-in-dialog.component.spec.ts @@ -1,13 +1,13 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { TeamSignInDialogComponent } from './team-sign-in-dialog.component'; -import { NO_ERRORS_SCHEMA } from "@angular/core"; -import { UserService } from "../../services/user.service"; -import { ConfigService } from "../../services/config.service"; -import { BehaviorSubject, Observable } from "rxjs"; -import { Config } from "../../domain/config"; -import { AuthService } from "angularx-social-login"; -import { User } from "../../domain/user"; +import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { UserService } from '../../services/user.service'; +import { ConfigService } from '../../services/config.service'; +import { BehaviorSubject, Observable } from 'rxjs'; +import { Config } from '../../domain/config'; +import { AuthService } from 'angularx-social-login'; +import { User } from '../../domain/user'; import { StudentService } from '../student.service'; import { RouterTestingModule } from '@angular/router/testing'; import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; @@ -25,8 +25,13 @@ export class MockUserService { } export class MockStudentService { - launchRun(runId: string, workgroupId: string, presentUserIds: number[], absentUserIds: number[]): Observable { - return Observable.create(observer => { + launchRun( + runId: string, + workgroupId: string, + presentUserIds: number[], + absentUserIds: number[] + ): Observable { + return Observable.create((observer) => { observer.next({ status: 'success', messageCode: 'passwordChanged' @@ -36,55 +41,49 @@ export class MockStudentService { } } -export class MockAuthService { - -} +export class MockAuthService {} export class MockConfigService { getConfig(): Observable { const config: Config = { - contextPath: "/wise", - logOutURL: "/logout", - currentTime: new Date("2018-10-17T00:00:00.0").getTime() + contextPath: '/wise', + logOutURL: '/logout', + currentTime: new Date('2018-10-17T00:00:00.0').getTime() }; - return Observable.create(observer => { + return Observable.create((observer) => { observer.next(config); observer.complete(); }); } } - - describe('TeamSignInDialogComponent', () => { let component: TeamSignInDialogComponent; let fixture: ComponentFixture; const runObj = { id: 1, - name: "Test", + name: 'Test', workgroupMembers: [ { - "id": 123, - "firstName": "Spongebob", - "lastName": "Squarepants", - "username": "SpongebobS0123" + id: 123, + firstName: 'Spongebob', + lastName: 'Squarepants', + username: 'SpongebobS0123' }, { - "id": 154, - "firstName": "Patrick", - "lastName": "Starr", - "username": "PatrickS0619" + id: 154, + firstName: 'Patrick', + lastName: 'Starr', + username: 'PatrickS0619' } ] }; beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ TeamSignInDialogComponent ], - imports: [ - RouterTestingModule - ], + declarations: [TeamSignInDialogComponent], + imports: [RouterTestingModule], providers: [ { provide: AuthService, useClass: MockAuthService }, { provide: ConfigService, useClass: MockConfigService }, @@ -93,9 +92,8 @@ describe('TeamSignInDialogComponent', () => { { provide: UserService, useClass: MockUserService }, { provide: StudentService, useClass: MockStudentService } ], - schemas: [ NO_ERRORS_SCHEMA ] - }) - .compileComponents(); + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); })); beforeEach(() => { diff --git a/src/main/webapp/site/src/app/student/team-sign-in-dialog/team-sign-in-dialog.component.ts b/src/main/webapp/site/src/app/student/team-sign-in-dialog/team-sign-in-dialog.component.ts index d475fd0b56..7814e0ad51 100644 --- a/src/main/webapp/site/src/app/student/team-sign-in-dialog/team-sign-in-dialog.component.ts +++ b/src/main/webapp/site/src/app/student/team-sign-in-dialog/team-sign-in-dialog.component.ts @@ -1,11 +1,11 @@ import { Component, Inject, Input, OnInit } from '@angular/core'; -import { UserService } from "../../services/user.service"; -import { Student } from "../../domain/student"; -import { StudentRun } from "../student-run"; -import { MAT_DIALOG_DATA, MatDialogRef } from "@angular/material/dialog"; -import { AuthService, GoogleLoginProvider } from "angularx-social-login"; -import { ConfigService } from "../../services/config.service"; -import { StudentService } from "../student.service"; +import { UserService } from '../../services/user.service'; +import { Student } from '../../domain/student'; +import { StudentRun } from '../student-run'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; +import { AuthService, GoogleLoginProvider } from 'angularx-social-login'; +import { ConfigService } from '../../services/config.service'; +import { StudentService } from '../student.service'; import { Router } from '@angular/router'; @Component({ @@ -14,7 +14,6 @@ import { Router } from '@angular/router'; styleUrls: ['./team-sign-in-dialog.component.scss'] }) export class TeamSignInDialogComponent implements OnInit { - user: Student; run: StudentRun = new StudentRun(); teamMembers: any[] = []; @@ -23,13 +22,15 @@ export class TeamSignInDialogComponent implements OnInit { isGoogleAuthenticationEnabled: boolean = false; canLaunch: boolean = false; - constructor(private configService: ConfigService, - private socialAuthService: AuthService, - private userService: UserService, - private studentService: StudentService, - private router: Router, - public dialogRef: MatDialogRef, - @Inject(MAT_DIALOG_DATA) public data: any) { + constructor( + private configService: ConfigService, + private socialAuthService: AuthService, + private userService: UserService, + private studentService: StudentService, + private router: Router, + public dialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) public data: any + ) { this.run = this.data.run; this.user = this.getUser().getValue(); if (this.run.workgroupMembers != null) { @@ -71,99 +72,134 @@ export class TeamSignInDialogComponent implements OnInit { } isShowSignInWithGoogle(teamMember) { - return this.isNotSignedIn(teamMember) && this.isGoogleAuthenticationEnabled && - (!this.isExistingStudent(teamMember) || this.isGoogleUser(teamMember)); + return ( + this.isNotSignedIn(teamMember) && + this.isGoogleAuthenticationEnabled && + (!this.isExistingStudent(teamMember) || this.isGoogleUser(teamMember)) + ); } signIn(teamMember: any) { - this.userService.checkAuthentication(teamMember.username, teamMember.password).subscribe((response) => { - if (response.isUsernameValid === true && response.isPasswordValid === true) { - this.studentService.canBeAddedToWorkgroup(this.run.id, this.run.workgroupId, response.userId) - .subscribe((canBeAddedToWorkgroupResponse) => { - if (canBeAddedToWorkgroupResponse.isTeacher) { - alert($localize`A teacher cannot be added as a team member.`); - teamMember.username = null; - } else if (canBeAddedToWorkgroupResponse.status && this.allowSignIn(teamMember, 1)) { - for (const member of canBeAddedToWorkgroupResponse.workgroupMembers) { - if (!this.isLoggedInUser(member.username)) { - const currentMember = this.getTeamMemberByUsernameOrAvailableSlot(member.username); - this.updateTeamMember(this.teamMembers.indexOf(currentMember), member); + this.userService + .checkAuthentication(teamMember.username, teamMember.password) + .subscribe((response) => { + if (response.isUsernameValid === true && response.isPasswordValid === true) { + this.studentService + .canBeAddedToWorkgroup(this.run.id, this.run.workgroupId, response.userId) + .subscribe((canBeAddedToWorkgroupResponse) => { + if (canBeAddedToWorkgroupResponse.isTeacher) { + alert($localize`A teacher cannot be added as a team member.`); + teamMember.username = null; + } else if (canBeAddedToWorkgroupResponse.status && this.allowSignIn(teamMember, 1)) { + for (const member of canBeAddedToWorkgroupResponse.workgroupMembers) { + if (!this.isLoggedInUser(member.username)) { + const currentMember = this.getTeamMemberByUsernameOrAvailableSlot( + member.username + ); + this.updateTeamMember(this.teamMembers.indexOf(currentMember), member); + } + } + this.markAsSignedIn(teamMember); + if (canBeAddedToWorkgroupResponse.addUserToWorkgroup) { + this.run.workgroupId = canBeAddedToWorkgroupResponse.workgroupId; + } + } else if ( + canBeAddedToWorkgroupResponse.workgroupMembers.length === + this.run.maxStudentsPerTeam + ) { + alert( + $localize`${this.getNameDisplay( + response + )}:studentName: is already in a team that is full` + ); + teamMember.username = null; + } else if (!this.allowSignIn(teamMember, 1)) { + alert( + $localize`${this.getNameDisplay(response)}:studentName: is already in the team` + ); + if (!this.isExistingStudent(teamMember)) { + teamMember.username = null; + } + } else { + alert( + $localize`${this.getNameDisplay( + response + )}:studentName: is already on another team` + ); + teamMember.username = null; } - } - this.markAsSignedIn(teamMember); - if (canBeAddedToWorkgroupResponse.addUserToWorkgroup) { - this.run.workgroupId = canBeAddedToWorkgroupResponse.workgroupId; - } - } else if (canBeAddedToWorkgroupResponse.workgroupMembers.length === this.run.maxStudentsPerTeam) { - alert($localize`${this.getNameDisplay(response)}:studentName: is already in a team that is full`); - teamMember.username = null; - } else if (!this.allowSignIn(teamMember, 1)) { - alert($localize`${this.getNameDisplay(response)}:studentName: is already in the team`); - if (!this.isExistingStudent(teamMember)) { - teamMember.username = null; - } - } else { - alert($localize`${this.getNameDisplay(response)}:studentName: is already on another team`); - teamMember.username = null; - } - }); - } else if (response.isUsernameValid !== true) { - alert($localize`Invalid username. Please try again.`); - teamMember.username = null; - } else if (response.isPasswordValid !== true) { - alert($localize`Invalid password. Please try again.`); - } - teamMember.password = null; - }); + }); + } else if (response.isUsernameValid !== true) { + alert($localize`Invalid username. Please try again.`); + teamMember.username = null; + } else if (response.isPasswordValid !== true) { + alert($localize`Invalid password. Please try again.`); + } + teamMember.password = null; + }); } - socialSignIn(socialPlatform : string, teamMember: any) { + socialSignIn(socialPlatform: string, teamMember: any) { let socialPlatformProvider; - if (socialPlatform == "google"){ + if (socialPlatform == 'google') { socialPlatformProvider = GoogleLoginProvider.PROVIDER_ID; } - this.socialAuthService.signIn(socialPlatformProvider).then( - (userData) => { - const googleUserId = userData.id; - if (this.isExistingStudent(teamMember)) { - this.userService.isGoogleIdCorrect(googleUserId, teamMember.id).subscribe((isCorrect) => { - if (isCorrect) { - this.markAsSignedIn(teamMember); - } else { - alert($localize`Incorrect Google user. Please try again.`); - } - }); - } else { - this.userService.getUserByGoogleId(googleUserId).subscribe((response) => { - if (response.status === 'success') { - this.studentService.canBeAddedToWorkgroup(this.run.id, this.run.workgroupId, response.userId) - .subscribe((canBeAddedToWorkgroupResponse) => { - if (canBeAddedToWorkgroupResponse.isTeacher) { - alert($localize`A teacher cannot be added as a team member.`); - } else if (canBeAddedToWorkgroupResponse.status && this.allowSignIn(response, 0)) { - for (const member of canBeAddedToWorkgroupResponse.workgroupMembers) { - if (!this.isLoggedInUser(member.username)) { - const currentMember = this.getTeamMemberByUsernameOrAvailableSlot(member.username); - this.updateTeamMember(this.teamMembers.indexOf(currentMember), member); - } + this.socialAuthService.signIn(socialPlatformProvider).then((userData) => { + const googleUserId = userData.id; + if (this.isExistingStudent(teamMember)) { + this.userService.isGoogleIdCorrect(googleUserId, teamMember.id).subscribe((isCorrect) => { + if (isCorrect) { + this.markAsSignedIn(teamMember); + } else { + alert($localize`Incorrect Google user. Please try again.`); + } + }); + } else { + this.userService.getUserByGoogleId(googleUserId).subscribe((response) => { + if (response.status === 'success') { + this.studentService + .canBeAddedToWorkgroup(this.run.id, this.run.workgroupId, response.userId) + .subscribe((canBeAddedToWorkgroupResponse) => { + if (canBeAddedToWorkgroupResponse.isTeacher) { + alert($localize`A teacher cannot be added as a team member.`); + } else if (canBeAddedToWorkgroupResponse.status && this.allowSignIn(response, 0)) { + for (const member of canBeAddedToWorkgroupResponse.workgroupMembers) { + if (!this.isLoggedInUser(member.username)) { + const currentMember = this.getTeamMemberByUsernameOrAvailableSlot( + member.username + ); + this.updateTeamMember(this.teamMembers.indexOf(currentMember), member); } - this.markAsSignedIn(teamMember); - } else if (canBeAddedToWorkgroupResponse.workgroupMembers.length === this.run.maxStudentsPerTeam) { - alert($localize`${this.getNameDisplay(response)}:studentName: is already in a team that is full`); - } else if (!this.allowSignIn(response, 0)) { - alert($localize`${this.getNameDisplay(response)}:studentName: is already in the team`); - } else { - alert($localize`${this.getNameDisplay(response)}:studentName: is already on another team`); } - }); - } else if (response.status === 'error') { - alert($localize`No WISE user with this Google ID found.`); - } - }); - } + this.markAsSignedIn(teamMember); + } else if ( + canBeAddedToWorkgroupResponse.workgroupMembers.length === + this.run.maxStudentsPerTeam + ) { + alert( + $localize`${this.getNameDisplay( + response + )}:studentName: is already in a team that is full` + ); + } else if (!this.allowSignIn(response, 0)) { + alert( + $localize`${this.getNameDisplay(response)}:studentName: is already in the team` + ); + } else { + alert( + $localize`${this.getNameDisplay( + response + )}:studentName: is already on another team` + ); + } + }); + } else if (response.status === 'error') { + alert($localize`No WISE user with this Google ID found.`); + } + }); } - ); + }); } markAsSignedIn(teamMember: any) { @@ -175,7 +211,7 @@ export class TeamSignInDialogComponent implements OnInit { } toggleAbsent(teamMember: any, isAbsent: boolean) { - isAbsent ? teamMember.status = 'absent' : teamMember.status = 'notSignedIn'; + isAbsent ? (teamMember.status = 'absent') : (teamMember.status = 'notSignedIn'); } isGoogleUser(teamMember: any) { @@ -246,25 +282,33 @@ export class TeamSignInDialogComponent implements OnInit { } } } - this.studentService.launchRun(this.run.id, this.run.workgroupId, presentUserIds, absentUserIds) - .subscribe((response: any) => { - if (response.status === 'error') { - let targetMember; - if (this.isLoggedInUserInWorkgroup(response.workgroupMembers)) { - this.updateTeamMembers(response.workgroupMembers); - targetMember = this.user; - } else { - targetMember = this.removeTeamMembersAlreadyInAWorkgroup(response.workgroupMembers); - } - const teamMatesDisplay = this.getWorkgroupTeammatesDisplay(response.workgroupMembers, targetMember.username); - setTimeout(() => { - alert($localize`${this.getNameDisplay(targetMember)}:studentName: is already in a team with ${teamMatesDisplay}:studentNames:`); - }, 100); + this.studentService + .launchRun(this.run.id, this.run.workgroupId, presentUserIds, absentUserIds) + .subscribe((response: any) => { + if (response.status === 'error') { + let targetMember; + if (this.isLoggedInUserInWorkgroup(response.workgroupMembers)) { + this.updateTeamMembers(response.workgroupMembers); + targetMember = this.user; } else { - this.router.navigateByUrl(response.startProjectUrl); - this.dialogRef.close(); + targetMember = this.removeTeamMembersAlreadyInAWorkgroup(response.workgroupMembers); } - }); + const teamMatesDisplay = this.getWorkgroupTeammatesDisplay( + response.workgroupMembers, + targetMember.username + ); + setTimeout(() => { + alert( + $localize`${this.getNameDisplay( + targetMember + )}:studentName: is already in a team with ${teamMatesDisplay}:studentNames:` + ); + }, 100); + } else { + this.router.navigateByUrl(response.startProjectUrl); + this.dialogRef.close(); + } + }); } getNameDisplay(user: any) { @@ -295,8 +339,10 @@ export class TeamSignInDialogComponent implements OnInit { } updateTeamMembers(workgroupMembers: any[]) { - const existingWorkgroupMembersNotSignedIn = this.getExistingWorkgroupMembersNotSignedIn(workgroupMembers); - const existingWorkgroupMembersUsernames = workgroupMembers.map(member => { + const existingWorkgroupMembersNotSignedIn = this.getExistingWorkgroupMembersNotSignedIn( + workgroupMembers + ); + const existingWorkgroupMembersUsernames = workgroupMembers.map((member) => { return member.username; }); let existingWorkgroupMemberIndex = 0; @@ -304,7 +350,10 @@ export class TeamSignInDialogComponent implements OnInit { if (!existingWorkgroupMembersUsernames.includes(teamMember.username)) { this.clearTeamMember(index); if (existingWorkgroupMemberIndex < existingWorkgroupMembersNotSignedIn.length) { - this.updateTeamMember(index, existingWorkgroupMembersNotSignedIn[existingWorkgroupMemberIndex]); + this.updateTeamMember( + index, + existingWorkgroupMembersNotSignedIn[existingWorkgroupMemberIndex] + ); existingWorkgroupMemberIndex++; } } @@ -321,12 +370,15 @@ export class TeamSignInDialogComponent implements OnInit { } getExistingWorkgroupMembersNotSignedIn(workgroupMembers: any[]): any[] { - const teamMembersUsernames = this.teamMembers.map(member => { + const teamMembersUsernames = this.teamMembers.map((member) => { return member.username; }); const existingWorkgroupMembersNotSignedIn = []; for (const workgroupMember of workgroupMembers) { - if (!this.isLoggedInUser(workgroupMember.username) && !teamMembersUsernames.includes(workgroupMember.username)) { + if ( + !this.isLoggedInUser(workgroupMember.username) && + !teamMembersUsernames.includes(workgroupMember.username) + ) { existingWorkgroupMembersNotSignedIn.push(workgroupMember); } } @@ -334,7 +386,7 @@ export class TeamSignInDialogComponent implements OnInit { } removeTeamMembersAlreadyInAWorkgroup(workgroupMembers: any[]): any { - const workgroupMembersUsernames = workgroupMembers.map(member => { + const workgroupMembersUsernames = workgroupMembers.map((member) => { return member.username; }); let removedMember = null; diff --git a/src/main/webapp/site/src/app/teacher-hybrid-angular.module.ts b/src/main/webapp/site/src/app/teacher-hybrid-angular.module.ts index 3caab7c430..e6bfb21236 100644 --- a/src/main/webapp/site/src/app/teacher-hybrid-angular.module.ts +++ b/src/main/webapp/site/src/app/teacher-hybrid-angular.module.ts @@ -83,9 +83,7 @@ import { HtmlAuthoring } from '../../../wise5/components/html/html-authoring/htm WiseAuthoringTinymceEditorComponent, WorkgroupNodeStatusComponent ], - imports: [ - AngularJSModule - ], + imports: [AngularJSModule], providers: [ { provide: DataService, useExisting: TeacherDataService }, MilestoneService, diff --git a/src/main/webapp/site/src/app/teacher/account/edit-profile/edit-profile.component.spec.ts b/src/main/webapp/site/src/app/teacher/account/edit-profile/edit-profile.component.spec.ts index 05676f393e..e8fe132d86 100644 --- a/src/main/webapp/site/src/app/teacher/account/edit-profile/edit-profile.component.spec.ts +++ b/src/main/webapp/site/src/app/teacher/account/edit-profile/edit-profile.component.spec.ts @@ -1,21 +1,20 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { EditProfileComponent } from './edit-profile.component'; -import { UserService } from "../../../services/user.service"; -import { Teacher } from "../../../domain/teacher"; +import { UserService } from '../../../services/user.service'; +import { Teacher } from '../../../domain/teacher'; import { Observable, BehaviorSubject } from 'rxjs'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { ReactiveFormsModule } from '@angular/forms'; import { MatInputModule } from '@angular/material/input'; import { MatSelectModule } from '@angular/material/select'; import { MatSnackBarModule } from '@angular/material/snack-bar'; -import { TeacherService } from "../../teacher.service"; -import { NO_ERRORS_SCHEMA } from "@angular/core"; +import { TeacherService } from '../../teacher.service'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; import { By } from '@angular/platform-browser'; -import { User } from "../../../domain/user"; +import { User } from '../../../domain/user'; import { configureTestSuite } from 'ng-bullet'; export class MockUserService { - user: User; getUser(): BehaviorSubject { @@ -39,7 +38,7 @@ export class MockUserService { return userBehaviorSubject; } getLanguages() { - return Observable.create( observer => { + return Observable.create((observer) => { observer.next([]); observer.complete(); }); @@ -60,7 +59,7 @@ export class MockUserService { export class MockTeacherService { updateProfile() { - return Observable.create(observer => { + return Observable.create((observer) => { observer.next({}); observer.complete(); }); @@ -86,7 +85,7 @@ describe('EditProfileComponent', () => { configureTestSuite(() => { TestBed.configureTestingModule({ - declarations: [ EditProfileComponent ], + declarations: [EditProfileComponent], imports: [ BrowserAnimationsModule, ReactiveFormsModule, @@ -98,7 +97,7 @@ describe('EditProfileComponent', () => { { provide: TeacherService, useClass: MockTeacherService }, { provide: UserService, useClass: MockUserService } ], - schemas: [ NO_ERRORS_SCHEMA ] + schemas: [NO_ERRORS_SCHEMA] }); }); @@ -130,7 +129,7 @@ describe('EditProfileComponent', () => { expect(submitButton.disabled).toBe(false); }); - it('should disable submit button when form is submitted', async() => { + it('should disable submit button when form is submitted', async () => { const submitButton = getSubmitButton(); const form = getForm(); form.triggerEventHandler('submit', null); @@ -138,7 +137,7 @@ describe('EditProfileComponent', () => { expect(submitButton.disabled).toBe(true); }); - it('should update the user', async() => { + it('should update the user', async () => { component.editProfileFormGroup.get('language').setValue('Spanish'); submitForm(); fixture.detectChanges(); diff --git a/src/main/webapp/site/src/app/teacher/account/edit-profile/edit-profile.component.ts b/src/main/webapp/site/src/app/teacher/account/edit-profile/edit-profile.component.ts index a4fff66c97..6ed9c50c25 100644 --- a/src/main/webapp/site/src/app/teacher/account/edit-profile/edit-profile.component.ts +++ b/src/main/webapp/site/src/app/teacher/account/edit-profile/edit-profile.component.ts @@ -1,10 +1,10 @@ import { Component, OnInit } from '@angular/core'; -import { FormControl, FormGroup, Validators, FormBuilder } from "@angular/forms"; +import { FormControl, FormGroup, Validators, FormBuilder } from '@angular/forms'; import { finalize } from 'rxjs/operators'; import { MatSnackBar } from '@angular/material/snack-bar'; -import { UserService } from "../../../services/user.service"; -import { Teacher } from "../../../domain/teacher"; -import { TeacherService } from "../../teacher.service"; +import { UserService } from '../../../services/user.service'; +import { Teacher } from '../../../domain/teacher'; +import { TeacherService } from '../../teacher.service'; @Component({ selector: 'app-edit-profile', @@ -12,7 +12,6 @@ import { TeacherService } from "../../teacher.service"; styleUrls: ['./edit-profile.component.scss'] }) export class EditProfileComponent implements OnInit { - user: Teacher; schoolLevels: any[] = [ { id: 'ELEMENTARY_SCHOOL', label: $localize`Elementary School` }, @@ -38,10 +37,12 @@ export class EditProfileComponent implements OnInit { language: new FormControl('', [Validators.required]) }); - constructor(private fb: FormBuilder, - private teacherService: TeacherService, - private userService: UserService, - public snackBar: MatSnackBar) { + constructor( + private fb: FormBuilder, + private teacherService: TeacherService, + private userService: UserService, + public snackBar: MatSnackBar + ) { this.user = this.getUser().getValue(); this.setControlFieldValue('firstName', this.user.firstName); this.setControlFieldValue('lastName', this.user.lastName); @@ -70,8 +71,7 @@ export class EditProfileComponent implements OnInit { this.editProfileFormGroup.controls[name].setValue(value); } - ngOnInit() { - } + ngOnInit() {} saveChanges() { this.isSaving = true; @@ -84,16 +84,36 @@ export class EditProfileComponent implements OnInit { const schoolLevel: string = this.getControlFieldValue('schoolLevel'); const language: string = this.getControlFieldValue('language'); const username = this.user.username; - this.teacherService.updateProfile(username, displayName, email, city, state, country, schoolName, schoolLevel, language) - .pipe( - finalize(() => { - this.isSaving = false; - }) - ) - .subscribe((response) => { - this.handleUpdateProfileResponse(response); - this.userService.updateTeacherUser(displayName, email, city, state, country, schoolName, schoolLevel, language); + this.teacherService + .updateProfile( + username, + displayName, + email, + city, + state, + country, + schoolName, + schoolLevel, + language + ) + .pipe( + finalize(() => { + this.isSaving = false; }) + ) + .subscribe((response) => { + this.handleUpdateProfileResponse(response); + this.userService.updateTeacherUser( + displayName, + email, + city, + state, + country, + schoolName, + schoolLevel, + language + ); + }); } getControlFieldValue(fieldName) { diff --git a/src/main/webapp/site/src/app/teacher/account/edit/edit.component.spec.ts b/src/main/webapp/site/src/app/teacher/account/edit/edit.component.spec.ts index 92d4627e61..b4f26edf77 100644 --- a/src/main/webapp/site/src/app/teacher/account/edit/edit.component.spec.ts +++ b/src/main/webapp/site/src/app/teacher/account/edit/edit.component.spec.ts @@ -8,11 +8,10 @@ describe('EditComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ EditComponent ], - providers: [ ], - schemas: [ NO_ERRORS_SCHEMA ] - }) - .compileComponents(); + declarations: [EditComponent], + providers: [], + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); })); beforeEach(() => { diff --git a/src/main/webapp/site/src/app/teacher/account/edit/edit.component.ts b/src/main/webapp/site/src/app/teacher/account/edit/edit.component.ts index 38bda4060c..6f77a1e9b6 100644 --- a/src/main/webapp/site/src/app/teacher/account/edit/edit.component.ts +++ b/src/main/webapp/site/src/app/teacher/account/edit/edit.component.ts @@ -6,9 +6,7 @@ import { Component, OnInit } from '@angular/core'; styleUrls: ['./edit.component.scss'] }) export class EditComponent implements OnInit { + constructor() {} - constructor() { } - - ngOnInit() { } - + ngOnInit() {} } diff --git a/src/main/webapp/site/src/app/teacher/auth.guard.spec.ts b/src/main/webapp/site/src/app/teacher/auth.guard.spec.ts index 6a7290f3ae..7f09ea301a 100644 --- a/src/main/webapp/site/src/app/teacher/auth.guard.spec.ts +++ b/src/main/webapp/site/src/app/teacher/auth.guard.spec.ts @@ -1,34 +1,29 @@ import { TestBed, inject } from '@angular/core/testing'; import { AuthGuard } from './auth.guard'; -import { UserService } from "../services/user.service"; -import { RouterTestingModule } from "@angular/router/testing"; -import { HttpClientTestingModule } from "@angular/common/http/testing"; -import { ConfigService } from "../services/config.service"; -import { NO_ERRORS_SCHEMA } from "@angular/core"; +import { UserService } from '../services/user.service'; +import { RouterTestingModule } from '@angular/router/testing'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { ConfigService } from '../services/config.service'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; -export class MockUserService { +export class MockUserService {} -} - -export class MockConfigService { - -} +export class MockConfigService {} describe('TeacherAuthGuard', () => { beforeEach(() => { TestBed.configureTestingModule({ - imports: [ HttpClientTestingModule, RouterTestingModule ], + imports: [HttpClientTestingModule, RouterTestingModule], providers: [ AuthGuard, { provide: UserService, useClass: MockUserService }, { provide: ConfigService, useClass: MockConfigService } ], - schemas: [ NO_ERRORS_SCHEMA ] + schemas: [NO_ERRORS_SCHEMA] }); }); - it('should create', inject([AuthGuard,UserService,ConfigService], (guard: AuthGuard) => { + it('should create', inject([AuthGuard, UserService, ConfigService], (guard: AuthGuard) => { expect(guard).toBeTruthy(); })); - }); diff --git a/src/main/webapp/site/src/app/teacher/auth.guard.ts b/src/main/webapp/site/src/app/teacher/auth.guard.ts index cec99ce909..c2ff5e7586 100644 --- a/src/main/webapp/site/src/app/teacher/auth.guard.ts +++ b/src/main/webapp/site/src/app/teacher/auth.guard.ts @@ -1,18 +1,12 @@ import { Injectable } from '@angular/core'; -import { - CanActivate, - ActivatedRouteSnapshot, - RouterStateSnapshot, - Router -} from '@angular/router'; -import { UserService } from "../services/user.service"; +import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router } from '@angular/router'; +import { UserService } from '../services/user.service'; @Injectable() export class AuthGuard implements CanActivate { - constructor(private userService: UserService, private router: Router) {} - canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean { + canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean { return this.checkLogin(state.url); } @@ -28,5 +22,4 @@ export class AuthGuard implements CanActivate { return false; } } - } diff --git a/src/main/webapp/site/src/app/teacher/create-run-dialog/create-run-dialog.component.spec.ts b/src/main/webapp/site/src/app/teacher/create-run-dialog/create-run-dialog.component.spec.ts index b4cf5ac887..6ab0f54a55 100644 --- a/src/main/webapp/site/src/app/teacher/create-run-dialog/create-run-dialog.component.spec.ts +++ b/src/main/webapp/site/src/app/teacher/create-run-dialog/create-run-dialog.component.spec.ts @@ -1,15 +1,15 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; -import { TeacherService } from "../teacher.service"; -import { CreateRunDialogComponent } from "./create-run-dialog.component"; -import { MatDialogRef, MatDialog } from "@angular/material/dialog"; -import { MAT_DIALOG_DATA } from "@angular/material/dialog"; -import { MatCheckboxModule } from "@angular/material/checkbox"; +import { TeacherService } from '../teacher.service'; +import { CreateRunDialogComponent } from './create-run-dialog.component'; +import { MatDialogRef, MatDialog } from '@angular/material/dialog'; +import { MAT_DIALOG_DATA } from '@angular/material/dialog'; +import { MatCheckboxModule } from '@angular/material/checkbox'; import { MatRadioModule } from '@angular/material/radio'; -import { ReactiveFormsModule } from "@angular/forms"; +import { ReactiveFormsModule } from '@angular/forms'; import { Observable } from 'rxjs'; import { of } from 'rxjs'; -import { Project } from "../../domain/project"; -import { Run } from "../../domain/run"; +import { Project } from '../../domain/project'; +import { Run } from '../../domain/run'; import { NO_ERRORS_SCHEMA } from '@angular/core'; import { By } from '@angular/platform-browser'; import { Course } from '../../domain/course'; @@ -21,7 +21,7 @@ import { configureTestSuite } from 'ng-bullet'; export class MockTeacherService { createRun() { - return Observable.create(observer => { + return Observable.create((observer) => { const run: Run = new Run(); run.runCode = 'Dog1234'; run.name = 'Photosynthesis'; @@ -35,14 +35,14 @@ export class MockTeacherService { setTabIndex() {} checkClassroomAuthorization(): Observable { - return Observable.create(""); + return Observable.create(''); } getClassroomCourses(): Observable { const courses: Course[] = []; const course = new Course({ id: '1', name: 'Test' }); courses.push(course); - return Observable.create(observer => { + return Observable.create((observer) => { observer.next(courses); observer.complete(); }); @@ -53,7 +53,7 @@ export class MockUserService { getUser(): BehaviorSubject { const user: User = new User(); user.username = 'test'; - return Observable.create(observer => { + return Observable.create((observer) => { observer.next(user); observer.complete(); }); @@ -76,9 +76,9 @@ describe('CreateRunDialogComponent', () => { const project: Project = new Project(); project.id = 1; project.metadata = { - "title": "Photosynthesis" + title: 'Photosynthesis' }; - project.projectThumb = "photo.png"; + project.projectThumb = 'photo.png'; const getSubmitButton = () => { return fixture.debugElement.nativeElement.querySelector('button[type="submit"]'); @@ -90,36 +90,34 @@ describe('CreateRunDialogComponent', () => { configureTestSuite(() => { TestBed.configureTestingModule({ - imports: [ - ReactiveFormsModule, - MatRadioModule, - MatCheckboxModule - ], - declarations: [ CreateRunDialogComponent ], + imports: [ReactiveFormsModule, MatRadioModule, MatCheckboxModule], + declarations: [CreateRunDialogComponent], providers: [ { provide: TeacherService, useClass: MockTeacherService }, { provide: UserService, useClass: MockUserService }, { provide: ConfigService, useClass: MockConfigService }, - { provide: MatDialog, useValue: { + { + provide: MatDialog, + useValue: { closeAll: () => {} - }}, + } + }, { - provide: MatDialogRef, useValue: { + provide: MatDialogRef, + useValue: { afterClosed: () => { - return Observable.create(observer => { + return Observable.create((observer) => { observer.next({}); observer.complete(); }); }, - close: () => { - - } + close: () => {} } }, { provide: MAT_DIALOG_DATA, useValue: { project: project } } ], - schemas: [ NO_ERRORS_SCHEMA ] - }) + schemas: [NO_ERRORS_SCHEMA] + }); }); beforeEach(() => { @@ -141,11 +139,11 @@ describe('CreateRunDialogComponent', () => { }); it('should get periods string', () => { - component.periodsGroup.controls[0].get("checkbox").setValue(true); - component.periodsGroup.controls[2].get("checkbox").setValue(true); - component.periodsGroup.controls[4].get("checkbox").setValue(true); + component.periodsGroup.controls[0].get('checkbox').setValue(true); + component.periodsGroup.controls[2].get('checkbox').setValue(true); + component.periodsGroup.controls[4].get('checkbox').setValue(true); component.customPeriods.setValue('hello'); - expect(component.getPeriodsString()).toEqual("1,3,5,hello"); + expect(component.getPeriodsString()).toEqual('1,3,5,hello'); }); it('should disable submit button and invalidate form on initial state (when no period is selected)', () => { @@ -155,10 +153,10 @@ describe('CreateRunDialogComponent', () => { }); it('should validate form when period is selected or custom period is entered', () => { - component.periodsGroup.controls[0].get("checkbox").setValue(true); + component.periodsGroup.controls[0].get('checkbox').setValue(true); fixture.detectChanges(); expect(component.form.valid).toBeTruthy(); - component.periodsGroup.controls[0].get("checkbox").setValue(false); + component.periodsGroup.controls[0].get('checkbox').setValue(false); component.customPeriods.setValue('Section A, Section B'); fixture.detectChanges(); expect(component.form.valid).toBeTruthy(); @@ -166,13 +164,13 @@ describe('CreateRunDialogComponent', () => { it('should enable submit button when form is valid', () => { const submitButton = getSubmitButton(); - component.periodsGroup.controls[0].get("checkbox").setValue(true); + component.periodsGroup.controls[0].get('checkbox').setValue(true); fixture.detectChanges(); expect(submitButton.disabled).toBe(false); }); - it('should show the confirmation message when form is successfully submitted', async() => { - component.periodsGroup.controls[0].get("checkbox").setValue(true); + it('should show the confirmation message when form is successfully submitted', async () => { + component.periodsGroup.controls[0].get('checkbox').setValue(true); const form = getForm(); form.triggerEventHandler('submit', null); fixture.detectChanges(); @@ -181,8 +179,8 @@ describe('CreateRunDialogComponent', () => { expect(compiled.textContent).toContain('Photosynthesis'); }); - it('should create run with locked after end date false', async() => { - component.periodsGroup.controls[0].get("checkbox").setValue(true); + it('should create run with locked after end date false', async () => { + component.periodsGroup.controls[0].get('checkbox').setValue(true); component.form.controls['isLockedAfterEndDate'].setValue(false); const startDate = new Date(); const endDate = new Date(startDate.getTime() + 86400000); @@ -192,11 +190,17 @@ describe('CreateRunDialogComponent', () => { spyOn(teacherService, 'createRun').and.returnValue(of({})); component.create(); expect(teacherService.createRun).toHaveBeenCalledWith( - 1, '1,', '3', jasmine.any(Number), jasmine.any(Number), false); + 1, + '1,', + '3', + jasmine.any(Number), + jasmine.any(Number), + false + ); }); - it('should create run with locked after end date true', async() => { - component.periodsGroup.controls[0].get("checkbox").setValue(true); + it('should create run with locked after end date true', async () => { + component.periodsGroup.controls[0].get('checkbox').setValue(true); component.form.controls['isLockedAfterEndDate'].setValue(true); const startDate = new Date(); const endDate = new Date(startDate.getTime() + 86400000); @@ -206,17 +210,23 @@ describe('CreateRunDialogComponent', () => { spyOn(teacherService, 'createRun').and.returnValue(of({})); component.create(); expect(teacherService.createRun).toHaveBeenCalledWith( - 1, '1,', '3', jasmine.any(Number), jasmine.any(Number), true); + 1, + '1,', + '3', + jasmine.any(Number), + jasmine.any(Number), + true + ); }); - it('should enable locked after end date checkbox', async() => { + it('should enable locked after end date checkbox', async () => { component.form.controls['endDate'].setValue(null); component.updateLockedAfterEndDateCheckbox(); expect(component.form.controls['isLockedAfterEndDate'].value).toEqual(false); expect(component.form.controls['isLockedAfterEndDate'].disabled).toEqual(true); }); - it('should disable locked after end date checkbox', async() => { + it('should disable locked after end date checkbox', async () => { component.form.controls['endDate'].setValue(new Date()); component.updateLockedAfterEndDateCheckbox(); expect(component.form.controls['isLockedAfterEndDate'].value).toEqual(false); diff --git a/src/main/webapp/site/src/app/teacher/create-run-dialog/create-run-dialog.component.ts b/src/main/webapp/site/src/app/teacher/create-run-dialog/create-run-dialog.component.ts index 89086afce8..4289e0a94a 100644 --- a/src/main/webapp/site/src/app/teacher/create-run-dialog/create-run-dialog.component.ts +++ b/src/main/webapp/site/src/app/teacher/create-run-dialog/create-run-dialog.component.ts @@ -1,10 +1,10 @@ import { Component, Inject } from '@angular/core'; import { MatDialogRef, MAT_DIALOG_DATA, MatDialog } from '@angular/material/dialog'; -import { FormArray, FormBuilder, FormControl, FormGroup, Validators } from "@angular/forms"; +import { FormArray, FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms'; import { finalize } from 'rxjs/operators'; -import { Project } from "../../domain/project"; -import { Run } from "../../domain/run"; -import { TeacherService } from "../teacher.service"; +import { Project } from '../../domain/project'; +import { Run } from '../../domain/run'; +import { TeacherService } from '../teacher.service'; import { UserService } from '../../services/user.service'; import { ConfigService } from '../../services/config.service'; import { ListClassroomCoursesDialogComponent } from '../list-classroom-courses-dialog/list-classroom-courses-dialog.component'; @@ -13,10 +13,8 @@ import { ListClassroomCoursesDialogComponent } from '../list-classroom-courses-d selector: 'create-run-dialog', templateUrl: './create-run-dialog.component.html', styleUrls: ['./create-run-dialog.component.scss'] - }) export class CreateRunDialogComponent { - form: FormGroup; project: Project; periodsGroup: FormArray; @@ -30,13 +28,15 @@ export class CreateRunDialogComponent { isCreated: boolean = false; run: Run = null; - constructor(public dialog: MatDialog, - public dialogRef: MatDialogRef, - @Inject(MAT_DIALOG_DATA) public data: any, - private teacherService: TeacherService, - private userService: UserService, - private configService: ConfigService, - private fb: FormBuilder) { + constructor( + public dialog: MatDialog, + public dialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) public data: any, + private teacherService: TeacherService, + private userService: UserService, + private configService: ConfigService, + private fb: FormBuilder + ) { this.project = data.project; this.maxStudentsPerTeam = 3; } @@ -44,10 +44,15 @@ export class CreateRunDialogComponent { ngOnInit() { this.setPeriodOptions(); let hiddenControl = new FormControl('', Validators.required); - this.periodsGroup = new FormArray(this.periodOptions.map(period => new FormGroup({ - name: new FormControl(period), - checkbox: new FormControl(false) - }))); + this.periodsGroup = new FormArray( + this.periodOptions.map( + (period) => + new FormGroup({ + name: new FormControl(period), + checkbox: new FormControl(false) + }) + ) + ); this.periodsGroup.valueChanges.subscribe((v) => { hiddenControl.setValue(this.getPeriodsString()); }); @@ -86,7 +91,7 @@ export class CreateRunDialogComponent { } get selectedPeriodsControl() { - return this.form.get("selectedPeriods"); + return this.form.get('selectedPeriods'); } mapPeriods(items: any[]): string[] { @@ -106,20 +111,27 @@ export class CreateRunDialogComponent { } const isLockedAfterEndDate = this.form.controls['isLockedAfterEndDate'].value; const maxStudentsPerTeam = this.form.controls['maxStudentsPerTeam'].value; - this.teacherService.createRun(this.project.id, combinedPeriods, maxStudentsPerTeam, startDate, - endDate, isLockedAfterEndDate) - .pipe( - finalize(() => { - this.isCreating = false; - }) - ) - .subscribe((newRun: Run) => { - this.run = new Run(newRun); - this.dialogRef.afterClosed().subscribe(result => { - this.teacherService.addNewRun(this.run); - }); - this.isCreated = true; + this.teacherService + .createRun( + this.project.id, + combinedPeriods, + maxStudentsPerTeam, + startDate, + endDate, + isLockedAfterEndDate + ) + .pipe( + finalize(() => { + this.isCreating = false; + }) + ) + .subscribe((newRun: Run) => { + this.run = new Run(newRun); + this.dialogRef.afterClosed().subscribe((result) => { + this.teacherService.addNewRun(this.run); }); + this.isCreated = true; + }); } getPeriodsString(): string { @@ -145,28 +157,32 @@ export class CreateRunDialogComponent { } checkClassroomAuthorization() { - this.teacherService.getClassroomAuthorizationUrl(this.userService.getUser().getValue().username).subscribe(({ authorizationUrl }) => { - if (authorizationUrl == null) { - this.getClassroomCourses(); - } else { - const authWindow = window.open(authorizationUrl, "authorize", "width=600,height=800"); - const timer = setInterval(() => { - if (authWindow.closed) { - clearInterval(timer); - this.checkClassroomAuthorization(); - } - }, 1000); - } - }); + this.teacherService + .getClassroomAuthorizationUrl(this.userService.getUser().getValue().username) + .subscribe(({ authorizationUrl }) => { + if (authorizationUrl == null) { + this.getClassroomCourses(); + } else { + const authWindow = window.open(authorizationUrl, 'authorize', 'width=600,height=800'); + const timer = setInterval(() => { + if (authWindow.closed) { + clearInterval(timer); + this.checkClassroomAuthorization(); + } + }, 1000); + } + }); } getClassroomCourses() { - this.teacherService.getClassroomCourses(this.userService.getUser().getValue().username).subscribe(courses => { - this.dialog.open(ListClassroomCoursesDialogComponent, { - data: { run: this.run, courses }, - panelClass: 'mat-dialog-md' + this.teacherService + .getClassroomCourses(this.userService.getUser().getValue().username) + .subscribe((courses) => { + this.dialog.open(ListClassroomCoursesDialogComponent, { + data: { run: this.run, courses }, + panelClass: 'mat-dialog-md' + }); }); - }); } updateLockedAfterEndDateCheckbox() { diff --git a/src/main/webapp/site/src/app/teacher/discourse-recent-activity/discourse-recent-activity.component.spec.ts b/src/main/webapp/site/src/app/teacher/discourse-recent-activity/discourse-recent-activity.component.spec.ts index de44617ab4..9073e5b032 100644 --- a/src/main/webapp/site/src/app/teacher/discourse-recent-activity/discourse-recent-activity.component.spec.ts +++ b/src/main/webapp/site/src/app/teacher/discourse-recent-activity/discourse-recent-activity.component.spec.ts @@ -1,7 +1,7 @@ -import { HttpClientTestingModule, HttpTestingController } from "@angular/common/http/testing"; -import { TestBed } from "@angular/core/testing"; -import { ConfigService } from "../../services/config.service"; -import { DiscourseRecentActivityComponent } from "./discourse-recent-activity.component"; +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; +import { TestBed } from '@angular/core/testing'; +import { ConfigService } from '../../services/config.service'; +import { DiscourseRecentActivityComponent } from './discourse-recent-activity.component'; describe('DiscourseRecentActivityComponent', () => { let component: DiscourseRecentActivityComponent; @@ -11,7 +11,7 @@ describe('DiscourseRecentActivityComponent', () => { const sampleLatestResponse = { users: [], topic_list: { - topics: [{id:1},{id:2},{id:3},{id:4},{id:5}] + topics: [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }, { id: 5 }] } }; @@ -23,7 +23,7 @@ describe('DiscourseRecentActivityComponent', () => { beforeEach(() => { TestBed.configureTestingModule({ - imports: [ HttpClientTestingModule ], + imports: [HttpClientTestingModule], providers: [ DiscourseRecentActivityComponent, { provide: ConfigService, useClass: MockConfigService } diff --git a/src/main/webapp/site/src/app/teacher/discourse-recent-activity/discourse-recent-activity.component.ts b/src/main/webapp/site/src/app/teacher/discourse-recent-activity/discourse-recent-activity.component.ts index 6d9e50eeda..18833c6c5d 100644 --- a/src/main/webapp/site/src/app/teacher/discourse-recent-activity/discourse-recent-activity.component.ts +++ b/src/main/webapp/site/src/app/teacher/discourse-recent-activity/discourse-recent-activity.component.ts @@ -1,6 +1,6 @@ -import { HttpClient } from "@angular/common/http"; -import { Component } from "@angular/core"; -import { ConfigService } from "../../services/config.service"; +import { HttpClient } from '@angular/common/http'; +import { Component } from '@angular/core'; +import { ConfigService } from '../../services/config.service'; @Component({ selector: 'discourse-recent-activity', @@ -8,20 +8,21 @@ import { ConfigService } from "../../services/config.service"; styleUrls: ['discourse-recent-activity.component.scss'] }) export class DiscourseRecentActivityComponent { - discourseURL: string; topics: any; - constructor(private configService: ConfigService, private http: HttpClient) { - } + constructor(private configService: ConfigService, private http: HttpClient) {} ngOnInit() { this.discourseURL = this.configService.getDiscourseURL(); - this.http.get(`${this.discourseURL}/latest.json?order=activity`) - .subscribe(({topic_list, users}: any)=> { - this.topics = topic_list.topics - .filter(topic => { return !topic.pinned_globally; }) + this.http + .get(`${this.discourseURL}/latest.json?order=activity`) + .subscribe(({ topic_list, users }: any) => { + this.topics = topic_list.topics + .filter((topic) => { + return !topic.pinned_globally; + }) .slice(0, 3); - }); + }); } } diff --git a/src/main/webapp/site/src/app/teacher/edit-run-warning-dialog/edit-run-warning-dialog.component.spec.ts b/src/main/webapp/site/src/app/teacher/edit-run-warning-dialog/edit-run-warning-dialog.component.spec.ts index e5025dbd16..5a824b8101 100644 --- a/src/main/webapp/site/src/app/teacher/edit-run-warning-dialog/edit-run-warning-dialog.component.spec.ts +++ b/src/main/webapp/site/src/app/teacher/edit-run-warning-dialog/edit-run-warning-dialog.component.spec.ts @@ -1,15 +1,20 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { EditRunWarningDialogComponent } from './edit-run-warning-dialog.component'; import { ConfigService } from '../../services/config.service'; -import { MAT_DIALOG_DATA, MatDialog, MatDialogRef, MatDialogModule } from '@angular/material/dialog'; +import { + MAT_DIALOG_DATA, + MatDialog, + MatDialogRef, + MatDialogModule +} from '@angular/material/dialog'; import { NO_ERRORS_SCHEMA } from '@angular/core'; -import { Run } from "../../domain/run"; +import { Run } from '../../domain/run'; import { configureTestSuite } from 'ng-bullet'; import { RouterTestingModule } from '@angular/router/testing'; export class MockConfigService { getContextPath(): string { - return ""; + return ''; } } @@ -21,25 +26,25 @@ describe('EditRunWarningDialogComponent', () => { let component: EditRunWarningDialogComponent; let fixture: ComponentFixture; - const run = new Run({ id: 1, project: {id: 1, name: "Test"}}); + const run = new Run({ id: 1, project: { id: 1, name: 'Test' } }); run.project.metadata = { - title: "Test Project" + title: 'Test Project' }; configureTestSuite(() => { TestBed.configureTestingModule({ - declarations: [ EditRunWarningDialogComponent ], + declarations: [EditRunWarningDialogComponent], providers: [ { provide: MatDialogRef, useValue: mockDialogRef }, - { provide: MAT_DIALOG_DATA, useValue: { data: { run }}}, + { provide: MAT_DIALOG_DATA, useValue: { data: { run } } }, { provide: ConfigService, useClass: MockConfigService } ], - imports: [ RouterTestingModule, MatDialogModule ], - schemas: [ NO_ERRORS_SCHEMA ] - }) + imports: [RouterTestingModule, MatDialogModule], + schemas: [NO_ERRORS_SCHEMA] + }); }); beforeEach(() => { @@ -54,6 +59,6 @@ describe('EditRunWarningDialogComponent', () => { }); it('should create editLink', () => { - expect(component.editLink !== '').toBeTruthy() - }) + expect(component.editLink !== '').toBeTruthy(); + }); }); diff --git a/src/main/webapp/site/src/app/teacher/edit-run-warning-dialog/edit-run-warning-dialog.component.ts b/src/main/webapp/site/src/app/teacher/edit-run-warning-dialog/edit-run-warning-dialog.component.ts index 8b23c9584c..3e98acd889 100644 --- a/src/main/webapp/site/src/app/teacher/edit-run-warning-dialog/edit-run-warning-dialog.component.ts +++ b/src/main/webapp/site/src/app/teacher/edit-run-warning-dialog/edit-run-warning-dialog.component.ts @@ -1,7 +1,7 @@ import { Component, Inject, OnInit } from '@angular/core'; import { MAT_DIALOG_DATA, MatDialog, MatDialogRef } from '@angular/material/dialog'; import { ConfigService } from '../../services/config.service'; -import { Run } from "../../domain/run"; +import { Run } from '../../domain/run'; import { Router } from '@angular/router'; @Component({ @@ -10,14 +10,15 @@ import { Router } from '@angular/router'; styleUrls: ['./edit-run-warning-dialog.component.scss'] }) export class EditRunWarningDialogComponent implements OnInit { - run: Run; editLink: string = ''; - constructor(public dialog: MatDialog, - public dialogRef: MatDialogRef, - @Inject(MAT_DIALOG_DATA) public data: any, - private router: Router, - private configService: ConfigService) { + constructor( + public dialog: MatDialog, + public dialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) public data: any, + private router: Router, + private configService: ConfigService + ) { this.run = data.run; this.dialog.closeAll(); } diff --git a/src/main/webapp/site/src/app/teacher/list-classroom-courses-dialog/list-classroom-courses-dialog.component.spec.ts b/src/main/webapp/site/src/app/teacher/list-classroom-courses-dialog/list-classroom-courses-dialog.component.spec.ts index 78b62b5c40..1ecf0f223c 100644 --- a/src/main/webapp/site/src/app/teacher/list-classroom-courses-dialog/list-classroom-courses-dialog.component.spec.ts +++ b/src/main/webapp/site/src/app/teacher/list-classroom-courses-dialog/list-classroom-courses-dialog.component.spec.ts @@ -3,12 +3,17 @@ import { ListClassroomCoursesDialogComponent } from './list-classroom-courses-di import { TeacherService } from '../teacher.service'; import { UserService } from '../../services/user.service'; import { MatCheckboxModule } from '@angular/material/checkbox'; -import { MAT_DIALOG_DATA, MatDialog, MatDialogRef, MatDialogModule } from '@angular/material/dialog'; +import { + MAT_DIALOG_DATA, + MatDialog, + MatDialogRef, + MatDialogModule +} from '@angular/material/dialog'; import { BehaviorSubject } from 'rxjs/internal/BehaviorSubject'; import { User } from '../../domain/user'; import { Observable } from 'rxjs/internal/Observable'; -import { NO_ERRORS_SCHEMA } from "@angular/core"; -import { ReactiveFormsModule } from "@angular/forms"; +import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { ReactiveFormsModule } from '@angular/forms'; import { OverlayModule } from '@angular/cdk/overlay'; export class MockTeacherService { @@ -19,7 +24,7 @@ export class MockUserService { getUser(): BehaviorSubject { const user: User = new User(); user.username = 'test'; - return Observable.create(observer => { + return Observable.create((observer) => { observer.next(user); observer.complete(); }); @@ -32,29 +37,26 @@ describe('ListClassroomCoursesDialogComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - imports: [ - ReactiveFormsModule, - MatCheckboxModule, - OverlayModule, - MatDialogModule - ], - declarations: [ ListClassroomCoursesDialogComponent ], + imports: [ReactiveFormsModule, MatCheckboxModule, OverlayModule, MatDialogModule], + declarations: [ListClassroomCoursesDialogComponent], providers: [ - { provide: MatDialogRef, useValue: { close: () => {} }}, - { provide: MAT_DIALOG_DATA, useValue: { - run: { - id: 1, - name: 'Test', - accessCode: 'Test123' - }, - courses: [{ id: '1', name: 'Test' }] - }}, + { provide: MatDialogRef, useValue: { close: () => {} } }, + { + provide: MAT_DIALOG_DATA, + useValue: { + run: { + id: 1, + name: 'Test', + accessCode: 'Test123' + }, + courses: [{ id: '1', name: 'Test' }] + } + }, { provide: TeacherService, useClass: MockTeacherService }, { provide: UserService, useClass: MockUserService } - ], - schemas: [ NO_ERRORS_SCHEMA ] - }) - .compileComponents(); + ], + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); })); beforeEach(() => { diff --git a/src/main/webapp/site/src/app/teacher/list-classroom-courses-dialog/list-classroom-courses-dialog.component.ts b/src/main/webapp/site/src/app/teacher/list-classroom-courses-dialog/list-classroom-courses-dialog.component.ts index 76052acb00..3b657cb6c9 100644 --- a/src/main/webapp/site/src/app/teacher/list-classroom-courses-dialog/list-classroom-courses-dialog.component.ts +++ b/src/main/webapp/site/src/app/teacher/list-classroom-courses-dialog/list-classroom-courses-dialog.component.ts @@ -3,8 +3,15 @@ import { TeacherService } from '../teacher.service'; import { Course } from '../../domain/course'; import { MAT_DIALOG_DATA, MatDialog, MatDialogRef } from '@angular/material/dialog'; import { UserService } from '../../services/user.service'; -import { FormArray, FormBuilder, FormControl, FormGroup, Validators, ValidatorFn } from "@angular/forms"; -import { Run } from "../../domain/run"; +import { + FormArray, + FormBuilder, + FormControl, + FormGroup, + Validators, + ValidatorFn +} from '@angular/forms'; +import { Run } from '../../domain/run'; @Component({ selector: 'app-list-classroom-courses-dialog', @@ -25,12 +32,14 @@ export class ListClassroomCoursesDialogComponent implements OnInit { coursesSuccessfullyAdded: any[] = []; coursesFailedToAdd: any[] = []; - constructor(public dialog: MatDialog, - public dialogRef: MatDialogRef, - @Inject(MAT_DIALOG_DATA) public data: any, - private teacherService: TeacherService, - private userService: UserService, - private fb: FormBuilder) { + constructor( + public dialog: MatDialog, + public dialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) public data: any, + private teacherService: TeacherService, + private userService: UserService, + private fb: FormBuilder + ) { this.run = data.run; for (const course of data.courses) { this.courses.push(new Course(course)); @@ -38,11 +47,9 @@ export class ListClassroomCoursesDialogComponent implements OnInit { } ngOnInit() { - const descriptionText =$localize`Hi class! Please complete the "${this.data.run.name}:unitTitle:" WISE unit. (Access Code: ${this.data.run.runCode}:accessCode:)`; + const descriptionText = $localize`Hi class! Please complete the "${this.data.run.name}:unitTitle:" WISE unit. (Access Code: ${this.data.run.runCode}:accessCode:)`; const description = new FormControl(descriptionText, Validators.required); - this.coursesControl = new FormArray(this.courses.map(() => - new FormControl(false) - )); + this.coursesControl = new FormArray(this.courses.map(() => new FormControl(false))); this.coursesControl.valueChanges.subscribe((controls) => { this.courseIds = []; controls.forEach((value, index) => { @@ -51,10 +58,13 @@ export class ListClassroomCoursesDialogComponent implements OnInit { } }); }); - this.form = this.fb.group({ - selectedCourses: this.coursesControl, - description: description - }, { validator: this.isCourseSelected() }); + this.form = this.fb.group( + { + selectedCourses: this.coursesControl, + description: description + }, + { validator: this.isCourseSelected() } + ); } isCourseSelected(): ValidatorFn { @@ -72,9 +82,16 @@ export class ListClassroomCoursesDialogComponent implements OnInit { endTime = date.toString(); } } - this.teacherService.addToClassroom(this.data.run.runCode, this.data.run.name, this.courseIds, - this.userService.getUser().getValue().username, endTime, - this.form.controls['description'].value).subscribe((response) => { + this.teacherService + .addToClassroom( + this.data.run.runCode, + this.data.run.name, + this.courseIds, + this.userService.getUser().getValue().username, + endTime, + this.form.controls['description'].value + ) + .subscribe((response) => { this.showAddToClassroomResults(response.courses); }); } @@ -111,7 +128,7 @@ export class ListClassroomCoursesDialogComponent implements OnInit { } get selectedCoursesControl() { - return this.form.get("selectedCourses"); + return this.form.get('selectedCourses'); } closeAll() { diff --git a/src/main/webapp/site/src/app/teacher/milestone/milestone-report-data/milestone-report-data.component.spec.ts b/src/main/webapp/site/src/app/teacher/milestone/milestone-report-data/milestone-report-data.component.spec.ts index 1873b65cc0..9bd38d7727 100644 --- a/src/main/webapp/site/src/app/teacher/milestone/milestone-report-data/milestone-report-data.component.spec.ts +++ b/src/main/webapp/site/src/app/teacher/milestone/milestone-report-data/milestone-report-data.component.spec.ts @@ -12,7 +12,7 @@ describe('MilestoneReportDataComponent', () => { comp.scoreValues = [5]; comp.data = { average: 4.3333333333333, - counts: {1: 0, 2:0, 3: 1, 4: 0, 5: 2}, + counts: { 1: 0, 2: 0, 3: 1, 4: 0, 5: 2 }, scoreCount: 3, scoreSum: 13 }; diff --git a/src/main/webapp/site/src/app/teacher/milestone/milestone-report-data/milestone-report-data.component.ts b/src/main/webapp/site/src/app/teacher/milestone/milestone-report-data/milestone-report-data.component.ts index 01aab79f3c..7f60ebfab9 100644 --- a/src/main/webapp/site/src/app/teacher/milestone/milestone-report-data/milestone-report-data.component.ts +++ b/src/main/webapp/site/src/app/teacher/milestone/milestone-report-data/milestone-report-data.component.ts @@ -2,10 +2,9 @@ import { Component, Input } from '@angular/core'; @Component({ selector: 'milestone-report-data', - template: `{{output}}` + template: `{{ output }}` }) export class MilestoneReportDataComponent { - @Input() calc: string; @@ -20,11 +19,10 @@ export class MilestoneReportDataComponent { output: string = ''; - constructor() { - } + constructor() {} ngOnInit() { - this.data = JSON.parse(this.data.replace(/\'/g, "\"")); + this.data = JSON.parse(this.data.replace(/\'/g, '"')); if (this.calc === 'percent') { this.output = this.getPercent(); } diff --git a/src/main/webapp/site/src/app/teacher/run-menu/run-menu.component.spec.ts b/src/main/webapp/site/src/app/teacher/run-menu/run-menu.component.spec.ts index 63df5e0f38..65f0b75803 100644 --- a/src/main/webapp/site/src/app/teacher/run-menu/run-menu.component.spec.ts +++ b/src/main/webapp/site/src/app/teacher/run-menu/run-menu.component.spec.ts @@ -1,27 +1,27 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; -import { RunMenuComponent } from "./run-menu.component"; -import { TeacherService } from "../teacher.service"; -import { Project } from "../../domain/project"; +import { RunMenuComponent } from './run-menu.component'; +import { TeacherService } from '../teacher.service'; +import { Project } from '../../domain/project'; import { BehaviorSubject, Observable } from 'rxjs'; -import { MatDialog } from "@angular/material/dialog"; +import { MatDialog } from '@angular/material/dialog'; import { MatMenuModule } from '@angular/material/menu'; -import { ConfigService } from "../../services/config.service"; -import { UserService } from "../../services/user.service"; -import { User } from "../../domain/user"; -import { TeacherRun } from "../teacher-run"; +import { ConfigService } from '../../services/config.service'; +import { UserService } from '../../services/user.service'; +import { User } from '../../domain/user'; +import { TeacherRun } from '../teacher-run'; import { NO_ERRORS_SCHEMA } from '@angular/core'; import { Course } from '../../domain/course'; import { RouterTestingModule } from '@angular/router/testing'; export class MockTeacherService { checkClassroomAuthorization(): Observable { - return Observable.create(""); + return Observable.create(''); } getClassroomCourses(): Observable { const courses: Course[] = []; const course = new Course({ id: '1', name: 'Test' }); courses.push(course); - return Observable.create(observer => { + return Observable.create((observer) => { observer.next(courses); observer.complete(); }); @@ -37,7 +37,7 @@ export class MockUserService { user.username = 'DemoTeacher'; user.id = 123456; user.isGoogleUser = false; - return Observable.create(observer => { + return Observable.create((observer) => { observer.next(user); observer.complete(); }); @@ -62,17 +62,16 @@ describe('RunMenuComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - imports: [ MatMenuModule, RouterTestingModule ], - declarations: [ RunMenuComponent ], + imports: [MatMenuModule, RouterTestingModule], + declarations: [RunMenuComponent], providers: [ { provide: TeacherService, useClass: MockTeacherService }, { provide: UserService, useClass: MockUserService }, { provide: ConfigService, useClass: MockConfigService }, { provide: MatDialog, useValue: {} } ], - schemas: [ NO_ERRORS_SCHEMA ] - }) - .compileComponents(); + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); })); beforeEach(() => { @@ -80,7 +79,7 @@ describe('RunMenuComponent', () => { component = fixture.componentInstance; const run: TeacherRun = new TeacherRun(); run.id = 1; - run.name = "Photosynthesis"; + run.name = 'Photosynthesis'; const owner = new User(); owner.id = 1; run.owner = owner; diff --git a/src/main/webapp/site/src/app/teacher/run-settings-dialog/run-settings-dialog.component.spec.ts b/src/main/webapp/site/src/app/teacher/run-settings-dialog/run-settings-dialog.component.spec.ts index 4fc0b8cb91..f8599f5c5b 100644 --- a/src/main/webapp/site/src/app/teacher/run-settings-dialog/run-settings-dialog.component.spec.ts +++ b/src/main/webapp/site/src/app/teacher/run-settings-dialog/run-settings-dialog.component.spec.ts @@ -1,10 +1,10 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { RunSettingsDialogComponent } from './run-settings-dialog.component'; -import { MatDialogRef, MatDialog, MAT_DIALOG_DATA } from "@angular/material/dialog"; +import { MatDialogRef, MatDialog, MAT_DIALOG_DATA } from '@angular/material/dialog'; import { MatSnackBarModule } from '@angular/material/snack-bar'; import { NO_ERRORS_SCHEMA } from '@angular/core'; -import { Run } from "../../domain/run"; -import { TeacherService } from "../teacher.service"; +import { Run } from '../../domain/run'; +import { TeacherService } from '../teacher.service'; import { Observable } from 'rxjs'; import { of } from 'rxjs'; import { MomentModule } from 'ngx-moment'; @@ -12,42 +12,42 @@ import { configureTestSuite } from 'ng-bullet'; export class MockTeacherService { addPeriodToRun(runId, periodName) { - return Observable.create(observer => { + return Observable.create((observer) => { const response: any = {}; observer.next(response); observer.complete(); }); } deletePeriodFromRun(runId, periodName) { - return Observable.create(observer => { + return Observable.create((observer) => { const response: any = {}; observer.next(response); observer.complete(); }); } changeMaxStudentsPerTeam(runId, maxStudentsPerTeam) { - return Observable.create(observer => { + return Observable.create((observer) => { const response: any = {}; observer.next(response); observer.complete(); }); } updateStartTime(runId, maxStudentsPerTeam) { - return Observable.create(observer => { + return Observable.create((observer) => { const response: any = {}; observer.next(response); observer.complete(); }); } updateEndTime(runId, maxStudentsPerTeam) { - return Observable.create(observer => { + return Observable.create((observer) => { const response: any = {}; observer.next(response); observer.complete(); }); } updateIsLockedAfterEndDate(runId, isLockedAfterEndDate) { - return Observable.create(observer => { + return Observable.create((observer) => { const response: any = {}; observer.next(response); observer.complete(); @@ -80,16 +80,17 @@ describe('RunSettingsDialogComponent', () => { configureTestSuite(() => TestBed.configureTestingModule({ - declarations: [ RunSettingsDialogComponent ], - imports: [ MatSnackBarModule, MomentModule ], + declarations: [RunSettingsDialogComponent], + imports: [MatSnackBarModule, MomentModule], providers: [ { provide: MatDialog, useValue: {} }, { provide: MatDialogRef, useValue: {} }, { provide: MAT_DIALOG_DATA, useValue: { run: createNewRun() } }, { provide: TeacherService, useClass: MockTeacherService } ], - schemas: [ NO_ERRORS_SCHEMA ] - })); + schemas: [NO_ERRORS_SCHEMA] + }) + ); beforeEach(() => { fixture = TestBed.createComponent(RunSettingsDialogComponent); @@ -109,7 +110,7 @@ describe('RunSettingsDialogComponent', () => { it('should populate the correct number of students per team', () => { const radioGroup = fixture.debugElement.nativeElement.querySelector('mat-radio-group'); - expect(radioGroup.ngModel).toBe("1"); + expect(radioGroup.ngModel).toBe('1'); }); it('should populate the correct start date', () => { @@ -127,7 +128,7 @@ describe('RunSettingsDialogComponent', () => { }); it('should add a period', () => { - component.run.periods.push("4"); + component.run.periods.push('4'); fixture.detectChanges(); const periodContainers = fixture.debugElement.nativeElement.querySelectorAll('.info-block'); expect(periodContainers.length).toBe(4); @@ -141,10 +142,10 @@ describe('RunSettingsDialogComponent', () => { }); it('should change the students per team', () => { - component.maxStudentsPerTeam = "3"; + component.maxStudentsPerTeam = '3'; const radioGroup = fixture.debugElement.nativeElement.querySelector('mat-radio-group'); fixture.detectChanges(); - expect(radioGroup.ngModel).toBe("3"); + expect(radioGroup.ngModel).toBe('3'); }); it('should change the start date', () => { diff --git a/src/main/webapp/site/src/app/teacher/run-settings-dialog/run-settings-dialog.component.ts b/src/main/webapp/site/src/app/teacher/run-settings-dialog/run-settings-dialog.component.ts index 546fad4d34..5376d12d66 100644 --- a/src/main/webapp/site/src/app/teacher/run-settings-dialog/run-settings-dialog.component.ts +++ b/src/main/webapp/site/src/app/teacher/run-settings-dialog/run-settings-dialog.component.ts @@ -1,9 +1,9 @@ import { Component, Inject, OnInit } from '@angular/core'; import { MatDialog, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; import { MatSnackBar } from '@angular/material/snack-bar'; -import { LibraryProjectDetailsComponent } from "../../modules/library/library-project-details/library-project-details.component"; -import { Run } from "../../domain/run"; -import { TeacherService } from "../teacher.service"; +import { LibraryProjectDetailsComponent } from '../../modules/library/library-project-details/library-project-details.component'; +import { Run } from '../../domain/run'; +import { TeacherService } from '../teacher.service'; import * as moment from 'moment'; @Component({ @@ -11,9 +11,7 @@ import * as moment from 'moment'; templateUrl: './run-settings-dialog.component.html', styleUrls: ['./run-settings-dialog.component.scss'] }) - export class RunSettingsDialogComponent implements OnInit { - run: Run; newPeriodName: string; maxStudentsPerTeam: string; @@ -34,11 +32,13 @@ export class RunSettingsDialogComponent implements OnInit { targetEndDate: Date; messageCodeToMessage: any; - constructor(public dialog: MatDialog, - public dialogRef: MatDialogRef, - @Inject(MAT_DIALOG_DATA) public data: any, - private teacherService: TeacherService, - public snackBar: MatSnackBar) { + constructor( + public dialog: MatDialog, + public dialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) public data: any, + private teacherService: TeacherService, + public snackBar: MatSnackBar + ) { this.run = data.run; this.maxStudentsPerTeam = this.run.maxStudentsPerTeam + ''; this.startDate = new Date(this.run.startTime); @@ -68,8 +68,7 @@ export class RunSettingsDialogComponent implements OnInit { }; } - ngOnInit() { - } + ngOnInit() {} newPeriodNameKeyUp(event) { if (this.isEnterKeyWithNewPeriodName(event)) { @@ -104,17 +103,19 @@ export class RunSettingsDialogComponent implements OnInit { deletePeriod(periodName) { this.clearErrorMessages(); if (confirm(`Are you sure you want to delete this period: ${periodName}?`)) { - this.teacherService.deletePeriodFromRun(this.run.id, periodName).subscribe((response: any) => { - if (response.status === 'success') { - this.run = response.run; - this.updateDataRun(this.run); - this.clearErrorMessages(); - this.showConfirmMessage(); - } else { - this.deletePeriodMessage = this.translateMessageCode(response.messageCode); - alert(this.deletePeriodMessage); - } - }); + this.teacherService + .deletePeriodFromRun(this.run.id, periodName) + .subscribe((response: any) => { + if (response.status === 'success') { + this.run = response.run; + this.updateDataRun(this.run); + this.clearErrorMessages(); + this.showConfirmMessage(); + } else { + this.deletePeriodMessage = this.translateMessageCode(response.messageCode); + alert(this.deletePeriodMessage); + } + }); } } @@ -124,20 +125,25 @@ export class RunSettingsDialogComponent implements OnInit { if (maxStudentsPerTeam === 3) { maxStudentsPerTeamText = $localize`1-3`; } - if (confirm($localize`Are you sure you want to change the students per team to ${maxStudentsPerTeamText}:value:?`)) { - this.teacherService.updateRunStudentsPerTeam( - this.run.id, maxStudentsPerTeam).subscribe((response: any) => { - if (response.status === 'success') { - this.run = response.run; - this.updateDataRun(this.run); - this.clearErrorMessages(); - this.showConfirmMessage(); - } else { - this.rollbackMaxStudentsPerTeam(); - this.maxStudentsPerTeamMessage = this.translateMessageCode(response.messageCode); - alert(this.maxStudentsPerTeamMessage); - } - }); + if ( + confirm( + $localize`Are you sure you want to change the students per team to ${maxStudentsPerTeamText}:value:?` + ) + ) { + this.teacherService + .updateRunStudentsPerTeam(this.run.id, maxStudentsPerTeam) + .subscribe((response: any) => { + if (response.status === 'success') { + this.run = response.run; + this.updateDataRun(this.run); + this.clearErrorMessages(); + this.showConfirmMessage(); + } else { + this.rollbackMaxStudentsPerTeam(); + this.maxStudentsPerTeamMessage = this.translateMessageCode(response.messageCode); + alert(this.maxStudentsPerTeamMessage); + } + }); return true; } else { this.rollbackMaxStudentsPerTeam(); @@ -150,19 +156,25 @@ export class RunSettingsDialogComponent implements OnInit { if (this.startDate) { const startDate = this.startDate; const formattedStartDate = moment(startDate).format('ddd MMM DD YYYY'); - if (confirm($localize`Are you sure you want to change the start date to ${formattedStartDate}:date:?`)) { - this.teacherService.updateRunStartTime(this.run.id, startDate.getTime()).subscribe((response: any) => { - if (response.status === 'success') { - this.run = response.run; - this.updateDataRun(this.run); - this.rememberPreviousStartDate(); - this.clearErrorMessages(); - this.showConfirmMessage(); - this.setDateRange(); - } else { - this.startDateMessage = this.translateMessageCode(response.messageCode); - } - }); + if ( + confirm( + $localize`Are you sure you want to change the start date to ${formattedStartDate}:date:?` + ) + ) { + this.teacherService + .updateRunStartTime(this.run.id, startDate.getTime()) + .subscribe((response: any) => { + if (response.status === 'success') { + this.run = response.run; + this.updateDataRun(this.run); + this.rememberPreviousStartDate(); + this.clearErrorMessages(); + this.showConfirmMessage(); + this.setDateRange(); + } else { + this.startDateMessage = this.translateMessageCode(response.messageCode); + } + }); } else { this.rollbackStartDate(); } @@ -240,16 +252,17 @@ export class RunSettingsDialogComponent implements OnInit { } updateIsLockedAfterEndDate() { - this.teacherService.updateIsLockedAfterEndDate(this.run.id, this.isLockedAfterEndDate) - .subscribe((response: any) => { - if (response.status === 'success') { - this.run = response.run; - this.updateDataRun(this.run); - this.clearErrorMessages(); - } else { - this.isLockedAfterEndDateMessage = this.translateMessageCode(response.messageCode); - } - }); + this.teacherService + .updateIsLockedAfterEndDate(this.run.id, this.isLockedAfterEndDate) + .subscribe((response: any) => { + if (response.status === 'success') { + this.run = response.run; + this.updateDataRun(this.run); + this.clearErrorMessages(); + } else { + this.isLockedAfterEndDateMessage = this.translateMessageCode(response.messageCode); + } + }); } rollbackMaxStudentsPerTeam() { diff --git a/src/main/webapp/site/src/app/teacher/share-run-code-dialog/share-run-code-dialog.component.spec.ts b/src/main/webapp/site/src/app/teacher/share-run-code-dialog/share-run-code-dialog.component.spec.ts index 3dccdfb090..d52f0838e1 100644 --- a/src/main/webapp/site/src/app/teacher/share-run-code-dialog/share-run-code-dialog.component.spec.ts +++ b/src/main/webapp/site/src/app/teacher/share-run-code-dialog/share-run-code-dialog.component.spec.ts @@ -1,21 +1,21 @@ import { async, ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { ShareRunCodeDialogComponent } from './share-run-code-dialog.component'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; -import { MatDialogModule, MatDialogRef, MAT_DIALOG_DATA } from "@angular/material/dialog"; +import { MatDialogModule, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; import { MatSnackBarModule } from '@angular/material/snack-bar'; -import { NO_ERRORS_SCHEMA } from "@angular/core"; -import { ConfigService } from "../../services/config.service"; -import { TeacherService } from "../teacher.service"; -import { UserService } from "../../services/user.service"; -import { TeacherRun } from "../teacher-run"; +import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { ConfigService } from '../../services/config.service'; +import { TeacherService } from '../teacher.service'; +import { UserService } from '../../services/user.service'; +import { TeacherRun } from '../teacher-run'; import { Project } from '../../domain/project'; const runObj = new TeacherRun(); runObj.id = 1; -runObj.runCode = 'Dog123' +runObj.runCode = 'Dog123'; const project = new Project(); project.id = 1; -project.name = "Photosynthesis"; +project.name = 'Photosynthesis'; runObj.project = project; export class MockConfigService { @@ -32,9 +32,7 @@ export class MockConfigService { } } -export class MockTeacherService { - -} +export class MockTeacherService {} export class MockUserService { isGoogleUser() { @@ -46,21 +44,22 @@ describe('ShareRunCodeDialogComponent', () => { let component: ShareRunCodeDialogComponent; let fixture: ComponentFixture; - beforeEach(waitForAsync(() => { - TestBed.configureTestingModule({ - declarations: [ ShareRunCodeDialogComponent ], - imports: [ BrowserAnimationsModule, MatDialogModule, MatSnackBarModule ], - providers: [ - { provide: ConfigService, useClass: MockConfigService }, - { provide: TeacherService, useClass: MockTeacherService }, - { provide: UserService, useClass: MockUserService }, - { provide: MatDialogRef, useValue: {} }, - { provide: MAT_DIALOG_DATA, useValue: { run: runObj } }, - ], - schemas: [ NO_ERRORS_SCHEMA ] + beforeEach( + waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [ShareRunCodeDialogComponent], + imports: [BrowserAnimationsModule, MatDialogModule, MatSnackBarModule], + providers: [ + { provide: ConfigService, useClass: MockConfigService }, + { provide: TeacherService, useClass: MockTeacherService }, + { provide: UserService, useClass: MockUserService }, + { provide: MatDialogRef, useValue: {} }, + { provide: MAT_DIALOG_DATA, useValue: { run: runObj } } + ], + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); }) - .compileComponents(); - })); + ); beforeEach(() => { fixture = TestBed.createComponent(ShareRunCodeDialogComponent); diff --git a/src/main/webapp/site/src/app/teacher/share-run-code-dialog/share-run-code-dialog.component.ts b/src/main/webapp/site/src/app/teacher/share-run-code-dialog/share-run-code-dialog.component.ts index f691f67e20..3ea4e5362b 100644 --- a/src/main/webapp/site/src/app/teacher/share-run-code-dialog/share-run-code-dialog.component.ts +++ b/src/main/webapp/site/src/app/teacher/share-run-code-dialog/share-run-code-dialog.component.ts @@ -17,14 +17,15 @@ export class ShareRunCodeDialogComponent { code: string; link: string; - constructor(private dialogRef: MatDialogRef, - @Inject(MAT_DIALOG_DATA) private data: any, - private dialog: MatDialog, - private snackBar: MatSnackBar, - private teacherService: TeacherService, - private userService: UserService, - private configService: ConfigService) { - } + constructor( + private dialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) private data: any, + private dialog: MatDialog, + private snackBar: MatSnackBar, + private teacherService: TeacherService, + private userService: UserService, + private configService: ConfigService + ) {} ngOnInit() { this.run = new TeacherRun(this.data.run); @@ -36,7 +37,7 @@ export class ShareRunCodeDialogComponent { copyMsg() { this.snackBar.open($localize`Copied to clipboard.`); } - + isGoogleUser() { return this.userService.isGoogleUser(); } @@ -66,7 +67,7 @@ export class ShareRunCodeDialogComponent { getClassroomCourses() { this.teacherService .getClassroomCourses(this.userService.getUser().getValue().username) - .subscribe(courses => { + .subscribe((courses) => { const panelClass = courses.length ? 'mat-dialog--md' : ''; this.dialog.open(ListClassroomCoursesDialogComponent, { data: { run: this.run, courses }, diff --git a/src/main/webapp/site/src/app/teacher/share-run-dialog/share-run-dialog.component.spec.ts b/src/main/webapp/site/src/app/teacher/share-run-dialog/share-run-dialog.component.spec.ts index 9152e7802a..1b938d7688 100644 --- a/src/main/webapp/site/src/app/teacher/share-run-dialog/share-run-dialog.component.spec.ts +++ b/src/main/webapp/site/src/app/teacher/share-run-dialog/share-run-dialog.component.spec.ts @@ -1,60 +1,61 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { ShareRunDialogComponent } from './share-run-dialog.component'; -import { Observable } from "rxjs"; +import { Observable } from 'rxjs'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; -import { TeacherService } from "../teacher.service"; -import { Run } from "../../domain/run"; -import { MatDialog, MatDialogRef, MAT_DIALOG_DATA } from "@angular/material/dialog"; +import { TeacherService } from '../teacher.service'; +import { Run } from '../../domain/run'; +import { MatDialog, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; import { MatAutocompleteModule } from '@angular/material/autocomplete'; import { MatSnackBarModule } from '@angular/material/snack-bar'; import { MatTableModule } from '@angular/material/table'; -import { NO_ERRORS_SCHEMA } from "@angular/core"; +import { NO_ERRORS_SCHEMA } from '@angular/core'; import { UserService } from '../../services/user.service'; const runObj = { id: 1, - name: "Photosynthesis", - periods: ["1"], + name: 'Photosynthesis', + periods: ['1'], numStudents: 4, owner: { id: 2, - displayName: "Patrick Star" + displayName: 'Patrick Star' }, - sharedOwners: [{ - id: 4, - firstName: "Spongebob", - lastName: "Squarepants", - permissions: [1,3] - }], + sharedOwners: [ + { + id: 4, + firstName: 'Spongebob', + lastName: 'Squarepants', + permissions: [1, 3] + } + ], project: { id: 9, owner: { id: 2, - displayName: "Patrick Star" + displayName: 'Patrick Star' }, - sharedOwners: [{ - id: 4, - firstName: "Spongebob", - lastName: "Squarepants", - permissions: [2] - }] + sharedOwners: [ + { + id: 4, + firstName: 'Spongebob', + lastName: 'Squarepants', + permissions: [2] + } + ] }, isOwner: () => true }; export class MockTeacherService { retrieveAllTeacherUsernames(): Observable { - let usernames : any[] = [ - "Spongebob Squarepants", - "Patrick Star" - ]; - return Observable.create( observer => { + let usernames: any[] = ['Spongebob Squarepants', 'Patrick Star']; + return Observable.create((observer) => { observer.next(usernames); observer.complete(); }); } getRun(runId: string): Observable { - return Observable.create( observer => { + return Observable.create((observer) => { const run: any = runObj; observer.next(run); observer.complete(); @@ -74,26 +75,22 @@ describe('ShareRunDialogComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ ShareRunDialogComponent ], - imports: [ - BrowserAnimationsModule, - MatAutocompleteModule, - MatSnackBarModule, - MatTableModule - ], + declarations: [ShareRunDialogComponent], + imports: [BrowserAnimationsModule, MatAutocompleteModule, MatSnackBarModule, MatTableModule], providers: [ { provide: TeacherService, useClass: MockTeacherService }, { provide: MatDialogRef, useValue: {} }, { provide: MAT_DIALOG_DATA, useValue: { run: runObj } }, - { provide: MatDialog, useValue: { - closeAll: () => { + { + provide: MatDialog, + useValue: { + closeAll: () => {} } - }}, + }, { provide: UserService, useClass: MockUserService } ], - schemas: [ NO_ERRORS_SCHEMA ] - }) - .compileComponents(); + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); })); beforeEach(() => { diff --git a/src/main/webapp/site/src/app/teacher/share-run-dialog/share-run-dialog.component.ts b/src/main/webapp/site/src/app/teacher/share-run-dialog/share-run-dialog.component.ts index 921a9999ca..725037adf1 100644 --- a/src/main/webapp/site/src/app/teacher/share-run-dialog/share-run-dialog.component.ts +++ b/src/main/webapp/site/src/app/teacher/share-run-dialog/share-run-dialog.component.ts @@ -1,9 +1,9 @@ import { Component, Inject } from '@angular/core'; -import { TeacherService } from "../teacher.service"; +import { TeacherService } from '../teacher.service'; import { MAT_DIALOG_DATA, MatDialogRef, MatDialog } from '@angular/material/dialog'; import { MatSnackBar } from '@angular/material/snack-bar'; -import { MatTableDataSource } from "@angular/material/table"; -import { ShareItemDialogComponent } from "../../modules/library/share-item-dialog/share-item-dialog.component"; +import { MatTableDataSource } from '@angular/material/table'; +import { ShareItemDialogComponent } from '../../modules/library/share-item-dialog/share-item-dialog.component'; import { UserService } from '../../services/user.service'; import { UtilService } from '../../services/util.service'; import { TeacherRun } from '../teacher-run'; @@ -14,7 +14,6 @@ import { TeacherRun } from '../teacher-run'; styleUrls: ['./share-run-dialog.component.scss'] }) export class ShareRunDialogComponent extends ShareItemDialogComponent { - run: TeacherRun; dataSource: MatTableDataSource = new MatTableDataSource(); displayedColumns: string[] = ['name', 'permissions']; @@ -23,13 +22,15 @@ export class ShareRunDialogComponent extends ShareItemDialogComponent { isTransfer: boolean = false; transferRunWarning: boolean = false; - constructor(public dialogRef: MatDialogRef, - @Inject(MAT_DIALOG_DATA) public data: any, - public teacherService: TeacherService, - private userService: UserService, - private utilService: UtilService, - public snackBar: MatSnackBar, - public dialog: MatDialog) { + constructor( + public dialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) public data: any, + public teacherService: TeacherService, + private userService: UserService, + private utilService: UtilService, + public snackBar: MatSnackBar, + public dialog: MatDialog + ) { super(dialogRef, data, teacherService, snackBar); this.teacherService.getRun(this.data.run.id).subscribe((run: TeacherRun) => { this.run = new TeacherRun(run); @@ -38,7 +39,7 @@ export class ShareRunDialogComponent extends ShareItemDialogComponent { this.owner = this.run.owner; this.isOwner = this.run.isOwner(this.userService.getUserId()); this.populateSharedOwners(this.run.sharedOwners); - this.getSharedOwners().subscribe(sharedOwners => { + this.getSharedOwners().subscribe((sharedOwners) => { this.sharedOwners = sharedOwners.sort(this.utilService.sortByUsername); this.updateAllOwners(); }); @@ -73,36 +74,38 @@ export class ShareRunDialogComponent extends ShareItemDialogComponent { setDefaultRunPermissions(sharedOwner) { sharedOwner.runPermissions = { - 1: true, // View student work - 2: false, // Grade and manage run - 3: false, // View student names - 16: false // Admin (read, write, share) + 1: true, // View student work + 2: false, // Grade and manage run + 3: false, // View student names + 16: false // Admin (read, write, share) }; } setDefaultProjectPermissions(sharedOwner) { sharedOwner.projectPermissions = { - 1: false, // View the project - 2: false, // Edit the project - 16: false // Admin (read, write, share) + 1: false, // View the project + 2: false, // Edit the project + 16: false // Admin (read, write, share) }; } runPermissionChanged(sharedOwnerId, permissionId, isAddingPermission) { if (isAddingPermission) { - this.teacherService.addSharedOwnerRunPermission(this.runId, sharedOwnerId, permissionId) - .subscribe((response: any) => { - if (response.status === 'success') { - this.addRunPermissionToSharedOwner(sharedOwnerId, permissionId); - } - }); + this.teacherService + .addSharedOwnerRunPermission(this.runId, sharedOwnerId, permissionId) + .subscribe((response: any) => { + if (response.status === 'success') { + this.addRunPermissionToSharedOwner(sharedOwnerId, permissionId); + } + }); } else { - this.teacherService.removeSharedOwnerRunPermission(this.runId, sharedOwnerId, permissionId) - .subscribe((response: any) => { - if (response.status === 'success') { - this.removeRunPermissionFromSharedOwner(sharedOwnerId, permissionId); - } - }); + this.teacherService + .removeSharedOwnerRunPermission(this.runId, sharedOwnerId, permissionId) + .subscribe((response: any) => { + if (response.status === 'success') { + this.removeRunPermissionFromSharedOwner(sharedOwnerId, permissionId); + } + }); } } @@ -121,18 +124,21 @@ export class ShareRunDialogComponent extends ShareItemDialogComponent { shareRun() { this.isDuplicateSharedTeacher = false; const sharedOwnerUsername = this.teacherSearchControl.value; - if (this.run.owner.username !== sharedOwnerUsername && - (!this.isSharedOwner(sharedOwnerUsername) || this.isTransfer)) { + if ( + this.run.owner.username !== sharedOwnerUsername && + (!this.isSharedOwner(sharedOwnerUsername) || this.isTransfer) + ) { if (this.isTransfer) { this.transferRunWarning = true; } else { - this.teacherService.addSharedOwner(this.runId, sharedOwnerUsername) - .subscribe(newSharedOwner => { - this.setDefaultRunPermissions(newSharedOwner); - this.setDefaultProjectPermissions(newSharedOwner); - this.addSharedOwner(newSharedOwner); - this.teacherSearchControl.setValue(''); - }); + this.teacherService + .addSharedOwner(this.runId, sharedOwnerUsername) + .subscribe((newSharedOwner) => { + this.setDefaultRunPermissions(newSharedOwner); + this.setDefaultProjectPermissions(newSharedOwner); + this.addSharedOwner(newSharedOwner); + this.teacherSearchControl.setValue(''); + }); document.getElementById('share-run-dialog-search').blur(); } } else { @@ -142,8 +148,7 @@ export class ShareRunDialogComponent extends ShareItemDialogComponent { completeRunOwnershipTransfer() { const newOwnerUsername = this.teacherSearchControl.value; - this.teacherService.transferRunOwnership(this.runId, newOwnerUsername) - .subscribe(run => { + this.teacherService.transferRunOwnership(this.runId, newOwnerUsername).subscribe((run) => { if (run != null) { this.updateRunAndProjectPermissions(run); this.closeTransferRunDialog(); @@ -164,14 +169,16 @@ export class ShareRunDialogComponent extends ShareItemDialogComponent { this.isOwner = run.isOwner(this.userService.getUserId()); this.populateSharedOwners(run.sharedOwners); this.snackBar.open( - $localize`Transferred classroom unit ownership to ${run.owner.firstName}:firstName: ${run.owner.lastName}:lastName:.`); + $localize`Transferred classroom unit ownership to ${run.owner.firstName}:firstName: ${run.owner.lastName}:lastName:.` + ); } unshareRun(sharedOwner) { - this.teacherService.removeSharedOwner(this.runId, sharedOwner.username) - .subscribe((response) => { - this.removeSharedOwner(sharedOwner); - }); + this.teacherService + .removeSharedOwner(this.runId, sharedOwner.username) + .subscribe((response) => { + this.removeSharedOwner(sharedOwner); + }); } openTransferRunDialog() { diff --git a/src/main/webapp/site/src/app/teacher/teacher-home/teacher-home.component.spec.ts b/src/main/webapp/site/src/app/teacher/teacher-home/teacher-home.component.spec.ts index 15977bf2da..829e750bac 100644 --- a/src/main/webapp/site/src/app/teacher/teacher-home/teacher-home.component.spec.ts +++ b/src/main/webapp/site/src/app/teacher/teacher-home/teacher-home.component.spec.ts @@ -3,7 +3,7 @@ import { defer, Observable } from 'rxjs'; import { UserService } from '../../services/user.service'; import { TeacherService } from '../teacher.service'; import { User } from '../../domain/user'; -import { Project} from '../../domain/project'; +import { Project } from '../../domain/project'; import { TeacherHomeComponent } from './teacher-home.component'; import { Run } from '../../domain/run'; import { NO_ERRORS_SCHEMA, Component } from '@angular/core'; @@ -18,7 +18,7 @@ export function fakeAsyncResponse(data: T) { export class MockTeacherService { getRuns(): Observable { - const runs : Run[] = []; + const runs: Run[] = []; const run1 = new Run(); run1.id = 1; run1.name = 'Photosynthesis'; @@ -39,7 +39,7 @@ export class MockTeacherService { run2.project = project2; runs.push(run1); runs.push(run2); - return Observable.create( observer => { + return Observable.create((observer) => { observer.next(runs); observer.complete(); }); @@ -65,7 +65,7 @@ export class MockUserService { user.role = 'teacher'; user.username = 'DemoTeacher'; user.id = 123456; - return Observable.create( observer => { + return Observable.create((observer) => { observer.next(user); observer.complete(); }); @@ -74,7 +74,7 @@ export class MockUserService { export class MockConfigService { getConfig(): Observable { - return Observable.create( observer => { + return Observable.create((observer) => { const config: Config = { contextPath: '/wise', logOutURL: '/logout', @@ -108,17 +108,16 @@ describe('TeacherHomeComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ TeacherHomeComponent ], - imports: [ RouterTestingModule ], + declarations: [TeacherHomeComponent], + imports: [RouterTestingModule], providers: [ { provide: TeacherService, useClass: MockTeacherService }, { provide: UserService, useClass: MockUserService }, { provide: ConfigService, useClass: MockConfigService }, { provide: LibraryService, useClass: MockLibraryService } ], - schemas: [ NO_ERRORS_SCHEMA ] - }) - .compileComponents(); + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); })); beforeEach(() => { diff --git a/src/main/webapp/site/src/app/teacher/teacher-home/teacher-home.component.ts b/src/main/webapp/site/src/app/teacher/teacher-home/teacher-home.component.ts index c16383e207..bb91704863 100644 --- a/src/main/webapp/site/src/app/teacher/teacher-home/teacher-home.component.ts +++ b/src/main/webapp/site/src/app/teacher/teacher-home/teacher-home.component.ts @@ -1,7 +1,7 @@ import { Component, OnInit, ViewChild } from '@angular/core'; -import { UserService } from "../../services/user.service"; -import { User } from "../../domain/user"; -import { ConfigService } from "../../services/config.service"; +import { UserService } from '../../services/user.service'; +import { User } from '../../domain/user'; +import { ConfigService } from '../../services/config.service'; import { MatTabGroup } from '@angular/material/tabs'; import { LibraryService } from '../../services/library.service'; import { Router } from '@angular/router'; @@ -22,11 +22,12 @@ export class TeacherHomeComponent implements OnInit { { path: 'library', label: $localize`Unit Library` } ]; - constructor(private userService: UserService, - private configService: ConfigService, - private libraryService: LibraryService, - private router: Router) { - } + constructor( + private userService: UserService, + private configService: ConfigService, + private libraryService: LibraryService, + private router: Router + ) {} ngOnInit() { this.getUser(); @@ -43,10 +44,9 @@ export class TeacherHomeComponent implements OnInit { } getUser() { - this.userService.getUser() - .subscribe(user => { - this.user = user; - }); + this.userService.getUser().subscribe((user) => { + this.user = user; + }); } openAuthoringTool() { diff --git a/src/main/webapp/site/src/app/teacher/teacher-routing.module.ts b/src/main/webapp/site/src/app/teacher/teacher-routing.module.ts index 2fc1c7049c..e2ca39b61f 100644 --- a/src/main/webapp/site/src/app/teacher/teacher-routing.module.ts +++ b/src/main/webapp/site/src/app/teacher/teacher-routing.module.ts @@ -1,11 +1,11 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; -import { AuthGuard } from "./auth.guard"; -import { TeacherComponent } from "./teacher.component"; -import { TeacherHomeComponent } from "./teacher-home/teacher-home.component"; -import { EditComponent } from "./account/edit/edit.component"; -import { TeacherProjectLibraryComponent } from "../modules/library/teacher-project-library/teacher-project-library.component"; +import { AuthGuard } from './auth.guard'; +import { TeacherComponent } from './teacher.component'; +import { TeacherHomeComponent } from './teacher-home/teacher-home.component'; +import { EditComponent } from './account/edit/edit.component'; +import { TeacherProjectLibraryComponent } from '../modules/library/teacher-project-library/teacher-project-library.component'; import { TeacherRunListComponent } from './teacher-run-list/teacher-run-list.component'; import { OfficialLibraryComponent } from '../modules/library/official-library/official-library.component'; import { CommunityLibraryComponent } from '../modules/library/community-library/community-library.component'; @@ -39,17 +39,17 @@ const teacherRoutes: Routes = [ } ] }, - {path: '', loadChildren: () => import('../teacher-hybrid-angular.module').then(m => m.TeacherAngularJSModule)} - ], + { + path: '', + loadChildren: () => + import('../teacher-hybrid-angular.module').then((m) => m.TeacherAngularJSModule) + } + ] } ]; @NgModule({ - imports: [ - RouterModule.forChild(teacherRoutes) - ], - exports: [ - RouterModule - ] + imports: [RouterModule.forChild(teacherRoutes)], + exports: [RouterModule] }) -export class TeacherRoutingModule { } +export class TeacherRoutingModule {} diff --git a/src/main/webapp/site/src/app/teacher/teacher-run-list-item/teacher-run-list-item.component.spec.ts b/src/main/webapp/site/src/app/teacher/teacher-run-list-item/teacher-run-list-item.component.spec.ts index 1ab2531602..929d28e794 100644 --- a/src/main/webapp/site/src/app/teacher/teacher-run-list-item/teacher-run-list-item.component.spec.ts +++ b/src/main/webapp/site/src/app/teacher/teacher-run-list-item/teacher-run-list-item.component.spec.ts @@ -1,19 +1,17 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { TeacherRunListItemComponent } from './teacher-run-list-item.component'; -import { Project} from "../../domain/project"; -import { TeacherService } from "../teacher.service"; -import { TeacherRun } from "../teacher-run"; -import { ConfigService } from "../../services/config.service"; -import { NO_ERRORS_SCHEMA } from "@angular/core"; -import { MomentModule } from "ngx-moment"; -import { MatDialogModule } from "@angular/material/dialog"; +import { Project } from '../../domain/project'; +import { TeacherService } from '../teacher.service'; +import { TeacherRun } from '../teacher-run'; +import { ConfigService } from '../../services/config.service'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { MomentModule } from 'ngx-moment'; +import { MatDialogModule } from '@angular/material/dialog'; import { configureTestSuite } from 'ng-bullet'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { RouterTestingModule } from '@angular/router/testing'; -export class MockTeacherService { - -} +export class MockTeacherService {} export class MockConfigService { getContextPath(): string { @@ -33,13 +31,13 @@ describe('TeacherRunListItemComponent', () => { configureTestSuite(() => { TestBed.configureTestingModule({ - declarations: [ TeacherRunListItemComponent ], - imports: [ MatDialogModule, MomentModule, BrowserAnimationsModule, RouterTestingModule ], + declarations: [TeacherRunListItemComponent], + imports: [MatDialogModule, MomentModule, BrowserAnimationsModule, RouterTestingModule], providers: [ { provide: TeacherService, useClass: MockTeacherService }, { provide: ConfigService, useClass: MockConfigService } ], - schemas: [ NO_ERRORS_SCHEMA ] + schemas: [NO_ERRORS_SCHEMA] }); }); @@ -48,16 +46,16 @@ describe('TeacherRunListItemComponent', () => { component = fixture.componentInstance; const run = new TeacherRun(); run.id = 1; - run.name = "Photosynthesis"; + run.name = 'Photosynthesis'; run.startTime = new Date('2018-10-17T00:00:00.0').getTime(); run.endTime = new Date('2018-10-18T23:59:59.0').getTime(); run.numStudents = 30; run.periods = ['1', '2']; - run.runCode = 'Dog123' + run.runCode = 'Dog123'; const project = new Project(); project.id = 1; - project.name = "Photosynthesis"; - project.projectThumb = ""; + project.name = 'Photosynthesis'; + project.projectThumb = ''; run.project = project; component.run = run; fixture.detectChanges(); diff --git a/src/main/webapp/site/src/app/teacher/teacher-run-list-item/teacher-run-list-item.component.ts b/src/main/webapp/site/src/app/teacher/teacher-run-list-item/teacher-run-list-item.component.ts index 57f0177d6d..9ad3d743bd 100644 --- a/src/main/webapp/site/src/app/teacher/teacher-run-list-item/teacher-run-list-item.component.ts +++ b/src/main/webapp/site/src/app/teacher/teacher-run-list-item/teacher-run-list-item.component.ts @@ -1,8 +1,8 @@ import { Component, OnInit, Input, ElementRef } from '@angular/core'; import { DomSanitizer } from '@angular/platform-browser'; import { SafeStyle } from '@angular/platform-browser'; -import { TeacherRun } from "../teacher-run"; -import { ConfigService } from "../../services/config.service"; +import { TeacherRun } from '../teacher-run'; +import { ConfigService } from '../../services/config.service'; import { flash } from '../../animations'; import { Router } from '@angular/router'; import { MatDialog } from '@angular/material/dialog'; @@ -12,10 +12,9 @@ import { ShareRunCodeDialogComponent } from '../share-run-code-dialog/share-run- selector: 'app-teacher-run-list-item', templateUrl: './teacher-run-list-item.component.html', styleUrls: ['./teacher-run-list-item.component.scss'], - animations: [ flash ] + animations: [flash] }) export class TeacherRunListItemComponent implements OnInit { - @Input() run: TeacherRun = new TeacherRun(); @@ -26,11 +25,13 @@ export class TeacherRunListItemComponent implements OnInit { animateDuration: string = '0s'; animateDelay: string = '0s'; - constructor(private sanitizer: DomSanitizer, - private configService: ConfigService, - private router: Router, - private elRef: ElementRef, - private dialog: MatDialog,) { + constructor( + private sanitizer: DomSanitizer, + private configService: ConfigService, + private router: Router, + private elRef: ElementRef, + private dialog: MatDialog + ) { this.sanitizer = sanitizer; } @@ -45,8 +46,9 @@ export class TeacherRunListItemComponent implements OnInit { const contextPath = this.configService.getContextPath(); this.editLink = `${contextPath}/author/authorproject.html?projectId=${this.run.project.id}`; if (this.run.project.wiseVersion === 4) { - this.gradeAndManageLink = `${this.configService.getWISE4Hostname()}` + - `/teacher/classroomMonitor/classroomMonitor?runId=${this.run.id}&gradingType=monitor`; + this.gradeAndManageLink = + `${this.configService.getWISE4Hostname()}` + + `/teacher/classroomMonitor/classroomMonitor?runId=${this.run.id}&gradingType=monitor`; } else { this.gradeAndManageLink = `${contextPath}/teacher/manage/unit/${this.run.id}`; } @@ -56,7 +58,7 @@ export class TeacherRunListItemComponent implements OnInit { this.animateDelay = '1s'; setTimeout(() => { this.run.isHighlighted = false; - }, 7000) + }, 7000); } } diff --git a/src/main/webapp/site/src/app/teacher/teacher-run-list/teacher-run-list.component.spec.ts b/src/main/webapp/site/src/app/teacher/teacher-run-list/teacher-run-list.component.spec.ts index 62d98d66da..e4c4ab7c99 100644 --- a/src/main/webapp/site/src/app/teacher/teacher-run-list/teacher-run-list.component.spec.ts +++ b/src/main/webapp/site/src/app/teacher/teacher-run-list/teacher-run-list.component.spec.ts @@ -12,7 +12,7 @@ import { configureTestSuite } from 'ng-bullet'; import { Router } from '@angular/router'; import { RouterTestingModule } from '@angular/router/testing'; -@Component({selector: 'app-teacher-run-list-item', template: ''}) +@Component({ selector: 'app-teacher-run-list-item', template: '' }) class TeacherRunListItemStubComponent { @Input() run: TeacherRun = new TeacherRun(); @@ -29,7 +29,7 @@ export class MockTeacherService { run1.id = 1; run1.name = 'Photosynthesis'; run1.numStudents = 30; - run1.periods = ['1','2']; + run1.periods = ['1', '2']; run1.startTime = new Date('2018-01-01T00:00:00.0').getTime(); const project1 = new Project(); project1.id = 1; @@ -40,7 +40,7 @@ export class MockTeacherService { run2.id = 2; run2.name = 'Plate Tectonics'; run2.numStudents = 15; - run2.periods = ['3','4']; + run2.periods = ['3', '4']; run2.startTime = new Date('2018-03-03T00:00:00.0').getTime(); const project2 = new Project(); project2.id = 1; @@ -49,26 +49,23 @@ export class MockTeacherService { run2.project = project2; runs.push(run1); runs.push(run2); - return Observable.create( observer => { + return Observable.create((observer) => { observer.next(runs); observer.complete(); }); } getSharedRuns(): Observable { const runs: TeacherRun[] = []; - return Observable.create(observer => { - observer.next(runs); - observer.complete(); - } - ); + return Observable.create((observer) => { + observer.next(runs); + observer.complete(); + }); } - newRunSource$ = fakeAsyncResponse( - { - id: 3, - name: 'Global Climate Change', - periods: ['1', '2'] - } - ); + newRunSource$ = fakeAsyncResponse({ + id: 3, + name: 'Global Climate Change', + periods: ['1', '2'] + }); } export class MockConfigService { @@ -83,13 +80,13 @@ describe('TeacherRunListComponent', () => { configureTestSuite(() => { TestBed.configureTestingModule({ - declarations: [ TeacherRunListComponent ], - imports: [ MomentModule, RouterTestingModule ], + declarations: [TeacherRunListComponent], + imports: [MomentModule, RouterTestingModule], providers: [ { provide: TeacherService, useClass: MockTeacherService }, - { provide: ConfigService, useClass: MockConfigService }, + { provide: ConfigService, useClass: MockConfigService } ], - schemas: [ NO_ERRORS_SCHEMA ] + schemas: [NO_ERRORS_SCHEMA] }); }); @@ -130,5 +127,5 @@ describe('TeacherRunListComponent', () => { component.runs.push(run3); component.runs.sort(component.sortByStartTimeDesc); expect(isRunsSortedByStartTimeDesc(component.runs)).toBeTruthy(); - }) + }); }); diff --git a/src/main/webapp/site/src/app/teacher/teacher-run-list/teacher-run-list.component.ts b/src/main/webapp/site/src/app/teacher/teacher-run-list/teacher-run-list.component.ts index aceb86bca7..6c5d954bce 100644 --- a/src/main/webapp/site/src/app/teacher/teacher-run-list/teacher-run-list.component.ts +++ b/src/main/webapp/site/src/app/teacher/teacher-run-list/teacher-run-list.component.ts @@ -8,7 +8,7 @@ import { Router } from '@angular/router'; @Component({ selector: 'app-teacher-run-list', templateUrl: './teacher-run-list.component.html', - styleUrls: ['./teacher-run-list.component.scss'], + styleUrls: ['./teacher-run-list.component.scss'] }) export class TeacherRunListComponent implements OnInit { runs: TeacherRun[] = []; @@ -24,10 +24,12 @@ export class TeacherRunListComponent implements OnInit { isSharedRunsRetrieved: boolean = false; showAll: boolean = false; - constructor(private teacherService: TeacherService, - private configService: ConfigService, - router: Router) { - teacherService.newRunSource$.subscribe(run => { + constructor( + private teacherService: TeacherService, + private configService: ConfigService, + router: Router + ) { + teacherService.newRunSource$.subscribe((run) => { const teacherRun: TeacherRun = new TeacherRun(run); teacherRun.isHighlighted = true; this.runs.unshift(teacherRun); @@ -56,7 +58,7 @@ export class TeacherRunListComponent implements OnInit { } getRuns(): void { - this.teacherService.getRuns().subscribe(runs => { + this.teacherService.getRuns().subscribe((runs) => { this.personalRuns = []; for (const personalRun of runs) { this.personalRuns.push(new TeacherRun(personalRun)); @@ -76,7 +78,7 @@ export class TeacherRunListComponent implements OnInit { } getSharedRuns(): void { - this.teacherService.getSharedRuns().subscribe(runs => { + this.teacherService.getSharedRuns().subscribe((runs) => { this.sharedRuns = []; for (const sharedRun of runs) { const teacherRun = new TeacherRun(sharedRun); @@ -190,9 +192,7 @@ export class TeacherRunListComponent implements OnInit { } performSearchAndFilter(): void { - this.filteredRuns = this.searchValue - ? this.performSearch(this.searchValue) - : this.runs; + this.filteredRuns = this.searchValue ? this.performSearch(this.searchValue) : this.runs; this.performFilter(this.filterValue); } @@ -220,25 +220,16 @@ export class TeacherRunListComponent implements OnInit { searchValue = searchValue.toLocaleLowerCase(); // TODO: extract this for global use? return this.runs.filter((run: TeacherRun) => - Object.keys(run).some(prop => { + Object.keys(run).some((prop) => { const value = run[prop]; if (typeof value === 'undefined' || value === null) { return false; } else if (typeof value === 'object') { - return ( - JSON.stringify(value) - .toLocaleLowerCase() - .indexOf(searchValue) !== -1 - ); + return JSON.stringify(value).toLocaleLowerCase().indexOf(searchValue) !== -1; } else { - return ( - value - .toString() - .toLocaleLowerCase() - .indexOf(searchValue) !== -1 - ); + return value.toString().toLocaleLowerCase().indexOf(searchValue) !== -1; } - }), + }) ); } diff --git a/src/main/webapp/site/src/app/teacher/teacher-run.ts b/src/main/webapp/site/src/app/teacher/teacher-run.ts index 4261215ec3..6eb4480b1a 100644 --- a/src/main/webapp/site/src/app/teacher/teacher-run.ts +++ b/src/main/webapp/site/src/app/teacher/teacher-run.ts @@ -1,4 +1,4 @@ -import { Run } from "../domain/run"; +import { Run } from '../domain/run'; export class TeacherRun extends Run { isHighlighted: boolean; diff --git a/src/main/webapp/site/src/app/teacher/teacher.component.spec.ts b/src/main/webapp/site/src/app/teacher/teacher.component.spec.ts index 56d48cb0a1..7b4a27c306 100644 --- a/src/main/webapp/site/src/app/teacher/teacher.component.spec.ts +++ b/src/main/webapp/site/src/app/teacher/teacher.component.spec.ts @@ -1,8 +1,8 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { TeacherComponent } from './teacher.component'; -import { RouterTestingModule } from "@angular/router/testing"; +import { RouterTestingModule } from '@angular/router/testing'; import { Router } from '@angular/router'; -import { NO_ERRORS_SCHEMA } from "@angular/core"; +import { NO_ERRORS_SCHEMA } from '@angular/core'; describe('TeacherComponent', () => { let component: TeacherComponent; @@ -11,12 +11,11 @@ describe('TeacherComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - imports: [ RouterTestingModule ], + imports: [RouterTestingModule], providers: [], - declarations: [ TeacherComponent ], - schemas: [ NO_ERRORS_SCHEMA ] - }) - .compileComponents(); + declarations: [TeacherComponent], + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); })); beforeEach(() => { diff --git a/src/main/webapp/site/src/app/teacher/teacher.component.ts b/src/main/webapp/site/src/app/teacher/teacher.component.ts index ba3b580bab..139fe09417 100644 --- a/src/main/webapp/site/src/app/teacher/teacher.component.ts +++ b/src/main/webapp/site/src/app/teacher/teacher.component.ts @@ -7,14 +7,11 @@ import { Router } from '@angular/router'; styleUrls: ['./teacher.component.scss'] }) export class TeacherComponent implements OnInit { + constructor(private router: Router) {} - constructor(private router: Router) { } - - ngOnInit() { - } + ngOnInit() {} isShowingAngularJSApp() { - return this.router.url.includes('/teacher/edit') || - this.router.url.includes('/teacher/manage'); + return this.router.url.includes('/teacher/edit') || this.router.url.includes('/teacher/manage'); } } diff --git a/src/main/webapp/site/src/app/teacher/teacher.module.ts b/src/main/webapp/site/src/app/teacher/teacher.module.ts index cbe4353fe9..875c1301a6 100644 --- a/src/main/webapp/site/src/app/teacher/teacher.module.ts +++ b/src/main/webapp/site/src/app/teacher/teacher.module.ts @@ -1,10 +1,10 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; -import { SharedModule } from "../modules/shared/shared.module"; +import { SharedModule } from '../modules/shared/shared.module'; import { TeacherRoutingModule } from './teacher-routing.module'; import { TeacherComponent } from './teacher.component'; -import { TeacherHomeComponent } from "./teacher-home/teacher-home.component"; -import { AuthGuard } from "./auth.guard"; +import { TeacherHomeComponent } from './teacher-home/teacher-home.component'; +import { AuthGuard } from './auth.guard'; import { TeacherRunListComponent } from './teacher-run-list/teacher-run-list.component'; import { TeacherRunListItemComponent } from './teacher-run-list-item/teacher-run-list-item.component'; import { FlexLayoutModule } from '@angular/flex-layout'; @@ -29,9 +29,9 @@ import { MatTooltipModule } from '@angular/material/tooltip'; import { ClipboardModule } from '@angular/cdk/clipboard'; import { RunMenuComponent } from './run-menu/run-menu.component'; import { CreateRunDialogComponent } from './create-run-dialog/create-run-dialog.component'; -import { LibraryModule } from "../modules/library/library.module"; +import { LibraryModule } from '../modules/library/library.module'; import { ShareRunDialogComponent } from './share-run-dialog/share-run-dialog.component'; -import { TimelineModule } from "../modules/timeline/timeline.module"; +import { TimelineModule } from '../modules/timeline/timeline.module'; import { EditComponent } from './account/edit/edit.component'; import { EditProfileComponent } from './account/edit-profile/edit-profile.component'; import { RunSettingsDialogComponent } from './run-settings-dialog/run-settings-dialog.component'; @@ -42,10 +42,22 @@ import { DiscourseRecentActivityComponent } from './discourse-recent-activity/di import { ShareRunCodeDialogComponent } from './share-run-code-dialog/share-run-code-dialog.component'; const materialModules = [ - MatAutocompleteModule, MatButtonModule, MatCardModule, MatCheckboxModule, - MatDatepickerModule, MatDialogModule, MatDividerModule, MatIconModule, - MatMenuModule, MatNativeDateModule, MatProgressBarModule, MatRadioModule, - MatSnackBarModule, MatTabsModule, MatTableModule, MatTooltipModule + MatAutocompleteModule, + MatButtonModule, + MatCardModule, + MatCheckboxModule, + MatDatepickerModule, + MatDialogModule, + MatDividerModule, + MatIconModule, + MatMenuModule, + MatNativeDateModule, + MatProgressBarModule, + MatRadioModule, + MatSnackBarModule, + MatTabsModule, + MatTableModule, + MatTooltipModule ]; @NgModule({ imports: [ @@ -77,12 +89,7 @@ const materialModules = [ ListClassroomCoursesDialogComponent, ShareRunCodeDialogComponent ], - providers: [ - AuthGuard - ], - exports: [ - TeacherComponent, - materialModules - ] + providers: [AuthGuard], + exports: [TeacherComponent, materialModules] }) -export class TeacherModule { } +export class TeacherModule {} diff --git a/src/main/webapp/site/src/app/teacher/teacher.service.spec.ts b/src/main/webapp/site/src/app/teacher/teacher.service.spec.ts index 2bd812bd84..e892869fc7 100644 --- a/src/main/webapp/site/src/app/teacher/teacher.service.spec.ts +++ b/src/main/webapp/site/src/app/teacher/teacher.service.spec.ts @@ -7,11 +7,8 @@ import { TeacherService } from './teacher.service'; describe('TeacherService', () => { beforeEach(() => { TestBed.configureTestingModule({ - imports: [ HttpClientTestingModule ], - providers: [ - TeacherService, - { provide: MatDialog, useValue: {}} - ] + imports: [HttpClientTestingModule], + providers: [TeacherService, { provide: MatDialog, useValue: {} }] }); }); @@ -19,39 +16,40 @@ describe('TeacherService', () => { expect(service).toBeTruthy(); })); - it('should update run end time when it is null', - inject([TeacherService], (service: TeacherService) => { - const http = TestBed.get(HttpTestingController); - service.updateRunEndTime(1, null).subscribe(() => {}); - const req = http.expectOne('/api/teacher/run/update/endtime'); - expect(req.request.method).toEqual('POST'); - const httpParams = new HttpParams() - .set('runId', '1'); - expect(req.request.body).toEqual(httpParams); - })); + it('should update run end time when it is null', inject( + [TeacherService], + (service: TeacherService) => { + const http = TestBed.get(HttpTestingController); + service.updateRunEndTime(1, null).subscribe(() => {}); + const req = http.expectOne('/api/teacher/run/update/endtime'); + expect(req.request.method).toEqual('POST'); + const httpParams = new HttpParams().set('runId', '1'); + expect(req.request.body).toEqual(httpParams); + } + )); - it('should update run end time when it is not null', - inject([TeacherService], (service: TeacherService) => { - const http = TestBed.get(HttpTestingController); - const endTime = new Date().getTime(); - service.updateRunEndTime(1, endTime).subscribe(() => {}); - const req = http.expectOne('/api/teacher/run/update/endtime'); - expect(req.request.method).toEqual('POST'); - const httpParams = new HttpParams() - .set('runId', '1') - .set('endTime', endTime + ''); - expect(req.request.body).toEqual(httpParams); - })); + it('should update run end time when it is not null', inject( + [TeacherService], + (service: TeacherService) => { + const http = TestBed.get(HttpTestingController); + const endTime = new Date().getTime(); + service.updateRunEndTime(1, endTime).subscribe(() => {}); + const req = http.expectOne('/api/teacher/run/update/endtime'); + expect(req.request.method).toEqual('POST'); + const httpParams = new HttpParams().set('runId', '1').set('endTime', endTime + ''); + expect(req.request.body).toEqual(httpParams); + } + )); - it('should update is locked after end date', - inject([TeacherService], (service: TeacherService) => { - const http = TestBed.get(HttpTestingController); - service.updateIsLockedAfterEndDate(1, true).subscribe(() => {}); - const req = http.expectOne('/api/teacher/run/update/islockedafterenddate'); - expect(req.request.method).toEqual('POST'); - const httpParams = new HttpParams() - .set('runId', '1') - .set('isLockedAfterEndDate', 'true'); - expect(req.request.body).toEqual(httpParams); - })); + it('should update is locked after end date', inject( + [TeacherService], + (service: TeacherService) => { + const http = TestBed.get(HttpTestingController); + service.updateIsLockedAfterEndDate(1, true).subscribe(() => {}); + const req = http.expectOne('/api/teacher/run/update/islockedafterenddate'); + expect(req.request.method).toEqual('POST'); + const httpParams = new HttpParams().set('runId', '1').set('isLockedAfterEndDate', 'true'); + expect(req.request.body).toEqual(httpParams); + } + )); }); diff --git a/src/main/webapp/site/src/app/teacher/teacher.service.ts b/src/main/webapp/site/src/app/teacher/teacher.service.ts index 9e3409fc95..72b5bb8576 100644 --- a/src/main/webapp/site/src/app/teacher/teacher.service.ts +++ b/src/main/webapp/site/src/app/teacher/teacher.service.ts @@ -10,7 +10,6 @@ import { CopyProjectDialogComponent } from '../modules/library/copy-project-dial @Injectable() export class TeacherService { - private runsUrl = '/api/teacher/runs'; private sharedRunsUrl = '/api/teacher/sharedruns'; private registerUrl = '/api/teacher/register'; @@ -41,7 +40,7 @@ export class TeacherService { public newRunSource$ = this.newRunSource.asObservable(); private updateProfileUrl = '/api/teacher/profile/update'; - constructor(private http: HttpClient) { } + constructor(private http: HttpClient) {} copyProject(project: Project, dialog: MatDialog) { dialog.open(CopyProjectDialogComponent, { @@ -49,7 +48,7 @@ export class TeacherService { panelClass: 'mat-dialog--sm' }); } - + getRuns(): Observable { const headers = new HttpHeaders({ 'Cache-Control': 'no-cache' }); return this.http.get(this.runsUrl, { headers: headers }); @@ -72,28 +71,36 @@ export class TeacherService { const headers = { 'Content-Type': 'application/json' }; - return this.http.post(this.registerUrl, teacherUser, - { headers: headers, responseType: 'json' }); + return this.http.post(this.registerUrl, teacherUser, { + headers: headers, + responseType: 'json' + }); } - createRun(projectId: number, periods: string, maxStudentsPerTeam: number, startDate: number, - endDate: number, isLockedAfterEndDate: boolean): Observable { + createRun( + projectId: number, + periods: string, + maxStudentsPerTeam: number, + startDate: number, + endDate: number, + isLockedAfterEndDate: boolean + ): Observable { const headers = new HttpHeaders().set('Content-Type', 'application/x-www-form-urlencoded'); let body = new HttpParams(); - body = body.set('projectId', projectId + ""); + body = body.set('projectId', projectId + ''); body = body.set('periods', periods); - body = body.set('maxStudentsPerTeam', maxStudentsPerTeam + ""); - body = body.set('startDate', startDate + ""); + body = body.set('maxStudentsPerTeam', maxStudentsPerTeam + ''); + body = body.set('startDate', startDate + ''); if (endDate) { - body = body.set('endDate', endDate + ""); - body = body.set('isLockedAfterEndDate', isLockedAfterEndDate + ""); + body = body.set('endDate', endDate + ''); + body = body.set('isLockedAfterEndDate', isLockedAfterEndDate + ''); } return this.http.post(this.createRunUrl, body, { headers: headers }); } retrieveAllTeacherUsernames(): Observable { const headers = new HttpHeaders({ 'Cache-Control': 'no-cache' }); - return this.http.get(this.usernamesUrl, { headers: headers }) + return this.http.get(this.usernamesUrl, { headers: headers }); } addSharedOwner(runId: number, teacherUsername: string) { @@ -111,19 +118,19 @@ export class TeacherService { removeSharedOwner(runId: number, username: string) { const url = `${this.runPermissionUrl}/${runId}/${username}`; const headers = new HttpHeaders().set('Content-Type', 'application/x-www-form-urlencoded'); - return this.http.delete(url, {headers: headers}); + return this.http.delete(url, { headers: headers }); } addSharedOwnerRunPermission(runId: number, userId: string, permissionId: number) { const url = `${this.runPermissionUrl}/${runId}/${userId}/${permissionId}`; const headers = new HttpHeaders().set('Content-Type', 'application/x-www-form-urlencoded'); - return this.http.put(url, null, {headers: headers}); + return this.http.put(url, null, { headers: headers }); } removeSharedOwnerRunPermission(runId: number, userId: string, permissionId: number) { const url = `${this.runPermissionUrl}/${runId}/${userId}/${permissionId}`; const headers = new HttpHeaders().set('Content-Type', 'application/x-www-form-urlencoded'); - return this.http.delete(url, {headers: headers}); + return this.http.delete(url, { headers: headers }); } addSharedOwnerProjectPermission(projectId: number, userId: string, permissionId: number) { @@ -141,20 +148,30 @@ export class TeacherService { addSharedProjectOwner(projectId: number, username: string) { const url = `${this.projectPermissionUrl}/${projectId}/${username}`; const headers = new HttpHeaders().set('Content-Type', 'application/x-www-form-urlencoded'); - return this.http.put(url, null, {headers: headers}); + return this.http.put(url, null, { headers: headers }); } removeSharedProjectOwner(projectId: number, username: string) { const url = `${this.projectPermissionUrl}/${projectId}/${username}`; const headers = new HttpHeaders().set('Content-Type', 'application/x-www-form-urlencoded'); - return this.http.delete(url, {headers: headers}); + return this.http.delete(url, { headers: headers }); } addNewRun(run: Run) { this.newRunSource.next(run); } - updateProfile(username, displayName, email, city, state, country, schoolName, schoolLevel, language) { + updateProfile( + username, + displayName, + email, + city, + state, + country, + schoolName, + schoolLevel, + language + ) { const headers = new HttpHeaders().set('Content-Type', 'application/x-www-form-urlencoded'); let body = new HttpParams(); body = body.set('displayName', displayName); @@ -174,7 +191,7 @@ export class TeacherService { let body = new HttpParams(); body = body.set('runId', runId + ''); body = body.set('periodName', periodName); - return this.http.post(url, body, {headers: headers}); + return this.http.post(url, body, { headers: headers }); } deletePeriodFromRun(runId: number, periodName: string) { @@ -183,7 +200,7 @@ export class TeacherService { let body = new HttpParams(); body = body.set('runId', runId + ''); body = body.set('periodName', periodName); - return this.http.post(url, body, {headers: headers}); + return this.http.post(url, body, { headers: headers }); } updateRunStudentsPerTeam(runId: number, maxStudentsPerTeam: number) { @@ -192,7 +209,7 @@ export class TeacherService { let body = new HttpParams(); body = body.set('runId', runId + ''); body = body.set('maxStudentsPerTeam', maxStudentsPerTeam + ''); - return this.http.post(url, body, {headers: headers}); + return this.http.post(url, body, { headers: headers }); } updateRunStartTime(runId: number, startTime: number) { @@ -201,7 +218,7 @@ export class TeacherService { let body = new HttpParams(); body = body.set('runId', runId + ''); body = body.set('startTime', startTime + ''); - return this.http.post(url, body, {headers: headers}); + return this.http.post(url, body, { headers: headers }); } updateRunEndTime(runId: number, endTime: number) { @@ -212,16 +229,16 @@ export class TeacherService { if (endTime != null) { body = body.set('endTime', endTime + ''); } - return this.http.post(url, body, {headers: headers}); + return this.http.post(url, body, { headers: headers }); } updateIsLockedAfterEndDate(runId: number, isLockedAfterEndDate: boolean) { const url = `${this.updateRunIsLockedAfterEndDateUrl}`; const headers = new HttpHeaders().set('Content-Type', 'application/x-www-form-urlencoded'); const body = new HttpParams() - .set('runId', runId + '') - .set('isLockedAfterEndDate', isLockedAfterEndDate + ''); - return this.http.post(url, body, {headers: headers}); + .set('runId', runId + '') + .set('isLockedAfterEndDate', isLockedAfterEndDate + ''); + return this.http.post(url, body, { headers: headers }); } sendForgotUsernameEmail(email) { @@ -267,14 +284,21 @@ export class TeacherService { return this.http.get(this.classroomAuthorizationUrl, { headers, params }); } - getClassroomCourses(username: string): Observable { + getClassroomCourses(username: string): Observable { const headers = new HttpHeaders({ 'Cache-Control': 'no-cache' }); let params = new HttpParams(); params = params.set('username', username); - return this.http.get(this.listCoursesUrl, { headers, params }); + return this.http.get(this.listCoursesUrl, { headers, params }); } - addToClassroom(accessCode: string, unitTitle: string, courseIds: string[], username: string, endTime: string, description: string): Observable { + addToClassroom( + accessCode: string, + unitTitle: string, + courseIds: string[], + username: string, + endTime: string, + description: string + ): Observable { const headers = new HttpHeaders({ 'Content-Type': 'application/x-www-form-urlencoded' }); let params = new HttpParams() .set('accessCode', accessCode) @@ -283,6 +307,6 @@ export class TeacherService { .set('endTime', endTime) .set('description', description) .set('courseIds', JSON.stringify(courseIds)); - return this.http.post(this.addAssignmentUrl, params, {headers}); + return this.http.post(this.addAssignmentUrl, params, { headers }); } } diff --git a/src/main/webapp/site/src/app/teacher/use-with-class-warning-dialog/use-with-class-warning-dialog.component.spec.ts b/src/main/webapp/site/src/app/teacher/use-with-class-warning-dialog/use-with-class-warning-dialog.component.spec.ts index f8f4eb1775..056cd5ecf8 100644 --- a/src/main/webapp/site/src/app/teacher/use-with-class-warning-dialog/use-with-class-warning-dialog.component.spec.ts +++ b/src/main/webapp/site/src/app/teacher/use-with-class-warning-dialog/use-with-class-warning-dialog.component.spec.ts @@ -1,23 +1,23 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { UseWithClassWarningDialogComponent } from './use-with-class-warning-dialog.component'; -import { MatDialog, MatDialogRef } from "@angular/material/dialog"; -import { MAT_DIALOG_DATA } from "@angular/material/dialog"; -import { Project } from "../../domain/project"; -import { Observable } from "rxjs"; -import {NO_ERRORS_SCHEMA} from "@angular/core"; +import { MatDialog, MatDialogRef } from '@angular/material/dialog'; +import { MAT_DIALOG_DATA } from '@angular/material/dialog'; +import { Project } from '../../domain/project'; +import { Observable } from 'rxjs'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; describe('UseWithClassWarningDialogComponent', () => { let component: UseWithClassWarningDialogComponent; let fixture: ComponentFixture; const project: Project = new Project(); project.metadata = { - "title": "This is a test" + title: 'This is a test' }; beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ UseWithClassWarningDialogComponent ], + declarations: [UseWithClassWarningDialogComponent], providers: [ { provide: MatDialog, @@ -29,7 +29,7 @@ describe('UseWithClassWarningDialogComponent', () => { provide: MatDialogRef, useValue: { afterClosed: () => { - return Observable.create(observer => { + return Observable.create((observer) => { observer.next({}); observer.complete(); }); @@ -39,9 +39,8 @@ describe('UseWithClassWarningDialogComponent', () => { }, { provide: MAT_DIALOG_DATA, useValue: { project: project } } ], - schemas: [ NO_ERRORS_SCHEMA ] - }) - .compileComponents(); + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); })); beforeEach(() => { diff --git a/src/main/webapp/site/src/app/teacher/use-with-class-warning-dialog/use-with-class-warning-dialog.component.ts b/src/main/webapp/site/src/app/teacher/use-with-class-warning-dialog/use-with-class-warning-dialog.component.ts index 0492084cf2..5290098212 100644 --- a/src/main/webapp/site/src/app/teacher/use-with-class-warning-dialog/use-with-class-warning-dialog.component.ts +++ b/src/main/webapp/site/src/app/teacher/use-with-class-warning-dialog/use-with-class-warning-dialog.component.ts @@ -1,7 +1,7 @@ import { Component, Inject, OnInit } from '@angular/core'; import { MatDialog, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; -import { CreateRunDialogComponent } from "../create-run-dialog/create-run-dialog.component"; -import { Project } from "../../domain/project"; +import { CreateRunDialogComponent } from '../create-run-dialog/create-run-dialog.component'; +import { Project } from '../../domain/project'; @Component({ selector: 'app-use-with-class-warning-dialog', @@ -10,14 +10,15 @@ import { Project } from "../../domain/project"; }) export class UseWithClassWarningDialogComponent implements OnInit { project: Project; - constructor(public dialog: MatDialog, - public dialogRef: MatDialogRef, - @Inject(MAT_DIALOG_DATA) public data: any) { + constructor( + public dialog: MatDialog, + public dialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) public data: any + ) { this.project = data.project; } - ngOnInit() { - } + ngOnInit() {} proceedAnyway() { this.dialog.open(CreateRunDialogComponent, { diff --git a/src/main/webapp/site/src/app/track-scroll.directive.ts b/src/main/webapp/site/src/app/track-scroll.directive.ts index d612afb9d3..654fff4069 100644 --- a/src/main/webapp/site/src/app/track-scroll.directive.ts +++ b/src/main/webapp/site/src/app/track-scroll.directive.ts @@ -1,15 +1,15 @@ -import { Directive, Output, EventEmitter, HostListener, ElementRef } from "@angular/core"; +import { Directive, Output, EventEmitter, HostListener, ElementRef } from '@angular/core'; @Directive({ selector: '[track-scroll]' }) export class TrackScrollDirective { - @Output() yPositionChange:EventEmitter = new EventEmitter(); + @Output() yPositionChange: EventEmitter = new EventEmitter(); constructor(private el: ElementRef) {} @HostListener('scroll', ['$event']) - track(event:any) { + track(event: any) { this.yPositionChange.emit(this.el.nativeElement); } } diff --git a/src/main/webapp/site/src/main.ts b/src/main/webapp/site/src/main.ts index 91ec6da5f0..741c9eb862 100644 --- a/src/main/webapp/site/src/main.ts +++ b/src/main/webapp/site/src/main.ts @@ -8,5 +8,6 @@ if (environment.production) { enableProdMode(); } -platformBrowserDynamic().bootstrapModule(AppModule) - .catch(err => console.log(err)); +platformBrowserDynamic() + .bootstrapModule(AppModule) + .catch((err) => console.log(err)); diff --git a/src/main/webapp/site/src/polyfills.ts b/src/main/webapp/site/src/polyfills.ts index 50a6c00848..66702daa02 100644 --- a/src/main/webapp/site/src/polyfills.ts +++ b/src/main/webapp/site/src/polyfills.ts @@ -19,10 +19,8 @@ import '@angular/localize/init'; */ /*************************************************************************************************** -* BROWSER POLYFILLS -*/ - - + * BROWSER POLYFILLS + */ /** * Required to support Web Animations `@angular/platform-browser/animations`. @@ -30,12 +28,10 @@ import '@angular/localize/init'; **/ // import 'web-animations-js'; // Run `npm install --save web-animations-js`. - - /*************************************************************************************************** * Zone JS is required by default for Angular itself. */ -import 'zone.js/dist/zone'; // Included with Angular CLI. +import 'zone.js/dist/zone'; // Included with Angular CLI. /*************************************************************************************************** * APPLICATION IMPORTS diff --git a/src/main/webapp/site/src/test.ts b/src/main/webapp/site/src/test.ts index f2a13f7ea4..17f5b44c88 100644 --- a/src/main/webapp/site/src/test.ts +++ b/src/main/webapp/site/src/test.ts @@ -20,10 +20,7 @@ declare const require: any; __karma__.loaded = function () {}; // First, initialize the Angular testing environment. -getTestBed().initTestEnvironment( - BrowserDynamicTestingModule, - platformBrowserDynamicTesting() -); +getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting()); // Then we find all the tests. const context = require.context('../../', true, /\.spec\.ts$/); // And load the modules. diff --git a/src/main/webapp/wise5/authoringTool/addComponent/addComponentModule.ts b/src/main/webapp/wise5/authoringTool/addComponent/addComponentModule.ts index 959a71ca76..1d29be864a 100644 --- a/src/main/webapp/wise5/authoringTool/addComponent/addComponentModule.ts +++ b/src/main/webapp/wise5/authoringTool/addComponent/addComponentModule.ts @@ -3,30 +3,38 @@ import { ChooseNewComponent } from '../../../site/src/app/authoring-tool/add-com import { ChooseNewComponentLocation } from '../../../site/src/app/authoring-tool/add-component/choose-new-component-location/choose-new-component-location.component'; import { downgradeComponent } from '@angular/upgrade/static'; -export default angular.module('addComponentModule', ['ui.router']) - .directive('chooseNewComponent', - downgradeComponent({ component: ChooseNewComponent }) as angular.IDirectiveFactory) - .directive('chooseNewComponentLocation', - downgradeComponent({ component: ChooseNewComponentLocation }) as angular.IDirectiveFactory) - .config(['$stateProvider', $stateProvider => { - $stateProvider - .state('root.at.project.node.add-component', { - url: '/add-component', - abstract: true, - resolve: {} - }) - .state('root.at.project.node.add-component.choose-component', { - url: '/choose-component', - component: 'chooseNewComponent', - params: { - componentType: 'Animation' - } - }) - .state('root.at.project.node.add-component.choose-location', { - url: '/choose-location', - component: 'chooseNewComponentLocation', - params: { - componentType: 'Animation' - } - }); - }]); +export default angular + .module('addComponentModule', ['ui.router']) + .directive( + 'chooseNewComponent', + downgradeComponent({ component: ChooseNewComponent }) as angular.IDirectiveFactory + ) + .directive( + 'chooseNewComponentLocation', + downgradeComponent({ component: ChooseNewComponentLocation }) as angular.IDirectiveFactory + ) + .config([ + '$stateProvider', + ($stateProvider) => { + $stateProvider + .state('root.at.project.node.add-component', { + url: '/add-component', + abstract: true, + resolve: {} + }) + .state('root.at.project.node.add-component.choose-component', { + url: '/choose-component', + component: 'chooseNewComponent', + params: { + componentType: 'Animation' + } + }) + .state('root.at.project.node.add-component.choose-location', { + url: '/choose-location', + component: 'chooseNewComponentLocation', + params: { + componentType: 'Animation' + } + }); + } + ]); diff --git a/src/main/webapp/wise5/authoringTool/advanced/advanced-project-authoring.component.ts b/src/main/webapp/wise5/authoringTool/advanced/advanced-project-authoring.component.ts index 4e9700281c..43876c99d8 100644 --- a/src/main/webapp/wise5/authoringTool/advanced/advanced-project-authoring.component.ts +++ b/src/main/webapp/wise5/authoringTool/advanced/advanced-project-authoring.component.ts @@ -12,7 +12,6 @@ import { UpgradeModule } from '@angular/upgrade/static'; templateUrl: 'advanced-project-authoring.component.html' }) export class AdvancedProjectAuthoringComponent { - isJSONDisplayed: boolean = false; projectId: number; projectJSONString: string; @@ -45,8 +44,11 @@ export class AdvancedProjectAuthoringComponent { if (this.UtilService.isValidJSONString(this.projectJSONString)) { this.isJSONDisplayed = false; this.NotificationService.hideJSONValidMessage(); - } else if (confirm( - $localize`The JSON is invalid. Invalid JSON will not be saved.\nClick "OK" to revert back to the last valid JSON.\nClick "Cancel" to keep the invalid JSON open so you can fix it.`)) { + } else if ( + confirm( + $localize`The JSON is invalid. Invalid JSON will not be saved.\nClick "OK" to revert back to the last valid JSON.\nClick "Cancel" to keep the invalid JSON open so you can fix it.` + ) + ) { this.isJSONDisplayed = false; this.NotificationService.hideJSONValidMessage(); } @@ -84,8 +86,9 @@ export class AdvancedProjectAuthoringComponent { projectId: this.projectId, target: 'scriptFilename' }; - this.ProjectAssetService.openAssetChooser(params).then( - (data: any) => { this.assetSelected(data); }); + this.ProjectAssetService.openAssetChooser(params).then((data: any) => { + this.assetSelected(data); + }); } assetSelected({ assetItem }) { diff --git a/src/main/webapp/wise5/authoringTool/asset/projectAssetController.ts b/src/main/webapp/wise5/authoringTool/asset/projectAssetController.ts index 2e5129f804..2d9b14a267 100644 --- a/src/main/webapp/wise5/authoringTool/asset/projectAssetController.ts +++ b/src/main/webapp/wise5/authoringTool/asset/projectAssetController.ts @@ -95,25 +95,28 @@ class ProjectAssetController { } } - this.getProjectAssetsSubscription = - this.ProjectAssetService.getProjectAssets().subscribe((projectAssets) => { - if (projectAssets != null) { - this.projectAssets = projectAssets; - this.sortAssets(this.assetSortBy); - this.projectAssetTotalSizeMax = this.ProjectAssetService.totalSizeMax; + this.getProjectAssetsSubscription = this.ProjectAssetService.getProjectAssets().subscribe( + (projectAssets) => { + if (projectAssets != null) { + this.projectAssets = projectAssets; + this.sortAssets(this.assetSortBy); + this.projectAssetTotalSizeMax = this.ProjectAssetService.totalSizeMax; + } } - }); + ); - this.getTotalFileSizeSubscription = - this.ProjectAssetService.getTotalFileSize().subscribe((totalFileSize) => { - this.setTotalFileSize(totalFileSize); - }); + this.getTotalFileSizeSubscription = this.ProjectAssetService.getTotalFileSize().subscribe( + (totalFileSize) => { + this.setTotalFileSize(totalFileSize); + } + ); + + this.getTotalUnusedFileSizeSubscription = this.ProjectAssetService.getTotalUnusedFileSize().subscribe( + (totalUnusedFilesSize) => { + this.setTotalUnusedFilesSize(totalUnusedFilesSize); + } + ); - this.getTotalUnusedFileSizeSubscription = - this.ProjectAssetService.getTotalUnusedFileSize().subscribe((totalUnusedFilesSize) => { - this.setTotalUnusedFilesSize(totalUnusedFilesSize); - }); - if (this.ProjectAssetService.isProjectAssetsAvailable()) { this.ProjectAssetService.calculateAssetUsage(); } @@ -142,8 +145,8 @@ class ProjectAssetController { this.projectAssets.files.sort(this.sortAssetsSmallToLarge); } else if (sortBy === 'largeToSmall') { this.projectAssets.files = this.projectAssets.files - .sort(this.sortAssetsSmallToLarge) - .reverse(); + .sort(this.sortAssetsSmallToLarge) + .reverse(); } } diff --git a/src/main/webapp/wise5/authoringTool/authoringToolController.ts b/src/main/webapp/wise5/authoringTool/authoringToolController.ts index 3c6ca7297a..41bfcba15f 100644 --- a/src/main/webapp/wise5/authoringTool/authoringToolController.ts +++ b/src/main/webapp/wise5/authoringTool/authoringToolController.ts @@ -226,7 +226,7 @@ class AuthoringToolController { }; this.processUI(); - $transitions.onSuccess({}, $transition => { + $transitions.onSuccess({}, ($transition) => { this.isMenuOpen = false; this.processUI(); if ($transition.name === 'root.at.main') { @@ -234,8 +234,7 @@ class AuthoringToolController { } }); - this.showSessionWarningSubscription = - this.SessionService.showSessionWarning$.subscribe(() => { + this.showSessionWarningSubscription = this.SessionService.showSessionWarning$.subscribe(() => { const confirm = this.$mdDialog .confirm() .parent(angular.element(document.body)) @@ -280,15 +279,17 @@ class AuthoringToolController { this.setGlobalMessage(this.$translate('errorSavingProject'), false, null); }); - this.notLoggedInProjectNotSavedSubscription = - this.ProjectService.notLoggedInProjectNotSaved$.subscribe(() => { - this.setGlobalMessage(this.$translate('notLoggedInProjectNotSaved'), false, null); - }); + this.notLoggedInProjectNotSavedSubscription = this.ProjectService.notLoggedInProjectNotSaved$.subscribe( + () => { + this.setGlobalMessage(this.$translate('notLoggedInProjectNotSaved'), false, null); + } + ); - this.notAllowedToEditThisProjectSubscription = - this.ProjectService.notAllowedToEditThisProject$.subscribe(() => { - this.setGlobalMessage(this.$translate('notAllowedToEditThisProject'), false, null); - }); + this.notAllowedToEditThisProjectSubscription = this.ProjectService.notAllowedToEditThisProject$.subscribe( + () => { + this.setGlobalMessage(this.$translate('notAllowedToEditThisProject'), false, null); + } + ); if (this.$state.current.name === 'root.at.main') { this.saveEvent('projectListViewed', 'Navigation'); diff --git a/src/main/webapp/wise5/authoringTool/components/component-authoring.component.ts b/src/main/webapp/wise5/authoringTool/components/component-authoring.component.ts index 7c49a0a6b6..52c282a20b 100644 --- a/src/main/webapp/wise5/authoringTool/components/component-authoring.component.ts +++ b/src/main/webapp/wise5/authoringTool/components/component-authoring.component.ts @@ -1,9 +1,9 @@ -import { Directive, Input } from "@angular/core"; -import { Subject, Subscription } from "rxjs"; -import { debounceTime, distinctUntilChanged } from "rxjs/operators"; -import { ConfigService } from "../../services/configService"; -import { NodeService } from "../../services/nodeService"; -import { TeacherProjectService } from "../../services/teacherProjectService"; +import { Directive, Input } from '@angular/core'; +import { Subject, Subscription } from 'rxjs'; +import { debounceTime, distinctUntilChanged } from 'rxjs/operators'; +import { ConfigService } from '../../services/configService'; +import { NodeService } from '../../services/nodeService'; +import { TeacherProjectService } from '../../services/teacherProjectService'; @Directive() export abstract class ComponentAuthoring { @@ -33,7 +33,6 @@ export abstract class ComponentAuthoring { protected NodeService: NodeService, protected ProjectService: TeacherProjectService ) { - this.promptChange .pipe(debounceTime(1000), distinctUntilChanged()) .subscribe((prompt: string) => { @@ -43,18 +42,22 @@ export abstract class ComponentAuthoring { } ngOnInit() { - this.authoringComponentContent = this.ProjectService.getComponentByNodeIdAndComponentId(this.nodeId, this.componentId); + this.authoringComponentContent = this.ProjectService.getComponentByNodeIdAndComponentId( + this.nodeId, + this.componentId + ); this.resetUI(); this.idToOrder = this.ProjectService.idToOrder; this.componentChangedSubscription = this.ProjectService.componentChanged$.subscribe(() => { this.componentChanged(); }); - this.starterStateResponseSubscription = - this.NodeService.starterStateResponse$.subscribe((args: any) => { - if (this.isForThisComponent(args)) { - this.saveStarterState(args.starterState); + this.starterStateResponseSubscription = this.NodeService.starterStateResponse$.subscribe( + (args: any) => { + if (this.isForThisComponent(args)) { + this.saveStarterState(args.starterState); + } } - }); + ); } promptChanged(prompt: string): void { @@ -68,7 +71,8 @@ export abstract class ComponentAuthoring { resetUI(): void { this.componentContent = this.ConfigService.replaceStudentNames( - this.ProjectService.injectAssetPaths(this.authoringComponentContent)); + this.ProjectService.injectAssetPaths(this.authoringComponentContent) + ); this.isSaveButtonVisible = this.componentContent.showSaveButton; this.isSubmitButtonVisible = this.componentContent.showSubmitButton; this.isDirty = false; @@ -81,4 +85,4 @@ export abstract class ComponentAuthoring { } saveStarterState(starterState: any): void {} -} \ No newline at end of file +} diff --git a/src/main/webapp/wise5/authoringTool/components/editComponentController.ts b/src/main/webapp/wise5/authoringTool/components/editComponentController.ts index a3e5645fd0..7626e65f6a 100644 --- a/src/main/webapp/wise5/authoringTool/components/editComponentController.ts +++ b/src/main/webapp/wise5/authoringTool/components/editComponentController.ts @@ -1,14 +1,13 @@ import * as angular from 'angular'; -import { ConfigService } from "../../services/configService"; -import { UtilService } from "../../services/utilService"; -import { TeacherProjectService } from "../../services/teacherProjectService"; +import { ConfigService } from '../../services/configService'; +import { UtilService } from '../../services/utilService'; +import { TeacherProjectService } from '../../services/teacherProjectService'; import { ProjectAssetService } from '../../../site/src/app/services/projectAssetService'; import { NodeService } from '../../services/nodeService'; import { NotificationService } from '../../services/notificationService'; import { Subscription } from 'rxjs'; export abstract class EditComponentController { - $translate: any; allowedConnectedComponentTypes: string[]; authoringComponentContent: any; @@ -27,29 +26,33 @@ export abstract class EditComponentController { starterStateResponseSubscription: Subscription; constructor( - protected $filter: any, - protected ConfigService: ConfigService, - protected NodeService: NodeService, - protected NotificationService: NotificationService, - protected ProjectAssetService: ProjectAssetService, - protected ProjectService: TeacherProjectService, - protected UtilService: UtilService) { - } + protected $filter: any, + protected ConfigService: ConfigService, + protected NodeService: NodeService, + protected NotificationService: NotificationService, + protected ProjectAssetService: ProjectAssetService, + protected ProjectService: TeacherProjectService, + protected UtilService: UtilService + ) {} $onInit() { - this.authoringComponentContent = this.ProjectService.getComponentByNodeIdAndComponentId(this.nodeId, this.componentId); + this.authoringComponentContent = this.ProjectService.getComponentByNodeIdAndComponentId( + this.nodeId, + this.componentId + ); this.resetUI(); this.idToOrder = this.ProjectService.idToOrder; this.$translate = this.$filter('translate'); this.componentChangedSubscription = this.ProjectService.componentChanged$.subscribe(() => { this.componentChanged(); }); - this.starterStateResponseSubscription = - this.NodeService.starterStateResponse$.subscribe((args: any) => { - if (this.isForThisComponent(args)) { - this.saveStarterState(args.starterState); + this.starterStateResponseSubscription = this.NodeService.starterStateResponse$.subscribe( + (args: any) => { + if (this.isForThisComponent(args)) { + this.saveStarterState(args.starterState); + } } - }); + ); } $onDestroy() { @@ -64,7 +67,8 @@ export abstract class EditComponentController { resetUI(): void { this.componentContent = this.ConfigService.replaceStudentNames( - this.ProjectService.injectAssetPaths(this.authoringComponentContent)); + this.ProjectService.injectAssetPaths(this.authoringComponentContent) + ); this.isSaveButtonVisible = this.componentContent.showSaveButton; this.isSubmitButtonVisible = this.componentContent.showSubmitButton; this.isDirty = false; @@ -73,13 +77,12 @@ export abstract class EditComponentController { } openAssetChooser(params: any): any { - return this.ProjectAssetService.openAssetChooser(params).then( - (data: any) => { return this.assetSelected(data) } - ); + return this.ProjectAssetService.openAssetChooser(params).then((data: any) => { + return this.assetSelected(data); + }); } - assetSelected({ nodeId, componentId, assetItem, target }): void { - } + assetSelected({ nodeId, componentId, assetItem, target }): void {} setShowSubmitButtonValue(show) { if (show == null || show == false) { diff --git a/src/main/webapp/wise5/authoringTool/components/preview-component/previewComponent.ts b/src/main/webapp/wise5/authoringTool/components/preview-component/previewComponent.ts index 806d888de2..cb03fa0859 100644 --- a/src/main/webapp/wise5/authoringTool/components/preview-component/previewComponent.ts +++ b/src/main/webapp/wise5/authoringTool/components/preview-component/previewComponent.ts @@ -1,39 +1,42 @@ -import { NodeService } from "../../../services/nodeService"; -import { ProjectService } from "../../../services/projectService"; +import { NodeService } from '../../../services/nodeService'; +import { ProjectService } from '../../../services/projectService'; class PreviewComponentController { - componentContent: any; componentId: string; nodeId: string; static $inject = ['$scope', '$compile', '$element', 'NodeService', 'ProjectService']; - constructor(private $scope: any, private $compile: any, private $element: any, - private NodeService: NodeService, private ProjectService: ProjectService) { - } + constructor( + private $scope: any, + private $compile: any, + private $element: any, + private NodeService: NodeService, + private ProjectService: ProjectService + ) {} $onInit() { this.$scope.mode = 'authoringComponentPreview'; - this.$scope.componentTemplatePath = - this.NodeService.getComponentTemplatePath(this.componentContent.type); + this.$scope.componentTemplatePath = this.NodeService.getComponentTemplatePath( + this.componentContent.type + ); this.$scope.nodeId = this.nodeId; this.$scope.type = this.componentContent.type; this.$scope.$watch( - () => { - return this.componentContent; - }, - () => { - this.$scope.componentContent = - this.ProjectService.injectAssetPaths(this.componentContent); - this.compileComponent(); - }, - true); + () => { + return this.componentContent; + }, + () => { + this.$scope.componentContent = this.ProjectService.injectAssetPaths(this.componentContent); + this.compileComponent(); + }, + true + ); } compileComponent() { - const componentHTML = - `
+ const componentHTML = `
`; this.$element.html(componentHTML); diff --git a/src/main/webapp/wise5/authoringTool/components/shared/mainMenu/mainMenu.ts b/src/main/webapp/wise5/authoringTool/components/shared/mainMenu/mainMenu.ts index c8f798cf66..7a1de3cd50 100644 --- a/src/main/webapp/wise5/authoringTool/components/shared/mainMenu/mainMenu.ts +++ b/src/main/webapp/wise5/authoringTool/components/shared/mainMenu/mainMenu.ts @@ -1,12 +1,11 @@ -"use strict"; +'use strict'; const MainMenu = { bindings: { state: '<', views: '<' }, - template: - ` + template: `
diff --git a/src/main/webapp/wise5/authoringTool/components/shared/stepTools/stepTools.ts b/src/main/webapp/wise5/authoringTool/components/shared/stepTools/stepTools.ts index 7296f85329..e6f2efc1cf 100644 --- a/src/main/webapp/wise5/authoringTool/components/shared/stepTools/stepTools.ts +++ b/src/main/webapp/wise5/authoringTool/components/shared/stepTools/stepTools.ts @@ -51,10 +51,11 @@ class StepToolsController { this.nodeId = this.TeacherDataService.getCurrentNodeId(); this.idToOrder = this.ProjectService.idToOrder; this.updateModel(); - this.currentNodeChangedSubscription = this.TeacherDataService.currentNodeChanged$ - .subscribe(() => { - this.updateModel(); - }); + this.currentNodeChangedSubscription = this.TeacherDataService.currentNodeChanged$.subscribe( + () => { + this.updateModel(); + } + ); this.projectChangedSubscription = this.ProjectService.projectChanged$.subscribe(() => { this.projectId = this.ConfigService.getProjectId(); this.idToOrder = this.ProjectService.idToOrder; @@ -87,7 +88,7 @@ class StepToolsController { } else { if (!this.ProjectService.isGroupNode(this.nodeId)) { this.prevId = this.NodeService.getPrevNodeId(this.nodeId); - this.NodeService.getNextNodeId(this.nodeId).then(currentNodeId => { + this.NodeService.getNextNodeId(this.nodeId).then((currentNodeId) => { this.nextId = currentNodeId; }); } diff --git a/src/main/webapp/wise5/authoringTool/components/shared/toolbar/toolbar.ts b/src/main/webapp/wise5/authoringTool/components/shared/toolbar/toolbar.ts index d457c86fb4..8060e298f3 100644 --- a/src/main/webapp/wise5/authoringTool/components/shared/toolbar/toolbar.ts +++ b/src/main/webapp/wise5/authoringTool/components/shared/toolbar/toolbar.ts @@ -1,7 +1,7 @@ 'use strict'; -import { Directive } from "@angular/core"; -import { NotificationService } from "../../../../services/notificationService"; +import { Directive } from '@angular/core'; +import { NotificationService } from '../../../../services/notificationService'; @Directive() class ToolbarController { @@ -16,14 +16,16 @@ class ToolbarController { constructor(private $scope, NotificationService: NotificationService) { this.NotificationService = NotificationService; - this.setGlobalMessageSubscription = - this.NotificationService.setGlobalMessage$.subscribe(({ globalMessage }) => { - this.globalMessage = globalMessage; - }); - this.setIsJSONValidSubscription = - this.NotificationService.setIsJSONValid$.subscribe(({ isJSONValid }) => { - this.isJSONValid = isJSONValid; - }); + this.setGlobalMessageSubscription = this.NotificationService.setGlobalMessage$.subscribe( + ({ globalMessage }) => { + this.globalMessage = globalMessage; + } + ); + this.setIsJSONValidSubscription = this.NotificationService.setIsJSONValid$.subscribe( + ({ isJSONValid }) => { + this.isJSONValid = isJSONValid; + } + ); this.$scope.$on('$destroy', () => { this.ngOnDestroy(); }); diff --git a/src/main/webapp/wise5/authoringTool/components/shared/topBar/topBar.ts b/src/main/webapp/wise5/authoringTool/components/shared/topBar/topBar.ts index 47232168da..1a66f31b27 100644 --- a/src/main/webapp/wise5/authoringTool/components/shared/topBar/topBar.ts +++ b/src/main/webapp/wise5/authoringTool/components/shared/topBar/topBar.ts @@ -82,7 +82,6 @@ class TopBarController { runId: this.runId }); } - } goHome() { diff --git a/src/main/webapp/wise5/authoringTool/importComponent/choose-component-location.component.ts b/src/main/webapp/wise5/authoringTool/importComponent/choose-component-location.component.ts index 1bea2ea1c6..13aa22b9b7 100644 --- a/src/main/webapp/wise5/authoringTool/importComponent/choose-component-location.component.ts +++ b/src/main/webapp/wise5/authoringTool/importComponent/choose-component-location.component.ts @@ -1,27 +1,36 @@ -import { ConfigService } from "../../services/configService"; -import { TeacherProjectService } from "../../services/teacherProjectService"; -import { TeacherDataService } from "../../services/teacherDataService"; -import { ProjectAssetService } from "../../../site/src/app/services/projectAssetService"; -import { UtilService } from "../../services/utilService"; +import { ConfigService } from '../../services/configService'; +import { TeacherProjectService } from '../../services/teacherProjectService'; +import { TeacherDataService } from '../../services/teacherDataService'; +import { ProjectAssetService } from '../../../site/src/app/services/projectAssetService'; +import { UtilService } from '../../services/utilService'; class ChooseComponentLocationController { - components: any = []; nodeId: string; - static $inject = ['$state', '$stateParams', 'ConfigService', 'ProjectAssetService', - 'ProjectService', 'TeacherDataService', 'UtilService']; + static $inject = [ + '$state', + '$stateParams', + 'ConfigService', + 'ProjectAssetService', + 'ProjectService', + 'TeacherDataService', + 'UtilService' + ]; - constructor(private $state: any, private $stateParams: any, private ConfigService: ConfigService, - private ProjectAssetService: ProjectAssetService, - private ProjectService: TeacherProjectService, - private TeacherDataService: TeacherDataService, - private UtilService: UtilService) { - } + constructor( + private $state: any, + private $stateParams: any, + private ConfigService: ConfigService, + private ProjectAssetService: ProjectAssetService, + private ProjectService: TeacherProjectService, + private TeacherDataService: TeacherDataService, + private UtilService: UtilService + ) {} $onInit() { this.nodeId = this.TeacherDataService.getCurrentNodeId(); - this.components = this.ProjectService.getComponentsByNodeId(this.nodeId) + this.components = this.ProjectService.getComponentsByNodeId(this.nodeId); } getComponentTypeLabel(componentType) { @@ -33,23 +42,25 @@ class ChooseComponentLocationController { } importComponentAfter(insertAfterComponentId: string) { - this.importComponents(this.nodeId, insertAfterComponentId) - .then((newComponents) => { + this.importComponents(this.nodeId, insertAfterComponentId).then((newComponents) => { this.ProjectService.saveProject(); // refresh the project assets in case any of the imported components also imported assets this.ProjectAssetService.retrieveProjectAssets(); - this.$state.go('root.at.project.node', { projectId: this.ConfigService.getProjectId(), - nodeId: this.nodeId, newComponents: newComponents}) + this.$state.go('root.at.project.node', { + projectId: this.ConfigService.getProjectId(), + nodeId: this.nodeId, + newComponents: newComponents + }); }); } importComponents(nodeId: string, insertAfterComponentId: string) { return this.ProjectService.importComponents( - this.$stateParams.selectedComponents, - this.$stateParams.importFromProjectId, - nodeId, - insertAfterComponentId - ).then(newComponents => { + this.$stateParams.selectedComponents, + this.$stateParams.importFromProjectId, + nodeId, + insertAfterComponentId + ).then((newComponents) => { this.saveImportedComponentsEvent(newComponents); return newComponents; }); @@ -60,8 +71,15 @@ class ChooseComponentLocationController { for (let c = 0; c < importedComponents.length; c++) { importedComponents[c].toComponentId = newComponents[c].id; } - this.TeacherDataService.saveEvent('AuthoringTool', this.nodeId, null, null, 'Authoring', - 'componentImported', { componentsImported: importedComponents }); + this.TeacherDataService.saveEvent( + 'AuthoringTool', + this.nodeId, + null, + null, + 'Authoring', + 'componentImported', + { componentsImported: importedComponents } + ); } getImportedComponents() { @@ -78,8 +96,10 @@ class ChooseComponentLocationController { } cancel() { - this.$state.go('root.at.project.node', { projectId: this.ConfigService.getProjectId(), - nodeId: this.TeacherDataService.getCurrentNodeId() }); + this.$state.go('root.at.project.node', { + projectId: this.ConfigService.getProjectId(), + nodeId: this.TeacherDataService.getCurrentNodeId() + }); } } diff --git a/src/main/webapp/wise5/authoringTool/importComponent/choose-component.component.ts b/src/main/webapp/wise5/authoringTool/importComponent/choose-component.component.ts index 897b20aa64..3d1021b554 100644 --- a/src/main/webapp/wise5/authoringTool/importComponent/choose-component.component.ts +++ b/src/main/webapp/wise5/authoringTool/importComponent/choose-component.component.ts @@ -1,9 +1,8 @@ -import { TeacherProjectService } from "../../services/teacherProjectService"; -import { ConfigService } from "../../services/configService"; -import { TeacherDataService } from "../../services/teacherDataService"; +import { TeacherProjectService } from '../../services/teacherProjectService'; +import { ConfigService } from '../../services/configService'; +import { TeacherDataService } from '../../services/teacherDataService'; class ChooseComponentController { - importLibraryProjectId: number; importMyProjectId: number; importProject: any = null; @@ -15,10 +14,12 @@ class ChooseComponentController { static $inject = ['$state', 'ConfigService', 'ProjectService', 'TeacherDataService']; - constructor(private $state: any, private ConfigService: ConfigService, - private ProjectService: TeacherProjectService, - private TeacherDataService: TeacherDataService) { - } + constructor( + private $state: any, + private ConfigService: ConfigService, + private ProjectService: TeacherProjectService, + private TeacherDataService: TeacherDataService + ) {} $onInit() { this.importProjectIdToOrder = {}; @@ -28,7 +29,7 @@ class ChooseComponentController { this.importProjectId = null; this.importProject = null; this.myProjectsList = this.ConfigService.getAuthorableProjects(); - this.ProjectService.getLibraryProjects().then(libraryProjects => { + this.ProjectService.getLibraryProjects().then((libraryProjects) => { this.libraryProjectsList = this.ProjectService.sortAndFilterUniqueLibraryProjects( libraryProjects ); @@ -47,13 +48,14 @@ class ChooseComponentController { showImportProject(importProjectId) { this.importProjectId = importProjectId; - this.ProjectService.retrieveProjectById(this.importProjectId).then(projectJSON => { + this.ProjectService.retrieveProjectById(this.importProjectId).then((projectJSON) => { this.importProjectIdToOrder = {}; this.importProject = projectJSON; const result = this.ProjectService.getNodeOrderOfProject(this.importProject); this.importProjectIdToOrder = result.idToOrder; this.importProjectItems = result.nodes.filter((nodeOrder) => { - return nodeOrder.node.type !== 'group';}); + return nodeOrder.node.type !== 'group'; + }); }); } @@ -91,8 +93,10 @@ class ChooseComponentController { } cancel() { - this.$state.go('root.at.project.node', { projectId: this.ConfigService.getProjectId(), - nodeId: this.TeacherDataService.getCurrentNodeId() }); + this.$state.go('root.at.project.node', { + projectId: this.ConfigService.getProjectId(), + nodeId: this.TeacherDataService.getCurrentNodeId() + }); } } diff --git a/src/main/webapp/wise5/authoringTool/importComponent/importComponentModule.ts b/src/main/webapp/wise5/authoringTool/importComponent/importComponentModule.ts index f1c420920b..ab0fb23017 100644 --- a/src/main/webapp/wise5/authoringTool/importComponent/importComponentModule.ts +++ b/src/main/webapp/wise5/authoringTool/importComponent/importComponentModule.ts @@ -2,26 +2,30 @@ import * as angular from 'angular'; import { ChooseComponent } from './choose-component.component'; import { ChooseComponentLocation } from './choose-component-location.component'; -export default angular.module('importComponentModule', ['ui.router']) +export default angular + .module('importComponentModule', ['ui.router']) .component('chooseComponent', ChooseComponent) .component('chooseComponentLocation', ChooseComponentLocation) - .config(['$stateProvider', $stateProvider => { - $stateProvider - .state('root.at.project.node.import-component', { - url: '/import-component', - abstract: true, - resolve: {} - }) - .state('root.at.project.node.import-component.choose-step', { - url: '/choose-component', - component: 'chooseComponent' - }) - .state('root.at.project.node.import-component.choose-location', { - url: '/choose-location', - component: 'chooseComponentLocation', - params: { - importFromProjectId: '', - selectedComponents: [] - } - }); - }]); + .config([ + '$stateProvider', + ($stateProvider) => { + $stateProvider + .state('root.at.project.node.import-component', { + url: '/import-component', + abstract: true, + resolve: {} + }) + .state('root.at.project.node.import-component.choose-step', { + url: '/choose-component', + component: 'chooseComponent' + }) + .state('root.at.project.node.import-component.choose-location', { + url: '/choose-location', + component: 'chooseComponentLocation', + params: { + importFromProjectId: '', + selectedComponents: [] + } + }); + } + ]); diff --git a/src/main/webapp/wise5/authoringTool/importStep/importStepModule.ts b/src/main/webapp/wise5/authoringTool/importStep/importStepModule.ts index e26ac78e43..865d304299 100644 --- a/src/main/webapp/wise5/authoringTool/importStep/importStepModule.ts +++ b/src/main/webapp/wise5/authoringTool/importStep/importStepModule.ts @@ -7,13 +7,19 @@ import { downgradeComponent } from '@angular/upgrade/static'; const importStepModule = angular .module('importStepModule', ['ui.router']) - .directive('chooseImportStepComponent', - downgradeComponent({ component: ChooseImportStepComponent}) as angular.IDirectiveFactory) - .directive('chooseImportStepLocationComponent', - downgradeComponent({ component: ChooseImportStepLocationComponent}) as angular.IDirectiveFactory) + .directive( + 'chooseImportStepComponent', + downgradeComponent({ component: ChooseImportStepComponent }) as angular.IDirectiveFactory + ) + .directive( + 'chooseImportStepLocationComponent', + downgradeComponent({ + component: ChooseImportStepLocationComponent + }) as angular.IDirectiveFactory + ) .config([ '$stateProvider', - $stateProvider => { + ($stateProvider) => { $stateProvider .state('root.at.project.import-step', { url: '/import-step', diff --git a/src/main/webapp/wise5/authoringTool/info/projectInfoController.ts b/src/main/webapp/wise5/authoringTool/info/projectInfoController.ts index 1942f5c400..6bea7ff210 100644 --- a/src/main/webapp/wise5/authoringTool/info/projectInfoController.ts +++ b/src/main/webapp/wise5/authoringTool/info/projectInfoController.ts @@ -89,9 +89,11 @@ class ProjectInfoController { let choiceText = choice; let userLocale = this.ConfigService.getLocale(); let i18nMapping = this.metadataAuthoring.i18n; - let i18nMappingContainingChoiceTextArray = Object.values(i18nMapping).filter(onei18nMapping => { - return Object.values(onei18nMapping).indexOf(choice) != -1; - }); + let i18nMappingContainingChoiceTextArray = Object.values(i18nMapping).filter( + (onei18nMapping) => { + return Object.values(onei18nMapping).indexOf(choice) != -1; + } + ); if ( i18nMappingContainingChoiceTextArray != null && i18nMappingContainingChoiceTextArray.length > 0 @@ -130,7 +132,7 @@ class ProjectInfoController { } getFeaturedProjectIcons() { - this.ProjectService.getFeaturedProjectIcons().then(featuredProjectIcons => { + this.ProjectService.getFeaturedProjectIcons().then((featuredProjectIcons) => { this.projectIcons = featuredProjectIcons; }); } @@ -148,9 +150,9 @@ class ProjectInfoController { isPopup: true, target: 'projectIcon' }; - this.ProjectAssetService.openAssetChooser(params).then( - (data: any) => { this.assetSelected(data) } - ); + this.ProjectAssetService.openAssetChooser(params).then((data: any) => { + this.assetSelected(data); + }); } assetSelected(args) { diff --git a/src/main/webapp/wise5/authoringTool/main/authoringToolMainController.ts b/src/main/webapp/wise5/authoringTool/main/authoringToolMainController.ts index 584c4f5a39..1ad886de5d 100644 --- a/src/main/webapp/wise5/authoringTool/main/authoringToolMainController.ts +++ b/src/main/webapp/wise5/authoringTool/main/authoringToolMainController.ts @@ -151,7 +151,7 @@ class AuthoringToolMainController { this.startErrorCreatingProjectTimeout(); const projectJSONString = angular.toJson(this.project, 4); this.ProjectService.registerNewProject(this.project.metadata.title, projectJSONString) - .then(projectId => { + .then((projectId) => { this.cancelErrorCreatingProjectTimeout(); this.saveEvent('projectCreated', 'Authoring', {}, projectId); this.$state.go('root.at.project', { projectId: projectId }); @@ -216,9 +216,7 @@ class AuthoringToolMainController { previewProject(projectId) { const data = { constraints: true }; this.saveEvent('projectPreviewed', 'Authoring', data, projectId); - window.open( - `${this.ConfigService.getWISEBaseURL()}/preview/unit/${projectId}` - ); + window.open(`${this.ConfigService.getWISEBaseURL()}/preview/unit/${projectId}`); } goHome() { diff --git a/src/main/webapp/wise5/authoringTool/milestones/milestonesAuthoringController.ts b/src/main/webapp/wise5/authoringTool/milestones/milestonesAuthoringController.ts index 61a8014688..fb32b19972 100644 --- a/src/main/webapp/wise5/authoringTool/milestones/milestonesAuthoringController.ts +++ b/src/main/webapp/wise5/authoringTool/milestones/milestonesAuthoringController.ts @@ -26,22 +26,18 @@ class MilestonesAuthoringController { customScoreKey: string; customScoreValues: string; - static $inject = [ - '$filter', - 'ProjectService', - 'UtilService' - ]; - - constructor(private $filter, - private ProjectService: TeacherProjectService, - private UtilService: UtilService) { + static $inject = ['$filter', 'ProjectService', 'UtilService']; + + constructor( + private $filter, + private ProjectService: TeacherProjectService, + private UtilService: UtilService + ) { this.$translate = $filter('translate'); this.project = this.ProjectService.project; this.idToOrder = this.ProjectService.idToOrder; this.nodeIds = this.getStepNodeIds(); - this.availableSatisfyCriteria = [ - { value: 'isCompleted', text: 'Is Completed' } - ]; + this.availableSatisfyCriteria = [{ value: 'isCompleted', text: 'Is Completed' }]; this.availableSatisfyCriteriaFunctions = [ { value: 'percentOfScoresLessThan', @@ -57,7 +53,8 @@ class MilestonesAuthoringController { }, { value: 'percentOfScoresGreaterThanOrEqualTo', - text: this.$translate('percentOfScoresGreaterThanOrEqualTo') }, + text: this.$translate('percentOfScoresGreaterThanOrEqualTo') + }, { value: 'percentOfScoresEqualTo', text: this.$translate('percentOfScoresEqualTo') @@ -159,8 +156,9 @@ class MilestonesAuthoringController { } deleteMilestone(index) { - const message = - this.$translate('areYouSureYouWantToDeleteMilestoneX', { milestoneNumber: index + 1 }); + const message = this.$translate('areYouSureYouWantToDeleteMilestoneX', { + milestoneNumber: index + 1 + }); if (confirm(message)) { const deletedMilestones = this.project.achievements.items.splice(index, 1); const deletedMilestone = deletedMilestones[0]; @@ -179,7 +177,7 @@ class MilestonesAuthoringController { nodeId: '', componentId: '', name: '' - } + }; } getMilestoneSatisfyCriteriaIds() { @@ -217,8 +215,9 @@ class MilestonesAuthoringController { } deleteMilestoneSatisfyCriteria(milestone, index) { - const message = this.$translate('areYouSureYouWantToDeleteMilestoneSatisfyCriteriaX', - { milestoneSatisfyCriteriaNumber: index + 1 }); + const message = this.$translate('areYouSureYouWantToDeleteMilestoneSatisfyCriteriaX', { + milestoneSatisfyCriteriaNumber: index + 1 + }); if (confirm(message)) { const deletedMilestoneSatisfyCriterias = milestone.satisfyCriteria.splice(index, 1); const deletedMilestoneSatisfyCriteria = deletedMilestoneSatisfyCriterias[0]; @@ -231,8 +230,9 @@ class MilestonesAuthoringController { } copySatisfyCriteriaToMilestone(milestone, nodeId, componentId) { - const message = - this.$translate('areYouSureYouWantToCopyTheNodeIdAndComponentIdToTheRestOfThisMilestone'); + const message = this.$translate( + 'areYouSureYouWantToCopyTheNodeIdAndComponentIdToTheRestOfThisMilestone' + ); if (confirm(message)) { for (const template of milestone.report.templates) { for (const satisfyCriteria of template.satisfyCriteria) { @@ -294,7 +294,7 @@ class MilestonesAuthoringController { return { nodeId: '', componentId: '' - } + }; } addLocation(report, index) { @@ -369,7 +369,7 @@ class MilestonesAuthoringController { content: '', satisfyConditional: '', satisfyCriteria: [] - } + }; } getTemplateIds() { @@ -408,8 +408,9 @@ class MilestonesAuthoringController { } deleteTemplate(report, index) { - const message = - this.$translate('areYouSureYouWantToDeleteTemplateX', { templateNumber: index + 1 }); + const message = this.$translate('areYouSureYouWantToDeleteTemplateX', { + templateNumber: index + 1 + }); if (confirm(message)) { const deletedTemplates = report.templates.splice(index, 1); const deletedTemplate = deletedTemplates[0]; @@ -432,7 +433,7 @@ class MilestonesAuthoringController { function: '', type: 'autoScore', value: 3 - } + }; } getTemplateSatisfyCriteriaIds() { @@ -472,8 +473,9 @@ class MilestonesAuthoringController { } deleteTemplateSatisfyCriteria(template, index) { - const message = this.$translate('areYouSureYouWantToDeleteTemplateSatisfyCriteriaX', - { templateSatisfyCriteriaNumber: index + 1 }); + const message = this.$translate('areYouSureYouWantToDeleteTemplateSatisfyCriteriaX', { + templateSatisfyCriteriaNumber: index + 1 + }); if (confirm(message)) { const deletedSatisfyCriteria = template.satisfyCriteria.splice(index, 1); const deletedSatisfyCriterion = deletedSatisfyCriteria[0]; diff --git a/src/main/webapp/wise5/authoringTool/node/advanced/branch/node-advanced-branch-authoring.component.ts b/src/main/webapp/wise5/authoringTool/node/advanced/branch/node-advanced-branch-authoring.component.ts index 285e65eb19..ffeda6d7a8 100644 --- a/src/main/webapp/wise5/authoringTool/node/advanced/branch/node-advanced-branch-authoring.component.ts +++ b/src/main/webapp/wise5/authoringTool/node/advanced/branch/node-advanced-branch-authoring.component.ts @@ -1,11 +1,10 @@ -import { ConfigService } from "../../../../services/configService"; -import { TagService } from "../../../../services/tagService"; -import { TeacherDataService } from "../../../../services/teacherDataService"; -import { TeacherProjectService } from "../../../../services/teacherProjectService"; -import { UtilService } from "../../../../services/utilService"; +import { ConfigService } from '../../../../services/configService'; +import { TagService } from '../../../../services/tagService'; +import { TeacherDataService } from '../../../../services/teacherDataService'; +import { TeacherProjectService } from '../../../../services/teacherProjectService'; +import { UtilService } from '../../../../services/utilService'; class NodeAdvancedBranchAuthoringController { - branchCriteria: any; createBranchBranches = []; createBranchComponentId: string; @@ -18,34 +17,45 @@ class NodeAdvancedBranchAuthoringController { nodeId: string; $translate: any; - static $inject = ['$filter', 'ConfigService', 'TagService', 'ProjectService', - 'TeacherDataService', 'UtilService']; - - constructor(private $filter: any, private ConfigService: ConfigService, - private TagService: TagService, private ProjectService: TeacherProjectService, - private TeacherDataService: TeacherDataService, private UtilService: UtilService) { + static $inject = [ + '$filter', + 'ConfigService', + 'TagService', + 'ProjectService', + 'TeacherDataService', + 'UtilService' + ]; + + constructor( + private $filter: any, + private ConfigService: ConfigService, + private TagService: TagService, + private ProjectService: TeacherProjectService, + private TeacherDataService: TeacherDataService, + private UtilService: UtilService + ) { this.$translate = this.$filter('translate'); this.branchCriteria = [ - { - value: 'workgroupId', - text: this.$translate('WORKGROUP_ID') - }, - { - value: 'score', - text: this.$translate('SCORE') - }, - { - value: 'choiceChosen', - text: this.$translate('choiceChosen') - }, - { - value: 'random', - text: this.$translate('random') - }, - { - value: 'tag', - text: this.$translate('tag') - } + { + value: 'workgroupId', + text: this.$translate('WORKGROUP_ID') + }, + { + value: 'score', + text: this.$translate('SCORE') + }, + { + value: 'choiceChosen', + text: this.$translate('choiceChosen') + }, + { + value: 'random', + text: this.$translate('random') + }, + { + value: 'tag', + text: this.$translate('tag') + } ]; } @@ -53,7 +63,7 @@ class NodeAdvancedBranchAuthoringController { this.nodeId = this.TeacherDataService.getCurrentNodeId(); this.node = this.ProjectService.getNodeById(this.nodeId); this.items = this.ProjectService.idToOrder; - this.populateBranchAuthoring() + this.populateBranchAuthoring(); } populateBranchAuthoring() { @@ -736,4 +746,4 @@ class NodeAdvancedBranchAuthoringController { export const NodeAdvancedBranchAuthoringComponent = { templateUrl: `/wise5/authoringTool/node/advanced/branch/node-advanced-branch-authoring.component.html`, controller: NodeAdvancedBranchAuthoringController -} +}; diff --git a/src/main/webapp/wise5/authoringTool/node/advanced/constraint/node-advanced-constraint-authoring.component.ts b/src/main/webapp/wise5/authoringTool/node/advanced/constraint/node-advanced-constraint-authoring.component.ts index d8208d3f76..73d786134f 100644 --- a/src/main/webapp/wise5/authoringTool/node/advanced/constraint/node-advanced-constraint-authoring.component.ts +++ b/src/main/webapp/wise5/authoringTool/node/advanced/constraint/node-advanced-constraint-authoring.component.ts @@ -1,9 +1,8 @@ -import { TeacherDataService } from "../../../../services/teacherDataService"; -import { TeacherProjectService } from "../../../../services/teacherProjectService"; -import { UtilService } from "../../../../services/utilService"; +import { TeacherDataService } from '../../../../services/teacherDataService'; +import { TeacherProjectService } from '../../../../services/teacherProjectService'; +import { UtilService } from '../../../../services/utilService'; class NodeAdvancedConstraintAuthoringController { - constraintActions: any[]; items: any[]; node: any; @@ -14,247 +13,248 @@ class NodeAdvancedConstraintAuthoringController { static $inject = ['$filter', '$timeout', 'ProjectService', 'TeacherDataService', 'UtilService']; - constructor(private $filter: any, - private $timeout: any, - private ProjectService: TeacherProjectService, - private TeacherDataService: TeacherDataService, - private UtilService: UtilService) { + constructor( + private $filter: any, + private $timeout: any, + private ProjectService: TeacherProjectService, + private TeacherDataService: TeacherDataService, + private UtilService: UtilService + ) { this.$translate = this.$filter('translate'); this.constraintActions = [ - { - value: '', - text: this.$translate('pleaseChooseAnAction') - }, - { - value: 'makeAllNodesAfterThisNotVisitable', - text: this.$translate('makeAllNodesAfterThisNotVisitable') - }, - { - value: 'makeAllNodesAfterThisNotVisible', - text: this.$translate('makeAllNodesAfterThisNotVisible') - }, - { - value: 'makeAllOtherNodesNotVisitable', - text: this.$translate('makeAllOtherNodesNotVisitable') - }, - { - value: 'makeAllOtherNodesNotVisible', - text: this.$translate('makeAllOtherNodesNotVisible') - }, - { - value: 'makeThisNodeNotVisitable', - text: this.$translate('makeThisNodeNotVisitable') - }, - { - value: 'makeThisNodeNotVisible', - text: this.$translate('makeThisNodeNotVisible') - } + { + value: '', + text: this.$translate('pleaseChooseAnAction') + }, + { + value: 'makeAllNodesAfterThisNotVisitable', + text: this.$translate('makeAllNodesAfterThisNotVisitable') + }, + { + value: 'makeAllNodesAfterThisNotVisible', + text: this.$translate('makeAllNodesAfterThisNotVisible') + }, + { + value: 'makeAllOtherNodesNotVisitable', + text: this.$translate('makeAllOtherNodesNotVisitable') + }, + { + value: 'makeAllOtherNodesNotVisible', + text: this.$translate('makeAllOtherNodesNotVisible') + }, + { + value: 'makeThisNodeNotVisitable', + text: this.$translate('makeThisNodeNotVisitable') + }, + { + value: 'makeThisNodeNotVisible', + text: this.$translate('makeThisNodeNotVisible') + } ]; this.removalConditionals = [ - { - value: 'all', - text: this.$translate('all') - }, - { - value: 'any', - text: this.$translate('any') - } + { + value: 'all', + text: this.$translate('all') + }, + { + value: 'any', + text: this.$translate('any') + } ]; this.removalCriteria = [ - { - value: '', - text: this.$translate('pleaseChooseARemovalCriteria') - }, - { - value: 'isCompleted', - text: this.$translate('isCompleted'), - params: [ - { - value: 'nodeId', - text: this.$translate('step') - } - ] - }, - { - value: 'score', - text: this.$translate('SCORE'), - params: [ - { - value: 'nodeId', - text: this.$translate('step') - }, - { - value: 'component', - text: this.$translate('component') - }, - { - value: 'scores', - text: this.$translate('scoresParens') - } - ] - }, - { - value: 'branchPathTaken', - text: this.$translate('branchPathTaken'), - params: [ - { - value: 'fromNodeId', - text: this.$translate('fromStep') - }, - { - value: 'toNodeId', - text: this.$translate('toStep') - } - ] - }, - { - value: 'choiceChosen', - text: this.$translate('choiceChosen'), - params: [ - { - value: 'nodeId', - text: this.$translate('step') - }, - { - value: 'componentId', - text: this.$translate('component') - }, - { - value: 'choiceIds', - text: this.$translate('choices') - } - ] - }, - { - value: 'isCorrect', - text: this.$translate('IS_CORRECT'), - params: [ - { - value: 'nodeId', - text: this.$translate('step') - }, - { - value: 'componentId', - text: this.$translate('component') - } - ] - }, - { - value: 'usedXSubmits', - text: this.$translate('usedXSubmits'), - params: [ - { - value: 'nodeId', - text: this.$translate('step') - }, - { - value: 'componentId', - text: this.$translate('component') - }, - { - value: 'requiredSubmitCount', - text: this.$translate('requiredSubmitCount') - } - ] - }, - { - value: 'isVisible', - text: this.$translate('isVisible'), - params: [ - { - value: 'nodeId', - text: this.$translate('step') - } - ] - }, - { - value: 'isVisitable', - text: this.$translate('isVisitable'), - params: [ - { - value: 'nodeId', - text: this.$translate('step') - } - ] - }, - { - value: 'isVisited', - text: this.$translate('isVisited'), - params: [ - { - value: 'nodeId', - text: this.$translate('step') - } - ] - }, - { - value: 'wroteXNumberOfWords', - text: this.$translate('wroteXNumberOfWords'), - params: [ - { - value: 'nodeId', - text: this.$translate('step') - }, - { - value: 'componentId', - text: this.$translate('component') - }, - { - value: 'requiredNumberOfWords', - text: this.$translate('requiredNumberOfWords') - } - ] - }, - { - value: 'addXNumberOfNotesOnThisStep', - text: this.$translate('addXNumberOfNotesOnThisStep'), - params: [ - { - value: 'nodeId', - text: this.$translate('step') - }, - { - value: 'requiredNumberOfNotes', - text: this.$translate('requiredNumberOfNotes') - } - ] - }, - { - value: 'fillXNumberOfRows', - text: this.$translate('fillXNumberOfRows'), - params: [ - { - value: 'nodeId', - text: this.$translate('step') - }, - { - value: 'componentId', - text: this.$translate('component') - }, - { - value: 'requiredNumberOfFilledRows', - defaultValue: null, - text: this.$translate('requiredNumberOfFilledRowsNotIncludingHeaderRow') - }, - { - value: 'tableHasHeaderRow', - defaultValue: true, - text: this.$translate('tableHasHeaderRow') - }, - { - value: 'requireAllCellsInARowToBeFilled', - defaultValue: true, - text: this.$translate('requireAllCellsInARowToBeFilled') - } - ] - }, - { - value: 'teacherRemoval', - text: this.$translate('teacherRemoval'), - params: [ - ] - } - ]; + { + value: '', + text: this.$translate('pleaseChooseARemovalCriteria') + }, + { + value: 'isCompleted', + text: this.$translate('isCompleted'), + params: [ + { + value: 'nodeId', + text: this.$translate('step') + } + ] + }, + { + value: 'score', + text: this.$translate('SCORE'), + params: [ + { + value: 'nodeId', + text: this.$translate('step') + }, + { + value: 'component', + text: this.$translate('component') + }, + { + value: 'scores', + text: this.$translate('scoresParens') + } + ] + }, + { + value: 'branchPathTaken', + text: this.$translate('branchPathTaken'), + params: [ + { + value: 'fromNodeId', + text: this.$translate('fromStep') + }, + { + value: 'toNodeId', + text: this.$translate('toStep') + } + ] + }, + { + value: 'choiceChosen', + text: this.$translate('choiceChosen'), + params: [ + { + value: 'nodeId', + text: this.$translate('step') + }, + { + value: 'componentId', + text: this.$translate('component') + }, + { + value: 'choiceIds', + text: this.$translate('choices') + } + ] + }, + { + value: 'isCorrect', + text: this.$translate('IS_CORRECT'), + params: [ + { + value: 'nodeId', + text: this.$translate('step') + }, + { + value: 'componentId', + text: this.$translate('component') + } + ] + }, + { + value: 'usedXSubmits', + text: this.$translate('usedXSubmits'), + params: [ + { + value: 'nodeId', + text: this.$translate('step') + }, + { + value: 'componentId', + text: this.$translate('component') + }, + { + value: 'requiredSubmitCount', + text: this.$translate('requiredSubmitCount') + } + ] + }, + { + value: 'isVisible', + text: this.$translate('isVisible'), + params: [ + { + value: 'nodeId', + text: this.$translate('step') + } + ] + }, + { + value: 'isVisitable', + text: this.$translate('isVisitable'), + params: [ + { + value: 'nodeId', + text: this.$translate('step') + } + ] + }, + { + value: 'isVisited', + text: this.$translate('isVisited'), + params: [ + { + value: 'nodeId', + text: this.$translate('step') + } + ] + }, + { + value: 'wroteXNumberOfWords', + text: this.$translate('wroteXNumberOfWords'), + params: [ + { + value: 'nodeId', + text: this.$translate('step') + }, + { + value: 'componentId', + text: this.$translate('component') + }, + { + value: 'requiredNumberOfWords', + text: this.$translate('requiredNumberOfWords') + } + ] + }, + { + value: 'addXNumberOfNotesOnThisStep', + text: this.$translate('addXNumberOfNotesOnThisStep'), + params: [ + { + value: 'nodeId', + text: this.$translate('step') + }, + { + value: 'requiredNumberOfNotes', + text: this.$translate('requiredNumberOfNotes') + } + ] + }, + { + value: 'fillXNumberOfRows', + text: this.$translate('fillXNumberOfRows'), + params: [ + { + value: 'nodeId', + text: this.$translate('step') + }, + { + value: 'componentId', + text: this.$translate('component') + }, + { + value: 'requiredNumberOfFilledRows', + defaultValue: null, + text: this.$translate('requiredNumberOfFilledRowsNotIncludingHeaderRow') + }, + { + value: 'tableHasHeaderRow', + defaultValue: true, + text: this.$translate('tableHasHeaderRow') + }, + { + value: 'requireAllCellsInARowToBeFilled', + defaultValue: true, + text: this.$translate('requireAllCellsInARowToBeFilled') + } + ] + }, + { + value: 'teacherRemoval', + text: this.$translate('teacherRemoval'), + params: [] + } + ]; } $onInit() { @@ -278,10 +278,12 @@ class NodeAdvancedConstraintAuthoringController { action: '', targetId: this.nodeId, removalConditional: 'any', - removalCriteria: [{ - name: '', - params: {} - }] + removalCriteria: [ + { + name: '', + params: {} + } + ] }; if (this.node.constraints == null) { this.node.constraints = []; @@ -414,4 +416,4 @@ class NodeAdvancedConstraintAuthoringController { export const NodeAdvancedConstraintAuthoringComponent = { templateUrl: `/wise5/authoringTool/node/advanced/constraint/node-advanced-constraint-authoring.component.html`, controller: NodeAdvancedConstraintAuthoringController -} +}; diff --git a/src/main/webapp/wise5/authoringTool/node/advanced/general/node-advanced-general-authoring.component.ts b/src/main/webapp/wise5/authoringTool/node/advanced/general/node-advanced-general-authoring.component.ts index e6fec69742..78e32073eb 100644 --- a/src/main/webapp/wise5/authoringTool/node/advanced/general/node-advanced-general-authoring.component.ts +++ b/src/main/webapp/wise5/authoringTool/node/advanced/general/node-advanced-general-authoring.component.ts @@ -1,17 +1,17 @@ -import { Component } from "@angular/core"; -import { TeacherDataService } from "../../../../services/teacherDataService"; -import { TeacherProjectService } from "../../../../services/teacherProjectService"; +import { Component } from '@angular/core'; +import { TeacherDataService } from '../../../../services/teacherDataService'; +import { TeacherProjectService } from '../../../../services/teacherProjectService'; @Component({ templateUrl: 'node-advanced-general-authoring.component.html' }) export class NodeAdvancedGeneralAuthoringComponent { - node: any; - constructor(private ProjectService: TeacherProjectService, - private TeacherDataService: TeacherDataService) { - } + constructor( + private ProjectService: TeacherProjectService, + private TeacherDataService: TeacherDataService + ) {} ngOnInit() { this.node = this.TeacherDataService.getCurrentNode(); diff --git a/src/main/webapp/wise5/authoringTool/node/advanced/json/node-advanced-json-authoring.component.ts b/src/main/webapp/wise5/authoringTool/node/advanced/json/node-advanced-json-authoring.component.ts index 3c0488e315..e55f28cca5 100644 --- a/src/main/webapp/wise5/authoringTool/node/advanced/json/node-advanced-json-authoring.component.ts +++ b/src/main/webapp/wise5/authoringTool/node/advanced/json/node-advanced-json-authoring.component.ts @@ -1,26 +1,26 @@ -import { TeacherDataService } from "../../../../services/teacherDataService"; -import { TeacherProjectService } from "../../../../services/teacherProjectService"; +import { TeacherDataService } from '../../../../services/teacherDataService'; +import { TeacherProjectService } from '../../../../services/teacherProjectService'; import * as angular from 'angular'; -import { NotificationService } from "../../../../services/notificationService"; -import { Component } from "@angular/core"; -import { Subject, Subscription } from "rxjs"; -import { debounceTime, distinctUntilChanged } from "rxjs/operators"; +import { NotificationService } from '../../../../services/notificationService'; +import { Component } from '@angular/core'; +import { Subject, Subscription } from 'rxjs'; +import { debounceTime, distinctUntilChanged } from 'rxjs/operators'; @Component({ templateUrl: 'node-advanced-json-authoring.component.html' }) export class NodeAdvancedJsonAuthoringComponent { - node: any; nodeContentJSONString: string; nodeContentChanged: Subject = new Subject(); nodeContentChangedSubscription: Subscription; nodeId: string; - constructor(private NotificationService: NotificationService, - private ProjectService: TeacherProjectService, - private TeacherDataService: TeacherDataService) { - } + constructor( + private NotificationService: NotificationService, + private ProjectService: TeacherProjectService, + private TeacherDataService: TeacherDataService + ) {} ngOnInit() { this.nodeId = this.TeacherDataService.getCurrentNodeId(); @@ -28,11 +28,8 @@ export class NodeAdvancedJsonAuthoringComponent { this.nodeContentJSONString = angular.toJson(this.node, 4); this.NotificationService.showJSONValidMessage(); this.nodeContentChangedSubscription = this.nodeContentChanged - .pipe( - debounceTime(1000), - distinctUntilChanged() - ) - .subscribe(newText => { + .pipe(debounceTime(1000), distinctUntilChanged()) + .subscribe((newText) => { this.nodeContentJSONString = newText; this.autoSaveJSON(); }); diff --git a/src/main/webapp/wise5/authoringTool/node/advanced/node-advanced-authoring.component.ts b/src/main/webapp/wise5/authoringTool/node/advanced/node-advanced-authoring.component.ts index 0fe01340f8..d8fec8d9e0 100644 --- a/src/main/webapp/wise5/authoringTool/node/advanced/node-advanced-authoring.component.ts +++ b/src/main/webapp/wise5/authoringTool/node/advanced/node-advanced-authoring.component.ts @@ -1,18 +1,19 @@ -import { ConfigService } from "../../../services/configService"; -import { TeacherDataService } from "../../../services/teacherDataService"; -import { TeacherProjectService } from "../../../services/teacherProjectService"; +import { ConfigService } from '../../../services/configService'; +import { TeacherDataService } from '../../../services/teacherDataService'; +import { TeacherProjectService } from '../../../services/teacherProjectService'; class NodeAdvancedAuthoringController { - node: any; nodeId: string; static $inject = ['$state', 'ConfigService', 'ProjectService', 'TeacherDataService']; - constructor(private $state: any, private ConfigService: ConfigService, - private ProjectService: TeacherProjectService, - private TeacherDataService: TeacherDataService) { - } + constructor( + private $state: any, + private ConfigService: ConfigService, + private ProjectService: TeacherProjectService, + private TeacherDataService: TeacherDataService + ) {} $onInit() { this.nodeId = this.TeacherDataService.getCurrentNodeId(); @@ -20,8 +21,10 @@ class NodeAdvancedAuthoringController { } goBack() { - this.$state.go('root.at.project.node', { projectId: this.ConfigService.getProjectId(), - nodeId: this.nodeId }); + this.$state.go('root.at.project.node', { + projectId: this.ConfigService.getProjectId(), + nodeId: this.nodeId + }); } showCreateBranchView() { @@ -41,7 +44,7 @@ class NodeAdvancedAuthoringController { } showJSONView() { - this.$state.go('root.at.project.node.advanced.json') + this.$state.go('root.at.project.node.advanced.json'); } isGroupNode(nodeId) { @@ -52,4 +55,4 @@ class NodeAdvancedAuthoringController { export const NodeAdvancedAuthoringComponent = { templateUrl: `/wise5/authoringTool/node/advanced/node-advanced-authoring.component.html`, controller: NodeAdvancedAuthoringController -} +}; diff --git a/src/main/webapp/wise5/authoringTool/node/advanced/path/node-advanced-path-authoring.component.ts b/src/main/webapp/wise5/authoringTool/node/advanced/path/node-advanced-path-authoring.component.ts index fba9234f19..b32c6dfc5c 100644 --- a/src/main/webapp/wise5/authoringTool/node/advanced/path/node-advanced-path-authoring.component.ts +++ b/src/main/webapp/wise5/authoringTool/node/advanced/path/node-advanced-path-authoring.component.ts @@ -1,8 +1,7 @@ -import { TeacherDataService } from "../../../../services/teacherDataService"; -import { TeacherProjectService } from "../../../../services/teacherProjectService"; +import { TeacherDataService } from '../../../../services/teacherDataService'; +import { TeacherProjectService } from '../../../../services/teacherProjectService'; class NodeAdvancedPathAuthoringController { - canChangePathOptions = [null, true, false]; items: any[]; node: any; @@ -13,58 +12,60 @@ class NodeAdvancedPathAuthoringController { static $inject = ['$filter', 'ProjectService', 'TeacherDataService']; - constructor(private $filter: any, - private ProjectService: TeacherProjectService, - private TeacherDataService: TeacherDataService) { + constructor( + private $filter: any, + private ProjectService: TeacherProjectService, + private TeacherDataService: TeacherDataService + ) { this.$translate = this.$filter('translate'); this.transitionCriterias = [ - { - value: 'score', - text: this.$translate('getASpecificScoreOnAComponent'), - params: [ - { - value: 'nodeId', - text: this.$translate('nodeID') - }, - { - value: 'componentId', - text: this.$translate('componentID') - }, - { - value: 'scores', - text: this.$translate('scoresParens') - } - ] - }, - { - value: 'choiceChosen', - text: this.$translate('chooseASpecificChoiceOnAComponent'), - params: [ - { - value: 'nodeId', - text: this.$translate('nodeID') - }, - { - value: 'componentId', - text: this.$translate('componentID') - }, - { - value: 'choiceIds', - text: this.$translate('choices') - } - ] - }, - { - value: 'tag', - text: this.$translate('tagAssignedToWorkgroup'), - params: [ - { - value: 'tag', - text: this.$translate('tag') - } - ] - } - ]; + { + value: 'score', + text: this.$translate('getASpecificScoreOnAComponent'), + params: [ + { + value: 'nodeId', + text: this.$translate('nodeID') + }, + { + value: 'componentId', + text: this.$translate('componentID') + }, + { + value: 'scores', + text: this.$translate('scoresParens') + } + ] + }, + { + value: 'choiceChosen', + text: this.$translate('chooseASpecificChoiceOnAComponent'), + params: [ + { + value: 'nodeId', + text: this.$translate('nodeID') + }, + { + value: 'componentId', + text: this.$translate('componentID') + }, + { + value: 'choiceIds', + text: this.$translate('choices') + } + ] + }, + { + value: 'tag', + text: this.$translate('tagAssignedToWorkgroup'), + params: [ + { + value: 'tag', + text: this.$translate('tag') + } + ] + } + ]; } $onInit() { @@ -253,4 +254,4 @@ class NodeAdvancedPathAuthoringController { export const NodeAdvancedPathAuthoringComponent = { templateUrl: `/wise5/authoringTool/node/advanced/path/node-advanced-path-authoring.component.html`, controller: NodeAdvancedPathAuthoringController -} +}; diff --git a/src/main/webapp/wise5/authoringTool/node/editRubric/edit-rubric.component.ts b/src/main/webapp/wise5/authoringTool/node/editRubric/edit-rubric.component.ts index 19177433db..28a3a2fc23 100644 --- a/src/main/webapp/wise5/authoringTool/node/editRubric/edit-rubric.component.ts +++ b/src/main/webapp/wise5/authoringTool/node/editRubric/edit-rubric.component.ts @@ -1,11 +1,10 @@ -import { ProjectAssetService } from "../../../../site/src/app/services/projectAssetService"; -import { ConfigService } from "../../../services/configService"; -import { ProjectService } from "../../../services/projectService"; -import { TeacherDataService } from "../../../services/teacherDataService"; -import { UtilService } from "../../../services/utilService"; +import { ProjectAssetService } from '../../../../site/src/app/services/projectAssetService'; +import { ConfigService } from '../../../services/configService'; +import { ProjectService } from '../../../services/projectService'; +import { TeacherDataService } from '../../../services/teacherDataService'; +import { UtilService } from '../../../services/utilService'; class EditRubricComponentController { - node: any; nodeId: string; rubric: string; @@ -19,12 +18,12 @@ class EditRubricComponentController { ]; constructor( - private $state: any, - private ConfigService: ConfigService, - private ProjectService: ProjectService, - private TeacherDataService: TeacherDataService, - private UtilService: UtilService) { - } + private $state: any, + private ConfigService: ConfigService, + private ProjectService: ProjectService, + private TeacherDataService: TeacherDataService, + private UtilService: UtilService + ) {} $onInit(): void { this.nodeId = this.TeacherDataService.getCurrentNodeId(); @@ -38,14 +37,16 @@ class EditRubricComponentController { this.node.rubric = html; this.ProjectService.saveProject(); } - + goBack(): void { - this.$state.go('root.at.project.node', { projectId: this.ConfigService.getProjectId(), - nodeId: this.nodeId }); + this.$state.go('root.at.project.node', { + projectId: this.ConfigService.getProjectId(), + nodeId: this.nodeId + }); } } export const EditRubricComponent = { templateUrl: `/wise5/authoringTool/node/editRubric/edit-rubric.component.html`, controller: EditRubricComponentController -} +}; diff --git a/src/main/webapp/wise5/authoringTool/node/editRubric/editRubricModule.ts b/src/main/webapp/wise5/authoringTool/node/editRubric/editRubricModule.ts index 9f18fc7206..62e4614b97 100644 --- a/src/main/webapp/wise5/authoringTool/node/editRubric/editRubricModule.ts +++ b/src/main/webapp/wise5/authoringTool/node/editRubric/editRubricModule.ts @@ -1,11 +1,13 @@ import * as angular from 'angular'; -import { EditRubricComponent } from "./edit-rubric.component"; +import { EditRubricComponent } from './edit-rubric.component'; -export default angular.module('editRubricModule', ['ui.router']) +export default angular + .module('editRubricModule', ['ui.router']) .component('editRubricComponent', EditRubricComponent) - .config(['$stateProvider', $stateProvider => { - $stateProvider - .state('root.at.project.node.edit-rubric', { + .config([ + '$stateProvider', + ($stateProvider) => { + $stateProvider.state('root.at.project.node.edit-rubric', { url: '/edit-rubric', component: 'editRubricComponent' }); diff --git a/src/main/webapp/wise5/authoringTool/notebook/authorNotebookController.ts b/src/main/webapp/wise5/authoringTool/notebook/authorNotebookController.ts index 140b16500f..62d8c5e76f 100644 --- a/src/main/webapp/wise5/authoringTool/notebook/authorNotebookController.ts +++ b/src/main/webapp/wise5/authoringTool/notebook/authorNotebookController.ts @@ -67,9 +67,7 @@ class AuthorNotebookController { initializeNoteAuthoring(note) { const authoringReportNote = { - html: this.UtilService.replaceWISELinks( - this.ProjectService.replaceAssetPaths(note.content) - ) + html: this.UtilService.replaceWISELinks(this.ProjectService.replaceAssetPaths(note.content)) }; this.setReportIdToAuthoringNote(note.reportId, authoringReportNote); } diff --git a/src/main/webapp/wise5/authoringTool/project/projectController.ts b/src/main/webapp/wise5/authoringTool/project/projectController.ts index de041fa62a..155ab6b988 100644 --- a/src/main/webapp/wise5/authoringTool/project/projectController.ts +++ b/src/main/webapp/wise5/authoringTool/project/projectController.ts @@ -108,7 +108,7 @@ class ProjectController { this.ProjectService.notifyAuthorProjectBegin(this.projectId); }); this.projectURL = window.location.origin + this.ConfigService.getConfigParam('projectURL'); - this.$transitions.onSuccess({}, $transition => { + this.$transitions.onSuccess({}, ($transition) => { const stateName = $transition.$to().name; if (stateName === 'root.at.project') { this.saveEvent('projectHomeViewOpened', 'Navigation'); @@ -125,10 +125,11 @@ class ProjectController { this.refreshProject(); }); - this.scrollToBottomOfPageSubscription = - this.ProjectService.scrollToBottomOfPage$.subscribe(() => { - this.scrollToBottomOfPage(); - }); + this.scrollToBottomOfPageSubscription = this.ProjectService.scrollToBottomOfPage$.subscribe( + () => { + this.scrollToBottomOfPage(); + } + ); this.saveEvent('projectOpened', 'Navigation'); @@ -143,7 +144,7 @@ class ProjectController { this.endProjectAuthoringSession(); }); - this.$window.onbeforeunload = event => { + this.$window.onbeforeunload = (event) => { this.endProjectAuthoringSession(); }; } @@ -239,7 +240,10 @@ class ProjectController { branchIconClicked(nodeId) { this.TeacherDataService.endCurrentNodeAndSetCurrentNodeByNodeId(nodeId); - this.$state.go('root.at.project.node.advanced.path', { projectId: this.projectId, nodeId: nodeId }); + this.$state.go('root.at.project.node.advanced.path', { + projectId: this.projectId, + nodeId: nodeId + }); } createGroup() { @@ -537,7 +541,7 @@ class ProjectController { const selectedNodeIds = []; angular.forEach( this.items, - function(value, key) { + function (value, key) { if (value.checked) { selectedNodeIds.push(key); } @@ -563,7 +567,7 @@ class ProjectController { const selectedItemTypes = []; angular.forEach( this.items, - function(value, key) { + function (value, key) { if (value.checked) { let node = this.ProjectService.getNodeById(key); if (node != null) { @@ -592,13 +596,13 @@ class ProjectController { } unselectAllItems() { - angular.forEach(this.items, function(value, key) { + angular.forEach(this.items, function (value, key) { value.checked = false; }); - angular.forEach(this.inactiveGroupNodes, function(value, key) { + angular.forEach(this.inactiveGroupNodes, function (value, key) { value.checked = false; }); - angular.forEach(this.inactiveStepNodes, function(value, key) { + angular.forEach(this.inactiveStepNodes, function (value, key) { value.checked = false; }); this.stepNodeSelected = false; @@ -936,7 +940,7 @@ class ProjectController { } subscribeToCurrentAuthors(projectId) { - return this.$stomp.connect(this.ConfigService.getWebSocketURL()).then(frame => { + return this.$stomp.connect(this.ConfigService.getWebSocketURL()).then((frame) => { this.$stomp.subscribe( `/topic/current-authors/${projectId}`, (authors, headers, res) => { diff --git a/src/main/webapp/wise5/authoringTool/rubric/rubric-authoring.component.ts b/src/main/webapp/wise5/authoringTool/rubric/rubric-authoring.component.ts index 4e597abed6..644da518f3 100644 --- a/src/main/webapp/wise5/authoringTool/rubric/rubric-authoring.component.ts +++ b/src/main/webapp/wise5/authoringTool/rubric/rubric-authoring.component.ts @@ -5,15 +5,17 @@ import { UpgradeModule } from '@angular/upgrade/static'; import { Component } from '@angular/core'; @Component({ - templateUrl: 'rubric-authoring.component.html', + templateUrl: 'rubric-authoring.component.html' }) export class RubricAuthoringComponent { - rubric: string = ''; - constructor(private upgrade: UpgradeModule, private ConfigService: ConfigService, - private ProjectService: TeacherProjectService, private UtilService: UtilService) { - } + constructor( + private upgrade: UpgradeModule, + private ConfigService: ConfigService, + private ProjectService: TeacherProjectService, + private UtilService: UtilService + ) {} ngOnInit(): void { this.rubric = this.ProjectService.replaceAssetPaths(this.ProjectService.getProjectRubric()); @@ -21,7 +23,8 @@ export class RubricAuthoringComponent { rubricChanged(): void { const html = this.UtilService.insertWISELinks( - this.ConfigService.removeAbsoluteAssetPaths(this.rubric)); + this.ConfigService.removeAbsoluteAssetPaths(this.rubric) + ); this.ProjectService.setProjectRubric(html); this.ProjectService.saveProject(); } diff --git a/src/main/webapp/wise5/authoringTool/structure/automatedAssessment/automatedAssessmentChooseItemController.ts b/src/main/webapp/wise5/authoringTool/structure/automatedAssessment/automatedAssessmentChooseItemController.ts index 3dbd0e5ece..9ae4fb2a86 100644 --- a/src/main/webapp/wise5/authoringTool/structure/automatedAssessment/automatedAssessmentChooseItemController.ts +++ b/src/main/webapp/wise5/authoringTool/structure/automatedAssessment/automatedAssessmentChooseItemController.ts @@ -1,8 +1,7 @@ -import ConfigureStructureController from "../configureStructureController"; +import ConfigureStructureController from '../configureStructureController'; import { TeacherProjectService } from '../../../services/teacherProjectService'; export default class AutomatedAssessmentChooseItemController extends ConfigureStructureController { - automatedAssessmentProjectId: number; items = []; project: any; @@ -10,11 +9,27 @@ export default class AutomatedAssessmentChooseItemController extends ConfigureSt projectIdToOrder: any; projectItems: any; - static $inject = ['$filter', '$http', '$rootScope', '$state', '$stateParams', '$scope', - 'UtilService', 'ProjectService']; - - constructor($filter, $http, $rootScope, $state, $stateParams, $scope, UtilService, - private ProjectService: TeacherProjectService) { + static $inject = [ + '$filter', + '$http', + '$rootScope', + '$state', + '$stateParams', + '$scope', + 'UtilService', + 'ProjectService' + ]; + + constructor( + $filter, + $http, + $rootScope, + $state, + $stateParams, + $scope, + UtilService, + private ProjectService: TeacherProjectService + ) { super($filter, $http, $rootScope, $state, $stateParams, $scope, UtilService); } @@ -24,12 +39,14 @@ export default class AutomatedAssessmentChooseItemController extends ConfigureSt } showAutomatedAssessmentProject() { - this.ProjectService.retrieveProjectById(this.automatedAssessmentProjectId).then(projectJSON => { - this.project = projectJSON; - const nodeOrderOfProject = this.ProjectService.getNodeOrderOfProject(this.project); - this.projectIdToOrder = nodeOrderOfProject.idToOrder; - this.projectItems = nodeOrderOfProject.nodes; - }); + this.ProjectService.retrieveProjectById(this.automatedAssessmentProjectId).then( + (projectJSON) => { + this.project = projectJSON; + const nodeOrderOfProject = this.ProjectService.getNodeOrderOfProject(this.project); + this.projectIdToOrder = nodeOrderOfProject.idToOrder; + this.projectItems = nodeOrderOfProject.nodes; + } + ); } previewNode(node) { diff --git a/src/main/webapp/wise5/authoringTool/structure/automatedAssessment/automatedAssessmentConfigureController.ts b/src/main/webapp/wise5/authoringTool/structure/automatedAssessment/automatedAssessmentConfigureController.ts index 1e0f828a58..7170eeea41 100644 --- a/src/main/webapp/wise5/authoringTool/structure/automatedAssessment/automatedAssessmentConfigureController.ts +++ b/src/main/webapp/wise5/authoringTool/structure/automatedAssessment/automatedAssessmentConfigureController.ts @@ -1,15 +1,30 @@ -import ConfigureStructureController from "../configureStructureController"; - -export default class AutomatedAssessmentConfigureController - extends ConfigureStructureController { +import ConfigureStructureController from '../configureStructureController'; +export default class AutomatedAssessmentConfigureController extends ConfigureStructureController { node: any; importFromProjectId: number; - static $inject = ['$filter', '$http', '$rootScope', '$state', '$stateParams', '$scope', - 'UtilService', 'ProjectService']; + static $inject = [ + '$filter', + '$http', + '$rootScope', + '$state', + '$stateParams', + '$scope', + 'UtilService', + 'ProjectService' + ]; - constructor($filter, $http, $rootScope, $state, $stateParams, $scope, UtilService, ProjectService) { + constructor( + $filter, + $http, + $rootScope, + $state, + $stateParams, + $scope, + UtilService, + ProjectService + ) { super($filter, $http, $rootScope, $state, $stateParams, $scope, UtilService); } diff --git a/src/main/webapp/wise5/authoringTool/structure/configureStructureController.ts b/src/main/webapp/wise5/authoringTool/structure/configureStructureController.ts index 31dbb9bf04..6322e783e8 100644 --- a/src/main/webapp/wise5/authoringTool/structure/configureStructureController.ts +++ b/src/main/webapp/wise5/authoringTool/structure/configureStructureController.ts @@ -9,7 +9,15 @@ abstract class ConfigureStructureController { groupsPath: string; nodesPath: string; - static $inject = ['$filter', '$http', '$rootScope', '$state', '$stateParams', '$scope', 'UtilService']; + static $inject = [ + '$filter', + '$http', + '$rootScope', + '$state', + '$stateParams', + '$scope', + 'UtilService' + ]; constructor( protected $filter: any, @@ -42,15 +50,13 @@ abstract class ConfigureStructureController { } fetchGroups(groupsPath: string = this.groupsPath) { - this.$http.get(`${this.structureDir}/${groupsPath}`) - .then(({data : group}) => { + this.$http.get(`${this.structureDir}/${groupsPath}`).then(({ data: group }) => { this.structure.group = group; }); } fetchNodes(nodesPath: string = this.nodesPath) { - this.$http.get(`${this.structureDir}/${nodesPath}`) - .then(({data : nodes}) => { + this.$http.get(`${this.structureDir}/${nodesPath}`).then(({ data: nodes }) => { this.structure.nodes = nodes; }); } diff --git a/src/main/webapp/wise5/authoringTool/structure/guidanceChoice/guidanceChoiceController.ts b/src/main/webapp/wise5/authoringTool/structure/guidanceChoice/guidanceChoiceController.ts index 91b17cf170..5d973ad4a4 100644 --- a/src/main/webapp/wise5/authoringTool/structure/guidanceChoice/guidanceChoiceController.ts +++ b/src/main/webapp/wise5/authoringTool/structure/guidanceChoice/guidanceChoiceController.ts @@ -3,8 +3,15 @@ import ConfigureStructureController from '../configureStructureController'; class GuidanceChoiceController extends ConfigureStructureController { - static $inject = ['$filter', '$http', '$rootScope', '$state', '$stateParams', '$scope', - 'UtilService']; + static $inject = [ + '$filter', + '$http', + '$rootScope', + '$state', + '$stateParams', + '$scope', + 'UtilService' + ]; constructor($filter, $http, $rootScope, $state, $stateParams, $scope, UtilService) { super($filter, $http, $rootScope, $state, $stateParams, $scope, UtilService); diff --git a/src/main/webapp/wise5/authoringTool/structure/jigsaw/jigsawController.ts b/src/main/webapp/wise5/authoringTool/structure/jigsaw/jigsawController.ts index 370fb22e16..050c3b3dbd 100644 --- a/src/main/webapp/wise5/authoringTool/structure/jigsaw/jigsawController.ts +++ b/src/main/webapp/wise5/authoringTool/structure/jigsaw/jigsawController.ts @@ -4,19 +4,29 @@ import ConfigureStructureController from '../configureStructureController'; class JigsawController extends ConfigureStructureController { numGroups: string = '2'; - static $inject = ['$filter', '$http', '$rootScope', '$state', '$stateParams', '$scope', - 'UtilService']; + static $inject = [ + '$filter', + '$http', + '$rootScope', + '$state', + '$stateParams', + '$scope', + 'UtilService' + ]; constructor($filter, $http, $rootScope, $state, $stateParams, $scope, UtilService) { super($filter, $http, $rootScope, $state, $stateParams, $scope, UtilService); } $onInit() { - this.$scope.$watch(() => { - return this.numGroups; - }, numGroups => { - this.injectGroupAndNodes(numGroups); - }); + this.$scope.$watch( + () => { + return this.numGroups; + }, + (numGroups) => { + this.injectGroupAndNodes(numGroups); + } + ); } fetchGroups(numGroups: string) { diff --git a/src/main/webapp/wise5/authoringTool/structure/kiCycleUsingOER/kiCycleUsingOERController.ts b/src/main/webapp/wise5/authoringTool/structure/kiCycleUsingOER/kiCycleUsingOERController.ts index 2e007797d5..3368b09546 100644 --- a/src/main/webapp/wise5/authoringTool/structure/kiCycleUsingOER/kiCycleUsingOERController.ts +++ b/src/main/webapp/wise5/authoringTool/structure/kiCycleUsingOER/kiCycleUsingOERController.ts @@ -3,8 +3,15 @@ import ConfigureStructureController from '../configureStructureController'; class KICycleUSINGOERController extends ConfigureStructureController { - static $inject = ['$filter', '$http', '$rootScope', '$state', '$stateParams', '$scope', - 'UtilService']; + static $inject = [ + '$filter', + '$http', + '$rootScope', + '$state', + '$stateParams', + '$scope', + 'UtilService' + ]; constructor($filter, $http, $rootScope, $state, $stateParams, $scope, UtilService) { super($filter, $http, $rootScope, $state, $stateParams, $scope, UtilService); diff --git a/src/main/webapp/wise5/authoringTool/structure/peerReviewAndRevision/peerReviewAndRevisionController.ts b/src/main/webapp/wise5/authoringTool/structure/peerReviewAndRevision/peerReviewAndRevisionController.ts index 683d05ee5e..61c1076a67 100644 --- a/src/main/webapp/wise5/authoringTool/structure/peerReviewAndRevision/peerReviewAndRevisionController.ts +++ b/src/main/webapp/wise5/authoringTool/structure/peerReviewAndRevision/peerReviewAndRevisionController.ts @@ -3,8 +3,15 @@ import ConfigureStructureController from '../configureStructureController'; class PeerReviewAndRevisionController extends ConfigureStructureController { - static $inject = ['$filter', '$http', '$rootScope', '$state', '$stateParams', '$scope', - 'UtilService']; + static $inject = [ + '$filter', + '$http', + '$rootScope', + '$state', + '$stateParams', + '$scope', + 'UtilService' + ]; constructor($filter, $http, $rootScope, $state, $stateParams, $scope, UtilService) { super($filter, $http, $rootScope, $state, $stateParams, $scope, UtilService); diff --git a/src/main/webapp/wise5/authoringTool/structure/selfDirectedInvestigation/selfDirectedInvestigationController.ts b/src/main/webapp/wise5/authoringTool/structure/selfDirectedInvestigation/selfDirectedInvestigationController.ts index 24cbf47eaf..01ad99a297 100644 --- a/src/main/webapp/wise5/authoringTool/structure/selfDirectedInvestigation/selfDirectedInvestigationController.ts +++ b/src/main/webapp/wise5/authoringTool/structure/selfDirectedInvestigation/selfDirectedInvestigationController.ts @@ -3,8 +3,15 @@ import ConfigureStructureController from '../configureStructureController'; class SelfDirectedInvestigationController extends ConfigureStructureController { - static $inject = ['$filter', '$http', '$rootScope', '$state', '$stateParams', '$scope', - 'UtilService']; + static $inject = [ + '$filter', + '$http', + '$rootScope', + '$state', + '$stateParams', + '$scope', + 'UtilService' + ]; constructor($filter, $http, $rootScope, $state, $stateParams, $scope, UtilService) { super($filter, $http, $rootScope, $state, $stateParams, $scope, UtilService); diff --git a/src/main/webapp/wise5/authoringTool/structure/structureAuthoringModule.ts b/src/main/webapp/wise5/authoringTool/structure/structureAuthoringModule.ts index 4e3b3a3934..0c6d0e8968 100644 --- a/src/main/webapp/wise5/authoringTool/structure/structureAuthoringModule.ts +++ b/src/main/webapp/wise5/authoringTool/structure/structureAuthoringModule.ts @@ -26,7 +26,7 @@ const structureAuthoringModule = angular .controller('KICycleUsingOERController', KICycleUSINGOERController) .config([ '$stateProvider', - $stateProvider => { + ($stateProvider) => { $stateProvider .state('root.at.project.structure', { url: '/structure', diff --git a/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/manageStudents/manageStudentsModule.ts b/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/manageStudents/manageStudentsModule.ts index 21a77670e0..beca94151e 100644 --- a/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/manageStudents/manageStudentsModule.ts +++ b/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/manageStudents/manageStudentsModule.ts @@ -1,9 +1,10 @@ - import * as angular from 'angular'; import { ManageStudentsComponent } from '../../manageStudents/manage-students-component'; import { downgradeComponent } from '@angular/upgrade/static'; angular .module('manageStudents', []) - .directive('manageStudentsComponent', - downgradeComponent({ component: ManageStudentsComponent}) as angular.IDirectiveFactory); + .directive( + 'manageStudentsComponent', + downgradeComponent({ component: ManageStudentsComponent }) as angular.IDirectiveFactory + ); diff --git a/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/milestones/milestoneDetails/milestoneDetails.ts b/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/milestones/milestoneDetails/milestoneDetails.ts index a041090e4b..6442fff5f0 100644 --- a/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/milestones/milestoneDetails/milestoneDetails.ts +++ b/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/milestones/milestoneDetails/milestoneDetails.ts @@ -35,11 +35,12 @@ class MilestoneDetailsController { ) { this.$translate = $filter('translate'); this.periodId = this.TeacherDataService.getCurrentPeriod().periodId; - this.currentPeriodChangedSubscription = this.TeacherDataService.currentPeriodChanged$ - .subscribe(({ currentPeriod }) => { - this.periodId = currentPeriod.periodId; - this.saveMilestoneCurrentPeriodSelectedEvent(currentPeriod); - }); + this.currentPeriodChangedSubscription = this.TeacherDataService.currentPeriodChanged$.subscribe( + ({ currentPeriod }) => { + this.periodId = currentPeriod.periodId; + this.saveMilestoneCurrentPeriodSelectedEvent(currentPeriod); + } + ); this.$scope.$on('$destroy', () => { this.ngOnDestroy(); }); diff --git a/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/milestones/milestoneEdit/milestoneEdit.ts b/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/milestones/milestoneEdit/milestoneEdit.ts index 649b7ae430..ebd75271f4 100644 --- a/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/milestones/milestoneEdit/milestoneEdit.ts +++ b/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/milestones/milestoneEdit/milestoneEdit.ts @@ -47,11 +47,7 @@ class MilestoneDetailsController { createMilestone() { const projectAchievements = this.ProjectService.getAchievementItems(); if (projectAchievements != null) { - let tomorrow = this.moment() - .add('days', 1) - .hours(23) - .minutes(11) - .seconds(59); + let tomorrow = this.moment().add('days', 1).hours(23).minutes(11).seconds(59); this.milestone = { id: this.AchievementService.getAvailableAchievementId(), name: '', @@ -117,7 +113,7 @@ class MilestoneDetailsController { } change() { - let itemsArray = Object.keys(this.milestone.items).map(it => this.milestone.items[it]); + let itemsArray = Object.keys(this.milestone.items).map((it) => this.milestone.items[it]); let valid = this.$scope.milestoneEditForm.$valid && this.$filter('filter')(itemsArray, { checked: true }).length > 0; diff --git a/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/nodeGrading/nodeGrading.ts b/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/nodeGrading/nodeGrading.ts index 086d347ff2..141fec269f 100644 --- a/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/nodeGrading/nodeGrading.ts +++ b/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/nodeGrading/nodeGrading.ts @@ -11,11 +11,15 @@ import { downgradeComponent } from '@angular/upgrade/static'; const NodeGrading = angular .module('nodeGrading', []) .component('nodeGradingView', NodeGradingView) - .directive('componentSelect', - downgradeComponent({ component: ComponentSelectComponent }) as angular.IDirectiveFactory) + .directive( + 'componentSelect', + downgradeComponent({ component: ComponentSelectComponent }) as angular.IDirectiveFactory + ) .component('cmStepTools', StepTools) - .directive('workgroupInfo', - downgradeComponent({ component: WorkgroupInfoComponent }) as angular.IDirectiveFactory) + .directive( + 'workgroupInfo', + downgradeComponent({ component: WorkgroupInfoComponent }) as angular.IDirectiveFactory + ) .component('workgroupItem', WorkgroupItem); export default NodeGrading; diff --git a/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/nodeGrading/nodeGradingView/nodeGradingView.ts b/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/nodeGrading/nodeGradingView/nodeGradingView.ts index 2dd62b20db..d1d16a7073 100644 --- a/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/nodeGrading/nodeGradingView/nodeGradingView.ts +++ b/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/nodeGrading/nodeGradingView/nodeGradingView.ts @@ -27,11 +27,11 @@ class NodeGradingViewController { nodeId: string; numRubrics: number; sortOrder: object = { - 'team': ['-isVisible', 'workgroupId'], + team: ['-isVisible', 'workgroupId'], '-team': ['-isVisible', '-workgroupId'], - 'status': ['-isVisible', 'completionStatus', 'workgroupId'], + status: ['-isVisible', 'completionStatus', 'workgroupId'], '-status': ['-isVisible', '-completionStatus', 'workgroupId'], - 'score': ['-isVisible', 'score', 'workgroupId'], + score: ['-isVisible', 'score', 'workgroupId'], '-score': ['-isVisible', '-score', 'workgroupId'] }; sort: any; @@ -87,7 +87,7 @@ class NodeGradingViewController { } // TODO: add loading indicator - this.TeacherDataService.retrieveStudentDataByNodeId(this.nodeId).then(result => { + this.TeacherDataService.retrieveStudentDataByNodeId(this.nodeId).then((result) => { this.teacherWorkgroupId = this.ConfigService.getWorkgroupId(); this.workgroups = this.ConfigService.getClassmateUserInfos(); this.workgroupsById = {}; // object that will hold workgroup names, statuses, scores, notifications, etc. @@ -106,44 +106,48 @@ class NodeGradingViewController { this.maxScore = this.getMaxScore(); }); - this.notificationChangedSubscription = this.NotificationService.notificationChanged$ - .subscribe((notification) => { - if (notification.type === 'CRaterResult') { - // TODO: expand to encompass other notification types that should be shown to teacher - const workgroupId = notification.toWorkgroupId; - if (this.workgroupsById[workgroupId]) { - this.updateWorkgroup(workgroupId); + this.notificationChangedSubscription = this.NotificationService.notificationChanged$.subscribe( + (notification) => { + if (notification.type === 'CRaterResult') { + // TODO: expand to encompass other notification types that should be shown to teacher + const workgroupId = notification.toWorkgroupId; + if (this.workgroupsById[workgroupId]) { + this.updateWorkgroup(workgroupId); + } } } - }); - - this.annotationReceivedSubscription = - this.AnnotationService.annotationReceived$.subscribe(({ annotation }) => { - const workgroupId = annotation.toWorkgroupId; - const nodeId = annotation.nodeId; - if (nodeId === this.nodeId && this.workgroupsById[workgroupId]) { - this.updateWorkgroup(workgroupId); - } - }); + ); - this.studentWorkReceivedSubscription = this.TeacherDataService.studentWorkReceived$ - .subscribe((args: any) => { - const studentWork = args.studentWork; - if (studentWork != null) { - const workgroupId = studentWork.workgroupId; - const nodeId = studentWork.nodeId; + this.annotationReceivedSubscription = this.AnnotationService.annotationReceived$.subscribe( + ({ annotation }) => { + const workgroupId = annotation.toWorkgroupId; + const nodeId = annotation.nodeId; if (nodeId === this.nodeId && this.workgroupsById[workgroupId]) { this.updateWorkgroup(workgroupId); } } - }); + ); - this.currentPeriodChangedSubscription = this.TeacherDataService.currentPeriodChanged$ - .subscribe(() => { - if (!this.milestone) { - this.milestoneReport = this.MilestoneService.getMilestoneReportByNodeId(this.nodeId); + this.studentWorkReceivedSubscription = this.TeacherDataService.studentWorkReceived$.subscribe( + (args: any) => { + const studentWork = args.studentWork; + if (studentWork != null) { + const workgroupId = studentWork.workgroupId; + const nodeId = studentWork.nodeId; + if (nodeId === this.nodeId && this.workgroupsById[workgroupId]) { + this.updateWorkgroup(workgroupId); + } + } } - }); + ); + + this.currentPeriodChangedSubscription = this.TeacherDataService.currentPeriodChanged$.subscribe( + () => { + if (!this.milestone) { + this.milestoneReport = this.MilestoneService.getMilestoneReportByNodeId(this.nodeId); + } + } + ); if (!this.isDisplayInMilestone()) { this.saveNodeGradingViewDisplayedEvent(); diff --git a/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/nodeGrading/stepTools/stepTools.ts b/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/nodeGrading/stepTools/stepTools.ts index feb569f5d0..a8a857744c 100644 --- a/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/nodeGrading/stepTools/stepTools.ts +++ b/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/nodeGrading/stepTools/stepTools.ts @@ -32,10 +32,11 @@ class StepToolsController { this.idToOrder = this.ProjectService.idToOrder; this.updateModel(); - this.currentNodeChangedSubscription = this.TeacherDataService.currentNodeChanged$ - .subscribe(() => { - this.updateModel(); - }); + this.currentNodeChangedSubscription = this.TeacherDataService.currentNodeChanged$.subscribe( + () => { + this.updateModel(); + } + ); this.$scope.$on('$destroy', () => { this.ngOnDestroy(); }); @@ -59,7 +60,7 @@ class StepToolsController { this.nodeId = nodeId; this.prevId = this.NodeService.getPrevNodeIdWithWork(); this.nextId = null; - this.NodeService.getNextNodeIdWithWork().then(nextNodeId => { + this.NodeService.getNextNodeIdWithWork().then((nextNodeId) => { this.nextId = nextNodeId; }); this.toNodeId = this.nodeId; diff --git a/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/nodeGrading/workgroupInfo/workgroup-info.component.ts b/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/nodeGrading/workgroupInfo/workgroup-info.component.ts index 3f58a71451..690a7cedfa 100644 --- a/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/nodeGrading/workgroupInfo/workgroup-info.component.ts +++ b/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/nodeGrading/workgroupInfo/workgroup-info.component.ts @@ -8,7 +8,6 @@ import { ConfigService } from '../../../../services/configService'; templateUrl: 'workgroup-info.component.html' }) export class WorkgroupInfoComponent { - alertIconClass: string; alertIconName: string; alertLabel: string; diff --git a/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/nodeProgress/navItem/navItem.ts b/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/nodeProgress/navItem/navItem.ts index 666daf16a6..316dd83c08 100644 --- a/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/nodeProgress/navItem/navItem.ts +++ b/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/nodeProgress/navItem/navItem.ts @@ -30,7 +30,7 @@ class NavItemController { isGroup: boolean; item: any; maxScore: number; - newAlert: boolean = false;; + newAlert: boolean = false; nodeHasWork: boolean; nodeId: string; nodeTitle: string; @@ -177,24 +177,26 @@ class NavItemController { () => { return this.expanded; }, - value => { + (value) => { this.$scope.$parent.itemExpanded = value; } ); - this.studentStatusReceivedSubscription = - this.StudentStatusService.studentStatusReceived$.subscribe(() => { - this.setWorkgroupsOnNodeData(); - this.setCurrentNodeStatus(); - this.getAlertNotifications(); - }); + this.studentStatusReceivedSubscription = this.StudentStatusService.studentStatusReceived$.subscribe( + () => { + this.setWorkgroupsOnNodeData(); + this.setCurrentNodeStatus(); + this.getAlertNotifications(); + } + ); - this.currentPeriodChangedSubscription = this.TeacherDataService.currentPeriodChanged$ - .subscribe(({ currentPeriod }) => { - this.currentPeriod = currentPeriod; - this.setWorkgroupsOnNodeData(); - this.getAlertNotifications(); - }); + this.currentPeriodChangedSubscription = this.TeacherDataService.currentPeriodChanged$.subscribe( + ({ currentPeriod }) => { + this.currentPeriod = currentPeriod; + this.setWorkgroupsOnNodeData(); + this.getAlertNotifications(); + } + ); this.$scope.$on('$destroy', () => { this.ngOnDestroy(); @@ -249,9 +251,11 @@ class NavItemController { if (constraints == null) { return false; } else { - return (this.isShowingAllPeriods() && this.isLockedForAll(constraints)) || - (!this.isShowingAllPeriods() && this.isLockedForPeriod(constraints, - this.TeacherDataService.getCurrentPeriod().periodId)); + return ( + (this.isShowingAllPeriods() && this.isLockedForAll(constraints)) || + (!this.isShowingAllPeriods() && + this.isLockedForPeriod(constraints, this.TeacherDataService.getCurrentPeriod().periodId)) + ); } } @@ -266,9 +270,11 @@ class NavItemController { isLockedForPeriod(constraints: any, periodId: number): boolean { for (const constraint of constraints) { - if (constraint.action === 'makeThisNodeNotVisitable' && - constraint.targetId === this.nodeId && - constraint.removalCriteria[0].params.periodId === periodId) { + if ( + constraint.action === 'makeThisNodeNotVisitable' && + constraint.targetId === this.nodeId && + constraint.removalCriteria[0].params.periodId === periodId + ) { return true; } } @@ -292,23 +298,27 @@ class NavItemController { showToggleLockNodeConfirmation(isLocked: boolean) { let message = ''; if (isLocked) { - message = this.$translate('lockNodeConfirmation', { nodeTitle: this.nodeTitle, - periodName: this.getPeriodLabel() }); + message = this.$translate('lockNodeConfirmation', { + nodeTitle: this.nodeTitle, + periodName: this.getPeriodLabel() + }); } else { - message = this.$translate('unlockNodeConfirmation', { nodeTitle: this.nodeTitle, - periodName: this.getPeriodLabel() }); + message = this.$translate('unlockNodeConfirmation', { + nodeTitle: this.nodeTitle, + periodName: this.getPeriodLabel() + }); } - this.$mdToast.show( - this.$mdToast.simple() - .textContent(message) - .hideDelay(5000)); + this.$mdToast.show(this.$mdToast.simple().textContent(message).hideDelay(5000)); } unlockNode(node: any) { if (this.isShowingAllPeriods()) { this.unlockNodeForAllPeriods(node); } else { - this.ProjectService.removeTeacherRemovalConstraint(node, this.TeacherDataService.getCurrentPeriod().periodId); + this.ProjectService.removeTeacherRemovalConstraint( + node, + this.TeacherDataService.getCurrentPeriod().periodId + ); } } @@ -316,7 +326,10 @@ class NavItemController { if (this.isShowingAllPeriods()) { this.lockNodeForAllPeriods(node); } else { - this.ProjectService.addTeacherRemovalConstraint(node, this.TeacherDataService.getCurrentPeriod().periodId); + this.ProjectService.addTeacherRemovalConstraint( + node, + this.TeacherDataService.getCurrentPeriod().periodId + ); } } @@ -428,7 +441,7 @@ class NavItemController { this.workgroupsOnNodeData.push({ workgroupId: id, usernames: usernames, - avatarColor: avatarColor, + avatarColor: avatarColor }); } } @@ -463,8 +476,9 @@ class NavItemController { } getPeriodLabel() { - return this.isShowingAllPeriods() ? this.$translate('allPeriods') : - this.$translate('periodLabel', { name: this.currentPeriod.periodName }); + return this.isShowingAllPeriods() + ? this.$translate('allPeriods') + : this.$translate('periodLabel', { name: this.currentPeriod.periodName }); } getNodeLockedText(): string { diff --git a/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/nodeProgress/navItemScore/nav-item-score.component.ts b/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/nodeProgress/navItemScore/nav-item-score.component.ts index 7005cb95d5..ca62948bab 100644 --- a/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/nodeProgress/navItemScore/nav-item-score.component.ts +++ b/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/nodeProgress/navItemScore/nav-item-score.component.ts @@ -1,6 +1,6 @@ 'use strict'; -import { Component, Inject, Input, LOCALE_ID } from "@angular/core"; +import { Component, Inject, Input, LOCALE_ID } from '@angular/core'; import { formatNumber } from '@angular/common'; @Component({ @@ -8,7 +8,6 @@ import { formatNumber } from '@angular/common'; templateUrl: 'nav-item-score.component.html' }) export class NavItemScoreComponent { - @Input() averageScore: any; diff --git a/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/nodeProgress/nodeProgress.ts b/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/nodeProgress/nodeProgress.ts index ad47a1e6b7..69556f6b20 100644 --- a/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/nodeProgress/nodeProgress.ts +++ b/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/nodeProgress/nodeProgress.ts @@ -13,10 +13,14 @@ const NodeProgress = angular .module('nodeProgress', []) .component('nodeProgressView', NodeProgressView) .component('navItem', NavItem) - .directive('navItemProgress', - downgradeComponent({ component: NavItemProgressComponent }) as angular.IDirectiveFactory) - .directive('navItemScore', - downgradeComponent({ component: NavItemScoreComponent }) as angular.IDirectiveFactory) + .directive( + 'navItemProgress', + downgradeComponent({ component: NavItemProgressComponent }) as angular.IDirectiveFactory + ) + .directive( + 'navItemScore', + downgradeComponent({ component: NavItemScoreComponent }) as angular.IDirectiveFactory + ) .component('workgroupsOnNode', WorkgroupsOnNode) .component('workgroupProgress', WorkgroupProgress); diff --git a/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/nodeProgress/nodeProgressView/nodeProgressView.ts b/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/nodeProgress/nodeProgressView/nodeProgressView.ts index a6bbe20949..98cfd49f14 100644 --- a/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/nodeProgress/nodeProgressView/nodeProgressView.ts +++ b/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/nodeProgress/nodeProgressView/nodeProgressView.ts @@ -93,24 +93,26 @@ class NodeProgressViewController { this.showRubricButton = true; } - this.currentNodeChangedSubscription = this.TeacherDataService.currentNodeChanged$ - .subscribe(({ currentNode }) => { - this.nodeId = currentNode.id; - this.TeacherDataService.setCurrentNode(currentNode); - if (this.isGroupNode(this.nodeId)) { - this.currentGroup = currentNode; - this.currentGroupId = this.currentGroup.id; - this.$scope.currentgroupid = this.currentGroupId; + this.currentNodeChangedSubscription = this.TeacherDataService.currentNodeChanged$.subscribe( + ({ currentNode }) => { + this.nodeId = currentNode.id; + this.TeacherDataService.setCurrentNode(currentNode); + if (this.isGroupNode(this.nodeId)) { + this.currentGroup = currentNode; + this.currentGroupId = this.currentGroup.id; + this.$scope.currentgroupid = this.currentGroupId; + } + this.$state.go('root.cm.unit.node', { nodeId: this.nodeId }); } - this.$state.go('root.cm.unit.node', { nodeId: this.nodeId }); - }); + ); - this.currentWorkgroupChangedSubscription = - this.TeacherDataService.currentWorkgroupChanged$.subscribe(({ currentWorkgroup }) => { - this.currentWorkgroup = currentWorkgroup; - }); + this.currentWorkgroupChangedSubscription = this.TeacherDataService.currentWorkgroupChanged$.subscribe( + ({ currentWorkgroup }) => { + this.currentWorkgroup = currentWorkgroup; + } + ); - this.$transitions.onSuccess({}, $transition => { + this.$transitions.onSuccess({}, ($transition) => { const toNodeId = $transition.params('to').nodeId; const fromNodeId = $transition.params('from').nodeId; if (toNodeId && fromNodeId && toNodeId !== fromNodeId) { @@ -233,7 +235,7 @@ class NodeProgressViewController { '$scope', '$mdDialog', function DialogController($scope, $mdDialog) { - $scope.openInNewWindow = function() { + $scope.openInNewWindow = function () { let w = window.open('', '_blank'); w.document.write(windowString); $mdDialog.hide(); diff --git a/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/notebook/notebook.ts b/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/notebook/notebook.ts index 57c1f2a658..5f9786610f 100644 --- a/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/notebook/notebook.ts +++ b/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/notebook/notebook.ts @@ -7,6 +7,6 @@ import * as angular from 'angular'; const Notebook = angular .module('notebook', []) .component('notebookItemGrading', NotebookItemGrading) - .component('notebookWorkgroupGrading', NotebookWorkgroupGrading) + .component('notebookWorkgroupGrading', NotebookWorkgroupGrading); export default Notebook; diff --git a/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/notebook/notebookItemGrading/notebookItemGrading.ts b/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/notebook/notebookItemGrading/notebookItemGrading.ts index 856e300834..4442248822 100644 --- a/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/notebook/notebookItemGrading/notebookItemGrading.ts +++ b/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/notebook/notebookItemGrading/notebookItemGrading.ts @@ -28,7 +28,7 @@ class NotebookItemGradingController { score: any; toWorkgroupId: number; usernames: string; - annotationSavedToServerSubscription: any; + annotationSavedToServerSubscription: any; static $inject = [ '$scope', @@ -46,16 +46,17 @@ class NotebookItemGradingController { private TeacherDataService: TeacherDataService, private UtilService: UtilService ) { - this.annotationSavedToServerSubscription = - this.AnnotationService.annotationSavedToServer$.subscribe(({ annotation }) => { - // TODO: we're watching this here and in the parent component's controller; probably want to optimize! - const annotationNodeId = annotation.nodeId; - const annotationComponentId = annotation.componentId; - if (this.nodeId === annotationNodeId && this.componentId === annotationComponentId) { - this.processAnnotations(); + this.annotationSavedToServerSubscription = this.AnnotationService.annotationSavedToServer$.subscribe( + ({ annotation }) => { + // TODO: we're watching this here and in the parent component's controller; probably want to optimize! + const annotationNodeId = annotation.nodeId; + const annotationComponentId = annotation.componentId; + if (this.nodeId === annotationNodeId && this.componentId === annotationComponentId) { + this.processAnnotations(); + } } - }); - + ); + this.$scope.$on('$destroy', () => { this.ngOnDestroy(); }); @@ -98,7 +99,7 @@ class NotebookItemGradingController { // get the workgroup user names let usernamesArray = this.ConfigService.getUsernamesByWorkgroupId(this.toWorkgroupId); this.usernames = usernamesArray - .map(obj => { + .map((obj) => { return obj.name; }) .join(', '); @@ -259,7 +260,8 @@ const NotebookItemGrading = { maxScore: '<', notebookItem: '<' }, - templateUrl: 'wise5/classroomMonitor/classroomMonitorComponents/notebook/notebookItemGrading/notebookItemGrading.html', + templateUrl: + 'wise5/classroomMonitor/classroomMonitorComponents/notebook/notebookItemGrading/notebookItemGrading.html', controller: NotebookItemGradingController }; diff --git a/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/notebook/notebookWorkgroupGrading/notebookWorkgroupGrading.ts b/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/notebook/notebookWorkgroupGrading/notebookWorkgroupGrading.ts index d9f0dac4bf..c9c2d8dc4f 100644 --- a/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/notebook/notebookWorkgroupGrading/notebookWorkgroupGrading.ts +++ b/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/notebook/notebookWorkgroupGrading/notebookWorkgroupGrading.ts @@ -16,13 +16,15 @@ class NotebookWorkgroupGradingController { static $inject = ['NotebookService', 'ProjectService']; - constructor(private NotebookService: NotebookService, - private ProjectService: TeacherProjectService) {} + constructor( + private NotebookService: NotebookService, + private ProjectService: TeacherProjectService + ) {} $onInit() { this.themePath = this.ProjectService.getThemePath(); if (this.reportEnabled) { - const reportId = this.notebookConfig.itemTypes.report.notes[0].reportId + const reportId = this.notebookConfig.itemTypes.report.notes[0].reportId; this.maxScore = this.NotebookService.getMaxScoreByReportId(reportId); } this.notebook = this.NotebookService.getNotebookByWorkgroup(this.workgroup.workgroupId); @@ -38,7 +40,9 @@ class NotebookWorkgroupGradingController { } getNumActiveNotes() { - return this.workgroup.notes.filter(note => { return note.serverDeleteTime == null}).length; + return this.workgroup.notes.filter((note) => { + return note.serverDeleteTime == null; + }).length; } } @@ -52,7 +56,8 @@ const NotebookWorkgroupGrading = { reportTitle: '@', onUpdateExpand: '&' }, - templateUrl: 'wise5/classroomMonitor/classroomMonitorComponents/notebook/notebookWorkgroupGrading/notebookWorkgroupGrading.html', + templateUrl: + 'wise5/classroomMonitor/classroomMonitorComponents/notebook/notebookWorkgroupGrading/notebookWorkgroupGrading.html', controller: NotebookWorkgroupGradingController, controllerAs: '$ctrl' }; diff --git a/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/shared/componentGrading/componentGrading.ts b/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/shared/componentGrading/componentGrading.ts index 4d82f29c7b..e7332b11c0 100644 --- a/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/shared/componentGrading/componentGrading.ts +++ b/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/shared/componentGrading/componentGrading.ts @@ -28,7 +28,7 @@ class ComponentGradingController { runId: number; score: number; showAllAnnotations: boolean; - toWorkgroupId: number + toWorkgroupId: number; annotationSavedToServerSubscription: any; static $inject = [ @@ -60,15 +60,16 @@ class ComponentGradingController { this.UtilService = UtilService; this.$translate = $filter('translate'); - this.annotationSavedToServerSubscription = - this.AnnotationService.annotationSavedToServer$.subscribe(({ annotation }) => { - // TODO: we're watching this here and in the parent component's controller; probably want to optimize! - const annotationNodeId = annotation.nodeId; - const annotationComponentId = annotation.componentId; - if (this.nodeId === annotationNodeId && this.componentId === annotationComponentId) { - this.processAnnotations(); + this.annotationSavedToServerSubscription = this.AnnotationService.annotationSavedToServer$.subscribe( + ({ annotation }) => { + // TODO: we're watching this here and in the parent component's controller; probably want to optimize! + const annotationNodeId = annotation.nodeId; + const annotationComponentId = annotation.componentId; + if (this.nodeId === annotationNodeId && this.componentId === annotationComponentId) { + this.processAnnotations(); + } } - }); + ); this.$scope.$on('projectSaved', (event, args) => { this.maxScore = this.ProjectService.getMaxScoreForComponent(this.nodeId, this.componentId); @@ -296,7 +297,7 @@ class ComponentGradingController { data, clientSaveTime ); - this.AnnotationService.saveAnnotation(annotation).then(result => {}); + this.AnnotationService.saveAnnotation(annotation).then((result) => {}); } } } diff --git a/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/shared/nodeIcon/node-icon.component.ts b/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/shared/nodeIcon/node-icon.component.ts index 2f447c4d5e..550e80f838 100644 --- a/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/shared/nodeIcon/node-icon.component.ts +++ b/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/shared/nodeIcon/node-icon.component.ts @@ -9,7 +9,6 @@ import { Component, Input } from '@angular/core'; styleUrls: ['node-icon.component.scss'] }) export class NodeIconComponent { - @Input() customClass: string; diff --git a/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/shared/notificationsMenu/notificationsMenu.ts b/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/shared/notificationsMenu/notificationsMenu.ts index 2fd3e24060..783a21ef84 100644 --- a/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/shared/notificationsMenu/notificationsMenu.ts +++ b/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/shared/notificationsMenu/notificationsMenu.ts @@ -38,7 +38,7 @@ class NotificationsMenuController { } dismissAllNotifications() { - this.newNotifications.map(newNotification => { + this.newNotifications.map((newNotification) => { this.dismissNotification(newNotification); }); } diff --git a/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/shared/periodSelect/periodSelect.ts b/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/shared/periodSelect/periodSelect.ts index 3b505f10d4..e09c53d642 100644 --- a/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/shared/periodSelect/periodSelect.ts +++ b/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/shared/periodSelect/periodSelect.ts @@ -38,10 +38,11 @@ class PeriodSelectController { this.currentPeriod = null; this.periods = []; this.initializePeriods(); - this.currentPeriodChangedSubscription = this.TeacherDataService.currentPeriodChanged$ - .subscribe(({ currentPeriod }) => { - this.currentPeriod = currentPeriod; - }); + this.currentPeriodChangedSubscription = this.TeacherDataService.currentPeriodChanged$.subscribe( + ({ currentPeriod }) => { + this.currentPeriod = currentPeriod; + } + ); this.$scope.$on('$destroy', () => { this.ngOnDestroy(); }); diff --git a/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/shared/shared.ts b/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/shared/shared.ts index 81260aeb3c..2101005d12 100644 --- a/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/shared/shared.ts +++ b/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/shared/shared.ts @@ -23,30 +23,46 @@ import { downgradeComponent } from '@angular/upgrade/static'; const Shared = angular .module('cmShared', []) - .directive('alertStatusCorner', - downgradeComponent({ component: AlertStatusCornerComponent }) as angular.IDirectiveFactory) + .directive( + 'alertStatusCorner', + downgradeComponent({ component: AlertStatusCornerComponent }) as angular.IDirectiveFactory + ) .component('componentGrading', ComponentGrading) - .directive('componentNewWorkBadge', - downgradeComponent({ component: ComponentNewWorkBadgeComponent }) as angular.IDirectiveFactory) + .directive( + 'componentNewWorkBadge', + downgradeComponent({ component: ComponentNewWorkBadgeComponent }) as angular.IDirectiveFactory + ) .component('componentRevisionsInfo', ComponentRevisionsInfo) .component('cmMainMenu', MainMenu) .component('notificationsMenu', NotificationsMenu) .component('nodeInfo', NodeInfo) .component('pauseScreensMenu', PauseScreensMenu) .component('periodSelect', PeriodSelect) - .directive('statusIcon', - downgradeComponent({ component: StatusIconComponent }) as angular.IDirectiveFactory) + .directive( + 'statusIcon', + downgradeComponent({ component: StatusIconComponent }) as angular.IDirectiveFactory + ) .component('cmToolbar', Toolbar) .component('cmTopBar', TopBar) .component('workgroupComponentRevisions', WorkgroupComponentRevisions) .component('workgroupNodeGrading', WorkgroupNodeGrading) - .directive('workgroupNodeScore', - downgradeComponent({ component: WorkgroupNodeScoreComponent }) as angular.IDirectiveFactory) - .directive('workgroupNodeStatus', - downgradeComponent({ component: WorkgroupNodeStatusComponent }) as angular.IDirectiveFactory) - .directive('workgroupSelectAutocomplete', - downgradeComponent({ component: WorkgroupSelectAutocompleteComponent }) as angular.IDirectiveFactory) - .directive('workgroupSelectDropdown', - downgradeComponent({ component: WorkgroupSelectDropdownComponent }) as angular.IDirectiveFactory) + .directive( + 'workgroupNodeScore', + downgradeComponent({ component: WorkgroupNodeScoreComponent }) as angular.IDirectiveFactory + ) + .directive( + 'workgroupNodeStatus', + downgradeComponent({ component: WorkgroupNodeStatusComponent }) as angular.IDirectiveFactory + ) + .directive( + 'workgroupSelectAutocomplete', + downgradeComponent({ + component: WorkgroupSelectAutocompleteComponent + }) as angular.IDirectiveFactory + ) + .directive( + 'workgroupSelectDropdown', + downgradeComponent({ component: WorkgroupSelectDropdownComponent }) as angular.IDirectiveFactory + ); export default Shared; diff --git a/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/shared/topBar/topBar.ts b/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/shared/topBar/topBar.ts index 330137467f..eced33e332 100644 --- a/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/shared/topBar/topBar.ts +++ b/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/shared/topBar/topBar.ts @@ -55,10 +55,11 @@ class TopBarController { } this.avatarColor = this.ConfigService.getAvatarColorForWorkgroupId(this.workgroupId); this.userInfo = this.ConfigService.getMyUserInfo(); - this.notificationChangedSubscription = this.NotificationService.notificationChanged$ - .subscribe(() => { - this.setNotifications(); - }); + this.notificationChangedSubscription = this.NotificationService.notificationChanged$.subscribe( + () => { + this.setNotifications(); + } + ); this.themePath = this.ProjectService.getThemePath(); this.contextPath = this.ConfigService.getContextPath(); @@ -100,15 +101,15 @@ class TopBarController { setNotifications() { // get all notifications for the logged in teacher // TODO: take into account shared teacher users! - let userNotifications = this.notifications.filter(notification => { + let userNotifications = this.notifications.filter((notification) => { return notification.toWorkgroupId === this.workgroupId; }); - this.newNotifications = userNotifications.filter(notification => { + this.newNotifications = userNotifications.filter((notification) => { return notification.timeDismissed == null; }); - this.dismissedNotifications = userNotifications.filter(notification => { + this.dismissedNotifications = userNotifications.filter((notification) => { return notification.timeDismissed != null; }); } @@ -147,9 +148,7 @@ class TopBarController { previewProject() { this.saveEvent('projectPreviewed').then(() => { - window.open( - `${this.ConfigService.getConfigParam('previewProjectURL')}` - ); + window.open(`${this.ConfigService.getConfigParam('previewProjectURL')}`); }); } diff --git a/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/shared/workgroupComponentRevisions/workgroupComponentRevisions.ts b/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/shared/workgroupComponentRevisions/workgroupComponentRevisions.ts index 2a2152e38b..e6ae4239e4 100644 --- a/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/shared/workgroupComponentRevisions/workgroupComponentRevisions.ts +++ b/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/shared/workgroupComponentRevisions/workgroupComponentRevisions.ts @@ -39,7 +39,7 @@ class WorkgroupComponentRevisionsController { populateData() { this.data = {}; this.total = 0; - this.getNodeEnteredEvents().then(({events}) => { + this.getNodeEnteredEvents().then(({ events }) => { const nodeVisits = []; for (const event of events) { nodeVisits.push({ @@ -50,11 +50,7 @@ class WorkgroupComponentRevisionsController { let nVisits = nodeVisits.length; // group all component states by node visit - for ( - let cStatesIndex = this.componentStates.length - 1; - cStatesIndex > -1; - cStatesIndex-- - ) { + for (let cStatesIndex = this.componentStates.length - 1; cStatesIndex > -1; cStatesIndex--) { let componentState = this.componentStates[cStatesIndex]; let id = componentState.id; let componentSaveTime = componentState.serverSaveTime; @@ -92,8 +88,9 @@ class WorkgroupComponentRevisionsController { isRevision = true; } else { // Double check to see if there is an annotation associated with the component. - for (const annotation of - this.AnnotationService.getAnnotationsByStudentWorkId(state.id)) { + for (const annotation of this.AnnotationService.getAnnotationsByStudentWorkId( + state.id + )) { if (['score', 'autoScore', 'comment', 'autoComment'].includes(annotation.type)) { isRevision = true; break; diff --git a/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/shared/workgroupNodeGrading/workgroupNodeGrading.ts b/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/shared/workgroupNodeGrading/workgroupNodeGrading.ts index 84135d7739..d51705049d 100644 --- a/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/shared/workgroupNodeGrading/workgroupNodeGrading.ts +++ b/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/shared/workgroupNodeGrading/workgroupNodeGrading.ts @@ -29,7 +29,7 @@ class WorkgroupNodeGradingController { $onInit() { this.nodeContent = this.getNodeContent(); - this.components = this.getComponents().filter(component => { + this.components = this.getComponents().filter((component) => { return this.ProjectService.componentHasWork(component); }); this.teacherWorkgroupId = this.ConfigService.getWorkgroupId(); diff --git a/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/shared/workgroupNodeScore/workgroup-node-score.component.ts b/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/shared/workgroupNodeScore/workgroup-node-score.component.ts index 3f1fd99f20..8a3e91bc40 100644 --- a/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/shared/workgroupNodeScore/workgroup-node-score.component.ts +++ b/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/shared/workgroupNodeScore/workgroup-node-score.component.ts @@ -7,10 +7,9 @@ import { Input, Component } from '@angular/core'; templateUrl: 'workgroup-node-score.component.html' }) export class WorkgroupNodeScoreComponent { - @Input() score: number; @Input() maxScore: number; -}; +} diff --git a/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/studentGrading/stepItem/stepItem.ts b/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/studentGrading/stepItem/stepItem.ts index 736ec872a8..ce0638521d 100644 --- a/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/studentGrading/stepItem/stepItem.ts +++ b/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/studentGrading/stepItem/stepItem.ts @@ -27,8 +27,8 @@ class StepItemController { $onChanges(changesObj) { if (changesObj.maxScore) { - this.maxScore = typeof changesObj.maxScore.currentValue === 'number' ? - changesObj.maxScore.currentValue : 0; + this.maxScore = + typeof changesObj.maxScore.currentValue === 'number' ? changesObj.maxScore.currentValue : 0; } if (changesObj.stepData) { let stepData = angular.copy(changesObj.stepData.currentValue); diff --git a/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/studentGrading/studentGrading.ts b/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/studentGrading/studentGrading.ts index 832e664e50..2c0ca45b36 100644 --- a/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/studentGrading/studentGrading.ts +++ b/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/studentGrading/studentGrading.ts @@ -8,8 +8,10 @@ import { downgradeComponent } from '@angular/upgrade/static'; const StudentGrading = angular .module('studentGrading', []) - .directive('stepInfo', - downgradeComponent({ component: StepInfoComponent }) as angular.IDirectiveFactory) + .directive( + 'stepInfo', + downgradeComponent({ component: StepInfoComponent }) as angular.IDirectiveFactory + ) .component('stepItem', StepItem) .component('studentGradingTools', StudentGradingTools); diff --git a/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/studentGrading/studentGradingTools/studentGradingTools.ts b/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/studentGrading/studentGradingTools/studentGradingTools.ts index 316e5b10cb..09912b9b9b 100644 --- a/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/studentGrading/studentGradingTools/studentGradingTools.ts +++ b/src/main/webapp/wise5/classroomMonitor/classroomMonitorComponents/studentGrading/studentGradingTools/studentGradingTools.ts @@ -18,13 +18,7 @@ class StudentGradingToolsController { workgroups: any; currentPeriodChangedSubscription: any; - static $inject = [ - '$scope', - '$state', - 'orderByFilter', - 'ConfigService', - 'TeacherDataService' - ]; + static $inject = ['$scope', '$state', 'orderByFilter', 'ConfigService', 'TeacherDataService']; constructor( private $scope: any, @@ -39,11 +33,12 @@ class StudentGradingToolsController { this.icons = { prev: 'chevron_right', next: 'chevron_left' }; } - this.currentPeriodChangedSubscription = this.TeacherDataService.currentPeriodChanged$ - .subscribe(({ currentPeriod }) => { - this.periodId = currentPeriod.periodId; - this.filterForPeriod(); - }); + this.currentPeriodChangedSubscription = this.TeacherDataService.currentPeriodChanged$.subscribe( + ({ currentPeriod }) => { + this.periodId = currentPeriod.periodId; + this.filterForPeriod(); + } + ); this.$scope.$on('$destroy', () => { this.ngOnDestroy(); }); @@ -57,8 +52,7 @@ class StudentGradingToolsController { this.currentPeriodChangedSubscription.unsubscribe(); } - $onInit() { - } + $onInit() {} $onChanges() { this.avatarColor = this.ConfigService.getAvatarColorForWorkgroupId(this.workgroupId); diff --git a/src/main/webapp/wise5/classroomMonitor/classroomMonitorController.ts b/src/main/webapp/wise5/classroomMonitor/classroomMonitorController.ts index 597c0d2b2c..539e2a5829 100644 --- a/src/main/webapp/wise5/classroomMonitor/classroomMonitorController.ts +++ b/src/main/webapp/wise5/classroomMonitor/classroomMonitorController.ts @@ -154,24 +154,25 @@ class ClassroomMonitorController { } ); }); - + this.SessionService.logOut$.subscribe(() => { this.logOut(); }); - $transitions.onSuccess({}, $transition => { + $transitions.onSuccess({}, ($transition) => { this.menuOpen = false; this.processUI(); }); - this.serverConnectionStatusSubscription = - this.NotificationService.serverConnectionStatus$.subscribe((isConnected: boolean) => { - if (isConnected) { - this.handleServerReconnect(); - } else { - this.handleServerDisconnect(); + this.serverConnectionStatusSubscription = this.NotificationService.serverConnectionStatus$.subscribe( + (isConnected: boolean) => { + if (isConnected) { + this.handleServerReconnect(); + } else { + this.handleServerDisconnect(); + } } - }); + ); // TODO: make dynamic, set somewhere like in config? this.logoPath = this.ProjectService.getThemePath() + '/images/WISE-logo-ffffff.svg'; diff --git a/src/main/webapp/wise5/classroomMonitor/dataExport/dataExportController.ts b/src/main/webapp/wise5/classroomMonitor/dataExport/dataExportController.ts index f9f7080c57..a8ff660ddd 100644 --- a/src/main/webapp/wise5/classroomMonitor/dataExport/dataExportController.ts +++ b/src/main/webapp/wise5/classroomMonitor/dataExport/dataExportController.ts @@ -158,7 +158,7 @@ class DataExportController { selectedNodesMap = this.getSelectedNodesMap(selectedNodes); } } - this.TeacherDataService.getExport('allStudentWork', selectedNodes).then(result => { + this.TeacherDataService.getExport('allStudentWork', selectedNodes).then((result) => { var workgroups = this.ConfigService.getClassmateUserInfosSortedByWorkgroupId(); var runId = this.ConfigService.getRunId(); var rows = []; @@ -350,8 +350,16 @@ class DataExportController { row.fill(''); row[columnNameToNumber['#']] = rowCounter; row[columnNameToNumber['Workgroup ID']] = workgroupId; - this.setStudentIDsAndNames(row, columnNameToNumber, wiseId1, studentName1, wiseId2, - studentName2, wiseId3, studentName3); + this.setStudentIDsAndNames( + row, + columnNameToNumber, + wiseId1, + studentName1, + wiseId2, + studentName2, + wiseId3, + studentName3 + ); row[columnNameToNumber['Class Period']] = periodName; row[columnNameToNumber['Project ID']] = this.ConfigService.getProjectId(); row[columnNameToNumber['Project Name']] = this.ProjectService.getProjectTitle(); @@ -600,8 +608,16 @@ class DataExportController { return row; } - setStudentIDsAndNames(row: any[], columnNameToNumber: any, wiseId1: number, studentName1: string, - wiseId2: number, studentName2: string, wiseId3: number, studentName3: string) { + setStudentIDsAndNames( + row: any[], + columnNameToNumber: any, + wiseId1: number, + studentName1: string, + wiseId2: number, + studentName2: string, + wiseId3: number, + studentName3: string + ) { if (wiseId1 != null) { row[columnNameToNumber['WISE ID 1']] = wiseId1; } @@ -785,10 +801,7 @@ class DataExportController { } escapeContent(str) { - return str - .replace(/[\n]/g, '\\n') - .replace(/[\r]/g, '\\r') - .replace(/[\t]/g, '\\t'); + return str.replace(/[\n]/g, '\\n').replace(/[\r]/g, '\\r').replace(/[\t]/g, '\\t'); } exportEvents() { @@ -1211,7 +1224,7 @@ class DataExportController { exportNotebookItems(exportType) { this.showDownloadingExportMessage(); - this.TeacherDataService.getExport(exportType).then(result => { + this.TeacherDataService.getExport(exportType).then((result) => { const notebookItems = result; const columnNames = [ 'ID', @@ -1326,7 +1339,7 @@ class DataExportController { exportNotifications() { this.showDownloadingExportMessage(); - this.TeacherDataService.getExport('notifications').then(result => { + this.TeacherDataService.getExport('notifications').then((result) => { const notifications = result; const columnNames = [ 'ID', @@ -1476,7 +1489,7 @@ class DataExportController { selectedNodes.push(selectedStep); } if (item.node.components != null && item.node.components.length > 0) { - item.node.components.map(component => { + item.node.components.map((component) => { if (component.checked) { const selectedComponent = { nodeId: nodeId, @@ -1566,7 +1579,7 @@ class DataExportController { nodeItem.node.components != null && nodeItem.node.components.length > 0 ) { - nodeItem.node.components.map(componentItem => { + nodeItem.node.components.map((componentItem) => { componentItem.checked = true; }); } @@ -1576,7 +1589,7 @@ class DataExportController { nodeItem.node.components != null && nodeItem.node.components.length > 0 ) { - nodeItem.node.components.map(componentItem => { + nodeItem.node.components.map((componentItem) => { componentItem.checked = false; }); } @@ -1597,7 +1610,7 @@ class DataExportController { projectItem.node.components != null && projectItem.node.components.length > 0 ) { - projectItem.node.components.map(componentItem => { + projectItem.node.components.map((componentItem) => { componentItem.checked = doSelect; }); } @@ -1612,19 +1625,11 @@ class DataExportController { } previewProject() { - window.open( - `${this.ConfigService.getConfigParam( - 'previewProjectURL' - )}` - ); + window.open(`${this.ConfigService.getConfigParam('previewProjectURL')}`); } previewNode(node) { - window.open( - `${this.ConfigService.getConfigParam( - 'previewProjectURL' - )}/${node.id}` - ); + window.open(`${this.ConfigService.getConfigParam('previewProjectURL')}/${node.id}`); } /** @@ -1652,7 +1657,7 @@ class DataExportController { } } - this.TeacherDataService.getExport('oneWorkgroupPerRow', selectedNodes).then(result => { + this.TeacherDataService.getExport('oneWorkgroupPerRow', selectedNodes).then((result) => { var rows = []; var projectId = this.ConfigService.getProjectId(); var projectTitle = this.ProjectService.getProjectTitle(); @@ -2408,7 +2413,7 @@ class DataExportController { selectedNodesMap = this.getSelectedNodesMap(selectedNodes); } } - this.TeacherDataService.getExport('rawData', selectedNodes).then(result => { + this.TeacherDataService.getExport('rawData', selectedNodes).then((result) => { var runId = this.ConfigService.getRunId(); var data: any = {}; var workgroups = this.ConfigService.getClassmateUserInfosSortedByWorkgroupId(); @@ -2581,7 +2586,7 @@ class DataExportController { exportDiscussionComponent(nodeId, component) { this.showDownloadingExportMessage(); const components = this.getComponentsParam(nodeId, component.id); - this.TeacherDataService.getExport('allStudentWork', components).then(result => { + this.TeacherDataService.getExport('allStudentWork', components).then((result) => { const columnNames = []; const columnNameToNumber = {}; let rows = [ @@ -2718,12 +2723,19 @@ class DataExportController { wiseId3 = userInfo.users[2].id; studentName3 = userInfo.users[2].name; } - this.setStudentIDsAndNames(row, columnNameToNumber, wiseId1, studentName1, wiseId2, - studentName2, wiseId3, studentName3); + this.setStudentIDsAndNames( + row, + columnNameToNumber, + wiseId1, + studentName1, + wiseId2, + studentName2, + wiseId3, + studentName3 + ); row[columnNameToNumber['Class Period']] = userInfo.periodName; } - row[columnNameToNumber['#']] = rowCounter; row[columnNameToNumber['Project ID']] = this.ConfigService.getProjectId(); row[columnNameToNumber['Project Name']] = this.ProjectService.getProjectTitle(); @@ -2833,9 +2845,13 @@ class DataExportController { const runId = this.ConfigService.getRunId(); const stepNumber = this.ProjectService.getNodePositionById(nodeId); const componentNumber = - this.ProjectService.getComponentPositionByNodeIdAndComponentId(nodeId, component.id) + 1; - const fileName = this.getMatchExportFileName(runId, stepNumber, componentNumber, - this.workSelectionType); + this.ProjectService.getComponentPositionByNodeIdAndComponentId(nodeId, component.id) + 1; + const fileName = this.getMatchExportFileName( + runId, + stepNumber, + componentNumber, + this.workSelectionType + ); const rows = this.getExportMatchComponentRows(nodeId, component); this.generateCSVFile(rows, fileName); this.hideDownloadingExportMessage(); @@ -2852,8 +2868,12 @@ class DataExportController { return rows; } - getMatchExportFileName(runId: number, stepNumber: number, componentNumber: number, - workSelectionType: string) { + getMatchExportFileName( + runId: number, + stepNumber: number, + componentNumber: number, + workSelectionType: string + ) { let allOrLatest = ''; if (workSelectionType === 'exportAllWork') { allOrLatest = 'all'; @@ -3158,7 +3178,7 @@ class DataExportController { exportVisitsClicked() { this.$state.go('root.cm.exportVisits'); } - + getComponentsParam(nodeId: string, componentId: string) { return [{ nodeId: nodeId, componentId: componentId }]; } diff --git a/src/main/webapp/wise5/classroomMonitor/dataExport/exportController.ts b/src/main/webapp/wise5/classroomMonitor/dataExport/exportController.ts index 57fa959f62..eccf6c506c 100644 --- a/src/main/webapp/wise5/classroomMonitor/dataExport/exportController.ts +++ b/src/main/webapp/wise5/classroomMonitor/dataExport/exportController.ts @@ -79,4 +79,4 @@ class ExportController { } } -export default ExportController; \ No newline at end of file +export default ExportController; diff --git a/src/main/webapp/wise5/classroomMonitor/dataExport/exportVisitsController.ts b/src/main/webapp/wise5/classroomMonitor/dataExport/exportVisitsController.ts index 4851db9ab3..f0a769e6a7 100644 --- a/src/main/webapp/wise5/classroomMonitor/dataExport/exportVisitsController.ts +++ b/src/main/webapp/wise5/classroomMonitor/dataExport/exportVisitsController.ts @@ -1,10 +1,10 @@ 'use strict'; -import { TeacherDataService } from "../../services/teacherDataService"; -import ExportController from "./exportController"; -import { ConfigService } from "../../services/configService"; -import { UtilService } from "../../services/utilService"; -import { TeacherProjectService } from "../../services/teacherProjectService"; +import { TeacherDataService } from '../../services/teacherDataService'; +import ExportController from './exportController'; +import { ConfigService } from '../../services/configService'; +import { UtilService } from '../../services/utilService'; +import { TeacherProjectService } from '../../services/teacherProjectService'; class ExportVisitsController extends ExportController { project: any; @@ -70,8 +70,9 @@ class ExportVisitsController extends ExportController { const nodeId = node.node.id; this.idToNode[nodeId] = node; this.idToStepNumber[nodeId] = this.ProjectService.getNodePositionById(nodeId); - this.idToStepNumberAndTitle[nodeId] = - this.ProjectService.getNodePositionAndTitleByNodeId(nodeId); + this.idToStepNumberAndTitle[nodeId] = this.ProjectService.getNodePositionAndTitleByNodeId( + nodeId + ); } } @@ -141,25 +142,32 @@ class ExportVisitsController extends ExportController { { name: 'Step Title', explanation: this.$translate('columnExplanationStepTitle') }, { name: 'Enter Time', explanation: this.$translate('columnExplanationEnterTime') }, { name: 'Exit Time', explanation: this.$translate('columnExplanationExitTime') }, - { name: 'Visit Duration (Seconds)', - explanation: this.$translate('columnExplanationVisitDurationSeconds') }, + { + name: 'Visit Duration (Seconds)', + explanation: this.$translate('columnExplanationVisitDurationSeconds') + }, { name: 'Visit Counter', explanation: this.$translate('columnExplanationVisitCounter') }, { name: 'Revisit Counter', explanation: this.$translate('columnExplanationRevisitCounter') }, { name: 'Previous Node ID', explanation: this.$translate('columnExplanationPreviousNodeID') }, - { name: 'Previous Step Title', - explanation: this.$translate('columnExplanationPreviousStepTitle') }, - { name: 'Node IDs Since Last Visit', - explanation: this.$translate('columnExplanationNodeIDsSinceLastVisit') }, - { name: 'Steps Since Last Visit', - explanation: this.$translate('columnExplanationStepsSinceLastVisit') } + { + name: 'Previous Step Title', + explanation: this.$translate('columnExplanationPreviousStepTitle') + }, + { + name: 'Node IDs Since Last Visit', + explanation: this.$translate('columnExplanationNodeIDsSinceLastVisit') + }, + { + name: 'Steps Since Last Visit', + explanation: this.$translate('columnExplanationStepsSinceLastVisit') + } ]; } initializeIdToUserInfo() { const workgroupIds = this.ConfigService.getClassmateWorkgroupIds(); for (const workgroupId of workgroupIds) { - this.idToUserInfo[workgroupId] = - this.ConfigService.getUserInfoByWorkgroupId(workgroupId); + this.idToUserInfo[workgroupId] = this.ConfigService.getUserInfoByWorkgroupId(workgroupId); } } @@ -210,13 +218,16 @@ class ExportVisitsController extends ExportController { const includeStudentEvents = true; const includeTeacherEvents = false; this.TeacherDataService.retrieveEventsExport( - includeStudentEvents, includeTeacherEvents, this.includeStudentNames).then((response) => { + includeStudentEvents, + includeTeacherEvents, + this.includeStudentNames + ).then((response) => { this.handleExportCallback(response); }); } getCheckedItems() { - const checkedItems = [] + const checkedItems = []; for (const node of this.nodes) { if (this.idToChecked[node.node.id]) { checkedItems.push(node.node.id); @@ -244,7 +255,7 @@ class ExportVisitsController extends ExportController { } previousEnteredEvent = null; } - }; + } if (previousEnteredEvent != null) { rows.push(this.createVisit(previousEnteredEvent, null, rows)); } @@ -293,18 +304,22 @@ class ExportVisitsController extends ExportController { } isErroneousExitedEvent(event: any, nextEvent: any) { - return this.isStepExitedEvent(event) && - this.isStepExitedEvent(nextEvent) && - this.isMatchingNodeId(event, nextEvent); + return ( + this.isStepExitedEvent(event) && + this.isStepExitedEvent(nextEvent) && + this.isMatchingNodeId(event, nextEvent) + ); } getDeletedSteps(events: any[]) { const deletedSteps = {}; for (const event of events) { const nodeId = event.nodeId; - if (nodeId != null && - this.ProjectService.getNodeById(nodeId) == null && - nodeId.startsWith('node')) { + if ( + nodeId != null && + this.ProjectService.getNodeById(nodeId) == null && + nodeId.startsWith('node') + ) { deletedSteps[event.nodeId] = true; } } @@ -316,10 +331,12 @@ class ExportVisitsController extends ExportController { } filterRows(rows: any[]) { - return rows.filter(row => { + return rows.filter((row) => { const nodeId = this.getCellInRow(row, 'Node ID'); - return this.checkedItems.includes(nodeId) || - (this.includeDeletedSteps && this.isDeletedStep(nodeId)); + return ( + this.checkedItems.includes(nodeId) || + (this.includeDeletedSteps && this.isDeletedStep(nodeId)) + ); }); } @@ -374,32 +391,49 @@ class ExportVisitsController extends ExportController { this.setCellInRow(visit, 'End Date', this.ConfigService.getFormattedEndDate()); this.setCellInRow(visit, 'Node ID', nodeId); this.setCellInRow(visit, 'Step Title', this.getStepNumberAndTitle(nodeId)); - this.setCellInRow(visit, 'Enter Time', - this.UtilService.convertMillisecondsToFormattedDateTime(nodeEnteredEvent.clientSaveTime)); + this.setCellInRow( + visit, + 'Enter Time', + this.UtilService.convertMillisecondsToFormattedDateTime(nodeEnteredEvent.clientSaveTime) + ); if (nodeExitedEvent == null) { this.setCellInRow(visit, 'Exit Time', '(Unknown Exit Time)'); this.setCellInRow(visit, 'Visit Duration (Seconds)', '(Unknown Visit Duration)'); } else if (nodeExitedEvent != null) { - this.setCellInRow(visit, 'Exit Time', - this.UtilService.convertMillisecondsToFormattedDateTime(nodeExitedEvent.clientSaveTime)); - this.setCellInRow(visit, 'Visit Duration (Seconds)', - this.getVisitDuration(nodeEnteredEvent, nodeExitedEvent)); + this.setCellInRow( + visit, + 'Exit Time', + this.UtilService.convertMillisecondsToFormattedDateTime(nodeExitedEvent.clientSaveTime) + ); + this.setCellInRow( + visit, + 'Visit Duration (Seconds)', + this.getVisitDuration(nodeEnteredEvent, nodeExitedEvent) + ); } this.setCellInRow(visit, 'Visit Counter', this.getVisitCounter(workgroupId, nodeId)); const revisitCounter = this.getRevisitCounter(workgroupId, nodeId); this.setCellInRow(visit, 'Revisit Counter', revisitCounter); const previousVisit = this.getPreviousVisit(previousVisits, workgroupId); if (previousVisit != null) { + this.setCellInRow(visit, 'Previous Node ID', this.getCellInRow(previousVisit, 'Node ID')); this.setCellInRow( - visit, 'Previous Node ID', this.getCellInRow(previousVisit, 'Node ID')); - this.setCellInRow( - visit, 'Previous Step Title', this.getCellInRow(previousVisit, 'Step Title')); + visit, + 'Previous Step Title', + this.getCellInRow(previousVisit, 'Step Title') + ); } if (revisitCounter > 0) { - this.setCellInRow(visit, 'Node IDs Since Last Visit', - this.getNodeIdsBetweenLastVisit(nodeId, previousVisits)); - this.setCellInRow(visit, 'Steps Since Last Visit', - this.getStepNumbersBetweenLastVisit(nodeId, previousVisits)); + this.setCellInRow( + visit, + 'Node IDs Since Last Visit', + this.getNodeIdsBetweenLastVisit(nodeId, previousVisits) + ); + this.setCellInRow( + visit, + 'Steps Since Last Visit', + this.getStepNumbersBetweenLastVisit(nodeId, previousVisits) + ); } this.incrementRowCounter(); return visit; @@ -439,7 +473,6 @@ class ExportVisitsController extends ExportController { } else if (output === 'stepNumber') { steps.unshift(this.getStepNumber(previousNodeId)); } - } } return steps.join(', '); diff --git a/src/main/webapp/wise5/classroomMonitor/manageStudents/manage-students-component.ts b/src/main/webapp/wise5/classroomMonitor/manageStudents/manage-students-component.ts index 2413275d91..cf67f2572f 100644 --- a/src/main/webapp/wise5/classroomMonitor/manageStudents/manage-students-component.ts +++ b/src/main/webapp/wise5/classroomMonitor/manageStudents/manage-students-component.ts @@ -9,15 +9,14 @@ import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser'; templateUrl: 'manage-students.html' }) export class ManageStudentsComponent { - iframeSrc: SafeResourceUrl; - constructor(private configService: ConfigService, private sanitizer: DomSanitizer) { - } + constructor(private configService: ConfigService, private sanitizer: DomSanitizer) {} ngOnInit() { this.iframeSrc = this.sanitizer.bypassSecurityTrustResourceUrl( - `${this.configService.getConfigParam('contextPath')}/teacher/management/viewmystudents` + - `?runId=${this.configService.getRunId()}`); + `${this.configService.getConfigParam('contextPath')}/teacher/management/viewmystudents` + + `?runId=${this.configService.getRunId()}` + ); } } diff --git a/src/main/webapp/wise5/classroomMonitor/notebook/notebookGradingController.ts b/src/main/webapp/wise5/classroomMonitor/notebook/notebookGradingController.ts index 918dcea420..d32fc19bd3 100644 --- a/src/main/webapp/wise5/classroomMonitor/notebook/notebookGradingController.ts +++ b/src/main/webapp/wise5/classroomMonitor/notebook/notebookGradingController.ts @@ -12,15 +12,15 @@ class NotebookGradingController { notebookConfig: any; notesEnabled: boolean = false; sortOrder: object = { - 'team': ['workgroupId'], + team: ['workgroupId'], '-team': ['-workgroupId'], - 'student': ['displayNames', 'workgroupId'], + student: ['displayNames', 'workgroupId'], '-student': ['-displayNames', 'workgroupId'], - 'notes': ['notes.length', 'workgroupId'], + notes: ['notes.length', 'workgroupId'], '-notes': ['-notes.length', 'workgroupId'], - 'status': ['report.serverSaveTime', 'workgroupId'], + status: ['report.serverSaveTime', 'workgroupId'], '-status': ['-report.serverSaveTime', 'workgroupId'], - 'score': ['score', 'workgroupId'], + score: ['score', 'workgroupId'], '-score': ['-score', 'workgroupId'] }; reportEnabled: boolean = false; @@ -54,7 +54,10 @@ class NotebookGradingController { } if (this.reportEnabled) { const reportId = this.notebookConfig.itemTypes.report.notes[0].reportId; - workgroup.report = this.NotebookService.getLatestNotebookReportItemByReportId(reportId, workgroup.workgroupId); + workgroup.report = this.NotebookService.getLatestNotebookReportItemByReportId( + reportId, + workgroup.workgroupId + ); } } this.setWorkgroupsById(); @@ -94,7 +97,7 @@ class NotebookGradingController { getWorkgroupNotes(workgroupId) { const notes = this.NotebookService.getPrivateNotebookItems(workgroupId); - return notes.filter(note => { + return notes.filter((note) => { return note.type !== 'report'; }); } @@ -128,8 +131,9 @@ class NotebookGradingController { } getNotebookConfigForWorkgroup(workgroupId) { - if (this.ConfigService.isRunOwner(workgroupId) || - this.ConfigService.isRunSharedTeacher(workgroupId) + if ( + this.ConfigService.isRunOwner(workgroupId) || + this.ConfigService.isRunSharedTeacher(workgroupId) ) { return this.NotebookService.getTeacherNotebookConfig(); } else { diff --git a/src/main/webapp/wise5/classroomMonitor/studentGrading/studentGradingController.ts b/src/main/webapp/wise5/classroomMonitor/studentGrading/studentGradingController.ts index 2bce5c9b3f..c909b602c6 100644 --- a/src/main/webapp/wise5/classroomMonitor/studentGrading/studentGradingController.ts +++ b/src/main/webapp/wise5/classroomMonitor/studentGrading/studentGradingController.ts @@ -21,12 +21,20 @@ class StudentGradingController { nodesInViewById: any; nodeVisibilityById: any; sortOrder: object = { - 'step': ['-isVisible', 'order'], + step: ['-isVisible', 'order'], '-step': ['-isVisible', '-order'], - 'status': ['-isVisible', 'completionStatus', '-hasNewAlert', 'order'], + status: ['-isVisible', 'completionStatus', '-hasNewAlert', 'order'], '-status': ['-isVisible', '-completionStatus', '-hasNewAlert', 'order'], - 'score': ['-isVisible', '-hasScore', '-hasMaxScore', 'scorePct', '-maxScore', 'score', 'order'], - '-score': ['-isVisible', '-hasScore', '-hasMaxScore', '-scorePct', '-maxScore', 'score', 'order'] + score: ['-isVisible', '-hasScore', '-hasMaxScore', 'scorePct', '-maxScore', 'score', 'order'], + '-score': [ + '-isVisible', + '-hasScore', + '-hasMaxScore', + '-scorePct', + '-maxScore', + 'score', + 'order' + ] }; permissions: any; projectCompletion: any; @@ -78,68 +86,72 @@ class StudentGradingController { this.setNodesById(); }); - this.notificationChangedSubscription = this.NotificationService.notificationChanged$ - .subscribe((notification) => { - if (notification.type === 'CRaterResult') { - // TODO: expand to encompass other notification types that should be shown to teacher - let workgroupId = notification.toWorkgroupId; - let nodeId = notification.nodeId; - if (workgroupId === this.workgroupId && this.nodesById[nodeId]) { - this.updateNode(nodeId); + this.notificationChangedSubscription = this.NotificationService.notificationChanged$.subscribe( + (notification) => { + if (notification.type === 'CRaterResult') { + // TODO: expand to encompass other notification types that should be shown to teacher + let workgroupId = notification.toWorkgroupId; + let nodeId = notification.nodeId; + if (workgroupId === this.workgroupId && this.nodesById[nodeId]) { + this.updateNode(nodeId); + } } } - }); - - this.annotationReceivedSubscription = - this.AnnotationService.annotationReceived$.subscribe(({ annotation }) => { - const workgroupId = annotation.toWorkgroupId; - const nodeId = annotation.nodeId; - if (workgroupId === this.workgroupId && this.nodesById[nodeId]) { - this.totalScore = this.TeacherDataService.getTotalScoreByWorkgroupId(workgroupId); - this.updateNode(nodeId); - } - }); - + ); - this.studentWorkReceivedSubscription = this.TeacherDataService.studentWorkReceived$ - .subscribe((args: any) => { - const studentWork = args.studentWork; - if (studentWork != null) { - let workgroupId = studentWork.workgroupId; - let nodeId = studentWork.nodeId; + this.annotationReceivedSubscription = this.AnnotationService.annotationReceived$.subscribe( + ({ annotation }) => { + const workgroupId = annotation.toWorkgroupId; + const nodeId = annotation.nodeId; if (workgroupId === this.workgroupId && this.nodesById[nodeId]) { + this.totalScore = this.TeacherDataService.getTotalScoreByWorkgroupId(workgroupId); this.updateNode(nodeId); } } - }); + ); - this.currentWorkgroupChangedSubscription = - this.TeacherDataService.currentWorkgroupChanged$.subscribe(({ currentWorkgroup }) => { - if (currentWorkgroup != null) { - let workgroupId = currentWorkgroup.workgroupId; - if (this.workgroupId !== workgroupId) { - this.$state.go('root.cm.team', { workgroupId: workgroupId }); + this.studentWorkReceivedSubscription = this.TeacherDataService.studentWorkReceived$.subscribe( + (args: any) => { + const studentWork = args.studentWork; + if (studentWork != null) { + let workgroupId = studentWork.workgroupId; + let nodeId = studentWork.nodeId; + if (workgroupId === this.workgroupId && this.nodesById[nodeId]) { + this.updateNode(nodeId); + } } } - }); + ); - this.currentPeriodChangedSubscription = this.TeacherDataService.currentPeriodChanged$ - .subscribe(({ currentPeriod }) => { - let periodId = currentPeriod.periodId; - let currentWorkgroup = this.TeacherDataService.getCurrentWorkgroup(); - if (!currentWorkgroup) { - let workgroups = angular.copy(this.ConfigService.getClassmateUserInfos()); - workgroups = this.orderBy(workgroups, 'workgroupId'); - let n = workgroups.length; - for (let i = 0; i < n; i++) { - let workgroup = workgroups[i]; - if (workgroup.periodId === periodId) { - this.TeacherDataService.setCurrentWorkgroup(workgroup); - break; + this.currentWorkgroupChangedSubscription = this.TeacherDataService.currentWorkgroupChanged$.subscribe( + ({ currentWorkgroup }) => { + if (currentWorkgroup != null) { + let workgroupId = currentWorkgroup.workgroupId; + if (this.workgroupId !== workgroupId) { + this.$state.go('root.cm.team', { workgroupId: workgroupId }); } } } - }); + ); + + this.currentPeriodChangedSubscription = this.TeacherDataService.currentPeriodChanged$.subscribe( + ({ currentPeriod }) => { + let periodId = currentPeriod.periodId; + let currentWorkgroup = this.TeacherDataService.getCurrentWorkgroup(); + if (!currentWorkgroup) { + let workgroups = angular.copy(this.ConfigService.getClassmateUserInfos()); + workgroups = this.orderBy(workgroups, 'workgroupId'); + let n = workgroups.length; + for (let i = 0; i < n; i++) { + let workgroup = workgroups[i]; + if (workgroup.periodId === periodId) { + this.TeacherDataService.setCurrentWorkgroup(workgroup); + break; + } + } + } + } + ); this.$scope.$on('$destroy', () => { this.TeacherDataService.setCurrentWorkgroup(null); diff --git a/src/main/webapp/wise5/classroomMonitor/studentProgress/studentProgressController.ts b/src/main/webapp/wise5/classroomMonitor/studentProgress/studentProgressController.ts index c8c5f7182a..16cb05a27b 100644 --- a/src/main/webapp/wise5/classroomMonitor/studentProgress/studentProgressController.ts +++ b/src/main/webapp/wise5/classroomMonitor/studentProgress/studentProgressController.ts @@ -13,15 +13,15 @@ class StudentProgressController { permissions: any; sort: any; sortOrder: object = { - 'team': ['workgroupId', 'username'], + team: ['workgroupId', 'username'], '-team': ['-workgroupId', 'username'], - 'student': ['username', 'workgroupId'], + student: ['username', 'workgroupId'], '-student': ['-username', 'workgroupId'], - 'score': ['scorePct', 'username'], + score: ['scorePct', 'username'], '-score': ['-scorePct', 'username'], - 'completion': ['completion.completionPct', 'username'], + completion: ['completion.completionPct', 'username'], '-completion': ['-completion.completionPct', 'username'], - 'location': ['location', 'username'], + location: ['location', 'username'], '-location': ['-location', 'username'] }; students: any; @@ -54,16 +54,18 @@ class StudentProgressController { this.permissions = this.ConfigService.getPermissions(); this.students = []; this.initializeStudents(); - this.studentStatusReceivedSubscription = - this.StudentStatusService.studentStatusReceived$.subscribe((args) => { - const studentStatus = args.studentStatus; - const workgroupId = studentStatus.workgroupId; - this.updateTeam(workgroupId); - }); - this.currentWorkgroupChangedSubscription = - this.TeacherDataService.currentWorkgroupChanged$.subscribe(({ currentWorkgroup }) => { - this.currentWorkgroup = currentWorkgroup; - }); + this.studentStatusReceivedSubscription = this.StudentStatusService.studentStatusReceived$.subscribe( + (args) => { + const studentStatus = args.studentStatus; + const workgroupId = studentStatus.workgroupId; + this.updateTeam(workgroupId); + } + ); + this.currentWorkgroupChangedSubscription = this.TeacherDataService.currentWorkgroupChanged$.subscribe( + ({ currentWorkgroup }) => { + this.currentWorkgroup = currentWorkgroup; + } + ); const context = 'ClassroomMonitor', nodeId = null, componentId = null, diff --git a/src/main/webapp/wise5/common-angular-js-module.ts b/src/main/webapp/wise5/common-angular-js-module.ts index 811c4ed1cb..7b6735dfd3 100644 --- a/src/main/webapp/wise5/common-angular-js-module.ts +++ b/src/main/webapp/wise5/common-angular-js-module.ts @@ -30,7 +30,7 @@ import './components/discussion/discussionComponentModule'; import './components/draw/drawComponentModule'; import './components/embedded/embeddedComponentModule'; import * as fabric from 'fabric'; -window['fabric'] = fabric.fabric +window['fabric'] = fabric.fabric; import Filters from './filters/filters'; import './lib/highcharts/highcharts-ng'; import * as Highcharts from './lib/highcharts/highcharts.src'; @@ -71,7 +71,8 @@ import SideMenu from './common/sideMenuComponent'; import { EditorComponent } from '@tinymce/tinymce-angular'; import { WiseTinymceEditorComponent } from './directives/wise-tinymce-editor/wise-tinymce-editor.component'; - angular.module('common', [ +angular + .module('common', [ angularDragula(angular), 'angularMoment', 'angular-toArrayFilter', @@ -102,10 +103,14 @@ import { WiseTinymceEditorComponent } from './directives/wise-tinymce-editor/wis 'ui.router' ]) .service('AchievementService', downgradeInjectable(AchievementService)) - .directive('editor', downgradeComponent( - { component: EditorComponent }) as angular.IDirectiveFactory) - .directive('wiseTinymceEditor', downgradeComponent( - { component: WiseTinymceEditorComponent }) as angular.IDirectiveFactory) + .directive( + 'editor', + downgradeComponent({ component: EditorComponent }) as angular.IDirectiveFactory + ) + .directive( + 'wiseTinymceEditor', + downgradeComponent({ component: WiseTinymceEditorComponent }) as angular.IDirectiveFactory + ) .factory('AnnotationService', downgradeInjectable(AnnotationService)) .factory('AudioRecorderService', downgradeInjectable(AudioRecorderService)) .factory('ConfigService', downgradeInjectable(ConfigService)) @@ -123,21 +128,21 @@ import { WiseTinymceEditorComponent } from './directives/wise-tinymce-editor/wis .component('sideMenu', SideMenu) .filter('Filters', Filters) .config([ - '$httpProvider', - '$locationProvider', - '$mdThemingProvider', - '$translateProvider', - '$translatePartialLoaderProvider', - ( - $httpProvider, - $locationProvider, - $mdThemingProvider, - $translateProvider, - $translatePartialLoaderProvider - ) => { - $httpProvider.interceptors.push('HttpInterceptor'); - $locationProvider.html5Mode(true); - $translateProvider + '$httpProvider', + '$locationProvider', + '$mdThemingProvider', + '$translateProvider', + '$translatePartialLoaderProvider', + ( + $httpProvider, + $locationProvider, + $mdThemingProvider, + $translateProvider, + $translatePartialLoaderProvider + ) => { + $httpProvider.interceptors.push('HttpInterceptor'); + $locationProvider.html5Mode(true); + $translateProvider .useLoader('$translatePartialLoader', { urlTemplate: '/wise5/{part}/i18n_{lang}.json' }) @@ -151,65 +156,66 @@ import { WiseTinymceEditorComponent } from './directives/wise-tinymce-editor/wis ) .determinePreferredLanguage() .useSanitizeValueStrategy('sanitizeParameters', 'escape'); - $translatePartialLoaderProvider.addPart('i18n'); - $mdThemingProvider.definePalette('primary', { - '50': 'e1f0f4', - '100': 'b8dbe4', - '200': '8ec6d4', - '300': '5faec2', - '400': '3d9db5', - '500': '1c8ca8', - '600': '197f98', - '700': '167188', - '800': '136377', - '900': '0e4957', - A100: 'abf3ff', - A200: '66e2ff', - A400: '17bee5', - A700: '00A1C6', - contrastDefaultColor: 'light', // whether, by default, text (contrast) - // on this palette should be dark or light - contrastDarkColors: [ - '50', - '100', //hues which contrast should be 'dark' by default - '200', - '300', - 'A100' - ], - contrastLightColors: undefined // could also specify this if default was 'dark' - }); - $mdThemingProvider.definePalette('accent', { - '50': 'fde9e6', - '100': 'fbcbc4', - '200': 'f8aca1', - '300': 'f4897b', - '400': 'f2705f', - '500': 'f05843', - '600': 'da503c', - '700': 'c34736', - '800': 'aa3e2f', - '900': '7d2e23', - A100: 'ff897d', - A200: 'ff7061', - A400: 'ff3829', - A700: 'cc1705', - contrastDefaultColor: 'light', - contrastDarkColors: ['50', '100', '200', '300', 'A100'], - contrastLightColors: undefined - }); - const lightMap = $mdThemingProvider.extendPalette('grey', { - A100: 'ffffff' - }); - $mdThemingProvider.definePalette('light', lightMap); - $mdThemingProvider.enableBrowserColor(); - moment.updateLocale('en', { - calendar: { - lastDay: '[Yesterday at] LT', - sameDay: '[Today at] LT', - nextDay: '[Tomorrow at] LT', - lastWeek: '[last] dddd [at] LT', - nextWeek: 'dddd [at] LT', - sameElse: 'll' - } - }); - }]); + $translatePartialLoaderProvider.addPart('i18n'); + $mdThemingProvider.definePalette('primary', { + '50': 'e1f0f4', + '100': 'b8dbe4', + '200': '8ec6d4', + '300': '5faec2', + '400': '3d9db5', + '500': '1c8ca8', + '600': '197f98', + '700': '167188', + '800': '136377', + '900': '0e4957', + A100: 'abf3ff', + A200: '66e2ff', + A400: '17bee5', + A700: '00A1C6', + contrastDefaultColor: 'light', // whether, by default, text (contrast) + // on this palette should be dark or light + contrastDarkColors: [ + '50', + '100', //hues which contrast should be 'dark' by default + '200', + '300', + 'A100' + ], + contrastLightColors: undefined // could also specify this if default was 'dark' + }); + $mdThemingProvider.definePalette('accent', { + '50': 'fde9e6', + '100': 'fbcbc4', + '200': 'f8aca1', + '300': 'f4897b', + '400': 'f2705f', + '500': 'f05843', + '600': 'da503c', + '700': 'c34736', + '800': 'aa3e2f', + '900': '7d2e23', + A100: 'ff897d', + A200: 'ff7061', + A400: 'ff3829', + A700: 'cc1705', + contrastDefaultColor: 'light', + contrastDarkColors: ['50', '100', '200', '300', 'A100'], + contrastLightColors: undefined + }); + const lightMap = $mdThemingProvider.extendPalette('grey', { + A100: 'ffffff' + }); + $mdThemingProvider.definePalette('light', lightMap); + $mdThemingProvider.enableBrowserColor(); + moment.updateLocale('en', { + calendar: { + lastDay: '[Yesterday at] LT', + sameDay: '[Today at] LT', + nextDay: '[Tomorrow at] LT', + lastWeek: '[last] dddd [at] LT', + nextWeek: 'dddd [at] LT', + sameElse: 'll' + } + }); + } + ]); diff --git a/src/main/webapp/wise5/components/animation/animationAuthoring.ts b/src/main/webapp/wise5/components/animation/animationAuthoring.ts index e23c5fa436..7bf3fe8523 100644 --- a/src/main/webapp/wise5/components/animation/animationAuthoring.ts +++ b/src/main/webapp/wise5/components/animation/animationAuthoring.ts @@ -5,7 +5,6 @@ import { EditComponentController } from '../../authoringTool/components/editComp @Directive() class AnimationAuthoringController extends EditComponentController { - static $inject = [ '$filter', 'ConfigService', @@ -296,7 +295,6 @@ class AnimationAuthoringController extends EditComponentController { getComponentByNodeIdAndComponentId(nodeId: string, componentId: string) { return this.ProjectService.getComponentByNodeIdAndComponentId(nodeId, componentId); } - } const AnimationAuthoring = { @@ -307,6 +305,6 @@ const AnimationAuthoring = { controller: AnimationAuthoringController, controllerAs: 'animationController', templateUrl: 'wise5/components/animation/authoring.html' -} +}; export default AnimationAuthoring; diff --git a/src/main/webapp/wise5/components/animation/animationAuthoringComponentModule.ts b/src/main/webapp/wise5/components/animation/animationAuthoringComponentModule.ts index 033576a060..2e4e7b9fd6 100644 --- a/src/main/webapp/wise5/components/animation/animationAuthoringComponentModule.ts +++ b/src/main/webapp/wise5/components/animation/animationAuthoringComponentModule.ts @@ -13,7 +13,7 @@ const animationAuthoringComponentModule = angular .component('editAnimationAdvanced', EditAnimationAdvancedComponent) .config([ '$translatePartialLoaderProvider', - $translatePartialLoaderProvider => { + ($translatePartialLoaderProvider) => { $translatePartialLoaderProvider.addPart('components/animation/i18n'); } ]); diff --git a/src/main/webapp/wise5/components/animation/animationComponentModule.ts b/src/main/webapp/wise5/components/animation/animationComponentModule.ts index efc3cae844..f9c5070da3 100644 --- a/src/main/webapp/wise5/components/animation/animationComponentModule.ts +++ b/src/main/webapp/wise5/components/animation/animationComponentModule.ts @@ -11,7 +11,7 @@ const animationComponentModule = angular .controller('AnimationController', AnimationController) .config([ '$translatePartialLoaderProvider', - $translatePartialLoaderProvider => { + ($translatePartialLoaderProvider) => { $translatePartialLoaderProvider.addPart('components/animation/i18n'); } ]); diff --git a/src/main/webapp/wise5/components/animation/animationController.ts b/src/main/webapp/wise5/components/animation/animationController.ts index 9b40dc3e2e..ec8bee82e3 100644 --- a/src/main/webapp/wise5/components/animation/animationController.ts +++ b/src/main/webapp/wise5/components/animation/animationController.ts @@ -686,11 +686,11 @@ class AnimationController extends ComponentController { const thisAnimationController = this; animateObject = svgObject .animate(t * this.millisecondsPerDataTime) - .during(function(pos, morph, eased, situation) { + .during(function (pos, morph, eased, situation) { let totalElapsedTime = t * pos; thisAnimationController.displayAndBroadcastTime(totalElapsedTime); }) - .after(function() { + .after(function () { this.attr({ x: xPixel, y: yPixel }); }); } @@ -704,7 +704,7 @@ class AnimationController extends ComponentController { return svgObject .animate(tDiff * this.millisecondsPerDataTime) .move(nextXPixel, nextYPixel) - .during(function(pos, morph, eased, situation) { + .during(function (pos, morph, eased, situation) { let totalElapsedTime = t + tDiff * pos; thisAnimationController.displayAndBroadcastTime(totalElapsedTime); }); @@ -750,7 +750,7 @@ class AnimationController extends ComponentController { svgObject.load(image); } else { // change the image after all the existing animations - animateObject = animateObject.after(function() { + animateObject = animateObject.after(function () { this.load(image); }); } diff --git a/src/main/webapp/wise5/components/animation/animationService.ts b/src/main/webapp/wise5/components/animation/animationService.ts index 00b1adac7a..979d679033 100644 --- a/src/main/webapp/wise5/components/animation/animationService.ts +++ b/src/main/webapp/wise5/components/animation/animationService.ts @@ -8,10 +8,11 @@ import { StudentDataService } from '../../services/studentDataService'; @Injectable() export class AnimationService extends ComponentService { - - constructor(private upgrade: UpgradeModule, - protected StudentDataService: StudentDataService, - protected UtilService: UtilService) { + constructor( + private upgrade: UpgradeModule, + protected StudentDataService: StudentDataService, + protected UtilService: UtilService + ) { super(StudentDataService, UtilService); } @@ -37,8 +38,13 @@ export class AnimationService extends ComponentService { return component; } - isCompleted(component: any, componentStates: any[], componentEvents: any[], nodeEvents: any[], - node: any) { + isCompleted( + component: any, + componentStates: any[], + componentEvents: any[], + nodeEvents: any[], + node: any + ) { return componentStates.length > 0; } diff --git a/src/main/webapp/wise5/components/animation/edit-animation-advanced/edit-animation-advanced.component.ts b/src/main/webapp/wise5/components/animation/edit-animation-advanced/edit-animation-advanced.component.ts index 91bbb2f1f0..0b5e5a82f6 100644 --- a/src/main/webapp/wise5/components/animation/edit-animation-advanced/edit-animation-advanced.component.ts +++ b/src/main/webapp/wise5/components/animation/edit-animation-advanced/edit-animation-advanced.component.ts @@ -1,4 +1,4 @@ -import { EditAdvancedComponentAngularJSController } from "../../../../site/src/app/authoring-tool/edit-advanced-component/editAdvancedComponentAngularJSController"; +import { EditAdvancedComponentAngularJSController } from '../../../../site/src/app/authoring-tool/edit-advanced-component/editAdvancedComponentAngularJSController'; class EditAnimationAdvancedController extends EditAdvancedComponentAngularJSController { allowedConnectedComponentTypes = ['Animation', 'Graph']; @@ -10,5 +10,6 @@ export const EditAnimationAdvancedComponent = { componentId: '@' }, controller: EditAnimationAdvancedController, - templateUrl: 'wise5/components/animation/edit-animation-advanced/edit-animation-advanced.component.html' -} + templateUrl: + 'wise5/components/animation/edit-animation-advanced/edit-animation-advanced.component.html' +}; diff --git a/src/main/webapp/wise5/components/audioOscillator/audioOscillatorAuthoring.ts b/src/main/webapp/wise5/components/audioOscillator/audioOscillatorAuthoring.ts index c338f8f444..12bc137b86 100644 --- a/src/main/webapp/wise5/components/audioOscillator/audioOscillatorAuthoring.ts +++ b/src/main/webapp/wise5/components/audioOscillator/audioOscillatorAuthoring.ts @@ -5,7 +5,6 @@ import { EditComponentController } from '../../authoringTool/components/editComp @Directive() class AudioOscillatorAuthoringController extends EditComponentController { - authoringSineChecked: boolean; authoringSquareChecked: boolean; authoringTriangleChecked: boolean; @@ -79,7 +78,6 @@ class AudioOscillatorAuthoringController extends EditComponentController { } } - const AudioOscillatorAuthoring = { bindings: { nodeId: '@', @@ -88,6 +86,6 @@ const AudioOscillatorAuthoring = { controller: AudioOscillatorAuthoringController, controllerAs: 'audioOscillatorController', templateUrl: 'wise5/components/audioOscillator/authoring.html' -} +}; export default AudioOscillatorAuthoring; diff --git a/src/main/webapp/wise5/components/audioOscillator/audioOscillatorAuthoringComponentModule.ts b/src/main/webapp/wise5/components/audioOscillator/audioOscillatorAuthoringComponentModule.ts index 175d2b7721..cc4cf21a48 100644 --- a/src/main/webapp/wise5/components/audioOscillator/audioOscillatorAuthoringComponentModule.ts +++ b/src/main/webapp/wise5/components/audioOscillator/audioOscillatorAuthoringComponentModule.ts @@ -13,7 +13,7 @@ const audioOscillatorAuthoringComponentModule = angular .component('editAudioOscillatorAdvanced', EditAudioOscillatorAdvancedComponent) .config([ '$translatePartialLoaderProvider', - $translatePartialLoaderProvider => { + ($translatePartialLoaderProvider) => { $translatePartialLoaderProvider.addPart('components/audioOscillator/i18n'); } ]); diff --git a/src/main/webapp/wise5/components/audioOscillator/audioOscillatorComponentModule.ts b/src/main/webapp/wise5/components/audioOscillator/audioOscillatorComponentModule.ts index 64809751f5..928be1542e 100644 --- a/src/main/webapp/wise5/components/audioOscillator/audioOscillatorComponentModule.ts +++ b/src/main/webapp/wise5/components/audioOscillator/audioOscillatorComponentModule.ts @@ -11,7 +11,7 @@ let audioOscillatorComponentModule = angular .controller('AudioOscillatorController', AudioOscillatorController) .config([ '$translatePartialLoaderProvider', - $translatePartialLoaderProvider => { + ($translatePartialLoaderProvider) => { $translatePartialLoaderProvider.addPart('components/audioOscillator/i18n'); } ]); diff --git a/src/main/webapp/wise5/components/audioOscillator/audioOscillatorService.ts b/src/main/webapp/wise5/components/audioOscillator/audioOscillatorService.ts index 271bfefd98..06902b19ac 100644 --- a/src/main/webapp/wise5/components/audioOscillator/audioOscillatorService.ts +++ b/src/main/webapp/wise5/components/audioOscillator/audioOscillatorService.ts @@ -6,10 +6,11 @@ import { UtilService } from '../../services/utilService'; @Injectable() export class AudioOscillatorService extends ComponentService { - - constructor(private upgrade: UpgradeModule, + constructor( + private upgrade: UpgradeModule, protected StudentDataService: StudentDataService, - protected UtilService: UtilService) { + protected UtilService: UtilService + ) { super(StudentDataService, UtilService); } @@ -33,8 +34,13 @@ export class AudioOscillatorService extends ComponentService { return component; } - isCompleted(component: any, componentStates: any[], componentEvents: any[], nodeEvents: any[], - node: any) { + isCompleted( + component: any, + componentStates: any[], + componentEvents: any[], + nodeEvents: any[], + node: any + ) { if (componentStates && componentStates.length) { const latestComponentState = componentStates[componentStates.length - 1]; return this.componentStateHasStudentWork(latestComponentState, component); diff --git a/src/main/webapp/wise5/components/audioOscillator/edit-audio-oscillator-advanced/edit-audio-oscillator-advanced.component.ts b/src/main/webapp/wise5/components/audioOscillator/edit-audio-oscillator-advanced/edit-audio-oscillator-advanced.component.ts index 2401417bbe..31efa79f6b 100644 --- a/src/main/webapp/wise5/components/audioOscillator/edit-audio-oscillator-advanced/edit-audio-oscillator-advanced.component.ts +++ b/src/main/webapp/wise5/components/audioOscillator/edit-audio-oscillator-advanced/edit-audio-oscillator-advanced.component.ts @@ -1,4 +1,4 @@ -import { EditAdvancedComponentAngularJSController } from "../../../../site/src/app/authoring-tool/edit-advanced-component/editAdvancedComponentAngularJSController"; +import { EditAdvancedComponentAngularJSController } from '../../../../site/src/app/authoring-tool/edit-advanced-component/editAdvancedComponentAngularJSController'; class EditAudioOscillatorAdvancedController extends EditAdvancedComponentAngularJSController { allowedConnectedComponentTypes = ['AudioOscillator']; @@ -10,5 +10,6 @@ export const EditAudioOscillatorAdvancedComponent = { componentId: '@' }, controller: EditAudioOscillatorAdvancedController, - templateUrl: 'wise5/components/audioOscillator/edit-audio-oscillator-advanced/edit-audio-oscillator-advanced.component.html' -} + templateUrl: + 'wise5/components/audioOscillator/edit-audio-oscillator-advanced/edit-audio-oscillator-advanced.component.html' +}; diff --git a/src/main/webapp/wise5/components/componentController.ts b/src/main/webapp/wise5/components/componentController.ts index a786659123..df26de6fc6 100644 --- a/src/main/webapp/wise5/components/componentController.ts +++ b/src/main/webapp/wise5/components/componentController.ts @@ -1,13 +1,13 @@ import * as angular from 'angular'; import * as $ from 'jquery'; -import { AnnotationService } from "../services/annotationService"; -import { ConfigService } from "../services/configService"; -import { NodeService } from "../services/nodeService"; -import { NotebookService } from "../services/notebookService"; -import { ProjectService } from "../services/projectService"; -import { StudentAssetService } from "../services/studentAssetService"; -import { UtilService } from "../services/utilService"; -import { StudentDataService } from "../services/studentDataService"; +import { AnnotationService } from '../services/annotationService'; +import { ConfigService } from '../services/configService'; +import { NodeService } from '../services/nodeService'; +import { NotebookService } from '../services/notebookService'; +import { ProjectService } from '../services/projectService'; +import { StudentAssetService } from '../services/studentAssetService'; +import { UtilService } from '../services/utilService'; +import { StudentDataService } from '../services/studentDataService'; import { NotificationService } from '../services/notificationService'; import { AudioRecorderService } from '../services/audioRecorderService'; import { Subscription } from 'rxjs'; @@ -52,22 +52,23 @@ class ComponentController { starterStateRequestSubscription: Subscription; constructor( - protected $filter: any, - protected $injector: any, - protected $mdDialog: any, - protected $q: any, - protected $rootScope: any, - protected $scope: any, - protected AnnotationService: AnnotationService, - protected AudioRecorderService: AudioRecorderService, - protected ConfigService: ConfigService, - protected NodeService: NodeService, - protected NotebookService: NotebookService, - protected NotificationService: NotificationService, - protected ProjectService: ProjectService, - protected StudentAssetService: StudentAssetService, - protected StudentDataService: StudentDataService, - protected UtilService: UtilService) { + protected $filter: any, + protected $injector: any, + protected $mdDialog: any, + protected $q: any, + protected $rootScope: any, + protected $scope: any, + protected AnnotationService: AnnotationService, + protected AudioRecorderService: AudioRecorderService, + protected ConfigService: ConfigService, + protected NodeService: NodeService, + protected NotebookService: NotebookService, + protected NotificationService: NotificationService, + protected ProjectService: ProjectService, + protected StudentAssetService: StudentAssetService, + protected StudentDataService: StudentDataService, + protected UtilService: UtilService + ) { this.$translate = this.$filter('translate'); this.nodeId = this.$scope.nodeId; this.componentContent = this.$scope.componentContent; @@ -91,7 +92,8 @@ class ComponentController { }; this.isStudentAttachmentEnabled = this.componentContent.isStudentAttachmentEnabled; - this.isStudentAudioRecordingEnabled = this.componentContent.isStudentAudioRecordingEnabled || false; + this.isStudentAudioRecordingEnabled = + this.componentContent.isStudentAudioRecordingEnabled || false; this.isPromptVisible = true; this.isSaveButtonVisible = false; this.isSubmitButtonVisible = false; @@ -104,7 +106,9 @@ class ComponentController { this.teacherWorkgroupId = this.$scope.teacherWorkgroupId; this.showAddToNotebookButton = - this.componentContent.showAddToNotebookButton == null ? true : this.componentContent.showAddToNotebookButton; + this.componentContent.showAddToNotebookButton == null + ? true + : this.componentContent.showAddToNotebookButton; if (this.isStudentMode()) { this.isPromptVisible = true; @@ -126,7 +130,11 @@ class ComponentController { } if (this.isStudentMode() || this.isGradingMode() || this.isGradingRevisionMode()) { - this.latestAnnotations = this.AnnotationService.getLatestComponentAnnotations(this.nodeId, this.componentId, this.workgroupId); + this.latestAnnotations = this.AnnotationService.getLatestComponentAnnotations( + this.nodeId, + this.componentId, + this.workgroupId + ); } if (this.isGradingMode() || this.isGradingRevisionMode()) { @@ -138,21 +146,23 @@ class ComponentController { } $onInit() { - this.snipImageSubscription = - this.ProjectService.snipImage$.subscribe(({ target, componentId }) => { - if (componentId === this.componentId) { - const imageObject = this.UtilService.getImageObjectFromImageElement(target); - if (imageObject != null) { - this.NotebookService.addNote(imageObject); + this.snipImageSubscription = this.ProjectService.snipImage$.subscribe( + ({ target, componentId }) => { + if (componentId === this.componentId) { + const imageObject = this.UtilService.getImageObjectFromImageElement(target); + if (imageObject != null) { + this.NotebookService.addNote(imageObject); + } } } - }); - this.starterStateRequestSubscription = - this.NodeService.starterStateRequest$.subscribe((args: any) => { - if (this.isForThisComponent(args)) { - this.generateStarterState(); + ); + this.starterStateRequestSubscription = this.NodeService.starterStateRequest$.subscribe( + (args: any) => { + if (this.isForThisComponent(args)) { + this.generateStarterState(); + } } - }); + ); } isStudentMode() { @@ -176,24 +186,29 @@ class ComponentController { } registerListeners() { - this.annotationSavedToServerSubscription = - this.AnnotationService.annotationSavedToServer$.subscribe(({ annotation }) => { - if (this.isEventTargetThisComponent(annotation)) { - this.latestAnnotations = this.AnnotationService - .getLatestComponentAnnotations(this.nodeId, this.componentId, this.workgroupId); + this.annotationSavedToServerSubscription = this.AnnotationService.annotationSavedToServer$.subscribe( + ({ annotation }) => { + if (this.isEventTargetThisComponent(annotation)) { + this.latestAnnotations = this.AnnotationService.getLatestComponentAnnotations( + this.nodeId, + this.componentId, + this.workgroupId + ); + } } - }); + ); - this.nodeSubmitClickedSubscription = - this.NodeService.nodeSubmitClicked$.subscribe(({ nodeId }) => { - if (this.nodeId === nodeId) { - this.handleNodeSubmit(); + this.nodeSubmitClickedSubscription = this.NodeService.nodeSubmitClicked$.subscribe( + ({ nodeId }) => { + if (this.nodeId === nodeId) { + this.handleNodeSubmit(); + } } - }); + ); this.registerStudentWorkSavedToServerListener(); this.$scope.$on('$destroy', () => { this.ngOnDestroy(); - }) + }); } ngOnDestroy() { @@ -246,16 +261,14 @@ class ComponentController { createOpenAssetChooserFunction() { return (params: any) => { this.openAssetChooser(params); - } + }; } /** * This function should be implemented by child components. * @param params and object containing key value pairs */ - openAssetChooser(params: any) { - - } + openAssetChooser(params: any) {} getFullAssetPath(fileName) { const assetsDirectoryPath = this.ConfigService.getProjectAssetsDirectoryPath(); @@ -264,21 +277,26 @@ class ComponentController { registerComponentWithParentNode() { if (this.$scope.$parent.nodeController != null) { - this.$scope.$parent.nodeController.registerComponentController(this.$scope, this.componentContent); + this.$scope.$parent.nodeController.registerComponentController( + this.$scope, + this.componentContent + ); } } broadcastDoneRenderingComponent() { this.NodeService.broadcastDoneRenderingComponent({ - nodeId: this.nodeId, componentId: this.componentId + nodeId: this.nodeId, + componentId: this.componentId }); } registerStudentWorkSavedToServerListener() { - this.studentWorkSavedToServerSubscription = - this.StudentDataService.studentWorkSavedToServer$.subscribe((args: any) => { - this.handleStudentWorkSavedToServer(args); - }); + this.studentWorkSavedToServerSubscription = this.StudentDataService.studentWorkSavedToServer$.subscribe( + (args: any) => { + this.handleStudentWorkSavedToServer(args); + } + ); } handleStudentWorkSavedToServer(args: any) { @@ -286,7 +304,9 @@ class ComponentController { if (this.isForThisComponent(componentState)) { this.setIsDirty(false); this.emitComponentDirty(this.getIsDirty()); - const clientSaveTime = this.ConfigService.convertToClientTimestamp(componentState.serverSaveTime); + const clientSaveTime = this.ConfigService.convertToClientTimestamp( + componentState.serverSaveTime + ); if (componentState.isSubmit) { this.setSubmittedMessage(clientSaveTime); this.lockIfNecessary(); @@ -304,9 +324,7 @@ class ComponentController { this.handleStudentWorkSavedToServerAdditionalProcessing(args); } - handleStudentWorkSavedToServerAdditionalProcessing(args: any) { - - } + handleStudentWorkSavedToServerAdditionalProcessing(args: any) {} handleNodeSubmit() { this.isSubmit = true; @@ -392,13 +410,15 @@ class ComponentController { this.StudentDataService.broadcastComponentSubmitTriggered({ nodeId: this.nodeId, componentId: this.componentId - }) + }); } disableComponentIfNecessary() { if (this.isLockAfterSubmit()) { - const componentStates = this.StudentDataService - .getComponentStatesByNodeIdAndComponentId(this.nodeId, this.componentId); + const componentStates = this.StudentDataService.getComponentStatesByNodeIdAndComponentId( + this.nodeId, + this.componentId + ); if (this.NodeService.isWorkSubmitted(componentStates)) { this.isDisabled = true; } @@ -454,8 +474,10 @@ class ComponentController { } processLatestStudentWork() { - const latestComponentState = - this.StudentDataService.getLatestComponentStateByNodeIdAndComponentId(this.nodeId, this.componentId); + const latestComponentState = this.StudentDataService.getLatestComponentStateByNodeIdAndComponentId( + this.nodeId, + this.componentId + ); if (latestComponentState) { const serverSaveTime = latestComponentState.serverSaveTime; @@ -588,8 +610,10 @@ class ComponentController { if (connectedComponents != null) { const componentStates = []; for (let connectedComponent of connectedComponents) { - const componentState = - this.StudentDataService.getLatestComponentStateByNodeIdAndComponentId(connectedComponent.nodeId, connectedComponent.componentId); + const componentState = this.StudentDataService.getLatestComponentStateByNodeIdAndComponentId( + connectedComponent.nodeId, + connectedComponent.componentId + ); if (componentState != null) { componentStates.push(this.UtilService.makeCopyOfJSONObject(componentState)); } @@ -605,9 +629,7 @@ class ComponentController { } } - setStudentWork(componentState) { - - } + setStudentWork(componentState) {} createMergedComponentState(componentStates) { return componentStates[0]; @@ -621,7 +643,9 @@ class ComponentController { const connectedComponentsAndTheirComponentStates = []; for (const connectedComponent of this.componentContent.connectedComponents) { const componentState = this.StudentDataService.getLatestComponentStateByNodeIdAndComponentId( - connectedComponent.nodeId, connectedComponent.componentId); + connectedComponent.nodeId, + connectedComponent.componentId + ); const connectedComponentsAndComponentState = { connectedComponent: connectedComponent, componentState: this.UtilService.makeCopyOfJSONObject(componentState) @@ -632,19 +656,17 @@ class ComponentController { } showCopyPublicNotebookItemButton() { - return this.ProjectService.isSpaceExists("public"); + return this.ProjectService.isSpaceExists('public'); } copyPublicNotebookItemButtonClicked() { - this.NotebookService.broadcastOpenNotebook( - { - nodeId: this.nodeId, - componentId: this.componentId, - insertMode: true, - requester: this.nodeId + '-' + this.componentId, - visibleSpace: "public" - } - ); + this.NotebookService.broadcastOpenNotebook({ + nodeId: this.nodeId, + componentId: this.componentId, + insertMode: true, + requester: this.nodeId + '-' + this.componentId, + visibleSpace: 'public' + }); } importWorkByStudentWorkId(studentWorkId) { @@ -670,8 +692,11 @@ class ComponentController { } isAddToNotebookEnabled() { - return this.isNotebookEnabled() && this.isStudentNoteClippingEnabled() && - this.showAddToNotebookButton; + return ( + this.isNotebookEnabled() && + this.isStudentNoteClippingEnabled() && + this.showAddToNotebookButton + ); } isEventTargetThisComponent(args) { @@ -761,9 +786,23 @@ class ComponentController { const componentId = this.componentId; const toWorkgroupId = this.ConfigService.getWorkgroupId(); if (type === 'autoScore') { - return this.AnnotationService.createAutoScoreAnnotation(runId, periodId, nodeId, componentId, toWorkgroupId, data); + return this.AnnotationService.createAutoScoreAnnotation( + runId, + periodId, + nodeId, + componentId, + toWorkgroupId, + data + ); } else if (type === 'autoComment') { - return this.AnnotationService.createAutoCommentAnnotation(runId, periodId, nodeId, componentId, toWorkgroupId, data); + return this.AnnotationService.createAutoCommentAnnotation( + runId, + periodId, + nodeId, + componentId, + toWorkgroupId, + data + ); } } @@ -776,26 +815,28 @@ class ComponentController { } registerNotebookItemChosenListener() { - this.notebookItemChosenSubscription = this.NotebookService.notebookItemChosen$ - .subscribe(({ requester, notebookItem }) => { - if (requester === `${this.nodeId}-${this.componentId}`) { - const studentWorkId = notebookItem.content.studentWorkIds[0]; - this.importWorkByStudentWorkId(studentWorkId); + this.notebookItemChosenSubscription = this.NotebookService.notebookItemChosen$.subscribe( + ({ requester, notebookItem }) => { + if (requester === `${this.nodeId}-${this.componentId}`) { + const studentWorkId = notebookItem.content.studentWorkIds[0]; + this.importWorkByStudentWorkId(studentWorkId); + } } - }); + ); } registerAudioRecordedListener() { - this.audioRecordedSubscription = - this.AudioRecorderService.audioRecorded$.subscribe(({requester, audioFile}) => { - if (requester === `${this.nodeId}-${this.componentId}`) { - this.StudentAssetService.uploadAsset(audioFile).then((studentAsset) => { - this.attachStudentAsset(studentAsset).then(() => { - this.StudentAssetService.deleteAsset(studentAsset); - }) - }); + this.audioRecordedSubscription = this.AudioRecorderService.audioRecorded$.subscribe( + ({ requester, audioFile }) => { + if (requester === `${this.nodeId}-${this.componentId}`) { + this.StudentAssetService.uploadAsset(audioFile).then((studentAsset) => { + this.attachStudentAsset(studentAsset).then(() => { + this.StudentAssetService.deleteAsset(studentAsset); + }); + }); + } } - }); + ); } /** @@ -830,26 +871,29 @@ class ComponentController { $scope.nodeId = nodeId; $scope.componentId = componentId; $scope.componentState = componentState; - $scope.closeDialog = function() { + $scope.closeDialog = function () { $mdDialog.hide(); }; } DialogController.$inject = ['$scope', '$mdDialog', 'nodeId', 'componentId', 'componentState']; - const doneRenderingComponentSubscription = - this.NodeService.doneRenderingComponent$.subscribe(({ nodeId, componentId }) => { - if (componentState.nodeId == nodeId && componentState.componentId == componentId) { - setTimeout(() => { - const componentService = this.$injector.get(componentState.componentType + 'Service'); - componentService.generateImageFromRenderedComponentState(componentState).then(image => { - clearTimeout(destroyDoneRenderingComponentListenerTimeout); - doneRenderingComponentSubscription.unsubscribe(); - deferred.resolve(image); - this.$mdDialog.hide(); - }); - }, 1000); + const doneRenderingComponentSubscription = this.NodeService.doneRenderingComponent$.subscribe( + ({ nodeId, componentId }) => { + if (componentState.nodeId == nodeId && componentState.componentId == componentId) { + setTimeout(() => { + const componentService = this.$injector.get(componentState.componentType + 'Service'); + componentService + .generateImageFromRenderedComponentState(componentState) + .then((image) => { + clearTimeout(destroyDoneRenderingComponentListenerTimeout); + doneRenderingComponentSubscription.unsubscribe(); + deferred.resolve(image); + this.$mdDialog.hide(); + }); + }, 1000); + } } - }); + ); /* * Set a timeout to destroy the listener in case there is an error creating the image and * we don't get to destroying it above. diff --git a/src/main/webapp/wise5/components/componentService.ts b/src/main/webapp/wise5/components/componentService.ts index 53165948d6..d2dd1356e9 100644 --- a/src/main/webapp/wise5/components/componentService.ts +++ b/src/main/webapp/wise5/components/componentService.ts @@ -1,12 +1,11 @@ 'use strict'; import { Injectable } from '@angular/core'; -import { StudentDataService } from "../services/studentDataService"; -import { UtilService } from "../services/utilService"; +import { StudentDataService } from '../services/studentDataService'; +import { UtilService } from '../services/utilService'; @Injectable() export class ComponentService { - constructor( protected StudentDataService: StudentDataService, protected UtilService: UtilService diff --git a/src/main/webapp/wise5/components/conceptMap/conceptMapAuthoring.ts b/src/main/webapp/wise5/components/conceptMap/conceptMapAuthoring.ts index 5e0d017be0..5da7215f29 100644 --- a/src/main/webapp/wise5/components/conceptMap/conceptMapAuthoring.ts +++ b/src/main/webapp/wise5/components/conceptMap/conceptMapAuthoring.ts @@ -182,7 +182,7 @@ class ConceptMapAuthoringController extends EditComponentController { saveStarterConceptMap(): void { if (confirm(this.$translate('conceptMap.areYouSureYouWantToSaveTheStarterConceptMap'))) { - this.NodeService.requestStarterState({nodeId: this.nodeId, componentId: this.componentId}); + this.NodeService.requestStarterState({ nodeId: this.nodeId, componentId: this.componentId }); } } @@ -237,7 +237,6 @@ class ConceptMapAuthoringController extends EditComponentController { this.componentChanged(); } } - } const ConceptMapAuthoring = { @@ -248,6 +247,6 @@ const ConceptMapAuthoring = { controller: ConceptMapAuthoringController, controllerAs: 'conceptMapController', templateUrl: 'wise5/components/conceptMap/authoring.html' -} +}; export default ConceptMapAuthoring; diff --git a/src/main/webapp/wise5/components/conceptMap/conceptMapAuthoringComponentModule.ts b/src/main/webapp/wise5/components/conceptMap/conceptMapAuthoringComponentModule.ts index 77cda9f1e9..f9d72cc94a 100644 --- a/src/main/webapp/wise5/components/conceptMap/conceptMapAuthoringComponentModule.ts +++ b/src/main/webapp/wise5/components/conceptMap/conceptMapAuthoringComponentModule.ts @@ -13,7 +13,7 @@ const conceptMapAuthoringComponentModule = angular .component('editConceptMapAdvanced', EditConceptMapAdvancedComponent) .config([ '$translatePartialLoaderProvider', - $translatePartialLoaderProvider => { + ($translatePartialLoaderProvider) => { $translatePartialLoaderProvider.addPart('components/conceptMap/i18n'); } ]); diff --git a/src/main/webapp/wise5/components/conceptMap/conceptMapComponentModule.ts b/src/main/webapp/wise5/components/conceptMap/conceptMapComponentModule.ts index f9d9870cd7..fdbe20b791 100644 --- a/src/main/webapp/wise5/components/conceptMap/conceptMapComponentModule.ts +++ b/src/main/webapp/wise5/components/conceptMap/conceptMapComponentModule.ts @@ -11,7 +11,7 @@ let conceptMapComponentModule = angular .controller('ConceptMapController', ConceptMapController) .config([ '$translatePartialLoaderProvider', - $translatePartialLoaderProvider => { + ($translatePartialLoaderProvider) => { $translatePartialLoaderProvider.addPart('components/conceptMap/i18n'); } ]); diff --git a/src/main/webapp/wise5/components/conceptMap/conceptMapController.ts b/src/main/webapp/wise5/components/conceptMap/conceptMapController.ts index 70131ece53..7f4ca60500 100644 --- a/src/main/webapp/wise5/components/conceptMap/conceptMapController.ts +++ b/src/main/webapp/wise5/components/conceptMap/conceptMapController.ts @@ -603,7 +603,7 @@ class ConceptMapController extends ComponentController { * any("Sun (Infrared Radiation) Space", "Sun (Heat) Space", "Sun (Solar Radiation) Space") * these dynamic arguments will be placed in the arguments variable */ - const any = function() { + const any = function () { return thisConceptMapService.any(componentContent, conceptMapData, arguments); }; @@ -618,7 +618,7 @@ class ConceptMapController extends ComponentController { * all("Sun (Infrared Radiation) Space", "Sun (Heat) Space", "Sun (Solar Radiation) Space") * these dynamic arguments will be placed in the arguments variable */ - const all = function() { + const all = function () { return thisConceptMapService.all(componentContent, conceptMapData, arguments); }; @@ -626,7 +626,7 @@ class ConceptMapController extends ComponentController { * create the setResult function that can be called in the custom rule * evaluator code */ - const setResult = function(result: any) { + const setResult = function (result: any) { thisResult = result; }; @@ -872,15 +872,15 @@ class ConceptMapController extends ComponentController { this.newlyCreatedLink = null; if (!this.isDisabled) { - this.draw.mousedown(event => { + this.draw.mousedown((event) => { this.svgMouseDown(event); }); - this.draw.mouseup(event => { + this.draw.mouseup((event) => { this.svgMouseUp(event); }); - this.draw.mousemove(event => { + this.draw.mousemove((event) => { this.svgMouseMove(event); }); @@ -902,7 +902,7 @@ class ConceptMapController extends ComponentController { * listen for the dragover event which occurs when the user is * dragging a node onto the svg */ - svg[0].addEventListener('dragover', event => { + svg[0].addEventListener('dragover', (event) => { /* * prevent the default because if we don't, the user won't * be able to drop a new node instance onto the svg in the @@ -927,7 +927,7 @@ class ConceptMapController extends ComponentController { * listen for the drop event which occurs when the student drops * a new node onto the svg */ - svg[0].addEventListener('drop', event => { + svg[0].addEventListener('drop', (event) => { /* * the user has dropped a new node onto the svg to create a * new instance of a node @@ -1294,40 +1294,40 @@ class ConceptMapController extends ComponentController { } setNodeMouseEvents(conceptMapNode) { - conceptMapNode.setNodeMouseOver(event => { + conceptMapNode.setNodeMouseOver((event) => { this.nodeMouseOver(event); }); - conceptMapNode.setNodeMouseOut(event => { + conceptMapNode.setNodeMouseOut((event) => { this.nodeMouseOut(event); }); - conceptMapNode.setConnectorMouseDown(event => { + conceptMapNode.setConnectorMouseDown((event) => { this.disableNodeDragging(); this.connectorMouseDown(event); }); - conceptMapNode.setNodeMouseDown(event => { + conceptMapNode.setNodeMouseDown((event) => { this.nodeMouseDown(event); }); - conceptMapNode.setNodeMouseUp(event => { + conceptMapNode.setNodeMouseUp((event) => { this.nodeMouseUp(event); }); - conceptMapNode.setDeleteButtonMouseDown(event => { + conceptMapNode.setDeleteButtonMouseDown((event) => { this.nodeDeleteButtonMouseDown(event); }); - conceptMapNode.setDeleteButtonMouseOver(event => { + conceptMapNode.setDeleteButtonMouseOver((event) => { this.nodeDeleteButtonMouseOver(event); }); - conceptMapNode.setDeleteButtonMouseOut(event => { + conceptMapNode.setDeleteButtonMouseOut((event) => { this.nodeDeleteButtonMouseOut(event); }); - conceptMapNode.setDragMove(event => { + conceptMapNode.setDragMove((event) => { this.nodeDragMove(event); }); } @@ -1623,23 +1623,23 @@ class ConceptMapController extends ComponentController { } setLinkMouseEvents(link) { - link.setLinkMouseDown(event => { + link.setLinkMouseDown((event) => { this.linkMouseDown(event); }); - link.setLinkTextMouseDown(event => { + link.setLinkTextMouseDown((event) => { this.linkTextMouseDown(event); }); - link.setLinkMouseOver(event => { + link.setLinkMouseOver((event) => { this.linkMouseOver(event); }); - link.setLinkMouseOut(event => { + link.setLinkMouseOut((event) => { this.linkMouseOut(event); }); - link.setDeleteButtonClicked(event => { + link.setDeleteButtonClicked((event) => { this.linkDeleteButtonClicked(event, link); }); } @@ -1819,7 +1819,7 @@ class ConceptMapController extends ComponentController { let svgString = serializer.serializeToString(svgElement); // find all the images in the svg and replace them with Base64 images - this.ConceptMapService.getHrefToBase64ImageReplacements(svgString).then(images => { + this.ConceptMapService.getHrefToBase64ImageReplacements(svgString).then((images) => { /* * Loop through all the image objects. Each object contains * an image href and a Base64 image. @@ -1849,7 +1849,7 @@ class ConceptMapController extends ComponentController { * in the onload callback function */ const thisUtilService = this.UtilService; - image.onload = event => { + image.onload = (event) => { const image: any = event.target; // set the dimensions of the canvas @@ -1935,7 +1935,7 @@ class ConceptMapController extends ComponentController { * @param componentState A component state. */ setComponentStateAsBackgroundImage(componentState) { - this.generateImageFromComponentState(componentState).then(image => { + this.generateImageFromComponentState(componentState).then((image) => { const stretchBackground = false; this.setBackgroundImage(image.url, stretchBackground); }); @@ -1960,8 +1960,11 @@ class ConceptMapController extends ComponentController { } generateStarterState(): void { - this.NodeService.respondStarterState({nodeId: this.nodeId, componentId: this.componentId, - starterState: this.getConceptMapData()}); + this.NodeService.respondStarterState({ + nodeId: this.nodeId, + componentId: this.componentId, + starterState: this.getConceptMapData() + }); } } diff --git a/src/main/webapp/wise5/components/conceptMap/conceptMapLink.ts b/src/main/webapp/wise5/components/conceptMap/conceptMapLink.ts index 7ffda756b1..0a887d5e01 100644 --- a/src/main/webapp/wise5/components/conceptMap/conceptMapLink.ts +++ b/src/main/webapp/wise5/components/conceptMap/conceptMapLink.ts @@ -894,12 +894,12 @@ class ConceptMapLink { this.deleteButtonGroup.y(y); // set the listener for when the mouse is over the group - this.deleteButtonGroup.mouseover(event => { + this.deleteButtonGroup.mouseover((event) => { this.deleteButtonGroupMouseOver(event); }); // set the listener for when the mouse moves out of the group - this.deleteButtonGroup.mouseout(event => { + this.deleteButtonGroup.mouseout((event) => { this.deleteButtonGroupMouseOut(event); }); diff --git a/src/main/webapp/wise5/components/conceptMap/conceptMapService.ts b/src/main/webapp/wise5/components/conceptMap/conceptMapService.ts index 7876820088..564898b6ab 100644 --- a/src/main/webapp/wise5/components/conceptMap/conceptMapService.ts +++ b/src/main/webapp/wise5/components/conceptMap/conceptMapService.ts @@ -13,7 +13,6 @@ import { UtilService } from '../../services/utilService'; @Injectable() export class ConceptMapService extends ComponentService { - constructor( private upgrade: UpgradeModule, private ConfigService: ConfigService, @@ -51,8 +50,13 @@ export class ConceptMapService extends ComponentService { return component; } - isCompleted(component: any, componentStates: any[], componentEvents: any[], nodeEvents: any[], - node: any) { + isCompleted( + component: any, + componentStates: any[], + componentEvents: any[], + nodeEvents: any[], + node: any + ) { if (componentStates != null && componentStates.length > 0) { if (this.isSubmitRequired(node, component)) { return this.hasComponentStateWithIsSubmitTrue(componentStates); @@ -70,7 +74,7 @@ export class ConceptMapService extends ComponentService { if ( componentState.isSubmit == true || (componentState.studentData.submitCounter != null && - componentState.studentData.submitCounter > 0) + componentState.studentData.submitCounter > 0) ) { return true; } @@ -98,8 +102,18 @@ export class ConceptMapService extends ComponentService { * @param showLabel whether to show the label * @param a ConceptMapNode */ - newConceptMapNode(draw: any, id: string, originalId: string, filePath: string, label: string, - x: number, y: number, width: number, height: number, showLabel: boolean) { + newConceptMapNode( + draw: any, + id: string, + originalId: string, + filePath: string, + label: string, + x: number, + y: number, + width: number, + height: number, + showLabel: boolean + ) { return new ConceptMapNode( draw, id, @@ -449,8 +463,12 @@ export class ConceptMapService extends ComponentService { * @returns the links with the given source node label, link label, and * destination node label */ - getLinksByLabels(conceptMapData: any, nodeLabel: string, linkLabel: string, - otherNodeLabel: string) { + getLinksByLabels( + conceptMapData: any, + nodeLabel: string, + linkLabel: string, + otherNodeLabel: string + ) { const resultLinks = []; const links = conceptMapData.links; if (links != null) { @@ -463,11 +481,17 @@ export class ConceptMapService extends ComponentService { return resultLinks; } - isLinkMatchingSourceLinkDestination(link: any, sourceLabel: string, linkLabel: string, - destinationLabel: string) { - return (sourceLabel === link.sourceNodeLabel || sourceLabel === 'any') && - (linkLabel === link.label || linkLabel === 'any') && - (destinationLabel === link.destinationNodeLabel || destinationLabel === 'any'); + isLinkMatchingSourceLinkDestination( + link: any, + sourceLabel: string, + linkLabel: string, + destinationLabel: string + ) { + return ( + (sourceLabel === link.sourceNodeLabel || sourceLabel === 'any') && + (linkLabel === link.label || linkLabel === 'any') && + (destinationLabel === link.destinationNodeLabel || destinationLabel === 'any') + ); } /** @@ -751,7 +775,7 @@ export class ConceptMapService extends ComponentService { let svgString = svgElement.innerHTML; // find all the images in the svg and replace them with Base64 images - this.getHrefToBase64ImageReplacements(svgString, true).then(images => { + this.getHrefToBase64ImageReplacements(svgString, true).then((images) => { /* * Loop through all the image objects. Each object contains * an image href and a Base64 image. @@ -801,7 +825,7 @@ export class ConceptMapService extends ComponentService { const thisUtilService = this.UtilService; // the function that is called after the image is fully loaded - image.onload = event => { + image.onload = (event) => { // get the image that was loaded const image: any = event.target; @@ -817,13 +841,13 @@ export class ConceptMapService extends ComponentService { const imageObject = thisUtilService.getImageObjectFromBase64String(base64Image); // create a student asset image - this.StudentAssetService.uploadAsset(imageObject).then(unreferencedAsset => { + this.StudentAssetService.uploadAsset(imageObject).then((unreferencedAsset) => { /* * make a copy of the unreferenced asset so that we * get a referenced asset */ this.StudentAssetService.copyAssetForReference(unreferencedAsset).then( - referencedAsset => { + (referencedAsset) => { if (referencedAsset != null) { /* * get the asset url @@ -945,7 +969,7 @@ export class ConceptMapService extends ComponentService { const ctx = myCanvas.getContext('2d'); // the function that is called after the image is fully loaded - image.onload = function(event) { + image.onload = function (event) { // get the image that was loaded const image: any = event.target; @@ -993,8 +1017,10 @@ export class ConceptMapService extends ComponentService { return this.hasAnyNodeOrLink(nodes, links); } else { if (this.hasStarterConceptMap(componentContent)) { - return this.isStudentConceptMapDifferentThanStarterConceptMap(conceptMapData, - componentContent.starterConceptMap); + return this.isStudentConceptMapDifferentThanStarterConceptMap( + conceptMapData, + componentContent.starterConceptMap + ); } else { return this.hasAnyNodeOrLink(nodes, links); } @@ -1036,8 +1062,10 @@ export class ConceptMapService extends ComponentService { * @return whether the student concept map is different than the starter * concept map */ - isStudentConceptMapDifferentThanStarterConceptMap(studentConceptMap: any, - starterConceptMap: any) { + isStudentConceptMapDifferentThanStarterConceptMap( + studentConceptMap: any, + starterConceptMap: any + ) { if (studentConceptMap != null && starterConceptMap != null) { if (!this.isAllNodesEqual(studentConceptMap.nodes, starterConceptMap.nodes)) { return true; @@ -1076,20 +1104,24 @@ export class ConceptMapService extends ComponentService { } isNodesEqual(node1: any, node2: any) { - return node1.originalId === node2.originalId && - node1.instanceId === node2.instanceId && - node1.x === node2.x && - node1.y === node2.y; + return ( + node1.originalId === node2.originalId && + node1.instanceId === node2.instanceId && + node1.x === node2.x && + node1.y === node2.y + ); } isLinksEqual(link1: any, link2: any) { - return link1.label === link2.label && - link1.originalId === link2.originalId && - link1.instanceId === link2.instanceId && - link1.sourceNodeOriginalId === link2.sourceNodeOriginalId && - link1.sourceNodeInstanceId === link2.sourceNodeInstanceId && - link1.destinationNodeOriginalId === link2.destinationNodeOriginalId && - link1.destinationNodeInstanceId === link2.destinationNodeInstanceId; + return ( + link1.label === link2.label && + link1.originalId === link2.originalId && + link1.instanceId === link2.instanceId && + link1.sourceNodeOriginalId === link2.sourceNodeOriginalId && + link1.sourceNodeInstanceId === link2.sourceNodeInstanceId && + link1.destinationNodeOriginalId === link2.destinationNodeOriginalId && + link1.destinationNodeInstanceId === link2.destinationNodeInstanceId + ); } /** @@ -1114,7 +1146,7 @@ export class ConceptMapService extends ComponentService { let svgString = serializer.serializeToString(svgElement); // find all the images in the svg and replace them with Base64 images - this.getHrefToBase64ImageReplacements(svgString).then(images => { + this.getHrefToBase64ImageReplacements(svgString).then((images) => { /* * Loop through all the image objects. Each object contains * an image href and a Base64 image. @@ -1150,7 +1182,7 @@ export class ConceptMapService extends ComponentService { const image = new Image(); // the function that is called after the image is fully loaded - image.onload = event => { + image.onload = (event) => { // get the image that was loaded let image: any = event.target; @@ -1166,7 +1198,7 @@ export class ConceptMapService extends ComponentService { const imageObject = this.UtilService.getImageObjectFromBase64String(base64Image); // add the image to the student assets - this.StudentAssetService.uploadAsset(imageObject).then(asset => { + this.StudentAssetService.uploadAsset(imageObject).then((asset) => { resolve(asset); }); }; diff --git a/src/main/webapp/wise5/components/conceptMap/edit-concept-map-advanced/edit-concept-map-advanced.component.ts b/src/main/webapp/wise5/components/conceptMap/edit-concept-map-advanced/edit-concept-map-advanced.component.ts index 7a510e8e23..522e3ccbf1 100644 --- a/src/main/webapp/wise5/components/conceptMap/edit-concept-map-advanced/edit-concept-map-advanced.component.ts +++ b/src/main/webapp/wise5/components/conceptMap/edit-concept-map-advanced/edit-concept-map-advanced.component.ts @@ -1,7 +1,7 @@ -import { EditAdvancedComponentAngularJSController } from "../../../../site/src/app/authoring-tool/edit-advanced-component/editAdvancedComponentAngularJSController"; -import { NodeService } from "../../../services/nodeService"; -import { TeacherProjectService } from "../../../services/teacherProjectService"; -import { UtilService } from "../../../services/utilService"; +import { EditAdvancedComponentAngularJSController } from '../../../../site/src/app/authoring-tool/edit-advanced-component/editAdvancedComponentAngularJSController'; +import { NodeService } from '../../../services/nodeService'; +import { TeacherProjectService } from '../../../services/teacherProjectService'; +import { UtilService } from '../../../services/utilService'; class EditConceptMapAdvancedController extends EditAdvancedComponentAngularJSController { allowedConnectedComponentTypes = ['ConceptMap', 'Draw', 'Embedded', 'Graph', 'Label', 'Table']; @@ -77,11 +77,7 @@ class EditConceptMapAdvancedController extends EditAdvancedComponentAngularJSCon ruleDeleteButtonClicked(index: number): void { const rule = this.authoringComponentContent.rules[index]; const ruleName = rule.name; - if ( - confirm( - $localize`Are you sure you want to delete this rule?\n\nRule Name: ${ruleName}` - ) - ) { + if (confirm($localize`Are you sure you want to delete this rule?\n\nRule Name: ${ruleName}`)) { this.authoringComponentContent.rules.splice(index, 1); this.componentChanged(); } @@ -147,5 +143,6 @@ export const EditConceptMapAdvancedComponent = { componentId: '@' }, controller: EditConceptMapAdvancedController, - templateUrl: 'wise5/components/conceptMap/edit-concept-map-advanced/edit-concept-map-advanced.component.html' -} + templateUrl: + 'wise5/components/conceptMap/edit-concept-map-advanced/edit-concept-map-advanced.component.html' +}; diff --git a/src/main/webapp/wise5/components/discussion/classResponseController.ts b/src/main/webapp/wise5/components/discussion/classResponseController.ts index 027217d859..92b0350c4d 100644 --- a/src/main/webapp/wise5/components/discussion/classResponseController.ts +++ b/src/main/webapp/wise5/components/discussion/classResponseController.ts @@ -51,7 +51,7 @@ class ClassResponseController { } injectLinks(response) { - return response.replace(this.urlMatcher, match => { + return response.replace(this.urlMatcher, (match) => { let matchUrl = match; if (!match.startsWith('http')) { /* diff --git a/src/main/webapp/wise5/components/discussion/discussionAuthoring.ts b/src/main/webapp/wise5/components/discussion/discussionAuthoring.ts index b44237f3fb..d30b4b0c2f 100644 --- a/src/main/webapp/wise5/components/discussion/discussionAuthoring.ts +++ b/src/main/webapp/wise5/components/discussion/discussionAuthoring.ts @@ -5,7 +5,6 @@ import { EditComponentController } from '../../authoringTool/components/editComp @Directive() class DiscussionAuthoringController extends EditComponentController { - static $inject = [ '$filter', 'ConfigService', @@ -13,7 +12,7 @@ class DiscussionAuthoringController extends EditComponentController { 'NotificationService', 'ProjectAssetService', 'ProjectService', - 'UtilService', + 'UtilService' ]; constructor( @@ -32,10 +31,9 @@ class DiscussionAuthoringController extends EditComponentController { NotificationService, ProjectAssetService, ProjectService, - UtilService, + UtilService ); } - } const DiscussionAuthoring = { @@ -46,6 +44,6 @@ const DiscussionAuthoring = { controller: DiscussionAuthoringController, controllerAs: 'discussionController', templateUrl: 'wise5/components/discussion/authoring.html' -} +}; export default DiscussionAuthoring; diff --git a/src/main/webapp/wise5/components/discussion/discussionAuthoringComponentModule.ts b/src/main/webapp/wise5/components/discussion/discussionAuthoringComponentModule.ts index 4e4168ab53..77bf783b60 100644 --- a/src/main/webapp/wise5/components/discussion/discussionAuthoringComponentModule.ts +++ b/src/main/webapp/wise5/components/discussion/discussionAuthoringComponentModule.ts @@ -13,7 +13,7 @@ const discussionAuthoringComponentModule = angular .component('editDiscussionAdvanced', EditDiscussionAdvancedComponent) .config([ '$translatePartialLoaderProvider', - $translatePartialLoaderProvider => { + ($translatePartialLoaderProvider) => { $translatePartialLoaderProvider.addPart('components/discussion/i18n'); } ]); diff --git a/src/main/webapp/wise5/components/discussion/discussionComponentModule.ts b/src/main/webapp/wise5/components/discussion/discussionComponentModule.ts index 0c21d17ffe..de6836789b 100644 --- a/src/main/webapp/wise5/components/discussion/discussionComponentModule.ts +++ b/src/main/webapp/wise5/components/discussion/discussionComponentModule.ts @@ -15,7 +15,7 @@ const discussionComponentModule = angular .component('classResponse', ClassResponse) .config([ '$translatePartialLoaderProvider', - $translatePartialLoaderProvider => { + ($translatePartialLoaderProvider) => { $translatePartialLoaderProvider.addPart('components/discussion/i18n'); } ]); diff --git a/src/main/webapp/wise5/components/discussion/discussionController.ts b/src/main/webapp/wise5/components/discussion/discussionController.ts index 5fc76f4877..bf1fbe6363 100644 --- a/src/main/webapp/wise5/components/discussion/discussionController.ts +++ b/src/main/webapp/wise5/components/discussion/discussionController.ts @@ -207,7 +207,7 @@ class DiscussionController extends ComponentController { } initializeScopeSubmitButtonClicked() { - this.$scope.submitbuttonclicked = componentStateReplyingTo => { + this.$scope.submitbuttonclicked = (componentStateReplyingTo) => { if (componentStateReplyingTo && componentStateReplyingTo.replyText) { const componentState = componentStateReplyingTo; const componentStateId = componentState.id; @@ -232,7 +232,7 @@ class DiscussionController extends ComponentController { const deferred = this.$q.defer(); if (this.$scope.discussionController.isDirty && this.$scope.discussionController.isSubmit) { const action = 'submit'; - this.$scope.discussionController.createComponentState(action).then(componentState => { + this.$scope.discussionController.createComponentState(action).then((componentState) => { this.$scope.discussionController.clearComponentValues(); this.$scope.discussionController.isDirty = false; deferred.resolve(componentState); @@ -251,20 +251,21 @@ class DiscussionController extends ComponentController { } registerStudentWorkSavedToServerListener() { - this.studentWorkSavedToServerSubscription = - this.StudentDataService.studentWorkSavedToServer$.subscribe((args: any) => { - const componentState = args.studentWork; - if (this.isWorkFromThisComponent(componentState)) { - if (this.isClassmateResponsesGated() && !this.retrievedClassmateResponses) { - this.getClassmateResponses(); - } else { - this.addClassResponse(componentState); + this.studentWorkSavedToServerSubscription = this.StudentDataService.studentWorkSavedToServer$.subscribe( + (args: any) => { + const componentState = args.studentWork; + if (this.isWorkFromThisComponent(componentState)) { + if (this.isClassmateResponsesGated() && !this.retrievedClassmateResponses) { + this.getClassmateResponses(); + } else { + this.addClassResponse(componentState); + } + this.disableComponentIfNecessary(); + this.sendPostToStudentsInThread(componentState); } - this.disableComponentIfNecessary(); - this.sendPostToStudentsInThread(componentState); + this.isSubmit = null; } - this.isSubmit = null; - }); + ); } sendPostToStudentsInThread(componentState) { @@ -278,7 +279,7 @@ class DiscussionController extends ComponentController { const componentId = componentState.componentId; const usernamesArray = this.ConfigService.getUsernamesByWorkgroupId(fromWorkgroupId); const usernames = usernamesArray - .map(obj => { + .map((obj) => { return obj.name; }) .join(', '); @@ -374,17 +375,18 @@ class DiscussionController extends ComponentController { } registerStudentWorkReceivedListener() { - this.studentWorkReceivedSubscription = this.StudentDataService.studentWorkReceived$ - .subscribe((componentState) => { - if ( - (this.isWorkFromThisComponent(componentState) || - this.isWorkFromConnectedComponent(componentState)) && - this.isWorkFromClassmate(componentState) && - this.retrievedClassmateResponses - ) { - this.addClassResponse(componentState); + this.studentWorkReceivedSubscription = this.StudentDataService.studentWorkReceived$.subscribe( + (componentState) => { + if ( + (this.isWorkFromThisComponent(componentState) || + this.isWorkFromConnectedComponent(componentState)) && + this.isWorkFromClassmate(componentState) && + this.retrievedClassmateResponses + ) { + this.addClassResponse(componentState); + } } - }); + ); } isWorkFromClassmate(componentState) { @@ -414,7 +416,7 @@ class DiscussionController extends ComponentController { () => { return this.$mdMedia('gt-sm'); }, - md => { + (md) => { this.$scope.mdScreen = md; } ); @@ -423,10 +425,11 @@ class DiscussionController extends ComponentController { getClassmateResponses(components = [{ nodeId: this.nodeId, componentId: this.componentId }]) { const runId = this.ConfigService.getRunId(); const periodId = this.ConfigService.getPeriodId(); - this.DiscussionService.getClassmateResponses(runId, periodId, components) - .then((result: any) => { - this.setClassResponses(result.studentWorkList, result.annotations); - }); + this.DiscussionService.getClassmateResponses(runId, periodId, components).then( + (result: any) => { + this.setClassResponses(result.studentWorkList, result.annotations); + } + ); } submitButtonClicked() { @@ -606,7 +609,7 @@ class DiscussionController extends ComponentController { threadHasPostFromThisComponentAndWorkgroupId(componentState) { const thisComponentId = this.componentId; const thisWorkgroupId = this.workgroupId; - return componentState => { + return (componentState) => { if ( componentState.componentId === thisComponentId && componentState.workgroupId === thisWorkgroupId @@ -630,13 +633,13 @@ class DiscussionController extends ComponentController { const usernames = this.ConfigService.getUsernamesByWorkgroupId(workgroupId); if (usernames.length > 0) { componentState.usernames = usernames - .map(function(obj) { + .map(function (obj) { return obj.name; }) .join(', '); } else if (componentState.usernamesArray != null) { componentState.usernames = componentState.usernamesArray - .map(function(obj) { + .map(function (obj) { return obj.name; }) .join(', '); @@ -786,7 +789,6 @@ class DiscussionController extends ComponentController { } return annotations; } - } export default DiscussionController; diff --git a/src/main/webapp/wise5/components/discussion/discussionService.ts b/src/main/webapp/wise5/components/discussion/discussionService.ts index ecad5d351b..7249536a45 100644 --- a/src/main/webapp/wise5/components/discussion/discussionService.ts +++ b/src/main/webapp/wise5/components/discussion/discussionService.ts @@ -11,7 +11,8 @@ import { HttpClient, HttpParams } from '@angular/common/http'; export class DiscussionService extends ComponentService { TeacherDataService: TeacherDataService; - constructor(private upgrade: UpgradeModule, + constructor( + private upgrade: UpgradeModule, private http: HttpClient, private ConfigService: ConfigService, protected StudentDataService: StudentDataService, @@ -90,10 +91,10 @@ export class DiscussionService extends ComponentService { getClassmateResponses(runId: number, periodId: number, components: any[]) { return new Promise((resolve, reject) => { let params = new HttpParams() - .set('runId', runId + '') - .set('periodId', periodId + '') - .set('getStudentWork', true + '') - .set('getAnnotations', true + ''); + .set('runId', runId + '') + .set('periodId', periodId + '') + .set('getStudentWork', true + '') + .set('getAnnotations', true + ''); for (const component of components) { params = params.append('components', JSON.stringify(component)); } @@ -101,9 +102,12 @@ export class DiscussionService extends ComponentService { params: params }; const url = this.ConfigService.getConfigParam('studentDataURL'); - this.http.get(url, options).toPromise().then(data => { - resolve(data); - }); + this.http + .get(url, options) + .toPromise() + .then((data) => { + resolve(data); + }); }); } @@ -155,8 +159,10 @@ export class DiscussionService extends ComponentService { return componentState.studentData.componentStateIdReplyingTo == null; } - isTopLevelComponentStateIdFound(topLevelComponentStateIdsFound: string[], - componentStateId: string) { + isTopLevelComponentStateIdFound( + topLevelComponentStateIdsFound: string[], + componentStateId: string + ) { return topLevelComponentStateIdsFound.indexOf(componentStateId) !== -1; } diff --git a/src/main/webapp/wise5/components/discussion/edit-discussion-advanced/edit-discussion-advanced.component.ts b/src/main/webapp/wise5/components/discussion/edit-discussion-advanced/edit-discussion-advanced.component.ts index dbae47ebd2..2f3256b47e 100644 --- a/src/main/webapp/wise5/components/discussion/edit-discussion-advanced/edit-discussion-advanced.component.ts +++ b/src/main/webapp/wise5/components/discussion/edit-discussion-advanced/edit-discussion-advanced.component.ts @@ -1,8 +1,8 @@ -import { EditAdvancedComponentAngularJSController } from "../../../../site/src/app/authoring-tool/edit-advanced-component/editAdvancedComponentAngularJSController"; +import { EditAdvancedComponentAngularJSController } from '../../../../site/src/app/authoring-tool/edit-advanced-component/editAdvancedComponentAngularJSController'; class EditDiscussionAdvancedController extends EditAdvancedComponentAngularJSController { allowedConnectedComponentTypes = ['Discussion']; - + connectedComponentTypeChanged(connectedComponent) { this.changeAllDiscussionConnectedComponentTypesToMatch(connectedComponent.type); super.connectedComponentTypeChanged(connectedComponent); @@ -28,5 +28,6 @@ export const EditDiscussionAdvancedComponent = { componentId: '@' }, controller: EditDiscussionAdvancedController, - templateUrl: 'wise5/components/discussion/edit-discussion-advanced/edit-discussion-advanced.component.html' -} + templateUrl: + 'wise5/components/discussion/edit-discussion-advanced/edit-discussion-advanced.component.html' +}; diff --git a/src/main/webapp/wise5/components/draw/drawAuthoring.ts b/src/main/webapp/wise5/components/draw/drawAuthoring.ts index ef431d6733..3b4ebccbf5 100644 --- a/src/main/webapp/wise5/components/draw/drawAuthoring.ts +++ b/src/main/webapp/wise5/components/draw/drawAuthoring.ts @@ -133,7 +133,7 @@ class DrawAuthoringController extends EditComponentController { saveStarterDrawData() { if (confirm(this.$translate('draw.areYouSureYouWantToSaveTheStarterDrawing'))) { - this.NodeService.requestStarterState({nodeId: this.nodeId, componentId: this.componentId}); + this.NodeService.requestStarterState({ nodeId: this.nodeId, componentId: this.componentId }); } } @@ -250,7 +250,6 @@ class DrawAuthoringController extends EditComponentController { setStampImage(index, fileName) { this.authoringComponentContent.stamps.Stamps[index] = fileName; } - } const DrawAuthoring = { @@ -261,6 +260,6 @@ const DrawAuthoring = { controller: DrawAuthoringController, controllerAs: 'drawController', templateUrl: 'wise5/components/draw/authoring.html' -} +}; export default DrawAuthoring; diff --git a/src/main/webapp/wise5/components/draw/drawAuthoringComponentModule.ts b/src/main/webapp/wise5/components/draw/drawAuthoringComponentModule.ts index ea3f14b857..4886711294 100644 --- a/src/main/webapp/wise5/components/draw/drawAuthoringComponentModule.ts +++ b/src/main/webapp/wise5/components/draw/drawAuthoringComponentModule.ts @@ -13,7 +13,7 @@ const drawAuthoringComponentModule = angular .component('editDrawAdvanced', EditDrawAdvancedComponent) .config([ '$translatePartialLoaderProvider', - $translatePartialLoaderProvider => { + ($translatePartialLoaderProvider) => { $translatePartialLoaderProvider.addPart('components/draw/i18n'); } ]); diff --git a/src/main/webapp/wise5/components/draw/drawComponentModule.ts b/src/main/webapp/wise5/components/draw/drawComponentModule.ts index c69f744795..f6caff832c 100644 --- a/src/main/webapp/wise5/components/draw/drawComponentModule.ts +++ b/src/main/webapp/wise5/components/draw/drawComponentModule.ts @@ -11,7 +11,7 @@ let drawComponentModule = angular .controller('DrawController', DrawController) .config([ '$translatePartialLoaderProvider', - $translatePartialLoaderProvider => { + ($translatePartialLoaderProvider) => { $translatePartialLoaderProvider.addPart('components/draw/i18n'); } ]); diff --git a/src/main/webapp/wise5/components/draw/drawController.ts b/src/main/webapp/wise5/components/draw/drawController.ts index 59acfa639d..65aeda78e9 100644 --- a/src/main/webapp/wise5/components/draw/drawController.ts +++ b/src/main/webapp/wise5/components/draw/drawController.ts @@ -279,7 +279,7 @@ class DrawController extends ComponentController { ); if (this.isStudentMode()) { - this.drawingTool.on('tool:changed', toolName => { + this.drawingTool.on('tool:changed', (toolName) => { const category = 'Tool'; const event = 'toolSelected'; const data = { @@ -529,8 +529,8 @@ class DrawController extends ComponentController { * @param studentAsset */ attachStudentAsset(studentAsset) { - this.StudentAssetService.copyAssetForReference(studentAsset).then(copiedAsset => { - fabric.Image.fromURL(copiedAsset.url, oImg => { + this.StudentAssetService.copyAssetForReference(studentAsset).then((copiedAsset) => { + fabric.Image.fromURL(copiedAsset.url, (oImg) => { oImg.scaleToWidth(200); // set max width and have height scale proportionally // TODO: center image or put them at mouse position? Wasn't straight-forward, tried below but had issues... //oImg.setLeft((this.drawingTool.canvas.width / 2) - (oImg.width / 2)); // center image vertically and horizontally @@ -604,14 +604,15 @@ class DrawController extends ComponentController { snipButtonClicked($event) { if (this.isDirty) { - const studentWorkSavedToServerSubscription = this.StudentDataService.studentWorkSavedToServer$ - .subscribe((args: any) => { - const componentState = args.studentWork; - if (this.isForThisComponent(componentState)) { - this.snipDrawing($event, componentState.id); - studentWorkSavedToServerSubscription.unsubscribe(); + const studentWorkSavedToServerSubscription = this.StudentDataService.studentWorkSavedToServer$.subscribe( + (args: any) => { + const componentState = args.studentWork; + if (this.isForThisComponent(componentState)) { + this.snipDrawing($event, componentState.id); + studentWorkSavedToServerSubscription.unsubscribe(); + } } - }); + ); this.saveButtonClicked(); } else { const studentWork = this.StudentDataService.getLatestComponentStateByNodeIdAndComponentId( @@ -681,14 +682,17 @@ class DrawController extends ComponentController { * @param componentState A component state. */ setComponentStateAsBackgroundImage(componentState) { - this.generateImageFromComponentState(componentState).then(image => { + this.generateImageFromComponentState(componentState).then((image) => { this.drawingTool.setBackgroundImage(image.url); }); } generateStarterState() { - this.NodeService.respondStarterState({nodeId: this.nodeId, componentId: this.componentId, - starterState: this.getDrawData()}); + this.NodeService.respondStarterState({ + nodeId: this.nodeId, + componentId: this.componentId, + starterState: this.getDrawData() + }); } } diff --git a/src/main/webapp/wise5/components/draw/drawService.ts b/src/main/webapp/wise5/components/draw/drawService.ts index 4c9b4c8133..820098855b 100644 --- a/src/main/webapp/wise5/components/draw/drawService.ts +++ b/src/main/webapp/wise5/components/draw/drawService.ts @@ -10,11 +10,12 @@ import { UtilService } from '../../services/utilService'; @Injectable() export class DrawService extends ComponentService { - - constructor(private upgrade: UpgradeModule, - private StudentAssetService: StudentAssetService, - protected StudentDataService: StudentDataService, - protected UtilService: UtilService) { + constructor( + private upgrade: UpgradeModule, + private StudentAssetService: StudentAssetService, + protected StudentDataService: StudentDataService, + protected UtilService: UtilService + ) { super(StudentDataService, UtilService); } @@ -52,8 +53,13 @@ export class DrawService extends ComponentService { return component; } - isCompleted(component: any, componentStates: any[], componentEvents: any[], nodeEvents: any[], - node: any) { + isCompleted( + component: any, + componentStates: any[], + componentEvents: any[], + nodeEvents: any[], + node: any + ) { if (componentStates != null && componentStates.length > 0) { if (this.isSubmitRequired(node, component)) { return this.hasComponentStateWithIsSubmitTrue(componentStates); @@ -137,7 +143,7 @@ export class DrawService extends ComponentService { const canvas = this.getDrawingToolCanvas(componentState.nodeId, componentState.componentId); const canvasBase64String = canvas.toDataURL('image/png'); const imageObject = this.UtilService.getImageObjectFromBase64String(canvasBase64String); - this.StudentAssetService.uploadAsset(imageObject).then(asset => { + this.StudentAssetService.uploadAsset(imageObject).then((asset) => { resolve(asset); }); }); diff --git a/src/main/webapp/wise5/components/draw/edit-draw-advanced/edit-draw-advanced.component.ts b/src/main/webapp/wise5/components/draw/edit-draw-advanced/edit-draw-advanced.component.ts index 9efbc47ce5..d313668982 100644 --- a/src/main/webapp/wise5/components/draw/edit-draw-advanced/edit-draw-advanced.component.ts +++ b/src/main/webapp/wise5/components/draw/edit-draw-advanced/edit-draw-advanced.component.ts @@ -1,4 +1,4 @@ -import { EditAdvancedComponentAngularJSController } from "../../../../site/src/app/authoring-tool/edit-advanced-component/editAdvancedComponentAngularJSController"; +import { EditAdvancedComponentAngularJSController } from '../../../../site/src/app/authoring-tool/edit-advanced-component/editAdvancedComponentAngularJSController'; class EditDrawAdvancedController extends EditAdvancedComponentAngularJSController { allowedConnectedComponentTypes = ['ConceptMap', 'Draw', 'Embedded', 'Graph', 'Label', 'Table']; @@ -29,8 +29,10 @@ class EditDrawAdvancedController extends EditAdvancedComponentAngularJSControlle } setImportWorkAsBackgroundIfApplicable(connectedComponent) { - const componentType = this.ProjectService.getComponentType(connectedComponent.nodeId, - connectedComponent.componentId); + const componentType = this.ProjectService.getComponentType( + connectedComponent.nodeId, + connectedComponent.componentId + ); if (['ConceptMap', 'Embedded', 'Graph', 'Label', 'Table'].includes(componentType)) { connectedComponent.importWorkAsBackground = true; } else { @@ -53,4 +55,4 @@ export const EditDrawAdvancedComponent = { }, controller: EditDrawAdvancedController, templateUrl: 'wise5/components/draw/edit-draw-advanced/edit-draw-advanced.component.html' -} +}; diff --git a/src/main/webapp/wise5/components/embedded/edit-embedded-advanced/edit-embedded-advanced.component.ts b/src/main/webapp/wise5/components/embedded/edit-embedded-advanced/edit-embedded-advanced.component.ts index 0fb796f03b..844b50ed6b 100644 --- a/src/main/webapp/wise5/components/embedded/edit-embedded-advanced/edit-embedded-advanced.component.ts +++ b/src/main/webapp/wise5/components/embedded/edit-embedded-advanced/edit-embedded-advanced.component.ts @@ -1,8 +1,20 @@ -import { EditAdvancedComponentAngularJSController } from "../../../../site/src/app/authoring-tool/edit-advanced-component/editAdvancedComponentAngularJSController"; +import { EditAdvancedComponentAngularJSController } from '../../../../site/src/app/authoring-tool/edit-advanced-component/editAdvancedComponentAngularJSController'; class EditEmbeddedAdvancedController extends EditAdvancedComponentAngularJSController { - allowedConnectedComponentTypes = ['Animation', 'AudioOscillator', 'ConceptMap', 'Discussion', - 'Draw', 'Embedded', 'Graph', 'Label', 'Match', 'MultipleChoice', 'OpenResponse', 'Table']; + allowedConnectedComponentTypes = [ + 'Animation', + 'AudioOscillator', + 'ConceptMap', + 'Discussion', + 'Draw', + 'Embedded', + 'Graph', + 'Label', + 'Match', + 'MultipleChoice', + 'OpenResponse', + 'Table' + ]; } export const EditEmbeddedAdvancedComponent = { @@ -11,5 +23,6 @@ export const EditEmbeddedAdvancedComponent = { componentId: '@' }, controller: EditEmbeddedAdvancedController, - templateUrl: 'wise5/components/embedded/edit-embedded-advanced/edit-embedded-advanced.component.html' -} + templateUrl: + 'wise5/components/embedded/edit-embedded-advanced/edit-embedded-advanced.component.html' +}; diff --git a/src/main/webapp/wise5/components/embedded/embeddedAuthoring.ts b/src/main/webapp/wise5/components/embedded/embeddedAuthoring.ts index abe740009e..5c6802ced1 100644 --- a/src/main/webapp/wise5/components/embedded/embeddedAuthoring.ts +++ b/src/main/webapp/wise5/components/embedded/embeddedAuthoring.ts @@ -1,11 +1,10 @@ 'use strict'; -import { Directive } from "@angular/core"; -import { EditComponentController } from "../../authoringTool/components/editComponentController"; +import { Directive } from '@angular/core'; +import { EditComponentController } from '../../authoringTool/components/editComponentController'; @Directive() class EmbeddedAuthoringController extends EditComponentController { - embeddedApplicationIFrameId: string; static $inject = [ @@ -18,21 +17,24 @@ class EmbeddedAuthoringController extends EditComponentController { 'UtilService' ]; - constructor($filter, + constructor( + $filter, + ConfigService, + NodeService, + NotificationService, + ProjectAssetService, + ProjectService, + UtilService + ) { + super( + $filter, ConfigService, NodeService, NotificationService, ProjectAssetService, ProjectService, - UtilService) { - super( - $filter, - ConfigService, - NodeService, - NotificationService, - ProjectAssetService, - ProjectService, - UtilService); + UtilService + ); } $onInit() { @@ -74,6 +76,6 @@ const EmbeddedAuthoring = { controller: EmbeddedAuthoringController, controllerAs: 'embeddedController', templateUrl: 'wise5/components/embedded/authoring.html' -} +}; export default EmbeddedAuthoring; diff --git a/src/main/webapp/wise5/components/embedded/embeddedAuthoringComponentModule.ts b/src/main/webapp/wise5/components/embedded/embeddedAuthoringComponentModule.ts index b8f464dbd0..24f1135d9c 100644 --- a/src/main/webapp/wise5/components/embedded/embeddedAuthoringComponentModule.ts +++ b/src/main/webapp/wise5/components/embedded/embeddedAuthoringComponentModule.ts @@ -13,7 +13,7 @@ const embeddedAuthoringComponentModule = angular .component('editEmbeddedAdvanced', EditEmbeddedAdvancedComponent) .config([ '$translatePartialLoaderProvider', - $translatePartialLoaderProvider => { + ($translatePartialLoaderProvider) => { $translatePartialLoaderProvider.addPart('components/embedded/i18n'); } ]); diff --git a/src/main/webapp/wise5/components/embedded/embeddedComponentModule.ts b/src/main/webapp/wise5/components/embedded/embeddedComponentModule.ts index 379eea3e08..03c063434a 100644 --- a/src/main/webapp/wise5/components/embedded/embeddedComponentModule.ts +++ b/src/main/webapp/wise5/components/embedded/embeddedComponentModule.ts @@ -5,9 +5,8 @@ import { downgradeInjectable } from '@angular/upgrade/static'; import { EmbeddedService } from './embeddedService'; import EmbeddedController from './embeddedController'; -const embeddedComponentModule = angular.module('embeddedComponentModule', [ - 'pascalprecht.translate' - ]) +const embeddedComponentModule = angular + .module('embeddedComponentModule', ['pascalprecht.translate']) .service('EmbeddedService', downgradeInjectable(EmbeddedService)) .controller('EmbeddedController', EmbeddedController) .config([ diff --git a/src/main/webapp/wise5/components/embedded/embeddedController.ts b/src/main/webapp/wise5/components/embedded/embeddedController.ts index 6edf986f7b..e09e5ffacd 100644 --- a/src/main/webapp/wise5/components/embedded/embeddedController.ts +++ b/src/main/webapp/wise5/components/embedded/embeddedController.ts @@ -142,16 +142,17 @@ class EmbeddedController extends ComponentController { * Watch for siblingComponentStudentDataChanged which occurs when the student data has changed * for another component in this step. */ - this.siblingComponentStudentDataChangedSubscription = - this.NodeService.siblingComponentStudentDataChanged$.subscribe((args: any) => { - if (this.isEventTargetThisComponent(args)) { - const message = { - messageType: 'siblingComponentStudentDataChanged', - componentState: args.componentState - }; - this.sendMessageToApplication(message); + this.siblingComponentStudentDataChangedSubscription = this.NodeService.siblingComponentStudentDataChanged$.subscribe( + (args: any) => { + if (this.isEventTargetThisComponent(args)) { + const message = { + messageType: 'siblingComponentStudentDataChanged', + componentState: args.componentState + }; + this.sendMessageToApplication(message); + } } - }); + ); this.initializeMessageEventListener(); this.broadcastDoneRenderingComponent(); @@ -172,7 +173,7 @@ class EmbeddedController extends ComponentController { } initializeMessageEventListener() { - this.messageEventListener = messageEvent => { + this.messageEventListener = (messageEvent) => { const messageEventData = messageEvent.data; if (messageEventData.messageType === 'event') { this.handleEventMessage(messageEventData); @@ -337,40 +338,41 @@ class EmbeddedController extends ComponentController { } registerStudentWorkSavedToServerListener() { - this.studentWorkSavedToServerSubscription = - this.StudentDataService.studentWorkSavedToServer$.subscribe((args: any) => { - const componentState = args.studentWork; - if (this.isForThisComponent(componentState)) { - this.isDirty = false; - this.StudentDataService.broadcastComponentDirty({ - componentId: this.componentId, - isDirty: false - }); - this.$scope.embeddedController.componentState = null; - const isAutoSave = componentState.isAutoSave; - const isSubmit = componentState.isSubmit; - const serverSaveTime = componentState.serverSaveTime; - const clientSaveTime = this.ConfigService.convertToClientTimestamp(serverSaveTime); - if (isSubmit) { - this.setSubmittedMessage(clientSaveTime); - this.submit(); - this.isSubmitDirty = false; - this.StudentDataService.broadcastComponentSubmitDirty({ + this.studentWorkSavedToServerSubscription = this.StudentDataService.studentWorkSavedToServer$.subscribe( + (args: any) => { + const componentState = args.studentWork; + if (this.isForThisComponent(componentState)) { + this.isDirty = false; + this.StudentDataService.broadcastComponentDirty({ componentId: this.componentId, isDirty: false }); - } else if (isAutoSave) { - this.setAutoSavedMessage(clientSaveTime); - } else { - this.setSavedMessage(clientSaveTime); + this.$scope.embeddedController.componentState = null; + const isAutoSave = componentState.isAutoSave; + const isSubmit = componentState.isSubmit; + const serverSaveTime = componentState.serverSaveTime; + const clientSaveTime = this.ConfigService.convertToClientTimestamp(serverSaveTime); + if (isSubmit) { + this.setSubmittedMessage(clientSaveTime); + this.submit(); + this.isSubmitDirty = false; + this.StudentDataService.broadcastComponentSubmitDirty({ + componentId: this.componentId, + isDirty: false + }); + } else if (isAutoSave) { + this.setAutoSavedMessage(clientSaveTime); + } else { + this.setSavedMessage(clientSaveTime); + } + const message = { + messageType: 'componentStateSaved', + componentState: componentState + }; + this.sendMessageToApplication(message); } - const message = { - messageType: 'componentStateSaved', - componentState: componentState - }; - this.sendMessageToApplication(message); } - }); + ); } iframeLoaded(contentLocation) { @@ -443,7 +445,7 @@ class EmbeddedController extends ComponentController { let modelElement: any = iframe.contents().find('html'); if (modelElement != null && modelElement.length > 0) { modelElement = modelElement[0]; - html2canvas(modelElement).then(canvas => { + html2canvas(modelElement).then((canvas) => { const base64Image = canvas.toDataURL('image/png'); const imageObject = this.UtilService.getImageObjectFromBase64String(base64Image); this.NotebookService.addNote(imageObject); diff --git a/src/main/webapp/wise5/components/embedded/embeddedService.ts b/src/main/webapp/wise5/components/embedded/embeddedService.ts index 470d86ed15..89e13b7519 100644 --- a/src/main/webapp/wise5/components/embedded/embeddedService.ts +++ b/src/main/webapp/wise5/components/embedded/embeddedService.ts @@ -11,11 +11,12 @@ import { StudentDataService } from '../../services/studentDataService'; @Injectable() export class EmbeddedService extends ComponentService { - - constructor(private upgrade: UpgradeModule, - protected StudentAssetService: StudentAssetService, - protected StudentDataService: StudentDataService, - protected UtilService: UtilService) { + constructor( + private upgrade: UpgradeModule, + protected StudentAssetService: StudentAssetService, + protected StudentDataService: StudentDataService, + protected UtilService: UtilService + ) { super(StudentDataService, UtilService); } @@ -37,8 +38,10 @@ export class EmbeddedService extends ComponentService { isCompleted(component: any, componentStates: any[], componentEvents: any[], nodeEvents: any[]) { if (componentStates != null) { - if (this.hasComponentStateWithIsCompletedField(componentStates) && - this.hasComponentStateWithIsCompletedTrue(componentStates)) { + if ( + this.hasComponentStateWithIsCompletedField(componentStates) && + this.hasComponentStateWithIsCompletedTrue(componentStates) + ) { return true; } } @@ -91,10 +94,10 @@ export class EmbeddedService extends ComponentService { generateImageFromRenderedComponentState(componentState: any) { const modelElement = this.getModelElement(componentState.componentId); return new Promise((resolve, reject) => { - html2canvas(modelElement).then(canvas => { + html2canvas(modelElement).then((canvas) => { const base64Image = canvas.toDataURL('image/png'); const imageObject = this.UtilService.getImageObjectFromBase64String(base64Image); - this.StudentAssetService.uploadAsset(imageObject).then(asset => { + this.StudentAssetService.uploadAsset(imageObject).then((asset) => { resolve(asset); }); }); diff --git a/src/main/webapp/wise5/components/graph/edit-graph-advanced/edit-graph-advanced.component.ts b/src/main/webapp/wise5/components/graph/edit-graph-advanced/edit-graph-advanced.component.ts index caa93ad89f..a8de359788 100644 --- a/src/main/webapp/wise5/components/graph/edit-graph-advanced/edit-graph-advanced.component.ts +++ b/src/main/webapp/wise5/components/graph/edit-graph-advanced/edit-graph-advanced.component.ts @@ -1,8 +1,15 @@ -import { EditAdvancedComponentAngularJSController } from "../../../../site/src/app/authoring-tool/edit-advanced-component/editAdvancedComponentAngularJSController"; +import { EditAdvancedComponentAngularJSController } from '../../../../site/src/app/authoring-tool/edit-advanced-component/editAdvancedComponentAngularJSController'; class EditGraphAdvancedController extends EditAdvancedComponentAngularJSController { - allowedConnectedComponentTypes = ['Animation', 'ConceptMap', 'Draw', 'Embedded', 'Graph', 'Label', - 'Table']; + allowedConnectedComponentTypes = [ + 'Animation', + 'ConceptMap', + 'Draw', + 'Embedded', + 'Graph', + 'Label', + 'Table' + ]; addXAxisPlotLine(): void { if (this.authoringComponentContent.xAxis.plotLines == null) { @@ -55,8 +62,10 @@ class EditGraphAdvancedController extends EditAdvancedComponentAngularJSControll addConnectedComponent() { this.addConnectedComponentAndSetComponentIdIfPossible(); - if (this.authoringComponentContent.connectedComponents.length > 1 || - this.authoringComponentContent.series.length > 0) { + if ( + this.authoringComponentContent.connectedComponents.length > 1 || + this.authoringComponentContent.series.length > 0 + ) { // enable trials so each connected component can put work in a different trial this.authoringComponentContent.enableTrials = true; } @@ -124,7 +133,7 @@ class EditGraphAdvancedController extends EditAdvancedComponentAngularJSControll setImportWorkAsBackgroundIfApplicable(connectedComponent) { const componentType = this.getConnectedComponentType(connectedComponent); - if (['ConceptMap','Draw','Label'].includes(componentType)) { + if (['ConceptMap', 'Draw', 'Label'].includes(componentType)) { connectedComponent.importWorkAsBackground = true; } else { delete connectedComponent.importWorkAsBackground; @@ -161,4 +170,4 @@ export const EditGraphAdvancedComponent = { }, controller: EditGraphAdvancedController, templateUrl: 'wise5/components/graph/edit-graph-advanced/edit-graph-advanced.component.html' -} +}; diff --git a/src/main/webapp/wise5/components/graph/graphAuthoring.ts b/src/main/webapp/wise5/components/graph/graphAuthoring.ts index 5825bde03f..4a538fd7b6 100644 --- a/src/main/webapp/wise5/components/graph/graphAuthoring.ts +++ b/src/main/webapp/wise5/components/graph/graphAuthoring.ts @@ -1,7 +1,7 @@ 'use strict'; -import { Directive } from "@angular/core"; -import { EditComponentController } from "../../authoringTool/components/editComponentController"; +import { Directive } from '@angular/core'; +import { EditComponentController } from '../../authoringTool/components/editComponentController'; @Directive() class GraphAuthoringController extends EditComponentController { @@ -27,14 +27,16 @@ class GraphAuthoringController extends EditComponentController { 'UtilService' ]; - constructor($filter, - ConfigService, - private GraphService, - NodeService, - NotificationService, - ProjectAssetService, - ProjectService, - UtilService) { + constructor( + $filter, + ConfigService, + private GraphService, + NodeService, + NotificationService, + ProjectAssetService, + ProjectService, + UtilService + ) { super( $filter, ConfigService, @@ -42,7 +44,8 @@ class GraphAuthoringController extends EditComponentController { NotificationService, ProjectAssetService, ProjectService, - UtilService); + UtilService + ); } $onInit() { @@ -153,7 +156,7 @@ class GraphAuthoringController extends EditComponentController { line: 'limits', scatter: 'limits', column: 'categories' - } + }; this.enableMultipleYAxes = this.isMultipleYAxesEnabled(); if (this.enableMultipleYAxes) { @@ -208,8 +211,9 @@ class GraphAuthoringController extends EditComponentController { if (seriesName == null || seriesName === '') { message = this.$translate('graph.areYouSureYouWantToDeleteTheSeries'); } else { - message = this.$translate('graph.areYouSureYouWantToDeleteTheNamedSeries', - { seriesName: seriesName }); + message = this.$translate('graph.areYouSureYouWantToDeleteTheNamedSeries', { + seriesName: seriesName + }); } if (confirm(message)) { this.authoringComponentContent.series.splice(index, 1); @@ -255,15 +259,18 @@ class GraphAuthoringController extends EditComponentController { deleteXAxisCategory(index) { let confirmMessage = ''; let categoryName = ''; - if (this.authoringComponentContent.xAxis != null && - this.authoringComponentContent.xAxis.categories != null) { + if ( + this.authoringComponentContent.xAxis != null && + this.authoringComponentContent.xAxis.categories != null + ) { categoryName = this.authoringComponentContent.xAxis.categories[index]; } if (categoryName == null || categoryName === '') { confirmMessage = this.$translate('graph.areYouSureYouWantToDeleteTheCategory'); } else { - confirmMessage = this.$translate('graph.areYouSureYouWantToDeleteTheNamedCategory', - { categoryName: categoryName }); + confirmMessage = this.$translate('graph.areYouSureYouWantToDeleteTheNamedCategory', { + categoryName: categoryName + }); } if (confirm(confirmMessage)) { this.authoringComponentContent.xAxis.categories.splice(index, 1); @@ -273,8 +280,10 @@ class GraphAuthoringController extends EditComponentController { addSeriesDataPoint(series) { if (series != null && series.data != null) { - if (this.authoringComponentContent.xAxis.type == null || - this.authoringComponentContent.xAxis.type === 'limits') { + if ( + this.authoringComponentContent.xAxis.type == null || + this.authoringComponentContent.xAxis.type === 'limits' + ) { series.data.push([]); } else if (this.authoringComponentContent.xAxis.type === 'categories') { series.data.push(null); @@ -316,8 +325,10 @@ class GraphAuthoringController extends EditComponentController { this.authoringComponentContent.xAxis.min = 0; this.authoringComponentContent.xAxis.max = 10; this.convertAllSeriesDataPoints(newValue); - } else if ((oldValue === 'limits' || oldValue === '' || oldValue == null) && - newValue === 'categories') { + } else if ( + (oldValue === 'limits' || oldValue === '' || oldValue == null) && + newValue === 'categories' + ) { delete this.authoringComponentContent.xAxis.min; delete this.authoringComponentContent.xAxis.max; delete this.authoringComponentContent.xAxis.units; @@ -483,8 +494,10 @@ class GraphAuthoringController extends EditComponentController { } decreaseYAxes(newNumYAxes) { - this.authoringComponentContent.yAxis = - this.authoringComponentContent.yAxis.slice(0, newNumYAxes); + this.authoringComponentContent.yAxis = this.authoringComponentContent.yAxis.slice( + 0, + newNumYAxes + ); } updateSeriesYAxesIfNecessary() { @@ -518,7 +531,7 @@ class GraphAuthoringController extends EditComponentController { addAnyMissingYAxisFieldsToAllYAxes(yAxis) { if (this.GraphService.isMultipleYAxes(yAxis)) { - yAxis.forEach(yAxis => this.addAnyMissingYAxisFields(yAxis)); + yAxis.forEach((yAxis) => this.addAnyMissingYAxisFields(yAxis)); } else { this.addAnyMissingYAxisFields(yAxis); } @@ -602,6 +615,6 @@ const GraphAuthoring = { controller: GraphAuthoringController, controllerAs: 'graphController', templateUrl: 'wise5/components/graph/authoring.html' -} +}; export default GraphAuthoring; diff --git a/src/main/webapp/wise5/components/graph/graphController.ts b/src/main/webapp/wise5/components/graph/graphController.ts index 45c3b59fd7..dbb2519ca3 100644 --- a/src/main/webapp/wise5/components/graph/graphController.ts +++ b/src/main/webapp/wise5/components/graph/graphController.ts @@ -209,7 +209,7 @@ class GraphController extends ComponentController { } applyHighchartsPlotLinesLabelFix() { - Highcharts.wrap(Highcharts.Axis.prototype, 'getPlotLinePath', function(proceed) { + Highcharts.wrap(Highcharts.Axis.prototype, 'getPlotLinePath', function (proceed) { var path = proceed.apply(this, Array.prototype.slice.call(arguments, 1)); if (path) { path.flat = false; @@ -250,7 +250,7 @@ class GraphController extends ComponentController { isYAxisLocked() { if (Array.isArray(this.componentContent.yAxis)) { return this.componentContent.yAxis - .map(yAxis => yAxis.locked) + .map((yAxis) => yAxis.locked) .reduce((accumulator, currentValue) => { return accumulator && currentValue; }); @@ -325,7 +325,7 @@ class GraphController extends ComponentController { } initializeFileUploadChanged() { - this.$scope.fileUploadChanged = element => { + this.$scope.fileUploadChanged = (element) => { const activeSeriesData = this.activeSeries.data; let overwrite = true; if (activeSeriesData.length > 0) { @@ -630,19 +630,19 @@ class GraphController extends ComponentController { * bind a listener multiple times. */ angular.element(document.querySelector(`#${this.chartId}`)).unbind(); - angular.element(document.querySelector(`#${this.chartId}`)).bind('mousedown', e => { + angular.element(document.querySelector(`#${this.chartId}`)).bind('mousedown', (e) => { this.mouseDown = true; this.mouseDownEventOccurred(e); }); - angular.element(document.querySelector(`#${this.chartId}`)).bind('mouseup', e => { + angular.element(document.querySelector(`#${this.chartId}`)).bind('mouseup', (e) => { this.mouseDown = false; }); - angular.element(document.querySelector(`#${this.chartId}`)).bind('mousemove', e => { + angular.element(document.querySelector(`#${this.chartId}`)).bind('mousemove', (e) => { if (this.mouseDown) { this.mouseDownEventOccurred(e); } }); - angular.element(document.querySelector(`#${this.chartId}`)).bind('mouseleave', e => { + angular.element(document.querySelector(`#${this.chartId}`)).bind('mouseleave', (e) => { this.mouseDown = false; }); this.setupMouseMoveListenerDone = true; @@ -957,7 +957,7 @@ class GraphController extends ComponentController { if (this.isSingleYAxis(this.yAxis)) { this.yAxis.allowDecimals = false; } else { - this.yAxis.forEach(yAxis => (yAxis.allowDecimals = false)); + this.yAxis.forEach((yAxis) => (yAxis.allowDecimals = false)); } } @@ -1088,7 +1088,7 @@ class GraphController extends ComponentController { zoomType: zoomType, plotBackgroundImage: this.backgroundImage, events: { - load: function() { + load: function () { deferred.resolve(this); }, click: this.createGraphClickHandler() @@ -1136,7 +1136,7 @@ class GraphController extends ComponentController { createTooltipFormatter() { const thisGraphController = this; - return function() { + return function () { let text = ''; if (thisGraphController.isLimitXAxisType(thisGraphController.xAxis)) { text = thisGraphController.getSeriesText(this.series); @@ -1242,7 +1242,7 @@ class GraphController extends ComponentController { createGraphClickHandler() { const thisGraphController = this; - return function(event) { + return function (event) { if (thisGraphController.graphType === 'line' || thisGraphController.graphType === 'scatter') { if (thisGraphController.isIgnoreClickEvent()) { return; @@ -1304,7 +1304,7 @@ class GraphController extends ComponentController { createLegendItemClickHandler() { const thisGraphController = this; - return function(event) { + return function (event) { const canHideSeries = thisGraphController.componentContent.canStudentHideSeriesOnLegendClick === true; if (canHideSeries) { @@ -1328,7 +1328,7 @@ class GraphController extends ComponentController { createPointDragEventHandler() { const thisGraphController: any = this; - return function(event) { + return function (event) { if (!thisGraphController.isDisabled) { const activeSeries = thisGraphController.activeSeries; if (thisGraphController.canEdit(activeSeries)) { @@ -1340,7 +1340,7 @@ class GraphController extends ComponentController { createPointDropEventHandler() { const thisGraphController: any = this; - return function(event) { + return function (event) { // the student has stopped dragging the point and dropped the point if (!thisGraphController.isDisabled && thisGraphController.dragging) { const activeSeries = thisGraphController.activeSeries; @@ -1364,8 +1364,8 @@ class GraphController extends ComponentController { createGraphCallbackHandler() { const thisGraphController = this; - return function(chart) { - thisGraphController.$timeout(function() { + return function (chart) { + thisGraphController.$timeout(function () { thisGraphController.showXPlotLineIfOn('Drag Me'); thisGraphController.showYPlotLineIfOn('Drag Me'); if ( @@ -1675,7 +1675,7 @@ class GraphController extends ComponentController { * data has changed. */ const action = 'change'; - this.createComponentState(action).then(componentState => { + this.createComponentState(action).then((componentState) => { if (this.addNextComponentStateToUndoStack) { if (this.previousComponentState != null) { this.undoStack.push(this.previousComponentState); @@ -1830,12 +1830,12 @@ class GraphController extends ComponentController { getTrialsFromClassmates(nodeId, componentId, periodId) { const deferred = this.$q.defer(); this.StudentDataService.getClassmateStudentWork(nodeId, componentId, periodId).then( - componentStates => { + (componentStates) => { const promises = []; for (const componentState of componentStates) { promises.push(this.getTrialsFromComponentState(nodeId, componentId, componentState)); } - this.$q.all(promises).then(promiseResults => { + this.$q.all(promises).then((promiseResults) => { const mergedTrials = []; for (const trials of promiseResults) { for (const trial of trials) { @@ -1894,8 +1894,8 @@ class GraphController extends ComponentController { * @param studentAsset CSV file student asset */ attachStudentAsset(studentAsset) { - this.StudentAssetService.copyAssetForReference(studentAsset).then(copiedAsset => { - this.StudentAssetService.getAssetContent(copiedAsset).then(assetContent => { + this.StudentAssetService.copyAssetForReference(studentAsset).then((copiedAsset) => { + this.StudentAssetService.getAssetContent(copiedAsset).then((assetContent) => { const rowData = this.UtilService.CSVToArray(assetContent, ','); const params = { skipFirstRow: true, @@ -2931,7 +2931,7 @@ class GraphController extends ComponentController { } handleConnectedComponentPromiseResults(connectedComponentBackgroundImage, isReset) { - return promiseResults => { + return (promiseResults) => { /* * First we will accumulate all the trials into one new component state and then we will * perform connected component processing. @@ -3005,7 +3005,7 @@ class GraphController extends ComponentController { * @return A promise that returns the url of the image that is generated from the component state. */ setComponentStateAsBackgroundImage(componentState) { - return this.generateImageFromComponentState(componentState).then(image => { + return this.generateImageFromComponentState(componentState).then((image) => { return image.url; }); } diff --git a/src/main/webapp/wise5/components/graph/graphService.ts b/src/main/webapp/wise5/components/graph/graphService.ts index 5cdee81725..22cecd4d74 100644 --- a/src/main/webapp/wise5/components/graph/graphService.ts +++ b/src/main/webapp/wise5/components/graph/graphService.ts @@ -13,10 +13,12 @@ import { UpgradeModule } from '@angular/upgrade/static'; export class GraphService extends ComponentService { seriesColors: string[] = ['blue', 'red', 'green', 'orange', 'purple', 'black']; - constructor(private upgrade: UpgradeModule, - private StudentAssetService: StudentAssetService, - protected StudentDataService: StudentDataService, - protected UtilService: UtilService) { + constructor( + private upgrade: UpgradeModule, + private StudentAssetService: StudentAssetService, + protected StudentDataService: StudentDataService, + protected UtilService: UtilService + ) { super(StudentDataService, UtilService); } @@ -92,8 +94,13 @@ export class GraphService extends ComponentService { return component; } - isCompleted(component: any, componentStates: any[], componentEvents: any[], nodeEvents: any[], - node: any) { + isCompleted( + component: any, + componentStates: any[], + componentEvents: any[], + nodeEvents: any[], + node: any + ) { if (this.canEdit(component)) { return this.hasCompletedComponentState(componentStates, node, component); } else { @@ -213,8 +220,10 @@ export class GraphService extends ComponentService { */ isStudentChangedAxisLimit(componentState: any, componentContent: any) { if (componentState != null && componentState.studentData != null && componentContent != null) { - if (this.isXAxisChanged(componentState, componentContent) || - this.isYAxisChanged(componentState, componentContent)) { + if ( + this.isXAxisChanged(componentState, componentContent) || + this.isYAxisChanged(componentState, componentContent) + ) { return true; } } @@ -223,8 +232,10 @@ export class GraphService extends ComponentService { isXAxisChanged(componentState: any, componentContent: any) { if (componentState.studentData.xAxis != null && componentContent.xAxis != null) { - if (componentState.studentData.xAxis.min != componentContent.xAxis.min || - componentState.studentData.xAxis.max != componentContent.xAxis.max) { + if ( + componentState.studentData.xAxis.min != componentContent.xAxis.min || + componentState.studentData.xAxis.max != componentContent.xAxis.max + ) { return true; } } @@ -233,8 +244,10 @@ export class GraphService extends ComponentService { isYAxisChanged(componentState: any, componentContent: any) { if (componentState.studentData.yAxis != null && componentContent.yAxis != null) { - if (componentState.studentData.yAxis.min != componentContent.yAxis.min || - componentState.studentData.yAxis.max != componentContent.yAxis.max) { + if ( + componentState.studentData.yAxis.min != componentContent.yAxis.min || + componentState.studentData.yAxis.max != componentContent.yAxis.max + ) { return true; } } @@ -303,10 +316,10 @@ export class GraphService extends ComponentService { generateImageFromRenderedComponentState(componentState: any) { return new Promise((resolve, reject) => { const highchartsDiv = this.getHighchartsDiv(componentState.componentId); - html2canvas(highchartsDiv).then(canvas => { + html2canvas(highchartsDiv).then((canvas) => { const base64Image = canvas.toDataURL('image/png'); const imageObject = this.UtilService.getImageObjectFromBase64String(base64Image); - this.StudentAssetService.uploadAsset(imageObject).then(asset => { + this.StudentAssetService.uploadAsset(imageObject).then((asset) => { resolve(asset); }); }); @@ -329,6 +342,4 @@ export class GraphService extends ComponentService { getSeriesColor(index: number): string { return this.seriesColors[index]; } - } - diff --git a/src/main/webapp/wise5/components/html/edit-html-advanced/edit-html-advanced.component.ts b/src/main/webapp/wise5/components/html/edit-html-advanced/edit-html-advanced.component.ts index 5639a20e28..3e2d1baa1d 100644 --- a/src/main/webapp/wise5/components/html/edit-html-advanced/edit-html-advanced.component.ts +++ b/src/main/webapp/wise5/components/html/edit-html-advanced/edit-html-advanced.component.ts @@ -1,13 +1,12 @@ -import { Component } from "@angular/core"; -import { EditAdvancedComponentComponent } from "../../../../site/src/app/authoring-tool/edit-advanced-component/edit-advanced-component.component"; -import { TeacherProjectService } from "../../../services/teacherProjectService"; +import { Component } from '@angular/core'; +import { EditAdvancedComponentComponent } from '../../../../site/src/app/authoring-tool/edit-advanced-component/edit-advanced-component.component'; +import { TeacherProjectService } from '../../../services/teacherProjectService'; @Component({ selector: 'edit-html-advanced', templateUrl: 'edit-html-advanced.component.html' }) export class EditHTMLAdvancedComponent extends EditAdvancedComponentComponent { - constructor(protected ProjectService: TeacherProjectService) { super(ProjectService); } diff --git a/src/main/webapp/wise5/components/html/htmlAuthoringComponentModule.ts b/src/main/webapp/wise5/components/html/htmlAuthoringComponentModule.ts index 5998b8130c..08ae55571a 100644 --- a/src/main/webapp/wise5/components/html/htmlAuthoringComponentModule.ts +++ b/src/main/webapp/wise5/components/html/htmlAuthoringComponentModule.ts @@ -19,7 +19,7 @@ const htmlComponentModule = angular ) .config([ '$translatePartialLoaderProvider', - $translatePartialLoaderProvider => { + ($translatePartialLoaderProvider) => { $translatePartialLoaderProvider.addPart('components/html/i18n'); } ]); diff --git a/src/main/webapp/wise5/components/html/htmlComponentModule.ts b/src/main/webapp/wise5/components/html/htmlComponentModule.ts index 4c28eaa8dd..44c4ec8af8 100644 --- a/src/main/webapp/wise5/components/html/htmlComponentModule.ts +++ b/src/main/webapp/wise5/components/html/htmlComponentModule.ts @@ -11,7 +11,7 @@ const htmlComponentModule = angular .controller('HTMLController', HTMLController) .config([ '$translatePartialLoaderProvider', - $translatePartialLoaderProvider => { + ($translatePartialLoaderProvider) => { $translatePartialLoaderProvider.addPart('components/html/i18n'); } ]); diff --git a/src/main/webapp/wise5/components/html/htmlService.ts b/src/main/webapp/wise5/components/html/htmlService.ts index 50346c476d..30b41f5b15 100644 --- a/src/main/webapp/wise5/components/html/htmlService.ts +++ b/src/main/webapp/wise5/components/html/htmlService.ts @@ -6,10 +6,11 @@ import { UpgradeModule } from '@angular/upgrade/static'; @Injectable() export class HTMLService extends ComponentService { - - constructor(private upgrade: UpgradeModule, - protected StudentDataService: StudentDataService, - protected UtilService: UtilService) { + constructor( + private upgrade: UpgradeModule, + protected StudentDataService: StudentDataService, + protected UtilService: UtilService + ) { super(StudentDataService, UtilService); } diff --git a/src/main/webapp/wise5/components/label/edit-label-advanced/edit-label-advanced.component.ts b/src/main/webapp/wise5/components/label/edit-label-advanced/edit-label-advanced.component.ts index 78835e48cf..a4a2f8d56b 100644 --- a/src/main/webapp/wise5/components/label/edit-label-advanced/edit-label-advanced.component.ts +++ b/src/main/webapp/wise5/components/label/edit-label-advanced/edit-label-advanced.component.ts @@ -1,8 +1,15 @@ -import { EditAdvancedComponentAngularJSController } from "../../../../site/src/app/authoring-tool/edit-advanced-component/editAdvancedComponentAngularJSController"; +import { EditAdvancedComponentAngularJSController } from '../../../../site/src/app/authoring-tool/edit-advanced-component/editAdvancedComponentAngularJSController'; class EditLabelAdvancedController extends EditAdvancedComponentAngularJSController { - allowedConnectedComponentTypes = ['ConceptMap', 'Draw', 'Embedded', 'Graph', 'Label', - 'OpenResponse', 'Table']; + allowedConnectedComponentTypes = [ + 'ConceptMap', + 'Draw', + 'Embedded', + 'Graph', + 'Label', + 'OpenResponse', + 'Table' + ]; automaticallySetConnectedComponentComponentIdIfPossible(connectedComponent: any): void { super.automaticallySetConnectedComponentComponentIdIfPossible(connectedComponent); @@ -48,4 +55,4 @@ export const EditLabelAdvancedComponent = { }, controller: EditLabelAdvancedController, templateUrl: 'wise5/components/label/edit-label-advanced/edit-label-advanced.component.html' -} +}; diff --git a/src/main/webapp/wise5/components/label/labelAuthoring.ts b/src/main/webapp/wise5/components/label/labelAuthoring.ts index 5a97b7ae9f..df29a8ac22 100644 --- a/src/main/webapp/wise5/components/label/labelAuthoring.ts +++ b/src/main/webapp/wise5/components/label/labelAuthoring.ts @@ -2,14 +2,13 @@ import * as $ from 'jquery'; import * as fabric from 'fabric'; -window['fabric'] = fabric.fabric +window['fabric'] = fabric.fabric; import html2canvas from 'html2canvas'; import { Directive } from '@angular/core'; import { EditComponentController } from '../../authoringTool/components/editComponentController'; @Directive() class LabelAuthoringController extends EditComponentController { - static $inject = [ '$filter', '$window', @@ -106,7 +105,7 @@ class LabelAuthoringController extends EditComponentController { saveStarterLabels(): void { if (confirm(this.$translate('label.areYouSureYouWantToSaveTheStarterLabels'))) { - this.NodeService.requestStarterState({nodeId: this.nodeId, componentId: this.componentId}); + this.NodeService.requestStarterState({ nodeId: this.nodeId, componentId: this.componentId }); } } @@ -163,7 +162,6 @@ class LabelAuthoringController extends EditComponentController { openColorViewer(): void { this.$window.open('http://www.javascripter.net/faq/colornam.htm'); } - } const LabelAuthoring = { @@ -174,6 +172,6 @@ const LabelAuthoring = { controller: LabelAuthoringController, controllerAs: 'labelController', templateUrl: 'wise5/components/label/authoring.html' -} +}; export default LabelAuthoring; diff --git a/src/main/webapp/wise5/components/label/labelAuthoringComponentModule.ts b/src/main/webapp/wise5/components/label/labelAuthoringComponentModule.ts index fee3bb84b7..cceefafef9 100644 --- a/src/main/webapp/wise5/components/label/labelAuthoringComponentModule.ts +++ b/src/main/webapp/wise5/components/label/labelAuthoringComponentModule.ts @@ -13,7 +13,7 @@ const labelAuthoringComponentModule = angular .component('editLabelAdvanced', EditLabelAdvancedComponent) .config([ '$translatePartialLoaderProvider', - $translatePartialLoaderProvider => { + ($translatePartialLoaderProvider) => { $translatePartialLoaderProvider.addPart('components/label/i18n'); } ]); diff --git a/src/main/webapp/wise5/components/label/labelComponentModule.ts b/src/main/webapp/wise5/components/label/labelComponentModule.ts index 5a6dc812b5..87ccd91df9 100644 --- a/src/main/webapp/wise5/components/label/labelComponentModule.ts +++ b/src/main/webapp/wise5/components/label/labelComponentModule.ts @@ -11,7 +11,7 @@ let labelComponentModule = angular .controller('LabelController', LabelController) .config([ '$translatePartialLoaderProvider', - $translatePartialLoaderProvider => { + ($translatePartialLoaderProvider) => { $translatePartialLoaderProvider.addPart('components/label/i18n'); } ]); diff --git a/src/main/webapp/wise5/components/label/labelController.ts b/src/main/webapp/wise5/components/label/labelController.ts index 4ec371c648..dc17a360cb 100644 --- a/src/main/webapp/wise5/components/label/labelController.ts +++ b/src/main/webapp/wise5/components/label/labelController.ts @@ -3,7 +3,7 @@ import * as angular from 'angular'; import * as $ from 'jquery'; import { fabric } from 'fabric'; -window['fabric'] = fabric +window['fabric'] = fabric; import ComponentController from '../componentController'; import { LabelService } from './labelService'; @@ -235,7 +235,7 @@ class LabelController extends ComponentController { } } this.$timeout( - angular.bind(this, function() { + angular.bind(this, function () { // wait for angular to completely render the html before we initialize the canvas this.setupCanvas(); @@ -245,7 +245,7 @@ class LabelController extends ComponentController { /** * Returns true iff there is student work that hasn't been saved yet */ - this.$scope.isDirty = function() { + this.$scope.isDirty = function () { return this.$scope.labelController.isDirty; }.bind(this); @@ -257,7 +257,7 @@ class LabelController extends ComponentController { * action (optional; default is false) * @return a promise of a component state containing the student data */ - this.$scope.getComponentState = function(isSubmit) { + this.$scope.getComponentState = function (isSubmit) { const deferred = this.$q.defer(); let getState = false; let action = 'change'; @@ -276,7 +276,7 @@ class LabelController extends ComponentController { if (getState) { // create a component state populated with the student data - this.$scope.labelController.createComponentState(action).then(componentState => { + this.$scope.labelController.createComponentState(action).then((componentState) => { deferred.resolve(componentState); }); } else { @@ -295,7 +295,7 @@ class LabelController extends ComponentController { * The student has changed the file input * @param element the file input element */ - this.$scope.fileUploadChanged = function(element) { + this.$scope.fileUploadChanged = function (element) { // get the current background image if any const backgroundImage = this.labelController.getBackgroundImage(); @@ -335,10 +335,12 @@ class LabelController extends ComponentController { if (files != null && files.length > 0) { // upload the file to the studentuploads folder - this.labelController.StudentAssetService.uploadAsset(files[0]).then(unreferencedAsset => { - // make a referenced copy of the unreferenced asset - this.labelController.StudentAssetService.copyAssetForReference(unreferencedAsset).then( - referencedAsset => { + this.labelController.StudentAssetService.uploadAsset(files[0]).then( + (unreferencedAsset) => { + // make a referenced copy of the unreferenced asset + this.labelController.StudentAssetService.copyAssetForReference( + unreferencedAsset + ).then((referencedAsset) => { if (referencedAsset != null) { // get the url of the referenced asset const imageURL = referencedAsset.url; @@ -349,9 +351,9 @@ class LabelController extends ComponentController { this.labelController.studentDataChanged(); } } - } - ); - }); + }); + } + ); } } }; @@ -810,7 +812,7 @@ class LabelController extends ComponentController { // listen for the mouse down event canvas.on( 'mouse:down', - angular.bind(this, function(options) { + angular.bind(this, function (options) { // get the object that was clicked on if any const activeObject = this.canvas.getActiveObject(); @@ -829,7 +831,7 @@ class LabelController extends ComponentController { // listen for the object moving event canvas.on( 'object:moving', - angular.bind(this, function(options) { + angular.bind(this, function (options) { const target = options.target; if (target != null) { @@ -955,7 +957,7 @@ class LabelController extends ComponentController { // listen for the text changed event canvas.on( 'text:changed', - angular.bind(this, function(options) { + angular.bind(this, function (options) { const target = options.target; if (target != null) { const type = target.get('type'); @@ -1667,7 +1669,7 @@ class LabelController extends ComponentController { null, spaceInbetweenLines, fontSize - ).then(image => { + ).then((image) => { // set the image as the background this.setBackgroundImage(image); @@ -1730,7 +1732,7 @@ class LabelController extends ComponentController { * @param componentState A component state. */ setComponentStateAsBackgroundImage(componentState) { - this.generateImageFromComponentState(componentState).then(image => { + this.generateImageFromComponentState(componentState).then((image) => { this.setBackgroundImage(image.url); }); } @@ -1836,8 +1838,11 @@ class LabelController extends ComponentController { } generateStarterState() { - this.NodeService.respondStarterState({nodeId: this.nodeId, componentId: this.componentId, - starterState: this.getLabelData()}); + this.NodeService.respondStarterState({ + nodeId: this.nodeId, + componentId: this.componentId, + starterState: this.getLabelData() + }); } } diff --git a/src/main/webapp/wise5/components/label/labelService.ts b/src/main/webapp/wise5/components/label/labelService.ts index 414e9e570f..d0e22e0e89 100644 --- a/src/main/webapp/wise5/components/label/labelService.ts +++ b/src/main/webapp/wise5/components/label/labelService.ts @@ -10,11 +10,12 @@ import { StudentDataService } from '../../services/studentDataService'; @Injectable() export class LabelService extends ComponentService { - - constructor(private upgrade: UpgradeModule, - private StudentAssetService: StudentAssetService, - protected StudentDataService: StudentDataService, - protected UtilService: UtilService) { + constructor( + private upgrade: UpgradeModule, + private StudentAssetService: StudentAssetService, + protected StudentDataService: StudentDataService, + protected UtilService: UtilService + ) { super(StudentDataService, UtilService); this.StudentAssetService = StudentAssetService; } @@ -40,8 +41,13 @@ export class LabelService extends ComponentService { return component; } - isCompleted(component: any, componentStates: any[], componentEvents: any[], nodeEvents: any[], - node: any) { + isCompleted( + component: any, + componentStates: any[], + componentEvents: any[], + nodeEvents: any[], + node: any + ) { if (!this.canEdit(component) && this.UtilService.hasNodeEnteredEvent(nodeEvents)) { return true; } @@ -85,8 +91,10 @@ export class LabelService extends ComponentService { return this.componentStateHasLabel(componentState); } else { if (this.componentHasStarterLabel(componentContent)) { - return componentState != null && - !this.labelArraysAreTheSame(componentState.studentData.labels, componentContent.labels); + return ( + componentState != null && + !this.labelArraysAreTheSame(componentState.studentData.labels, componentContent.labels) + ); } else { return this.componentStateHasLabel(componentState); } @@ -108,8 +116,10 @@ export class LabelService extends ComponentService { componentStateIsSameAsStarter(componentState: any, componentContent: any) { if (componentState != null) { if (this.componentHasStarterLabel(componentContent)) { - return this.labelArraysAreTheSame(componentState.studentData.labels, - componentContent.labels); + return this.labelArraysAreTheSame( + componentState.studentData.labels, + componentContent.labels + ); } else { return !this.componentStateHasLabel(componentState); } @@ -171,12 +181,14 @@ export class LabelService extends ComponentService { } labelFieldsAreTheSame(label1: any, label2: any) { - return label1.text === label2.text && - label1.pointX === label2.pointX && - label1.pointY === label2.pointY && - label1.textX === label2.textX && - label1.textY === label2.textY && - label1.color === label2.color; + return ( + label1.text === label2.text && + label1.pointX === label2.pointX && + label1.pointY === label2.pointY && + label1.textX === label2.textX && + label1.textY === label2.textY && + label1.color === label2.color + ); } /** @@ -275,7 +287,7 @@ export class LabelService extends ComponentService { const image = new Image(); const thisUtilService = this.UtilService; return new Promise((resolve, reject) => { - image.onload = event => { + image.onload = (event) => { const image: any = event.target; myCanvas.width = image.width; myCanvas.height = image.height; @@ -284,28 +296,30 @@ export class LabelService extends ComponentService { const imageObject = thisUtilService.getImageObjectFromBase64String(base64Image); // create a student asset image - this.StudentAssetService.uploadAsset(imageObject).then(unreferencedAsset => { + this.StudentAssetService.uploadAsset(imageObject).then((unreferencedAsset) => { /* * make a copy of the unreferenced asset so that we * get a referenced asset */ - this.StudentAssetService.copyAssetForReference(unreferencedAsset).then(referencedAsset => { - if (referencedAsset != null) { - /* - * get the asset url - * for example - * /wise/studentuploads/11261/297478/referenced/picture_1494016652542.png - * if we are in preview mode this url will be a base64 string instead - */ - const referencedAssetUrl = referencedAsset.url; - - // remove the unreferenced asset - this.StudentAssetService.deleteAsset(unreferencedAsset); - - // resolve the promise with the image url - resolve(referencedAssetUrl); + this.StudentAssetService.copyAssetForReference(unreferencedAsset).then( + (referencedAsset) => { + if (referencedAsset != null) { + /* + * get the asset url + * for example + * /wise/studentuploads/11261/297478/referenced/picture_1494016652542.png + * if we are in preview mode this url will be a base64 string instead + */ + const referencedAssetUrl = referencedAsset.url; + + // remove the unreferenced asset + this.StudentAssetService.deleteAsset(unreferencedAsset); + + // resolve the promise with the image url + resolve(referencedAssetUrl); + } } - }); + ); }); }; @@ -324,8 +338,10 @@ export class LabelService extends ComponentService { } getSVGTextElementString(fontSize: any, tspans: string) { - return `${tspans}`; + return ( + `${tspans}` + ); } /** diff --git a/src/main/webapp/wise5/components/match/edit-match-advanced/edit-match-advanced.component.ts b/src/main/webapp/wise5/components/match/edit-match-advanced/edit-match-advanced.component.ts index 5c023b30aa..cbb51a76c4 100644 --- a/src/main/webapp/wise5/components/match/edit-match-advanced/edit-match-advanced.component.ts +++ b/src/main/webapp/wise5/components/match/edit-match-advanced/edit-match-advanced.component.ts @@ -1,7 +1,7 @@ -import { EditAdvancedComponentAngularJSController } from "../../../../site/src/app/authoring-tool/edit-advanced-component/editAdvancedComponentAngularJSController"; -import { NodeService } from "../../../services/nodeService"; -import { NotebookService } from "../../../services/notebookService"; -import { TeacherProjectService } from "../../../services/teacherProjectService"; +import { EditAdvancedComponentAngularJSController } from '../../../../site/src/app/authoring-tool/edit-advanced-component/editAdvancedComponentAngularJSController'; +import { NodeService } from '../../../services/nodeService'; +import { NotebookService } from '../../../services/notebookService'; +import { TeacherProjectService } from '../../../services/teacherProjectService'; class EditMatchAdvancedController extends EditAdvancedComponentAngularJSController { allowedConnectedComponentTypes = ['Match']; @@ -28,4 +28,4 @@ export const EditMatchAdvancedComponent = { }, controller: EditMatchAdvancedController, templateUrl: 'wise5/components/label/edit-label-advanced/edit-label-advanced.component.html' -} +}; diff --git a/src/main/webapp/wise5/components/match/matchAuthoring.ts b/src/main/webapp/wise5/components/match/matchAuthoring.ts index 6e03248132..bf233fa5eb 100644 --- a/src/main/webapp/wise5/components/match/matchAuthoring.ts +++ b/src/main/webapp/wise5/components/match/matchAuthoring.ts @@ -381,7 +381,6 @@ class MatchAuthoringController extends EditComponentController { } return null; } - } const MatchAuthoring = { @@ -392,6 +391,6 @@ const MatchAuthoring = { controller: MatchAuthoringController, controllerAs: 'matchController', templateUrl: 'wise5/components/match/authoring.html' -} +}; export default MatchAuthoring; diff --git a/src/main/webapp/wise5/components/match/matchAuthoringComponentModule.ts b/src/main/webapp/wise5/components/match/matchAuthoringComponentModule.ts index a8ab287d5e..69e76d9b13 100644 --- a/src/main/webapp/wise5/components/match/matchAuthoringComponentModule.ts +++ b/src/main/webapp/wise5/components/match/matchAuthoringComponentModule.ts @@ -13,7 +13,7 @@ let matchAuthoringComponentModule = angular .component('editMatchAdvanced', EditMatchAdvancedComponent) .config([ '$translatePartialLoaderProvider', - $translatePartialLoaderProvider => { + ($translatePartialLoaderProvider) => { $translatePartialLoaderProvider.addPart('components/match/i18n'); } ]); diff --git a/src/main/webapp/wise5/components/match/matchComponentModule.ts b/src/main/webapp/wise5/components/match/matchComponentModule.ts index 5899e586ac..84d5ae3d5d 100644 --- a/src/main/webapp/wise5/components/match/matchComponentModule.ts +++ b/src/main/webapp/wise5/components/match/matchComponentModule.ts @@ -11,7 +11,7 @@ let matchComponentModule = angular .controller('MatchController', MatchController) .config([ '$translatePartialLoaderProvider', - $translatePartialLoaderProvider => { + ($translatePartialLoaderProvider) => { $translatePartialLoaderProvider.addPart('components/match/i18n'); } ]); diff --git a/src/main/webapp/wise5/components/match/matchController.ts b/src/main/webapp/wise5/components/match/matchController.ts index 2e695b876f..7ddab67742 100644 --- a/src/main/webapp/wise5/components/match/matchController.ts +++ b/src/main/webapp/wise5/components/match/matchController.ts @@ -115,15 +115,16 @@ class MatchController extends ComponentController { this.isSubmitButtonVisible = this.componentContent.showSubmitButton; if (this.shouldImportPrivateNotes()) { const allPrivateNotebookItems = this.NotebookService.getPrivateNotebookItems(); - this.privateNotebookItems = allPrivateNotebookItems.filter(note => { - return note.serverDeleteTime == null + this.privateNotebookItems = allPrivateNotebookItems.filter((note) => { + return note.serverDeleteTime == null; }); - this.notebookUpdatedSubscription = this.NotebookService.notebookUpdated$ - .subscribe((args) => { - if (args.notebookItem.type === 'note') { - this.addNotebookItemToSourceBucket(args.notebookItem); + this.notebookUpdatedSubscription = this.NotebookService.notebookUpdated$.subscribe( + (args) => { + if (args.notebookItem.type === 'note') { + this.addNotebookItemToSourceBucket(args.notebookItem); + } } - }); + ); } } else if (this.mode === 'grading' || this.mode === 'gradingRevision') { this.isPromptVisible = false; @@ -179,7 +180,7 @@ class MatchController extends ComponentController { * action (optional; default is false) * @return {promise} a promise of a component state containing the student data */ - this.$scope.getComponentState = isSubmit => { + this.$scope.getComponentState = (isSubmit) => { const deferred = this.$q.defer(); let hasDirtyWork = false; let action = 'change'; @@ -197,7 +198,7 @@ class MatchController extends ComponentController { } if (hasDirtyWork) { - this.$scope.matchController.createComponentState(action).then(componentState => { + this.$scope.matchController.createComponentState(action).then((componentState) => { deferred.resolve(componentState); }); } else { @@ -239,15 +240,14 @@ class MatchController extends ComponentController { this.disableDraggingIfNeeded(dragId); const drake = this.dragulaService.find(this.$scope, dragId).drake; this.showVisualIndicatorWhileDragging(drake); - this.autoScroll( - [document.querySelector('#content')], { + this.autoScroll([document.querySelector('#content')], { margin: 30, pixels: 50, scrollWhenOutside: true, - autoScroll: function() { - return this.down && drake.dragging; + autoScroll: function () { + return this.down && drake.dragging; } - }); + }); } registerStudentDataChangedOnDrop(dragId) { @@ -424,13 +424,13 @@ class MatchController extends ComponentController { isLatestComponentStateASubmit() {} getBucketIds() { - return this.buckets.map(b => { + return this.buckets.map((b) => { return b.id; }); } getChoiceIds() { - return this.choices.map(c => { + return this.choices.map((c) => { return c.id; }); } @@ -439,16 +439,18 @@ class MatchController extends ComponentController { const latestSubmitComponentStateBuckets = latestSubmitComponentState.studentData.buckets; const choicesThatChangedSinceLastSubmit = []; for (let currentComponentStateBucket of this.buckets) { - const currentComponentStateBucketChoiceIds = currentComponentStateBucket.items.map(choice => { - return choice.id; - }); + const currentComponentStateBucketChoiceIds = currentComponentStateBucket.items.map( + (choice) => { + return choice.id; + } + ); let bucketFromSubmitComponentState = this.MatchService.getBucketById( currentComponentStateBucket.id, latestSubmitComponentStateBuckets ); if (bucketFromSubmitComponentState != null) { const latestSubmitComponentStateChoiceIds = bucketFromSubmitComponentState.items.map( - choice => { + (choice) => { return choice.id; } ); @@ -791,7 +793,6 @@ class MatchController extends ComponentController { return null; } - /** * Check if the component has been authored with a correct choice * @return {boolean} whether the component has been authored with a correct choice @@ -941,7 +942,7 @@ class MatchController extends ComponentController { .placeholder(this.$translate('match.typeSomething')) .cancel(this.$translate('CANCEL')) .ok(this.$translate('OK')); - this.$mdDialog.show(confirm).then(result => { + this.$mdDialog.show(confirm).then((result) => { if (result != null && result != '') { const newChoice = { id: this.UtilService.generateKey(10), diff --git a/src/main/webapp/wise5/components/match/matchService.ts b/src/main/webapp/wise5/components/match/matchService.ts index a145b003af..80f69d9d8b 100644 --- a/src/main/webapp/wise5/components/match/matchService.ts +++ b/src/main/webapp/wise5/components/match/matchService.ts @@ -8,10 +8,11 @@ import { UpgradeModule } from '@angular/upgrade/static'; @Injectable() export class MatchService extends ComponentService { - - constructor(private upgrade: UpgradeModule, - protected StudentDataService: StudentDataService, - protected UtilService: UtilService) { + constructor( + private upgrade: UpgradeModule, + protected StudentDataService: StudentDataService, + protected UtilService: UtilService + ) { super(StudentDataService, UtilService); } @@ -29,8 +30,13 @@ export class MatchService extends ComponentService { return component; } - isCompleted(component: any, componentStates: any[], componentEvents: any[], nodeEvents: any[], - node: any) { + isCompleted( + component: any, + componentStates: any[], + componentEvents: any[], + nodeEvents: any[], + node: any + ) { if (componentStates && componentStates.length > 0) { const isSubmitRequired = this.isSubmitRequired(node, component); for (const componentState of componentStates) { @@ -68,7 +74,7 @@ export class MatchService extends ComponentService { } return false; } - + getChoiceById(id: string, choices: any[]): any { return this.getItemById(id, choices); } diff --git a/src/main/webapp/wise5/components/multipleChoice/edit-multiple-choice-advanced/edit-multiple-choice-advanced.component.ts b/src/main/webapp/wise5/components/multipleChoice/edit-multiple-choice-advanced/edit-multiple-choice-advanced.component.ts index be3b581c2e..807a2cfeff 100644 --- a/src/main/webapp/wise5/components/multipleChoice/edit-multiple-choice-advanced/edit-multiple-choice-advanced.component.ts +++ b/src/main/webapp/wise5/components/multipleChoice/edit-multiple-choice-advanced/edit-multiple-choice-advanced.component.ts @@ -1,7 +1,7 @@ -import { EditAdvancedComponentAngularJSController } from "../../../../site/src/app/authoring-tool/edit-advanced-component/editAdvancedComponentAngularJSController"; -import { NodeService } from "../../../services/nodeService"; -import { TeacherProjectService } from "../../../services/teacherProjectService"; -import { UtilService } from "../../../services/utilService"; +import { EditAdvancedComponentAngularJSController } from '../../../../site/src/app/authoring-tool/edit-advanced-component/editAdvancedComponentAngularJSController'; +import { NodeService } from '../../../services/nodeService'; +import { TeacherProjectService } from '../../../services/teacherProjectService'; +import { UtilService } from '../../../services/utilService'; class EditMultipleChoiceAdvancedController extends EditAdvancedComponentAngularJSController { allowedConnectedComponentTypes = ['MultipleChoice']; @@ -18,7 +18,8 @@ class EditMultipleChoiceAdvancedController extends EditAdvancedComponentAngularJ let numberOfAllowedComponents = 0; let allowedComponent = null; for (const component of this.getComponentsByNodeId(connectedComponent.nodeId)) { - if (this.isConnectedComponentTypeAllowed(component.type) && + if ( + this.isConnectedComponentTypeAllowed(component.type) && component.id != this.componentId ) { numberOfAllowedComponents += 1; @@ -74,5 +75,6 @@ export const EditMultipleChoiceAdvancedComponent = { componentId: '@' }, controller: EditMultipleChoiceAdvancedController, - templateUrl: 'wise5/components/multipleChoice/edit-multiple-choice-advanced/edit-multiple-choice-advanced.component.html' -} + templateUrl: + 'wise5/components/multipleChoice/edit-multiple-choice-advanced/edit-multiple-choice-advanced.component.html' +}; diff --git a/src/main/webapp/wise5/components/multipleChoice/multipleChoiceAuthoring.ts b/src/main/webapp/wise5/components/multipleChoice/multipleChoiceAuthoring.ts index 585127e5aa..eb4f7f4942 100644 --- a/src/main/webapp/wise5/components/multipleChoice/multipleChoiceAuthoring.ts +++ b/src/main/webapp/wise5/components/multipleChoice/multipleChoiceAuthoring.ts @@ -128,7 +128,6 @@ class MultipleChoiceAuthoringController extends EditComponentController { this.componentChanged(); } } - } const MultipleChoiceAuthoring = { @@ -139,6 +138,6 @@ const MultipleChoiceAuthoring = { controller: MultipleChoiceAuthoringController, controllerAs: 'multipleChoiceController', templateUrl: 'wise5/components/multipleChoice/authoring.html' -} +}; export default MultipleChoiceAuthoring; diff --git a/src/main/webapp/wise5/components/multipleChoice/multipleChoiceAuthoringComponentModule.ts b/src/main/webapp/wise5/components/multipleChoice/multipleChoiceAuthoringComponentModule.ts index 1ca887979b..c4e9dae8a3 100644 --- a/src/main/webapp/wise5/components/multipleChoice/multipleChoiceAuthoringComponentModule.ts +++ b/src/main/webapp/wise5/components/multipleChoice/multipleChoiceAuthoringComponentModule.ts @@ -13,7 +13,7 @@ const multipleChoiceAuthoringComponentModule = angular .component('editMultipleChoiceAdvanced', EditMultipleChoiceAdvancedComponent) .config([ '$translatePartialLoaderProvider', - $translatePartialLoaderProvider => { + ($translatePartialLoaderProvider) => { $translatePartialLoaderProvider.addPart('components/multipleChoice/i18n'); } ]); diff --git a/src/main/webapp/wise5/components/multipleChoice/multipleChoiceComponentModule.ts b/src/main/webapp/wise5/components/multipleChoice/multipleChoiceComponentModule.ts index 2dd69a74db..2825f7ee71 100644 --- a/src/main/webapp/wise5/components/multipleChoice/multipleChoiceComponentModule.ts +++ b/src/main/webapp/wise5/components/multipleChoice/multipleChoiceComponentModule.ts @@ -11,7 +11,7 @@ let multipleChoiceComponentModule = angular .controller('MultipleChoiceController', MultipleChoiceController) .config([ '$translatePartialLoaderProvider', - $translatePartialLoaderProvider => { + ($translatePartialLoaderProvider) => { $translatePartialLoaderProvider.addPart('components/multipleChoice/i18n'); } ]); diff --git a/src/main/webapp/wise5/components/multipleChoice/multipleChoiceController.ts b/src/main/webapp/wise5/components/multipleChoice/multipleChoiceController.ts index e19d518066..5bb6a2301d 100644 --- a/src/main/webapp/wise5/components/multipleChoice/multipleChoiceController.ts +++ b/src/main/webapp/wise5/components/multipleChoice/multipleChoiceController.ts @@ -165,7 +165,7 @@ class MultipleChoiceController extends ComponentController { * action (optional; default is false) * @return a promise of a component state containing the student data */ - this.$scope.getComponentState = function(isSubmit) { + this.$scope.getComponentState = function (isSubmit) { const deferred = this.$q.defer(); let getState = false; let action = 'change'; @@ -184,7 +184,7 @@ class MultipleChoiceController extends ComponentController { if (getState) { // create a component state populated with the student data - this.$scope.multipleChoiceController.createComponentState(action).then(componentState => { + this.$scope.multipleChoiceController.createComponentState(action).then((componentState) => { deferred.resolve(componentState); }); } else { diff --git a/src/main/webapp/wise5/components/multipleChoice/multipleChoiceService.ts b/src/main/webapp/wise5/components/multipleChoice/multipleChoiceService.ts index 1a05816049..49d7e847cc 100644 --- a/src/main/webapp/wise5/components/multipleChoice/multipleChoiceService.ts +++ b/src/main/webapp/wise5/components/multipleChoice/multipleChoiceService.ts @@ -8,10 +8,11 @@ import { UpgradeModule } from '@angular/upgrade/static'; @Injectable() export class MultipleChoiceService extends ComponentService { - - constructor(private upgrade: UpgradeModule, - protected StudentDataService: StudentDataService, - protected UtilService: UtilService) { + constructor( + private upgrade: UpgradeModule, + protected StudentDataService: StudentDataService, + protected UtilService: UtilService + ) { super(StudentDataService, UtilService); } @@ -38,8 +39,10 @@ export class MultipleChoiceService extends ComponentService { const nodeId = criteria.params.nodeId; const componentId = criteria.params.componentId; const constraintChoiceIds = criteria.params.choiceIds; - const latestComponentState = this.StudentDataService - .getLatestComponentStateByNodeIdAndComponentId(nodeId, componentId); + const latestComponentState = this.StudentDataService.getLatestComponentStateByNodeIdAndComponentId( + nodeId, + componentId + ); if (latestComponentState != null) { const studentChoices = latestComponentState.studentData.studentChoices; const studentChoiceIds = this.getStudentChoiceIdsFromStudentChoiceObjects(studentChoices); @@ -59,12 +62,12 @@ export class MultipleChoiceService extends ComponentService { isChoiceIdsMatch(choiceIds1: string[], choiceIds2: string[]) { if (choiceIds1.length === choiceIds2.length) { - for (let choiceId of choiceIds2) { - if (choiceIds1.indexOf(choiceId) === -1) { - return false; - } + for (let choiceId of choiceIds2) { + if (choiceIds1.indexOf(choiceId) === -1) { + return false; } - return true; + } + return true; } return false; } @@ -87,15 +90,22 @@ export class MultipleChoiceService extends ComponentService { return choiceIds; } - isCompleted(component: any, componentStates: any[], componentEvents: any[], nodeEvents: any[], - node: any) { + isCompleted( + component: any, + componentStates: any[], + componentEvents: any[], + nodeEvents: any[], + node: any + ) { if (componentStates && componentStates.length) { const isSubmitRequired = this.isSubmitRequired(node, component); for (let c = componentStates.length - 1; c >= 0; c--) { const componentState = componentStates[c]; const studentChoices = this.getStudentChoicesFromComponentState(componentState); - if (studentChoices != null && - (!isSubmitRequired || (isSubmitRequired && componentState.isSubmit))) { + if ( + studentChoices != null && + (!isSubmitRequired || (isSubmitRequired && componentState.isSubmit)) + ) { return true; } } @@ -121,7 +131,7 @@ export class MultipleChoiceService extends ComponentService { if (studentData != null) { const studentChoices = studentData.studentChoices; if (studentChoices != null) { - return studentChoices.map(studentChoice => studentChoice.text).join(', '); + return studentChoices.map((studentChoice) => studentChoice.text).join(', '); } } } diff --git a/src/main/webapp/wise5/components/openResponse/edit-open-response-advanced/edit-open-response-advanced.component.ts b/src/main/webapp/wise5/components/openResponse/edit-open-response-advanced/edit-open-response-advanced.component.ts index 7cd01d7419..f8508163a7 100644 --- a/src/main/webapp/wise5/components/openResponse/edit-open-response-advanced/edit-open-response-advanced.component.ts +++ b/src/main/webapp/wise5/components/openResponse/edit-open-response-advanced/edit-open-response-advanced.component.ts @@ -1,10 +1,9 @@ -import { EditAdvancedComponentAngularJSController } from "../../../../site/src/app/authoring-tool/edit-advanced-component/editAdvancedComponentAngularJSController"; -import { CRaterService } from "../../../services/cRaterService"; -import { NodeService } from "../../../services/nodeService"; -import { TeacherProjectService } from "../../../services/teacherProjectService"; +import { EditAdvancedComponentAngularJSController } from '../../../../site/src/app/authoring-tool/edit-advanced-component/editAdvancedComponentAngularJSController'; +import { CRaterService } from '../../../services/cRaterService'; +import { NodeService } from '../../../services/nodeService'; +import { TeacherProjectService } from '../../../services/teacherProjectService'; class EditOpenResponseAdvancedController extends EditAdvancedComponentAngularJSController { - allowedConnectedComponentTypes = ['OpenResponse']; cRaterItemIdIsValid: boolean = null; isVerifyingCRaterItemId: boolean = false; @@ -112,7 +111,7 @@ class EditOpenResponseAdvancedController extends EditAdvancedComponentAngularJSC verifyCRaterItemId(itemId: string): void { this.cRaterItemIdIsValid = null; this.isVerifyingCRaterItemId = true; - this.CRaterService.makeCRaterVerifyRequest(itemId).then(isValid => { + this.CRaterService.makeCRaterVerifyRequest(itemId).then((isValid) => { this.isVerifyingCRaterItemId = false; this.cRaterItemIdIsValid = isValid; }); @@ -142,8 +141,7 @@ class EditOpenResponseAdvancedController extends EditAdvancedComponentAngularJSC ' {{score}}. ' + $localize`Please talk to your teacher` + '.', - notificationMessageToTeacher: - '{{username}} ' + $localize`got a score of` + ' {{score}}.' + notificationMessageToTeacher: '{{username}} ' + $localize`got a score of` + ' {{score}}.' }; this.authoringComponentContent.notificationSettings.notifications.push(newNotification); this.componentChanged(); @@ -378,7 +376,6 @@ class EditOpenResponseAdvancedController extends EditAdvancedComponentAngularJSC this.componentChanged(); } } - } export const EditOpenResponseAdvancedComponent = { @@ -387,5 +384,6 @@ export const EditOpenResponseAdvancedComponent = { componentId: '@' }, controller: EditOpenResponseAdvancedController, - templateUrl: 'wise5/components/openResponse/edit-open-response-advanced/edit-open-response-advanced.component.html' -} + templateUrl: + 'wise5/components/openResponse/edit-open-response-advanced/edit-open-response-advanced.component.html' +}; diff --git a/src/main/webapp/wise5/components/openResponse/open-response-authoring/open-response-authoring.component.ts b/src/main/webapp/wise5/components/openResponse/open-response-authoring/open-response-authoring.component.ts index 5ab075ec74..62869cb6ed 100644 --- a/src/main/webapp/wise5/components/openResponse/open-response-authoring/open-response-authoring.component.ts +++ b/src/main/webapp/wise5/components/openResponse/open-response-authoring/open-response-authoring.component.ts @@ -5,6 +5,4 @@ import { ComponentAuthoring } from '../../../authoringTool/components/component- selector: 'open-response-authoring', templateUrl: 'open-response-authoring.component.html' }) -export class OpenResponseAuthoring extends ComponentAuthoring { - -} +export class OpenResponseAuthoring extends ComponentAuthoring {} diff --git a/src/main/webapp/wise5/components/openResponse/openResponseAuthoringComponentModule.ts b/src/main/webapp/wise5/components/openResponse/openResponseAuthoringComponentModule.ts index b240b040a3..59a952174b 100644 --- a/src/main/webapp/wise5/components/openResponse/openResponseAuthoringComponentModule.ts +++ b/src/main/webapp/wise5/components/openResponse/openResponseAuthoringComponentModule.ts @@ -9,12 +9,14 @@ import { EditOpenResponseAdvancedComponent } from './edit-open-response-advanced const openResponseAuthoringComponentModule = angular .module('openResponseAuthoringComponentModule', ['pascalprecht.translate']) .service('OpenResponseService', downgradeInjectable(OpenResponseService)) - .directive('openResponseAuthoring', downgradeComponent( - { component: OpenResponseAuthoring }) as angular.IDirectiveFactory) + .directive( + 'openResponseAuthoring', + downgradeComponent({ component: OpenResponseAuthoring }) as angular.IDirectiveFactory + ) .component('editOpenResponseAdvanced', EditOpenResponseAdvancedComponent) .config([ '$translatePartialLoaderProvider', - $translatePartialLoaderProvider => { + ($translatePartialLoaderProvider) => { $translatePartialLoaderProvider.addPart('components/openResponse/i18n'); } ]); diff --git a/src/main/webapp/wise5/components/openResponse/openResponseComponentModule.ts b/src/main/webapp/wise5/components/openResponse/openResponseComponentModule.ts index 8476cf207a..128a62d8d0 100644 --- a/src/main/webapp/wise5/components/openResponse/openResponseComponentModule.ts +++ b/src/main/webapp/wise5/components/openResponse/openResponseComponentModule.ts @@ -11,7 +11,7 @@ const openResponseComponentModule = angular .controller('OpenResponseController', OpenResponseController) .config([ '$translatePartialLoaderProvider', - $translatePartialLoaderProvider => { + ($translatePartialLoaderProvider) => { $translatePartialLoaderProvider.addPart('components/openResponse/i18n'); } ]); diff --git a/src/main/webapp/wise5/components/openResponse/openResponseController.ts b/src/main/webapp/wise5/components/openResponse/openResponseController.ts index 4fa7830350..48257aec59 100644 --- a/src/main/webapp/wise5/components/openResponse/openResponseController.ts +++ b/src/main/webapp/wise5/components/openResponse/openResponseController.ts @@ -113,8 +113,8 @@ class OpenResponseController extends ComponentController { autoresize_min_height: '100', image_advtab: true, content_css: themePath + '/style/tinymce.css', - setup: function(ed) { - ed.on('focus', function(e) { + setup: function (ed) { + ed.on('focus', function (e) { $(e.target.editorContainer) .addClass('input--focused') .parent() @@ -122,7 +122,7 @@ class OpenResponseController extends ComponentController { $('label[for="' + e.target.id + '"]').addClass('input-label--focused'); }); - ed.on('blur', function(e) { + ed.on('blur', function (e) { $(e.target.editorContainer) .removeClass('input--focused') .parent() @@ -204,7 +204,7 @@ class OpenResponseController extends ComponentController { /** * Returns true iff there is student work that hasn't been saved yet */ - this.$scope.isDirty = function() { + this.$scope.isDirty = function () { return this.$scope.openResponseController.isDirty; }.bind(this); @@ -216,7 +216,7 @@ class OpenResponseController extends ComponentController { * action (optional; default is false) * @return a promise of a component state containing the student data */ - this.$scope.getComponentState = function(isSubmit) { + this.$scope.getComponentState = function (isSubmit) { const deferred = this.$q.defer(); let getState = false; let action = 'change'; @@ -235,7 +235,7 @@ class OpenResponseController extends ComponentController { if (getState) { // create a component state populated with the student data - this.$scope.openResponseController.createComponentState(action).then(componentState => { + this.$scope.openResponseController.createComponentState(action).then((componentState) => { deferred.resolve(componentState); }); } else { @@ -256,7 +256,7 @@ class OpenResponseController extends ComponentController { // load script for this component, if any let script = this.componentContent.script; if (script != null) { - this.ProjectService.retrieveScript(script).then(script => { + this.ProjectService.retrieveScript(script).then((script) => { new Function(script).call(this); }); } @@ -484,9 +484,9 @@ class OpenResponseController extends ComponentController { this.CRaterService.makeCRaterScoringRequest(cRaterItemId, cRaterResponseId, studentData).then( (data: any) => { /* - * annotations we put in the component state will be - * removed from the component state and saved separately - */ + * annotations we put in the component state will be + * removed from the component state and saved separately + */ componentState.annotations = []; // get the CRater score @@ -539,8 +539,7 @@ class OpenResponseController extends ComponentController { ) { let globalAnnotationMaxCount = 0; if ( - this.componentContent.globalAnnotationSettings.globalAnnotationMaxCount != - null + this.componentContent.globalAnnotationSettings.globalAnnotationMaxCount != null ) { globalAnnotationMaxCount = this.componentContent.globalAnnotationSettings .globalAnnotationMaxCount; @@ -559,8 +558,7 @@ class OpenResponseController extends ComponentController { autoScoreAnnotation.clientSaveTime; // save annotation creation time if ( - globalAnnotationGroupsByNodeIdAndComponentId.length >= - globalAnnotationMaxCount + globalAnnotationGroupsByNodeIdAndComponentId.length >= globalAnnotationMaxCount ) { // we've already applied this annotation properties to maxCount annotations, so we don't need to apply it any more. annotationGroupForScore = null; @@ -573,7 +571,7 @@ class OpenResponseController extends ComponentController { annotationGroupForScore.unGlobalizeCriteria != null ) { // check if this annotation is global and what criteria needs to be met to un-globalize. - annotationGroupForScore.unGlobalizeCriteria.map(unGlobalizeCriteria => { + annotationGroupForScore.unGlobalizeCriteria.map((unGlobalizeCriteria) => { // if the un-globalize criteria is time-based (e.g. isVisitedAfter, isRevisedAfter, isVisitedAndRevisedAfter, etc), store the timestamp of this annotation in the criteria // so we can compare it when we check for criteria satisfaction. if (unGlobalizeCriteria.params != null) { @@ -598,9 +596,9 @@ class OpenResponseController extends ComponentController { } /* - * we are in the authoring view so we will set the - * latest score annotation manually - */ + * we are in the authoring view so we will set the + * latest score annotation manually + */ this.latestAnnotations.score = autoScoreAnnotation; } @@ -614,9 +612,9 @@ class OpenResponseController extends ComponentController { submitCounter > 1 ) { /* - * this step has multiple attempt scoring rules and this is - * a subsequent submit - */ + * this step has multiple attempt scoring rules and this is + * a subsequent submit + */ // get the feedback based upon the previous score and current score autoComment = this.CRaterService.getMultipleAttemptCRaterFeedbackTextByScore( this.componentContent, @@ -656,9 +654,9 @@ class OpenResponseController extends ComponentController { } /* - * we are in the authoring view so we will set the - * latest comment annotation manually - */ + * we are in the authoring view so we will set the + * latest comment annotation manually + */ this.latestAnnotations.comment = autoCommentAnnotation; } } @@ -829,28 +827,29 @@ class OpenResponseController extends ComponentController { snipButtonClicked($event) { if (this.isDirty) { - const studentWorkSavedToServerSubscription = this.StudentDataService.studentWorkSavedToServer$ - .subscribe((args: any) => { - const componentState = args.studentWork; - if ( - componentState && - this.nodeId === componentState.nodeId && - this.componentId === componentState.componentId - ) { - const imageObject = null; - const noteText = componentState.studentData.response; - const isEditTextEnabled = false; - const isFileUploadEnabled = false; - this.NotebookService.addNote( - imageObject, - noteText, - [componentState.id], - isEditTextEnabled, - isFileUploadEnabled - ); - studentWorkSavedToServerSubscription.unsubscribe(); + const studentWorkSavedToServerSubscription = this.StudentDataService.studentWorkSavedToServer$.subscribe( + (args: any) => { + const componentState = args.studentWork; + if ( + componentState && + this.nodeId === componentState.nodeId && + this.componentId === componentState.componentId + ) { + const imageObject = null; + const noteText = componentState.studentData.response; + const isEditTextEnabled = false; + const isFileUploadEnabled = false; + this.NotebookService.addNote( + imageObject, + noteText, + [componentState.id], + isEditTextEnabled, + isFileUploadEnabled + ); + studentWorkSavedToServerSubscription.unsubscribe(); + } } - }); + ); this.saveButtonClicked(); // trigger a save } else { const studentWork = this.StudentDataService.getLatestComponentStateByNodeIdAndComponentId( @@ -1031,9 +1030,11 @@ class OpenResponseController extends ComponentController { } hasAudioResponses() { - return this.attachments.filter(attachment => { - return attachment.type === 'audio'; - }).length > 0; + return ( + this.attachments.filter((attachment) => { + return attachment.type === 'audio'; + }).length > 0 + ); } removeAudioAttachment(attachment) { @@ -1043,7 +1044,7 @@ class OpenResponseController extends ComponentController { } removeAudioAttachments() { - this.attachments.forEach(attachment => { + this.attachments.forEach((attachment) => { if (attachment.type === 'audio') { this.removeAttachment(attachment); } diff --git a/src/main/webapp/wise5/components/openResponse/openResponseService.ts b/src/main/webapp/wise5/components/openResponse/openResponseService.ts index a426cefc50..3d6d0e16f8 100644 --- a/src/main/webapp/wise5/components/openResponse/openResponseService.ts +++ b/src/main/webapp/wise5/components/openResponse/openResponseService.ts @@ -8,10 +8,11 @@ import { UpgradeModule } from '@angular/upgrade/static'; @Injectable() export class OpenResponseService extends ComponentService { - - constructor(private upgrade: UpgradeModule, - protected StudentDataService: StudentDataService, - protected UtilService: UtilService) { + constructor( + private upgrade: UpgradeModule, + protected StudentDataService: StudentDataService, + protected UtilService: UtilService + ) { super(StudentDataService, UtilService); } @@ -27,8 +28,13 @@ export class OpenResponseService extends ComponentService { return component; } - isCompleted(component: any, componentStates: any[], componentEvents: any[], nodeEvents: any[], - node: any) { + isCompleted( + component: any, + componentStates: any[], + componentEvents: any[], + nodeEvents: any[], + node: any + ) { if (component.completionCriteria != null) { return this.StudentDataService.isCompletionCriteriaSatisfied(component.completionCriteria); } else if (this.hasComponentState(componentStates)) { @@ -57,16 +63,20 @@ export class OpenResponseService extends ComponentService { } isDisplayAnnotationForAutoScore(componentContent: any) { - if ((componentContent.cRater != null && !componentContent.cRater.showScore) || - componentContent.showAutoScore === false) { + if ( + (componentContent.cRater != null && !componentContent.cRater.showScore) || + componentContent.showAutoScore === false + ) { return false; } return true; } isDisplayAnnotationForAutoComment(componentContent: any) { - if ((componentContent.cRater != null && !componentContent.cRater.showFeedback) || - componentContent.showAutoFeedback === false) { + if ( + (componentContent.cRater != null && !componentContent.cRater.showFeedback) || + componentContent.showAutoFeedback === false + ) { return false; } return true; diff --git a/src/main/webapp/wise5/components/outsideURL/edit-outside-url-advanced/edit-outside-url-advanced.component.ts b/src/main/webapp/wise5/components/outsideURL/edit-outside-url-advanced/edit-outside-url-advanced.component.ts index 182c71350b..81464c017e 100644 --- a/src/main/webapp/wise5/components/outsideURL/edit-outside-url-advanced/edit-outside-url-advanced.component.ts +++ b/src/main/webapp/wise5/components/outsideURL/edit-outside-url-advanced/edit-outside-url-advanced.component.ts @@ -1,13 +1,12 @@ -import { Component } from "@angular/core"; -import { EditAdvancedComponentComponent } from "../../../../site/src/app/authoring-tool/edit-advanced-component/edit-advanced-component.component"; -import { TeacherProjectService } from "../../../services/teacherProjectService"; +import { Component } from '@angular/core'; +import { EditAdvancedComponentComponent } from '../../../../site/src/app/authoring-tool/edit-advanced-component/edit-advanced-component.component'; +import { TeacherProjectService } from '../../../services/teacherProjectService'; @Component({ selector: 'edit-outside-url-advanced', templateUrl: 'edit-outside-url-advanced.component.html' }) export class EditOutsideUrlAdvancedComponent extends EditAdvancedComponentComponent { - constructor(protected ProjectService: TeacherProjectService) { super(ProjectService); } diff --git a/src/main/webapp/wise5/components/outsideURL/outsideURLAuthoring.ts b/src/main/webapp/wise5/components/outsideURL/outsideURLAuthoring.ts index 0319a24588..2b8edf63c6 100644 --- a/src/main/webapp/wise5/components/outsideURL/outsideURLAuthoring.ts +++ b/src/main/webapp/wise5/components/outsideURL/outsideURLAuthoring.ts @@ -147,6 +147,6 @@ const OutsideURLAuthoring = { controller: OutsideURLAuthoringController, controllerAs: 'outsideURLController', templateUrl: 'wise5/components/outsideURL/authoring.html' -} +}; export default OutsideURLAuthoring; diff --git a/src/main/webapp/wise5/components/outsideURL/outsideURLAuthoringComponentModule.ts b/src/main/webapp/wise5/components/outsideURL/outsideURLAuthoringComponentModule.ts index e4be08811f..56cd213690 100644 --- a/src/main/webapp/wise5/components/outsideURL/outsideURLAuthoringComponentModule.ts +++ b/src/main/webapp/wise5/components/outsideURL/outsideURLAuthoringComponentModule.ts @@ -10,11 +10,13 @@ const outsideURLAuthoringComponentModule = angular .module('outsideURLAuthoringComponentModule', []) .service('OutsideURLService', downgradeInjectable(OutsideURLService)) .component('outsideUrlAuthoring', OutsideURLAuthoring) - .directive('editOutsideUrlAdvanced', downgradeComponent( - { component: EditOutsideUrlAdvancedComponent }) as angular.IDirectiveFactory) + .directive( + 'editOutsideUrlAdvanced', + downgradeComponent({ component: EditOutsideUrlAdvancedComponent }) as angular.IDirectiveFactory + ) .config([ '$translatePartialLoaderProvider', - $translatePartialLoaderProvider => { + ($translatePartialLoaderProvider) => { $translatePartialLoaderProvider.addPart('components/outsideURL/i18n'); } ]); diff --git a/src/main/webapp/wise5/components/outsideURL/outsideURLComponentModule.ts b/src/main/webapp/wise5/components/outsideURL/outsideURLComponentModule.ts index 592ee994ef..da81eb6529 100644 --- a/src/main/webapp/wise5/components/outsideURL/outsideURLComponentModule.ts +++ b/src/main/webapp/wise5/components/outsideURL/outsideURLComponentModule.ts @@ -11,7 +11,7 @@ let outsideURLComponentModule = angular .controller('OutsideURLController', OutsideURLController) .config([ '$translatePartialLoaderProvider', - $translatePartialLoaderProvider => { + ($translatePartialLoaderProvider) => { $translatePartialLoaderProvider.addPart('components/outsideURL/i18n'); } ]); diff --git a/src/main/webapp/wise5/components/outsideURL/outsideURLService.ts b/src/main/webapp/wise5/components/outsideURL/outsideURLService.ts index 202e6a594f..1719035fea 100644 --- a/src/main/webapp/wise5/components/outsideURL/outsideURLService.ts +++ b/src/main/webapp/wise5/components/outsideURL/outsideURLService.ts @@ -9,11 +9,12 @@ import { UtilService } from '../../services/utilService'; @Injectable() export class OutsideURLService extends ComponentService { - - constructor(private upgrade: UpgradeModule, - private http: HttpClient, - protected StudentDataService: StudentDataService, - protected UtilService: UtilService) { + constructor( + private upgrade: UpgradeModule, + private http: HttpClient, + protected StudentDataService: StudentDataService, + protected UtilService: UtilService + ) { super(StudentDataService, UtilService); } @@ -53,8 +54,11 @@ export class OutsideURLService extends ComponentService { } getOpenEducationalResources() { - return this.http.get(`wise5/components/outsideURL/resources.json`).toPromise().then(result => { - return result; - }); + return this.http + .get(`wise5/components/outsideURL/resources.json`) + .toPromise() + .then((result) => { + return result; + }); } } diff --git a/src/main/webapp/wise5/components/summary/edit-summary-advanced/edit-summary-advanced.component.ts b/src/main/webapp/wise5/components/summary/edit-summary-advanced/edit-summary-advanced.component.ts index 447d25aa2f..ef0980b970 100644 --- a/src/main/webapp/wise5/components/summary/edit-summary-advanced/edit-summary-advanced.component.ts +++ b/src/main/webapp/wise5/components/summary/edit-summary-advanced/edit-summary-advanced.component.ts @@ -1,8 +1,6 @@ -import { EditAdvancedComponentAngularJSController } from "../../../../site/src/app/authoring-tool/edit-advanced-component/editAdvancedComponentAngularJSController"; +import { EditAdvancedComponentAngularJSController } from '../../../../site/src/app/authoring-tool/edit-advanced-component/editAdvancedComponentAngularJSController'; -class EditSummaryAdvancedController extends EditAdvancedComponentAngularJSController { - -} +class EditSummaryAdvancedController extends EditAdvancedComponentAngularJSController {} export const EditSummaryAdvancedComponent = { bindings: { @@ -11,4 +9,4 @@ export const EditSummaryAdvancedComponent = { }, controller: EditSummaryAdvancedController, templateUrl: 'wise5/components/summary/edit-summary-advanced/edit-summary-advanced.component.html' -} \ No newline at end of file +}; diff --git a/src/main/webapp/wise5/components/summary/summaryAuthoring.ts b/src/main/webapp/wise5/components/summary/summaryAuthoring.ts index 20f63690f2..3cc8c5c4d1 100644 --- a/src/main/webapp/wise5/components/summary/summaryAuthoring.ts +++ b/src/main/webapp/wise5/components/summary/summaryAuthoring.ts @@ -207,8 +207,7 @@ class SummaryAuthoringController extends EditComponentController { * create a new array using concat() to actually trigger a change so the SummaryDisplay will * update when a custom label color is changed in the authoring view. */ - this.authoringComponentContent.customLabelColors = - this.authoringComponentContent.customLabelColors.concat(); + this.authoringComponentContent.customLabelColors = this.authoringComponentContent.customLabelColors.concat(); } } @@ -220,6 +219,6 @@ const SummaryAuthoring = { controller: SummaryAuthoringController, controllerAs: 'summaryController', templateUrl: 'wise5/components/summary/authoring.html' -} +}; export default SummaryAuthoring; diff --git a/src/main/webapp/wise5/components/summary/summaryAuthoringComponentModule.ts b/src/main/webapp/wise5/components/summary/summaryAuthoringComponentModule.ts index b8b52cabc9..b108a41c05 100644 --- a/src/main/webapp/wise5/components/summary/summaryAuthoringComponentModule.ts +++ b/src/main/webapp/wise5/components/summary/summaryAuthoringComponentModule.ts @@ -13,7 +13,7 @@ const summaryAuthoringComponentModule = angular .component('editSummaryAdvanced', EditSummaryAdvancedComponent) .config([ '$translatePartialLoaderProvider', - $translatePartialLoaderProvider => { + ($translatePartialLoaderProvider) => { $translatePartialLoaderProvider.addPart('components/summary/i18n'); } ]); diff --git a/src/main/webapp/wise5/components/summary/summaryComponentModule.ts b/src/main/webapp/wise5/components/summary/summaryComponentModule.ts index 801e723db4..f3ea716381 100644 --- a/src/main/webapp/wise5/components/summary/summaryComponentModule.ts +++ b/src/main/webapp/wise5/components/summary/summaryComponentModule.ts @@ -11,7 +11,7 @@ const summaryComponentModule = angular .controller('SummaryController', SummaryController) .config([ '$translatePartialLoaderProvider', - $translatePartialLoaderProvider => { + ($translatePartialLoaderProvider) => { $translatePartialLoaderProvider.addPart('components/summary/i18n'); } ]); diff --git a/src/main/webapp/wise5/components/summary/summaryService.ts b/src/main/webapp/wise5/components/summary/summaryService.ts index 8f5beedd2b..2ce93ebd29 100644 --- a/src/main/webapp/wise5/components/summary/summaryService.ts +++ b/src/main/webapp/wise5/components/summary/summaryService.ts @@ -11,9 +11,11 @@ export class SummaryService extends ComponentService { componentsWithScoresSummary: string[]; componentsWithResponsesSummary: string[]; - constructor(private upgrade: UpgradeModule, - protected StudentDataService: StudentDataService, - protected UtilService: UtilService) { + constructor( + private upgrade: UpgradeModule, + protected StudentDataService: StudentDataService, + protected UtilService: UtilService + ) { super(StudentDataService, UtilService); this.componentsWithScoresSummary = [ 'Animation', diff --git a/src/main/webapp/wise5/components/table/edit-table-advanced/edit-table-advanced.component.ts b/src/main/webapp/wise5/components/table/edit-table-advanced/edit-table-advanced.component.ts index ebbf3d5c87..867390d590 100644 --- a/src/main/webapp/wise5/components/table/edit-table-advanced/edit-table-advanced.component.ts +++ b/src/main/webapp/wise5/components/table/edit-table-advanced/edit-table-advanced.component.ts @@ -1,4 +1,4 @@ -import { EditAdvancedComponentAngularJSController } from "../../../../site/src/app/authoring-tool/edit-advanced-component/editAdvancedComponentAngularJSController"; +import { EditAdvancedComponentAngularJSController } from '../../../../site/src/app/authoring-tool/edit-advanced-component/editAdvancedComponentAngularJSController'; class EditTableAdvancedController extends EditAdvancedComponentAngularJSController { allowedConnectedComponentTypes = ['Embedded', 'Graph', 'Table']; @@ -114,8 +114,10 @@ class EditTableAdvancedController extends EditAdvancedComponentAngularJSControll } decreaseNumDataExplorerSeries(count: number): void { - this.authoringComponentContent.dataExplorerSeriesParams = - this.authoringComponentContent.dataExplorerSeriesParams.slice(0, count); + this.authoringComponentContent.dataExplorerSeriesParams = this.authoringComponentContent.dataExplorerSeriesParams.slice( + 0, + count + ); } numDataExplorerYAxisChanged(): void { @@ -130,7 +132,6 @@ class EditTableAdvancedController extends EditAdvancedComponentAngularJSControll } } } - } export const EditTableAdvancedComponent = { @@ -140,4 +141,4 @@ export const EditTableAdvancedComponent = { }, controller: EditTableAdvancedController, templateUrl: 'wise5/components/table/edit-table-advanced/edit-table-advanced.component.html' -} +}; diff --git a/src/main/webapp/wise5/components/table/tableAuthoring.ts b/src/main/webapp/wise5/components/table/tableAuthoring.ts index 790a9799a3..beef4e7fef 100644 --- a/src/main/webapp/wise5/components/table/tableAuthoring.ts +++ b/src/main/webapp/wise5/components/table/tableAuthoring.ts @@ -413,7 +413,6 @@ class TableAuthoringController extends EditComponentController { this.automaticallySetConnectedComponentFieldsIfPossible(connectedComponent); this.componentChanged(); } - } const TableAuthoring = { @@ -424,7 +423,6 @@ const TableAuthoring = { controller: TableAuthoringController, controllerAs: 'tableController', templateUrl: 'wise5/components/table/authoring.html' -} - +}; export default TableAuthoring; diff --git a/src/main/webapp/wise5/components/table/tableAuthoringComponentModule.ts b/src/main/webapp/wise5/components/table/tableAuthoringComponentModule.ts index 35caa3f7f5..60f9c96360 100644 --- a/src/main/webapp/wise5/components/table/tableAuthoringComponentModule.ts +++ b/src/main/webapp/wise5/components/table/tableAuthoringComponentModule.ts @@ -13,7 +13,7 @@ const tableAuthoringComponentModule = angular .component('editTableAdvanced', EditTableAdvancedComponent) .config([ '$translatePartialLoaderProvider', - $translatePartialLoaderProvider => { + ($translatePartialLoaderProvider) => { $translatePartialLoaderProvider.addPart('components/table/i18n'); } ]); diff --git a/src/main/webapp/wise5/components/table/tableComponentModule.ts b/src/main/webapp/wise5/components/table/tableComponentModule.ts index 161516d229..2bf4dd6bd4 100644 --- a/src/main/webapp/wise5/components/table/tableComponentModule.ts +++ b/src/main/webapp/wise5/components/table/tableComponentModule.ts @@ -11,7 +11,7 @@ let tableComponentModule = angular .controller('TableController', TableController) .config([ '$translatePartialLoaderProvider', - $translatePartialLoaderProvider => { + ($translatePartialLoaderProvider) => { $translatePartialLoaderProvider.addPart('components/table/i18n'); } ]); diff --git a/src/main/webapp/wise5/components/table/tableController.ts b/src/main/webapp/wise5/components/table/tableController.ts index 256f8a5c9d..3bc6303973 100644 --- a/src/main/webapp/wise5/components/table/tableController.ts +++ b/src/main/webapp/wise5/components/table/tableController.ts @@ -194,7 +194,7 @@ class TableController extends ComponentController { * @param componentState the component state from the connected * component that has changed */ - this.$scope.handleConnectedComponentStudentDataChanged = function( + this.$scope.handleConnectedComponentStudentDataChanged = function ( connectedComponent, connectedComponentParams, componentState @@ -250,7 +250,7 @@ class TableController extends ComponentController { * action (optional; default is false) * @return a promise of a component state containing the student data */ - this.$scope.getComponentState = function(isSubmit) { + this.$scope.getComponentState = function (isSubmit) { const deferred = this.$q.defer(); let getState = false; let action = 'change'; @@ -269,7 +269,7 @@ class TableController extends ComponentController { if (getState) { // create a component state populated with the student data - this.$scope.tableController.createComponentState(action).then(componentState => { + this.$scope.tableController.createComponentState(action).then((componentState) => { deferred.resolve(componentState); }); } else { @@ -293,8 +293,7 @@ class TableController extends ComponentController { if (this.dataExplorerGraphTypes.length > 0) { this.dataExplorerGraphType = this.dataExplorerGraphTypes[0].value; } - this.isDataExplorerScatterPlotRegressionLineEnabled = - this.componentContent.isDataExplorerScatterPlotRegressionLineEnabled; + this.isDataExplorerScatterPlotRegressionLineEnabled = this.componentContent.isDataExplorerScatterPlotRegressionLineEnabled; if (this.componentContent.numDataExplorerYAxis > 1) { this.dataExplorerYAxisLabels = Array(this.componentContent.numDataExplorerYAxis).fill(''); } @@ -302,77 +301,77 @@ class TableController extends ComponentController { } registerStudentWorkSavedToServerListener() { - this.studentWorkSavedToServerSubscription = - this.StudentDataService.studentWorkSavedToServer$.subscribe((args: any) => { - const componentState = args.studentWork; - if (this.isForThisComponent(componentState)) { - this.isDirty = false; - this.StudentDataService.broadcastComponentDirty({ - componentId: this.componentId, - isDirty: false - }); - const isAutoSave = componentState.isAutoSave; - const isSubmit = componentState.isSubmit; - const serverSaveTime = componentState.serverSaveTime; - const clientSaveTime = this.ConfigService.convertToClientTimestamp(serverSaveTime); - if (isSubmit) { - this.setSubmittedMessage(clientSaveTime); - this.lockIfNecessary(); - this.isSubmitDirty = false; - this.StudentDataService.broadcastComponentSubmitDirty({ + this.studentWorkSavedToServerSubscription = this.StudentDataService.studentWorkSavedToServer$.subscribe( + (args: any) => { + const componentState = args.studentWork; + if (this.isForThisComponent(componentState)) { + this.isDirty = false; + this.StudentDataService.broadcastComponentDirty({ componentId: this.componentId, isDirty: false }); - } else if (isAutoSave) { - this.setAutoSavedMessage(clientSaveTime); - } else { - this.setSavedMessage(clientSaveTime); + const isAutoSave = componentState.isAutoSave; + const isSubmit = componentState.isSubmit; + const serverSaveTime = componentState.serverSaveTime; + const clientSaveTime = this.ConfigService.convertToClientTimestamp(serverSaveTime); + if (isSubmit) { + this.setSubmittedMessage(clientSaveTime); + this.lockIfNecessary(); + this.isSubmitDirty = false; + this.StudentDataService.broadcastComponentSubmitDirty({ + componentId: this.componentId, + isDirty: false + }); + } else if (isAutoSave) { + this.setAutoSavedMessage(clientSaveTime); + } else { + this.setSavedMessage(clientSaveTime); + } } - } - // check if the component state is from a connected component - if ( - this.ProjectService.isConnectedComponent( - this.nodeId, - this.componentId, - componentState.componentId - ) - ) { - // get the connected component params - const connectedComponentParams: any = this.ProjectService.getConnectedComponentParams( - this.componentContent, - componentState.componentId - ); - - if (connectedComponentParams != null) { - if ( - connectedComponentParams.updateOn === 'save' || - (connectedComponentParams.updateOn === 'submit' && componentState.isSubmit) - ) { - let performUpdate = false; - - /* - * make a copy of the component state so we don't accidentally - * change any values in the referenced object - */ - const componentStateCopy = this.UtilService.makeCopyOfJSONObject(componentState); - - /* - * make sure the student hasn't entered any values into the - * table so that we don't overwrite any of their work. - */ - if (this.isTableEmpty() || this.isTableReset()) { + // check if the component state is from a connected component + if ( + this.ProjectService.isConnectedComponent( + this.nodeId, + this.componentId, + componentState.componentId + ) + ) { + // get the connected component params + const connectedComponentParams: any = this.ProjectService.getConnectedComponentParams( + this.componentContent, + componentState.componentId + ); + + if (connectedComponentParams != null) { + if ( + connectedComponentParams.updateOn === 'save' || + (connectedComponentParams.updateOn === 'submit' && componentState.isSubmit) + ) { + let performUpdate = false; + /* - * the student has not entered any values into the table - * so we can update it + * make a copy of the component state so we don't accidentally + * change any values in the referenced object */ - performUpdate = true; - } else { + const componentStateCopy = this.UtilService.makeCopyOfJSONObject(componentState); + /* - * the student has entered values into the table so we - * will ask them if they want to update it + * make sure the student hasn't entered any values into the + * table so that we don't overwrite any of their work. */ - /* + if (this.isTableEmpty() || this.isTableReset()) { + /* + * the student has not entered any values into the table + * so we can update it + */ + performUpdate = true; + } else { + /* + * the student has entered values into the table so we + * will ask them if they want to update it + */ + /* var answer = confirm('Do you want to update the connected table?'); if (answer) { @@ -380,28 +379,29 @@ class TableController extends ComponentController { performUpdate = true; } */ - performUpdate = true; - } + performUpdate = true; + } - if (performUpdate) { - // set the table data - this.$scope.tableController.setStudentWork(componentStateCopy); + if (performUpdate) { + // set the table data + this.$scope.tableController.setStudentWork(componentStateCopy); - // the table has changed - this.$scope.tableController.isDirty = true; - this.$scope.tableController.isSubmitDirty = true; - } + // the table has changed + this.$scope.tableController.isDirty = true; + this.$scope.tableController.isSubmitDirty = true; + } - /* - * remember the component state and connected component params - * in case we need to use them again later - */ - this.latestConnectedComponentState = componentStateCopy; - this.latestConnectedComponentParams = connectedComponentParams; + /* + * remember the component state and connected component params + * in case we need to use them again later + */ + this.latestConnectedComponentState = componentStateCopy; + this.latestConnectedComponentParams = connectedComponentParams; + } } } } - }); + ); } handleNodeSubmit() { @@ -1043,7 +1043,7 @@ class TableController extends ComponentController { tableElement = tableElement[0]; // convert the table element to a canvas element - html2canvas(tableElement).then(canvas => { + html2canvas(tableElement).then((canvas) => { // get the canvas as a base64 string const img_b64 = canvas.toDataURL('image/png'); diff --git a/src/main/webapp/wise5/components/table/tableService.ts b/src/main/webapp/wise5/components/table/tableService.ts index 325b277265..414eaafa6c 100644 --- a/src/main/webapp/wise5/components/table/tableService.ts +++ b/src/main/webapp/wise5/components/table/tableService.ts @@ -13,10 +13,12 @@ import { UpgradeModule } from '@angular/upgrade/static'; export class TableService extends ComponentService { $translate: any; - constructor(private upgrade: UpgradeModule, - private StudentAssetService: StudentAssetService, - protected StudentDataService: StudentDataService, - protected UtilService: UtilService) { + constructor( + private upgrade: UpgradeModule, + private StudentAssetService: StudentAssetService, + protected StudentDataService: StudentDataService, + protected UtilService: UtilService + ) { super(StudentDataService, UtilService); } @@ -213,7 +215,7 @@ export class TableService extends ComponentService { if (tableElement != null && tableElement.length > 0) { tableElement = tableElement[0]; // convert the table element to a canvas element - html2canvas(tableElement).then(canvas => { + html2canvas(tableElement).then((canvas) => { // get the canvas as a base64 string const img_b64 = canvas.toDataURL('image/png'); @@ -221,7 +223,7 @@ export class TableService extends ComponentService { const imageObject = this.UtilService.getImageObjectFromBase64String(img_b64); // add the image to the student assets - this.StudentAssetService.uploadAsset(imageObject).then(asset => { + this.StudentAssetService.uploadAsset(imageObject).then((asset) => { resolve(asset); }); }); diff --git a/src/main/webapp/wise5/directives/component/component.ts b/src/main/webapp/wise5/directives/component/component.ts index 550056e2cf..30bcffc15b 100644 --- a/src/main/webapp/wise5/directives/component/component.ts +++ b/src/main/webapp/wise5/directives/component/component.ts @@ -1,12 +1,11 @@ -import { ConfigService } from "../../services/configService"; -import { NodeService } from "../../services/nodeService"; -import { NotebookService } from "../../services/notebookService"; -import { ProjectService } from "../../services/projectService"; -import { StudentDataService } from "../../services/studentDataService"; +import { ConfigService } from '../../services/configService'; +import { NodeService } from '../../services/nodeService'; +import { NotebookService } from '../../services/notebookService'; +import { ProjectService } from '../../services/projectService'; +import { StudentDataService } from '../../services/studentDataService'; import * as angular from 'angular'; class ComponentController { - componentId: string; componentState: any; mode: string; @@ -14,13 +13,23 @@ class ComponentController { teacherWorkgroupId: number; workgroupId: number; - static $inject = ['$scope', 'ConfigService', 'NodeService', 'NotebookService', 'ProjectService', - 'StudentDataService']; + static $inject = [ + '$scope', + 'ConfigService', + 'NodeService', + 'NotebookService', + 'ProjectService', + 'StudentDataService' + ]; - constructor(private $scope: any, private ConfigService: ConfigService, - private NodeService: NodeService, private NotebookService: NotebookService, - private ProjectService: ProjectService, private StudentDataService: StudentDataService) { - } + constructor( + private $scope: any, + private ConfigService: ConfigService, + private NodeService: NodeService, + private NotebookService: NotebookService, + private ProjectService: ProjectService, + private StudentDataService: StudentDataService + ) {} $onInit() { this.$scope.mode = this.mode; diff --git a/src/main/webapp/wise5/directives/componentAnnotations/component-annotations.component.ts b/src/main/webapp/wise5/directives/componentAnnotations/component-annotations.component.ts index e01bb86bc0..117bca23a7 100644 --- a/src/main/webapp/wise5/directives/componentAnnotations/component-annotations.component.ts +++ b/src/main/webapp/wise5/directives/componentAnnotations/component-annotations.component.ts @@ -1,11 +1,11 @@ 'use strict'; -import { Component, Input } from "@angular/core"; -import { UpgradeModule } from "@angular/upgrade/static"; -import { Subscription } from "rxjs"; -import { ConfigService } from "../../services/configService"; -import { StudentDataService } from "../../services/studentDataService"; -import { VLEProjectService } from "../../vle/vleProjectService"; +import { Component, Input } from '@angular/core'; +import { UpgradeModule } from '@angular/upgrade/static'; +import { Subscription } from 'rxjs'; +import { ConfigService } from '../../services/configService'; +import { StudentDataService } from '../../services/studentDataService'; +import { VLEProjectService } from '../../vle/vleProjectService'; @Component({ selector: 'component-annotations', @@ -13,12 +13,11 @@ import { VLEProjectService } from "../../vle/vleProjectService"; templateUrl: 'component-annotations.component.html' }) export class ComponentAnnotationsComponent { - @Input() annotations: any; maxScoreDisplay: string; nodeId: string = null; - componentId: string = null + componentId: string = null; latestAnnotationTime: any = null; isNew: boolean; label: string = ''; @@ -30,20 +29,22 @@ export class ComponentAnnotationsComponent { showComment: boolean = true; studentWorkSavedToServerSubscription: Subscription; - constructor(private upgrade: UpgradeModule, - private ConfigService: ConfigService, - private ProjectService: VLEProjectService, - private StudentDataService: StudentDataService) { - } + constructor( + private upgrade: UpgradeModule, + private ConfigService: ConfigService, + private ProjectService: VLEProjectService, + private StudentDataService: StudentDataService + ) {} ngOnInit() { - this.maxScoreDisplay = (parseInt(this.maxScore) > 0) ? '/' + this.maxScore : ''; - this.studentWorkSavedToServerSubscription = - this.StudentDataService.studentWorkSavedToServer$.subscribe(({studentWork}) => { - if (studentWork.nodeId === this.nodeId && studentWork.componentId === this.componentId) { - this.isNew = false; + this.maxScoreDisplay = parseInt(this.maxScore) > 0 ? '/' + this.maxScore : ''; + this.studentWorkSavedToServerSubscription = this.StudentDataService.studentWorkSavedToServer$.subscribe( + ({ studentWork }) => { + if (studentWork.nodeId === this.nodeId && studentWork.componentId === this.componentId) { + this.isNew = false; + } } - }); + ); } ngOnChanges() { @@ -56,14 +57,18 @@ export class ComponentAnnotationsComponent { processAnnotations() { if (this.annotations.comment || this.annotations.score) { - this.nodeId = this.annotations.comment ? - this.annotations.comment.nodeId : this.annotations.score.nodeId; - this.componentId = this.annotations.comment ? - this.annotations.comment.componentId : this.annotations.score.nodeId; - this.showScore = this.annotations.score != null && - this.ProjectService.displayAnnotation(this.annotations.score); - this.showComment = this.annotations.comment != null && - this.ProjectService.displayAnnotation(this.annotations.comment); + this.nodeId = this.annotations.comment + ? this.annotations.comment.nodeId + : this.annotations.score.nodeId; + this.componentId = this.annotations.comment + ? this.annotations.comment.componentId + : this.annotations.score.nodeId; + this.showScore = + this.annotations.score != null && + this.ProjectService.displayAnnotation(this.annotations.score); + this.showComment = + this.annotations.comment != null && + this.ProjectService.displayAnnotation(this.annotations.comment); this.setLabelAndIcon(); } } @@ -85,7 +90,7 @@ export class ComponentAnnotationsComponent { getLatestAnnotationTime() { const latest = this.getLatestAnnotation(); if (latest) { - return this.ConfigService.convertToClientTimestamp(latest.serverSaveTime) + return this.ConfigService.convertToClientTimestamp(latest.serverSaveTime); } return null; } @@ -106,7 +111,9 @@ export class ComponentAnnotationsComponent { getLatestSaveTime() { const latestState = this.StudentDataService.getLatestComponentStateByNodeIdAndComponentId( - this.nodeId, this.componentId); + this.nodeId, + this.componentId + ); let saveTime = null; if (latestState) { saveTime = this.ConfigService.convertToClientTimestamp(latestState.serverSaveTime); @@ -119,10 +126,10 @@ export class ComponentAnnotationsComponent { let latestSaveTime = this.getLatestSaveTime(); let latestAnnotationTime = this.getLatestAnnotationTime(); let isNew = true; - if (latestVisitTime && (latestVisitTime > latestAnnotationTime)) { + if (latestVisitTime && latestVisitTime > latestAnnotationTime) { isNew = false; } - if (latestSaveTime && (latestSaveTime > latestAnnotationTime)) { + if (latestSaveTime && latestSaveTime > latestAnnotationTime) { isNew = false; } return isNew; diff --git a/src/main/webapp/wise5/directives/components.ts b/src/main/webapp/wise5/directives/components.ts index 5e032fde70..dd9e2bdd69 100644 --- a/src/main/webapp/wise5/directives/components.ts +++ b/src/main/webapp/wise5/directives/components.ts @@ -26,13 +26,19 @@ Components.component('draggable', Draggable); Components.component('globalAnnotations', GlobalAnnotations); Components.component('globalAnnotationsList', GlobalAnnotationsList); Components.component('listenForDeleteKeypress', ListenForDeleteKeypress); -Components.directive('milestoneReportData', - downgradeComponent({ component: MilestoneReportDataComponent}) as angular.IDirectiveFactory); +Components.directive( + 'milestoneReportData', + downgradeComponent({ component: MilestoneReportDataComponent }) as angular.IDirectiveFactory +); Components.component('milestoneReportGraph', MilestoneReportGraph); -Components.directive('nodeIcon', - downgradeComponent({ component: NodeIconComponent}) as angular.IDirectiveFactory); -Components.directive('possibleScore', - downgradeComponent({ component: PossibleScoreComponent}) as angular.IDirectiveFactory); +Components.directive( + 'nodeIcon', + downgradeComponent({ component: NodeIconComponent }) as angular.IDirectiveFactory +); +Components.directive( + 'possibleScore', + downgradeComponent({ component: PossibleScoreComponent }) as angular.IDirectiveFactory +); Components.component('summaryDisplay', SummaryDisplay); Components.component('wiselink', Wiselink); Components.directive('sticky', Sticky); diff --git a/src/main/webapp/wise5/directives/wise-tinymce-editor/wise-authoring-tinymce-editor.component.spec.ts b/src/main/webapp/wise5/directives/wise-tinymce-editor/wise-authoring-tinymce-editor.component.spec.ts index 9c0a934884..7d45d5cd18 100644 --- a/src/main/webapp/wise5/directives/wise-tinymce-editor/wise-authoring-tinymce-editor.component.spec.ts +++ b/src/main/webapp/wise5/directives/wise-tinymce-editor/wise-authoring-tinymce-editor.component.spec.ts @@ -1,18 +1,18 @@ -import { HttpClientTestingModule } from "@angular/common/http/testing"; -import { ComponentFixture, TestBed } from "@angular/core/testing"; -import { UpgradeModule } from "@angular/upgrade/static"; -import { ProjectAssetService } from "../../../site/src/app/services/projectAssetService"; -import { AnnotationService } from "../../services/annotationService"; -import { ConfigService } from "../../services/configService"; -import { NotebookService } from "../../services/notebookService"; -import { ProjectService } from "../../services/projectService"; -import { SessionService } from "../../services/sessionService"; -import { StudentAssetService } from "../../services/studentAssetService"; -import { StudentDataService } from "../../services/studentDataService"; -import { TagService } from "../../services/tagService"; -import { TeacherProjectService } from "../../services/teacherProjectService"; -import { UtilService } from "../../services/utilService"; -import { WiseAuthoringTinymceEditorComponent } from "./wise-authoring-tinymce-editor.component"; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { UpgradeModule } from '@angular/upgrade/static'; +import { ProjectAssetService } from '../../../site/src/app/services/projectAssetService'; +import { AnnotationService } from '../../services/annotationService'; +import { ConfigService } from '../../services/configService'; +import { NotebookService } from '../../services/notebookService'; +import { ProjectService } from '../../services/projectService'; +import { SessionService } from '../../services/sessionService'; +import { StudentAssetService } from '../../services/studentAssetService'; +import { StudentDataService } from '../../services/studentDataService'; +import { TagService } from '../../services/tagService'; +import { TeacherProjectService } from '../../services/teacherProjectService'; +import { UtilService } from '../../services/utilService'; +import { WiseAuthoringTinymceEditorComponent } from './wise-authoring-tinymce-editor.component'; let component: WiseAuthoringTinymceEditorComponent; let fixture: ComponentFixture; @@ -20,8 +20,8 @@ let fixture: ComponentFixture; describe('WiseAuthoringTinymceEditorComponent', () => { beforeEach(() => { TestBed.configureTestingModule({ - declarations: [ WiseAuthoringTinymceEditorComponent ], - imports: [ HttpClientTestingModule, UpgradeModule ], + declarations: [WiseAuthoringTinymceEditorComponent], + imports: [HttpClientTestingModule, UpgradeModule], providers: [ AnnotationService, ConfigService, @@ -59,4 +59,4 @@ function getAllowedFileTypeFromMeta() { function expectAllowedFileTypeToEqual(metaFileType: string, allowedFileTypes: string[]) { const meta = { filetype: metaFileType }; expect(component.getAllowedFileTypesFromMeta(meta)).toEqual(allowedFileTypes); -} \ No newline at end of file +} diff --git a/src/main/webapp/wise5/directives/wise-tinymce-editor/wise-authoring-tinymce-editor.component.ts b/src/main/webapp/wise5/directives/wise-tinymce-editor/wise-authoring-tinymce-editor.component.ts index 2e7defdd00..09607abbb4 100644 --- a/src/main/webapp/wise5/directives/wise-tinymce-editor/wise-authoring-tinymce-editor.component.ts +++ b/src/main/webapp/wise5/directives/wise-tinymce-editor/wise-authoring-tinymce-editor.component.ts @@ -1,9 +1,9 @@ -import { Component } from "@angular/core"; -import { ConfigService } from "../../services/configService"; -import { ProjectAssetService } from "../../../site/src/app/services/projectAssetService"; -import { TeacherProjectService } from "../../services/teacherProjectService"; -import { WiseTinymceEditorComponent } from "./wise-tinymce-editor.component"; -import { NotebookService } from "../../services/notebookService"; +import { Component } from '@angular/core'; +import { ConfigService } from '../../services/configService'; +import { ProjectAssetService } from '../../../site/src/app/services/projectAssetService'; +import { TeacherProjectService } from '../../services/teacherProjectService'; +import { WiseTinymceEditorComponent } from './wise-tinymce-editor.component'; +import { NotebookService } from '../../services/notebookService'; import 'tinymce'; declare let tinymce: any; @@ -14,7 +14,6 @@ declare let tinymce: any; templateUrl: 'wise-tinymce-editor.component.html' }) export class WiseAuthoringTinymceEditorComponent extends WiseTinymceEditorComponent { - protected toolbar: string = `undo redo | fontselect | formatselect | fontsizeselect | bold italic underline | forecolor backcolor | alignment numlist bullist | image media link wiselink | emoticons removeformat fullscreen`; @@ -30,7 +29,8 @@ export class WiseAuthoringTinymceEditorComponent extends WiseTinymceEditorCompon private ConfigService: ConfigService, NotebookService: NotebookService, private ProjectAssetService: ProjectAssetService, - private ProjectService: TeacherProjectService) { + private ProjectService: TeacherProjectService + ) { super(NotebookService); } @@ -43,11 +43,14 @@ export class WiseAuthoringTinymceEditorComponent extends WiseTinymceEditorCompon initializeInsertWISELinkPlugin(): void { const thisProjectService = this.ProjectService; tinymce.PluginManager.add('wiselink', function (editor: any, url: string) { - editor.ui.registry.addIcon('wiselink', '' + ''); + '3.1s-1.39 3.1-3.1 3.1h-4V17h4c2.76 0 5-2.24 5-5s-2.24-5-5-5z"/>' + ); editor.ui.registry.addButton('wiselink', { tooltip: $localize`Insert WISE Link`, icon: 'wiselink', @@ -61,11 +64,13 @@ export class WiseAuthoringTinymceEditorComponent extends WiseTinymceEditorCompon thisProjectService.openWISELinkChooser(params).then((result) => { let content = ''; if (result.wiseLinkType === 'link') { - content = `${result.wiseLinkText}`; } else if (result.wiseLinkType === 'button') { - content = ``; } diff --git a/src/main/webapp/wise5/directives/wise-tinymce-editor/wise-tinymce-editor.component.spec.ts b/src/main/webapp/wise5/directives/wise-tinymce-editor/wise-tinymce-editor.component.spec.ts index 5f4c80b621..edcc6484b9 100644 --- a/src/main/webapp/wise5/directives/wise-tinymce-editor/wise-tinymce-editor.component.spec.ts +++ b/src/main/webapp/wise5/directives/wise-tinymce-editor/wise-tinymce-editor.component.spec.ts @@ -1,18 +1,17 @@ - -import { HttpClientTestingModule } from "@angular/common/http/testing"; -import { ComponentFixture, TestBed } from "@angular/core/testing"; -import { UpgradeModule } from "@angular/upgrade/static"; -import { ProjectAssetService } from "../../../site/src/app/services/projectAssetService"; -import { AnnotationService } from "../../services/annotationService"; -import { ConfigService } from "../../services/configService"; -import { NotebookService } from "../../services/notebookService"; -import { ProjectService } from "../../services/projectService"; -import { SessionService } from "../../services/sessionService"; -import { StudentAssetService } from "../../services/studentAssetService"; -import { StudentDataService } from "../../services/studentDataService"; -import { TagService } from "../../services/tagService"; -import { UtilService } from "../../services/utilService"; -import { WiseTinymceEditorComponent } from "./wise-tinymce-editor.component"; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { UpgradeModule } from '@angular/upgrade/static'; +import { ProjectAssetService } from '../../../site/src/app/services/projectAssetService'; +import { AnnotationService } from '../../services/annotationService'; +import { ConfigService } from '../../services/configService'; +import { NotebookService } from '../../services/notebookService'; +import { ProjectService } from '../../services/projectService'; +import { SessionService } from '../../services/sessionService'; +import { StudentAssetService } from '../../services/studentAssetService'; +import { StudentDataService } from '../../services/studentDataService'; +import { TagService } from '../../services/tagService'; +import { UtilService } from '../../services/utilService'; +import { WiseTinymceEditorComponent } from './wise-tinymce-editor.component'; let component: WiseTinymceEditorComponent; let fixture: ComponentFixture; @@ -20,8 +19,8 @@ let fixture: ComponentFixture; describe('WiseTinymceEditorComponent', () => { beforeEach(() => { TestBed.configureTestingModule({ - declarations: [ WiseTinymceEditorComponent ], - imports: [ HttpClientTestingModule, UpgradeModule ], + declarations: [WiseTinymceEditorComponent], + imports: [HttpClientTestingModule, UpgradeModule], providers: [ AnnotationService, ConfigService, @@ -60,24 +59,24 @@ function getAttachmentsHTML() { const attachmentURLs: string[] = ['spongebob.png']; const text = ''; expect(component.getAttachmentsHTML(attachmentURLs, text)).toEqual( - '
' + - `' + - '
' + - '
' + '
' + + `' + + '
' + + '
' ); }); it('should get the attachments html when there are multiple attachments', () => { const attachmentURLs: string[] = ['spongebob.png', 'patrick.png']; const text = ''; expect(component.getAttachmentsHTML(attachmentURLs, text)).toEqual( - '
' + - `' + - `' + - '
' + - '
' + '
' + + `' + + `' + + '
' + + '
' ); }); } @@ -111,9 +110,9 @@ function getAudioHTML() { data.altsourcemime = altsourcemime; expect(component.getAudioHTML(data)).toEqual( '' + `` + + `` + + '' ); }); } @@ -124,10 +123,7 @@ function getAttachmentURLs() { const iconURL2 = 'patrick.png'; const notebookItem: any = { content: { - attachments: [ - { iconURL: iconURL1 }, - { iconURL: iconURL2 } - ] + attachments: [{ iconURL: iconURL1 }, { iconURL: iconURL2 }] } }; const attachmentURLs = component.getAttachmentURLs(notebookItem); @@ -135,4 +131,4 @@ function getAttachmentURLs() { expect(attachmentURLs[0]).toEqual(iconURL1); expect(attachmentURLs[1]).toEqual(iconURL2); }); -} \ No newline at end of file +} diff --git a/src/main/webapp/wise5/directives/wise-tinymce-editor/wise-tinymce-editor.component.ts b/src/main/webapp/wise5/directives/wise-tinymce-editor/wise-tinymce-editor.component.ts index d00f1b5bbc..868bdb1138 100644 --- a/src/main/webapp/wise5/directives/wise-tinymce-editor/wise-tinymce-editor.component.ts +++ b/src/main/webapp/wise5/directives/wise-tinymce-editor/wise-tinymce-editor.component.ts @@ -1,7 +1,7 @@ -import { Component, EventEmitter, Input, Output } from "@angular/core"; -import { debounceTime } from "rxjs/operators"; -import { Subject, Subscription } from "rxjs"; -import { NotebookService } from "../../services/notebookService"; +import { Component, EventEmitter, Input, Output } from '@angular/core'; +import { debounceTime } from 'rxjs/operators'; +import { Subject, Subscription } from 'rxjs'; +import { NotebookService } from '../../services/notebookService'; import 'tinymce'; declare let tinymce: any; @@ -32,14 +32,15 @@ export class WiseTinymceEditorComponent { private debouncerSubscription: Subscription; private notebookItemChosenSubscription: Subscription; - protected aValidAttributes: string = 'a[href|download|referrerpolicy|rel|target|type|style|' + - 'class|wiselink|node-id|component-id|link-text]'; - protected buttonValidAttributes: string = 'button[class|disabled|id|name|onblur|onclick|' + - 'ondblclick|onfocus|onkeydown|onkeypress|onkeyup|onmousedown|onmousemove|onmouseout|' + - 'onmouseover|onmouseup|style|tabindex|title|type|value|wiselink|node-id|component-id|' + - 'link-text]'; - protected extendedValidElements: string = - `${this.aValidAttributes},${this.buttonValidAttributes}`; + protected aValidAttributes: string = + 'a[href|download|referrerpolicy|rel|target|type|style|' + + 'class|wiselink|node-id|component-id|link-text]'; + protected buttonValidAttributes: string = + 'button[class|disabled|id|name|onblur|onclick|' + + 'ondblclick|onfocus|onkeydown|onkeypress|onkeyup|onmousedown|onmousemove|onmouseout|' + + 'onmouseover|onmouseup|style|tabindex|title|type|value|wiselink|node-id|component-id|' + + 'link-text]'; + protected extendedValidElements: string = `${this.aValidAttributes},${this.buttonValidAttributes}`; protected plugins: string[] = [ 'advlist', @@ -89,12 +90,13 @@ export class WiseTinymceEditorComponent { ngOnInit(): void { if (this.isAddNoteButtonAvailable) { - this.notebookItemChosenSubscription = - this.NotebookService.notebookItemChosen$.subscribe(({ requester, notebookItem }) => { - if (requester === 'report') { - this.insertWISENote(notebookItem); + this.notebookItemChosenSubscription = this.NotebookService.notebookItemChosen$.subscribe( + ({ requester, notebookItem }) => { + if (requester === 'report') { + this.insertWISENote(notebookItem); + } } - }); + ); this.addPluginName('wisenote'); this.initializeInsertWISENotePlugin(); this.toolbar += ` | wisenote`; @@ -151,15 +153,15 @@ export class WiseTinymceEditorComponent { initializeInsertWISENotePlugin(): void { const thisWiseTinymceEditorComponent = this; - tinymce.PluginManager.add('wisenote', function(editor: any, url: string) { + tinymce.PluginManager.add('wisenote', function (editor: any, url: string) { thisWiseTinymceEditorComponent.editor = editor; editor.ui.registry.addButton('wisenote', { tooltip: $localize`Insert from Notebook`, text: $localize`Insert note +`, - onAction: function() { + onAction: function () { thisWiseTinymceEditorComponent.openNotebook.emit('openNotebook'); } - }) + }); }); } @@ -179,7 +181,8 @@ export class WiseTinymceEditorComponent { } else { content = `
`; attachmentURLs.forEach((attachmentURL) => { - content += '${$localize`Image from notebook`}`; }); content += this.getTextHTML(text, true) + `
`; @@ -241,7 +244,7 @@ export class WiseTinymceEditorComponent { let content = ''; content += ` = new Subject(); @@ -65,55 +64,61 @@ export class AchievementService { const options = { params: this.createAchievementsURLParams(workgroupId, type) }; - return this.http.get(url, options).toPromise().then((studentAchievements: any[]) => { - for (const studentAchievement of studentAchievements) { - this.addOrUpdateStudentAchievement(studentAchievement); - if (this.ConfigService.getMode() === 'studentRun') { - const projectAchievement = this.ProjectService - .getAchievementByAchievementId(studentAchievement.achievementId); - if (projectAchievement != null) { - /* - * set the completed field to true in case we ever - * need to easily see which projectAchievements the student - * has completed - */ - projectAchievement.completed = true; - if (projectAchievement.deregisterFunction != null) { + return this.http + .get(url, options) + .toPromise() + .then((studentAchievements: any[]) => { + for (const studentAchievement of studentAchievements) { + this.addOrUpdateStudentAchievement(studentAchievement); + if (this.ConfigService.getMode() === 'studentRun') { + const projectAchievement = this.ProjectService.getAchievementByAchievementId( + studentAchievement.achievementId + ); + if (projectAchievement != null) { /* - * the student has completed this achievement - * so we no longer need to listen for it + * set the completed field to true in case we ever + * need to easily see which projectAchievements the student + * has completed */ - projectAchievement.deregisterFunction(); - this.debugOutput('deregistering ' + projectAchievement.id); + projectAchievement.completed = true; + if (projectAchievement.deregisterFunction != null) { + /* + * the student has completed this achievement + * so we no longer need to listen for it + */ + projectAchievement.deregisterFunction(); + this.debugOutput('deregistering ' + projectAchievement.id); + } } } } - } - if (this.ConfigService.getMode() === 'studentRun') { - /* - * Loop through all the projectAchievements and - * re-evaluate whether the student has completed each. - * This is to make sure students never get stuck in a - * state where they did everything required to complete - * a certain achievement but some error or bug occurred - * which prevented their student achievement from being - * saved and then they end up never being able to - * complete that achievement. We will avoid this - * situation by re-evaluating all the projectAchievements - * each time the student loads the VLE. - */ - const projectAchievements = this.ProjectService.getAchievementItems(); - for (const projectAchievement of projectAchievements) { - if (!this.isStudentAchievementExists(projectAchievement.id) && - this.isProjectAchievementSatisfied(projectAchievement)) { - this.createStudentAchievement(projectAchievement); + if (this.ConfigService.getMode() === 'studentRun') { + /* + * Loop through all the projectAchievements and + * re-evaluate whether the student has completed each. + * This is to make sure students never get stuck in a + * state where they did everything required to complete + * a certain achievement but some error or bug occurred + * which prevented their student achievement from being + * saved and then they end up never being able to + * complete that achievement. We will avoid this + * situation by re-evaluating all the projectAchievements + * each time the student loads the VLE. + */ + const projectAchievements = this.ProjectService.getAchievementItems(); + for (const projectAchievement of projectAchievements) { + if ( + !this.isStudentAchievementExists(projectAchievement.id) && + this.isProjectAchievementSatisfied(projectAchievement) + ) { + this.createStudentAchievement(projectAchievement); + } } } - } - this.registerAchievementListeners(); - return this.studentAchievementsByWorkgroupId; - }); + this.registerAchievementListeners(); + return this.studentAchievementsByWorkgroupId; + }); } } @@ -131,16 +136,18 @@ export class AchievementService { let found = false; for (let achievementIndex = 0; achievementIndex < achievements.length; achievementIndex++) { let achievement = achievements[achievementIndex]; - if (achievement.achievementId != null && - achievement.achievementId === studentAchievement.achievementId && - achievement.workgroupId != null && - achievement.workgroupId === studentAchievement.workgroupId) { + if ( + achievement.achievementId != null && + achievement.achievementId === studentAchievement.achievementId && + achievement.workgroupId != null && + achievement.workgroupId === studentAchievement.workgroupId + ) { /* * the achievement 10 character alphanumeric id matches and * the workgroup id matches so we will update it */ achievements[achievementIndex] = studentAchievement; - found = true; // remember this so we don't insert later. + found = true; // remember this so we don't insert later. break; } } @@ -153,9 +160,9 @@ export class AchievementService { createAchievementsURLBody(studentAchievement) { let body = new HttpParams() - .set('achievementId', studentAchievement.achievementId) - .set('workgroupId', studentAchievement.workgroupId) - .set('type', studentAchievement.type); + .set('achievementId', studentAchievement.achievementId) + .set('workgroupId', studentAchievement.workgroupId) + .set('type', studentAchievement.type); if (studentAchievement.id != null) { body = body.set('id', studentAchievement.id); } @@ -179,11 +186,14 @@ export class AchievementService { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } - } - return this.http.post(url, body, options).toPromise().then((achievement: any) => { - this.addOrUpdateStudentAchievement(achievement); - return achievement; - }); + }; + return this.http + .post(url, body, options) + .toPromise() + .then((achievement: any) => { + this.addOrUpdateStudentAchievement(achievement); + return achievement; + }); } } @@ -221,8 +231,11 @@ export class AchievementService { createListenerFunction(projectAchievement) { let deregisterListenerFunction = null; - if (projectAchievement.type === 'milestone' || projectAchievement.type === 'milestoneReport' || - projectAchievement.type === 'completion') { + if ( + projectAchievement.type === 'milestone' || + projectAchievement.type === 'milestoneReport' || + projectAchievement.type === 'completion' + ) { deregisterListenerFunction = this.createStudentWorkSavedListener(projectAchievement); } else if (projectAchievement.type === 'aggregate') { deregisterListenerFunction = this.createAggregateAchievementListener(projectAchievement); @@ -270,8 +283,12 @@ export class AchievementService { } const workgroupId = this.ConfigService.getWorkgroupId(); - const newAchievement = - this.createNewStudentAchievement(achievement.type, achievement.id, data, workgroupId); + const newAchievement = this.createNewStudentAchievement( + achievement.type, + achievement.id, + data, + workgroupId + ); const achievements = this.getStudentAchievementsByWorkgroupId(workgroupId); achievements.push(newAchievement); this.saveAchievementToServer(newAchievement); @@ -285,10 +302,13 @@ export class AchievementService { */ createStudentWorkSavedListener(projectAchievement) { this.debugOutput('registering ' + projectAchievement.id); - return this.StudentDataService.studentWorkSavedToServer$ - .subscribe((args: any) => { - this.debugOutput('createStudentWorkSavedListener checking ' + projectAchievement.id + - ' completed ' + args.nodeId); + return this.StudentDataService.studentWorkSavedToServer$.subscribe((args: any) => { + this.debugOutput( + 'createStudentWorkSavedListener checking ' + + projectAchievement.id + + ' completed ' + + args.nodeId + ); if (!this.isStudentAchievementExists(projectAchievement.id)) { if (this.isAchievementCompletedByStudent(projectAchievement)) { this.createStudentAchievement(projectAchievement); @@ -305,9 +325,11 @@ export class AchievementService { isProjectAchievementSatisfied(projectAchievement) { let completed = false; if (projectAchievement != null) { - if (projectAchievement.type === 'milestone' || - projectAchievement.type === 'milestoneReport' || - projectAchievement.type === 'completion') { + if ( + projectAchievement.type === 'milestone' || + projectAchievement.type === 'milestoneReport' || + projectAchievement.type === 'completion' + ) { completed = this.isAchievementCompletedByStudent(projectAchievement); } else if (projectAchievement.type === 'aggregate') { completed = this.checkAggregateAchievement(projectAchievement); @@ -333,7 +355,9 @@ export class AchievementService { isCriterionSatisfied(satisfyCriterion) { if (satisfyCriterion.name === 'isCompleted') { return this.StudentDataService.isCompleted( - satisfyCriterion.nodeId, satisfyCriterion.componentId); + satisfyCriterion.nodeId, + satisfyCriterion.componentId + ); } return false; } @@ -350,8 +374,12 @@ export class AchievementService { return this.achievementCompleted$.subscribe((args: any) => { const projectAchievement = thisAchievement; if (projectAchievement != null) { - this.debugOutput('createAggregateAchievementListener checking ' + projectAchievement.id + - ' completed ' + args.achievementId); + this.debugOutput( + 'createAggregateAchievementListener checking ' + + projectAchievement.id + + ' completed ' + + args.achievementId + ); const id = projectAchievement.id; if (!this.isStudentAchievementExists(id)) { const completed = this.checkAggregateAchievement(projectAchievement); diff --git a/src/main/webapp/wise5/services/annotationService.ts b/src/main/webapp/wise5/services/annotationService.ts index 7935fcad0a..43d2742c02 100644 --- a/src/main/webapp/wise5/services/annotationService.ts +++ b/src/main/webapp/wise5/services/annotationService.ts @@ -1,13 +1,13 @@ 'use strict'; -import { Injectable } from "@angular/core"; -import { UpgradeModule } from "@angular/upgrade/static"; -import { ProjectService } from "./projectService"; -import { ConfigService } from "./configService"; -import { UtilService } from "./utilService"; +import { Injectable } from '@angular/core'; +import { UpgradeModule } from '@angular/upgrade/static'; +import { ProjectService } from './projectService'; +import { ConfigService } from './configService'; +import { UtilService } from './utilService'; import * as angular from 'angular'; -import { HttpClient, HttpHeaders } from "@angular/common/http"; -import { Observable, Subject } from "rxjs"; +import { HttpClient, HttpHeaders } from '@angular/common/http'; +import { Observable, Subject } from 'rxjs'; @Injectable() export class AnnotationService { @@ -15,18 +15,19 @@ export class AnnotationService { annotations: any; dummyAnnotationId: number = 1; // used in preview mode when we simulate saving of annotation private annotationSavedToServerSource: Subject = new Subject(); - public annotationSavedToServer$: Observable = - this.annotationSavedToServerSource.asObservable(); + public annotationSavedToServer$: Observable = this.annotationSavedToServerSource.asObservable(); private annotationReceivedSource: Subject = new Subject(); public annotationReceived$: Observable = this.annotationReceivedSource.asObservable(); private displayGlobalAnnotationsSource: Subject = new Subject(); - public displayGlobalAnnotations$: Observable = - this.displayGlobalAnnotationsSource.asObservable(); + public displayGlobalAnnotations$: Observable = this.displayGlobalAnnotationsSource.asObservable(); - constructor(private upgrade: UpgradeModule, private http: HttpClient, - private ConfigService: ConfigService, private ProjectService: ProjectService, - private UtilService: UtilService) { - } + constructor( + private upgrade: UpgradeModule, + private http: HttpClient, + private ConfigService: ConfigService, + private ProjectService: ProjectService, + private UtilService: UtilService + ) {} getAnnotations() { return this.annotations; @@ -104,7 +105,7 @@ export class AnnotationService { } } return annotation; - }; + } /** * Create an annotation object @@ -121,9 +122,21 @@ export class AnnotationService { * @param clientSaveTime the client save time * @returns an annotation object */ - createAnnotation(annotationId, runId, periodId, fromWorkgroupId, - toWorkgroupId, nodeId, componentId, studentWorkId, localNotebookItemId, - notebookItemId, annotationType, data, clientSaveTime) { + createAnnotation( + annotationId, + runId, + periodId, + fromWorkgroupId, + toWorkgroupId, + nodeId, + componentId, + studentWorkId, + localNotebookItemId, + notebookItemId, + annotationType, + data, + clientSaveTime + ) { return { id: annotationId, runId: runId, @@ -139,7 +152,7 @@ export class AnnotationService { data: data, clientSaveTime: clientSaveTime }; - }; + } /** * Save the annotation to the server @@ -165,13 +178,17 @@ export class AnnotationService { workgroupId: this.ConfigService.getWorkgroupId(), annotations: angular.toJson(annotations) }; - const headers = new HttpHeaders({'Content-Type': 'application/x-www-form-urlencoded'}); - return this.http.post(this.ConfigService.getConfigParam('teacherDataURL'), $.param(params), - { headers: headers }).toPromise().then((savedAnnotationDataResponse: any) => { - return this.saveToServerSuccess(savedAnnotationDataResponse); - }); + const headers = new HttpHeaders({ 'Content-Type': 'application/x-www-form-urlencoded' }); + return this.http + .post(this.ConfigService.getConfigParam('teacherDataURL'), $.param(params), { + headers: headers + }) + .toPromise() + .then((savedAnnotationDataResponse: any) => { + return this.saveToServerSuccess(savedAnnotationDataResponse); + }); } - }; + } saveToServerSuccess(savedAnnotationDataResponse) { let localAnnotation = null; @@ -183,18 +200,17 @@ export class AnnotationService { for (let y = localAnnotations.length - 1; y >= 0; y--) { localAnnotation = localAnnotations[y]; - if (localAnnotation.id != null && - localAnnotation.id === savedAnnotation.id) { - + if (localAnnotation.id != null && localAnnotation.id === savedAnnotation.id) { // we have found the matching local annotation so we will update it localAnnotation.serverSaveTime = savedAnnotation.serverSaveTime; //localAnnotation.requestToken = null; // requestToken is no longer needed. - this.broadcastAnnotationSavedToServer({annotation: localAnnotation}); + this.broadcastAnnotationSavedToServer({ annotation: localAnnotation }); break; - } else if (localAnnotation.requestToken != null && - localAnnotation.requestToken === savedAnnotation.requestToken) { - + } else if ( + localAnnotation.requestToken != null && + localAnnotation.requestToken === savedAnnotation.requestToken + ) { // we have found the matching local annotation so we will update it localAnnotation.id = savedAnnotation.id; localAnnotation.serverSaveTime = savedAnnotation.serverSaveTime; @@ -213,7 +229,7 @@ export class AnnotationService { this.dummyAnnotationId++; } - this.broadcastAnnotationSavedToServer({annotation: localAnnotation}); + this.broadcastAnnotationSavedToServer({ annotation: localAnnotation }); break; } } @@ -240,7 +256,8 @@ export class AnnotationService { } isAnnotationMatch(annotation1, annotation2) { - return annotation1.id === annotation2.id && + return ( + annotation1.id === annotation2.id && annotation1.nodeId === annotation2.nodeId && annotation1.componentId === annotation2.componentId && annotation1.fromWorkgroupId === annotation2.fromWorkgroupId && @@ -248,7 +265,8 @@ export class AnnotationService { annotation1.type === annotation2.type && annotation1.studentWorkId === annotation2.studentWorkId && annotation1.runId === annotation2.runId && - annotation1.periodId === annotation2.periodId; + annotation1.periodId === annotation2.periodId + ); } /** @@ -257,7 +275,7 @@ export class AnnotationService { */ setAnnotations(annotations) { this.annotations = annotations; - }; + } /** * Get the total score for a workgroup @@ -272,7 +290,7 @@ export class AnnotationService { for (let a = annotations.length - 1; a >= 0; a--) { const annotation = annotations[a]; if (annotation != null && annotation.toWorkgroupId == workgroupId) { - if (annotation.type === 'score' || annotation.type === "autoScore") { + if (annotation.type === 'score' || annotation.type === 'autoScore') { const nodeId = annotation.nodeId; const componentId = annotation.componentId; const data = annotation.data; @@ -376,8 +394,7 @@ export class AnnotationService { * @param data the annotation data * @returns the auto score annotation */ - createAutoScoreAnnotation(runId, periodId, nodeId, componentId, - toWorkgroupId, data) { + createAutoScoreAnnotation(runId, periodId, nodeId, componentId, toWorkgroupId, data) { const annotationId = null; const fromWorkgroupId = null; const studentWorkId = null; @@ -386,9 +403,19 @@ export class AnnotationService { const annotationType = 'autoScore'; const clientSaveTime = Date.parse(new Date().toString()); const annotation = this.createAnnotation( - annotationId, runId, periodId, fromWorkgroupId, toWorkgroupId, - nodeId, componentId, studentWorkId, localNotebookItemId, notebookItemId, - annotationType, data, clientSaveTime + annotationId, + runId, + periodId, + fromWorkgroupId, + toWorkgroupId, + nodeId, + componentId, + studentWorkId, + localNotebookItemId, + notebookItemId, + annotationType, + data, + clientSaveTime ); return annotation; } @@ -403,8 +430,7 @@ export class AnnotationService { * @param data the annotation data * @returns the auto comment annotation */ - createAutoCommentAnnotation(runId, periodId, nodeId, componentId, - toWorkgroupId, data) { + createAutoCommentAnnotation(runId, periodId, nodeId, componentId, toWorkgroupId, data) { const annotationId = null; const fromWorkgroupId = null; const studentWorkId = null; @@ -412,10 +438,21 @@ export class AnnotationService { const notebookItemId = null; const annotationType = 'autoComment'; const clientSaveTime = Date.parse(new Date().toString()); - const annotation = this.createAnnotation(annotationId, runId, periodId, - fromWorkgroupId, toWorkgroupId, nodeId, componentId, studentWorkId, - localNotebookItemId, notebookItemId, annotationType, data, - clientSaveTime); + const annotation = this.createAnnotation( + annotationId, + runId, + periodId, + fromWorkgroupId, + toWorkgroupId, + nodeId, + componentId, + studentWorkId, + localNotebookItemId, + notebookItemId, + annotationType, + data, + clientSaveTime + ); return annotation; } @@ -431,17 +468,36 @@ export class AnnotationService { * @param data the annotation data * @returns the inappropriate flag annotation */ - createInappropriateFlagAnnotation(runId, periodId, nodeId, componentId, - fromWorkgroupId, toWorkgroupId, studentWorkId, data) { + createInappropriateFlagAnnotation( + runId, + periodId, + nodeId, + componentId, + fromWorkgroupId, + toWorkgroupId, + studentWorkId, + data + ) { const annotationId = null; const localNotebookItemId = null; const notebookItemId = null; const annotationType = 'inappropriateFlag'; const clientSaveTime = Date.parse(new Date().toString()); - const annotation = this.createAnnotation(annotationId, runId, periodId, - fromWorkgroupId, toWorkgroupId, nodeId, componentId, studentWorkId, - localNotebookItemId, notebookItemId, annotationType, data, - clientSaveTime); + const annotation = this.createAnnotation( + annotationId, + runId, + periodId, + fromWorkgroupId, + toWorkgroupId, + nodeId, + componentId, + studentWorkId, + localNotebookItemId, + notebookItemId, + annotationType, + data, + clientSaveTime + ); return annotation; } @@ -462,18 +518,31 @@ export class AnnotationService { * 'any' for auto graded comment or teacher graded comment * @return object containing the component's latest score and comment annotations */ - getLatestComponentAnnotations(nodeId, componentId, workgroupId, scoreType = null, - commentType = null) { - let latestScoreAnnotation = this.getLatestScoreAnnotation(nodeId, - componentId, workgroupId, scoreType); - let latestCommentAnnotation = this.getLatestCommentAnnotation(nodeId, - componentId, workgroupId, commentType); + getLatestComponentAnnotations( + nodeId, + componentId, + workgroupId, + scoreType = null, + commentType = null + ) { + let latestScoreAnnotation = this.getLatestScoreAnnotation( + nodeId, + componentId, + workgroupId, + scoreType + ); + let latestCommentAnnotation = this.getLatestCommentAnnotation( + nodeId, + componentId, + workgroupId, + commentType + ); return { - 'score': latestScoreAnnotation, - 'comment': latestCommentAnnotation + score: latestScoreAnnotation, + comment: latestCommentAnnotation }; - }; + } /** * Get the latest annotations for a given notebook item (as an object) @@ -483,14 +552,20 @@ export class AnnotationService { getLatestNotebookItemAnnotations(workgroupId, localNotebookItemId) { let latestScoreAnnotation = null; let latestCommentAnnotation = null; - latestScoreAnnotation = this.getLatestNotebookItemScoreAnnotation(workgroupId, localNotebookItemId); - latestCommentAnnotation = this.getLatestNotebookItemCommentAnnotation(workgroupId, localNotebookItemId); + latestScoreAnnotation = this.getLatestNotebookItemScoreAnnotation( + workgroupId, + localNotebookItemId + ); + latestCommentAnnotation = this.getLatestNotebookItemCommentAnnotation( + workgroupId, + localNotebookItemId + ); return { - 'score': latestScoreAnnotation, - 'comment': latestCommentAnnotation + score: latestScoreAnnotation, + comment: latestCommentAnnotation }; - }; + } /** * Get the latest score annotation for this workgroup and localNotebookItemId, or null if not found @@ -501,9 +576,12 @@ export class AnnotationService { let annotations = this.getAnnotations(); for (let a = annotations.length - 1; a >= 0; a--) { let annotation = annotations[a]; - if (annotation != null && annotation.type === "score" && - annotation.notebookItemId != null && - annotation.localNotebookItemId === localNotebookItemId) { + if ( + annotation != null && + annotation.type === 'score' && + annotation.notebookItemId != null && + annotation.localNotebookItemId === localNotebookItemId + ) { return annotation; } } @@ -519,16 +597,18 @@ export class AnnotationService { let annotations = this.getAnnotations(); for (let a = annotations.length - 1; a >= 0; a--) { let annotation = annotations[a]; - if (annotation != null && annotation.type === "comment" && - annotation.notebookItemId != null && - annotation.localNotebookItemId === localNotebookItemId) { + if ( + annotation != null && + annotation.type === 'comment' && + annotation.notebookItemId != null && + annotation.localNotebookItemId === localNotebookItemId + ) { return annotation; } } return null; } - /** * Get the latest score annotation * @param nodeId the node id @@ -558,9 +638,15 @@ export class AnnotationService { const tempToWorkgroupId = tempAnnotation.toWorkgroupId; const tempAnnotationType = tempAnnotation.type; - if (nodeId == tempNodeId && componentId == tempComponentId && - workgroupId == tempToWorkgroupId) { - if (scoreType === 'any' && (tempAnnotationType === 'autoScore' || tempAnnotationType === 'score')) { + if ( + nodeId == tempNodeId && + componentId == tempComponentId && + workgroupId == tempToWorkgroupId + ) { + if ( + scoreType === 'any' && + (tempAnnotationType === 'autoScore' || tempAnnotationType === 'score') + ) { acceptAnnotation = true; } else if (scoreType === 'autoScore' && tempAnnotationType === 'autoScore') { acceptAnnotation = true; @@ -581,9 +667,12 @@ export class AnnotationService { isThereAnyScoreAnnotation(nodeId, componentId, periodId) { const annotations = this.getAnnotations(); for (const annotation of annotations) { - if (annotation.nodeId === nodeId && annotation.componentId === componentId && - (this.UtilService.isMatchingPeriods(annotation.periodId, periodId)) && - (annotation.type === 'score' || annotation.type === 'autoScore')) { + if ( + annotation.nodeId === nodeId && + annotation.componentId === componentId && + this.UtilService.isMatchingPeriods(annotation.periodId, periodId) && + (annotation.type === 'score' || annotation.type === 'autoScore') + ) { return true; } } @@ -619,9 +708,15 @@ export class AnnotationService { const tempToWorkgroupId = tempAnnotation.toWorkgroupId; const tempAnnotationType = tempAnnotation.type; - if (nodeId == tempNodeId && componentId == tempComponentId && - workgroupId == tempToWorkgroupId) { - if (commentType === 'any' && (tempAnnotationType === 'autoComment' || tempAnnotationType === 'comment')) { + if ( + nodeId == tempNodeId && + componentId == tempComponentId && + workgroupId == tempToWorkgroupId + ) { + if ( + commentType === 'any' && + (tempAnnotationType === 'autoComment' || tempAnnotationType === 'comment') + ) { acceptAnnotation = true; } else if (commentType === 'autoComment' && tempAnnotationType === 'autoComment') { acceptAnnotation = true; @@ -661,11 +756,13 @@ export class AnnotationService { */ getAllGlobalAnnotationsByNodeIdAndComponentId(nodeId, componentId) { let allGlobalAnnotations = this.getAllGlobalAnnotations(); - let globalAnnotationsByNodeIdAndComponentId = allGlobalAnnotations.filter((globalAnnotation) => { - return globalAnnotation.nodeId === nodeId && globalAnnotation.componentId === componentId; - }); + let globalAnnotationsByNodeIdAndComponentId = allGlobalAnnotations.filter( + (globalAnnotation) => { + return globalAnnotation.nodeId === nodeId && globalAnnotation.componentId === componentId; + } + ); return globalAnnotationsByNodeIdAndComponentId; - }; + } /** * Get all global annotations that are active and inactive @@ -681,7 +778,7 @@ export class AnnotationService { } } return globalAnnotations; - }; + } /** * Get all global annotations that are active and inactive and groups them by annotation group name @@ -693,10 +790,16 @@ export class AnnotationService { if (annotation != null && annotation.data != null) { if (annotation.data.isGlobal) { // check if this global annotation can be grouped (has the same annotationGroupName as another that we've seen before) - if (annotation.data.annotationGroupName != null && annotation.data.annotationGroupCreatedTime != null) { + if ( + annotation.data.annotationGroupName != null && + annotation.data.annotationGroupCreatedTime != null + ) { let sameGroupFound = false; for (let globalAnnotationGroup of globalAnnotationGroups) { - if (globalAnnotationGroup.annotationGroupNameAndTime == (annotation.data.annotationGroupName + annotation.data.annotationGroupCreatedTime)) { + if ( + globalAnnotationGroup.annotationGroupNameAndTime == + annotation.data.annotationGroupName + annotation.data.annotationGroupCreatedTime + ) { // push this annotation to the end of the group globalAnnotationGroup.annotations.push(annotation); sameGroupFound = true; @@ -704,47 +807,52 @@ export class AnnotationService { } if (!sameGroupFound) { let annotationGroup = { - "annotationGroupNameAndTime": (annotation.data.annotationGroupName + annotation.data.annotationGroupCreatedTime), - "annotations": [annotation] + annotationGroupNameAndTime: + annotation.data.annotationGroupName + annotation.data.annotationGroupCreatedTime, + annotations: [annotation] }; globalAnnotationGroups.push(annotationGroup); } } else { // each global annotation should have a name, so it shouldn't get here - console.error(this.upgrade.$injector.get('$filter')('translate')('GLOBAL_ANNOTATION_DOES_NOT_HAVE_A_NAME') + annotation); + console.error( + this.upgrade.$injector.get('$filter')('translate')( + 'GLOBAL_ANNOTATION_DOES_NOT_HAVE_A_NAME' + ) + annotation + ); } } } } return globalAnnotationGroups; - }; + } /** * Get all global annotations that are active * @returns all global annotations that are active, in a group * [ * { - * annotationGroupName:"score1", - * annotations:[ - * { - * type:autoScore, - * value:1 - * }, - * { - * type:autoComment, - * value:"you received a score of 1." - * } - * ] - * }, + * annotationGroupName:"score1", + * annotations:[ + * { + * type:autoScore, + * value:1 + * }, + * { + * type:autoComment, + * value:"you received a score of 1." + * } + * ] + * }, * { - * annotationGroupName:"score2", - * annotations:[...] - * } + * annotationGroupName:"score2", + * annotations:[...] + * } * ] */ getActiveGlobalAnnotationGroups() { return this.activeGlobalAnnotationGroups; - }; + } /** * Calculates the active global annotations and groups them by annotation group name @@ -759,7 +867,12 @@ export class AnnotationService { if (annotation.data.annotationGroupName != null) { let sameGroupFound = false; for (let activeGlobalAnnotationGroup of this.activeGlobalAnnotationGroups) { - if (activeGlobalAnnotationGroup.annotationGroupName == (annotation.data.annotationGroupName + '_' + annotation.data.annotationGroupCreatedTime)) { + if ( + activeGlobalAnnotationGroup.annotationGroupName == + annotation.data.annotationGroupName + + '_' + + annotation.data.annotationGroupCreatedTime + ) { // push this annotation to the end of the group activeGlobalAnnotationGroup.annotations.push(annotation); sameGroupFound = true; @@ -767,17 +880,24 @@ export class AnnotationService { } if (!sameGroupFound) { let annotationGroup = { - "annotationGroupName": annotation.data.annotationGroupName + '_' + annotation.data.annotationGroupCreatedTime, - "annotations": [annotation], - "nodeId": annotation.nodeId, - "componentId": annotation.componentId, - "serverSaveTime": annotation.serverSaveTime + annotationGroupName: + annotation.data.annotationGroupName + + '_' + + annotation.data.annotationGroupCreatedTime, + annotations: [annotation], + nodeId: annotation.nodeId, + componentId: annotation.componentId, + serverSaveTime: annotation.serverSaveTime }; this.activeGlobalAnnotationGroups.push(annotationGroup); } } else { // each global annotation should have a name, so it shouldn't get here - console.error(this.upgrade.$injector.get('$filter')('translate')('GLOBAL_ANNOTATION_DOES_NOT_HAVE_A_NAME') + annotation); + console.error( + this.upgrade.$injector.get('$filter')('translate')( + 'GLOBAL_ANNOTATION_DOES_NOT_HAVE_A_NAME' + ) + annotation + ); } } } @@ -799,7 +919,7 @@ export class AnnotationService { } } return inActiveGlobalAnnotations; - }; + } /** * Get the latest teacher score annotation for a student work id @@ -879,9 +999,11 @@ export class AnnotationService { let totalScoreSoFar = 0; let annotationsCounted = 0; for (let annotation of this.getAllLatestScoreAnnotations(nodeId, componentId, periodId)) { - if (annotation.nodeId === nodeId && - annotation.componentId === componentId && - (periodId === -1 || annotation.periodId === periodId)) { + if ( + annotation.nodeId === nodeId && + annotation.componentId === componentId && + (periodId === -1 || annotation.periodId === periodId) + ) { let score = null; if (type != null) { score = this.getSubScore(annotation, type); @@ -903,11 +1025,13 @@ export class AnnotationService { for (let a = this.annotations.length - 1; a >= 0; a--) { const annotation = this.annotations[a]; const workgroupId = annotation.toWorkgroupId; - if (workgroupIdsFound[workgroupId] == null && - nodeId === annotation.nodeId && - componentId === annotation.componentId && - (periodId === -1 || periodId === annotation.periodId) && - ('score' === annotation.type || 'autoScore' === annotation.type)) { + if ( + workgroupIdsFound[workgroupId] == null && + nodeId === annotation.nodeId && + componentId === annotation.componentId && + (periodId === -1 || periodId === annotation.periodId) && + ('score' === annotation.type || 'autoScore' === annotation.type) + ) { workgroupIdsFound[workgroupId] = annotation; latestScoreAnnotations.push(annotation); } diff --git a/src/main/webapp/wise5/services/audioRecorderService.ts b/src/main/webapp/wise5/services/audioRecorderService.ts index 6edf8235ed..e6615c373a 100644 --- a/src/main/webapp/wise5/services/audioRecorderService.ts +++ b/src/main/webapp/wise5/services/audioRecorderService.ts @@ -1,17 +1,15 @@ -import { Injectable } from "@angular/core"; -import { UpgradeModule } from "@angular/upgrade/static"; -import { Observable, Subject } from "rxjs"; +import { Injectable } from '@angular/core'; +import { UpgradeModule } from '@angular/upgrade/static'; +import { Observable, Subject } from 'rxjs'; @Injectable() export class AudioRecorderService { - mediaRecorder: MediaRecorder; requester: string; private audioRecordedSource: Subject = new Subject(); public audioRecorded$: Observable = this.audioRecordedSource.asObservable(); - constructor(private upgrade: UpgradeModule) { - } + constructor(private upgrade: UpgradeModule) {} async init(constraints) { try { @@ -26,7 +24,7 @@ export class AudioRecorderService { requester: this.requester, audioFile: this.getAudioFile(event.data) }); - } + }; this.mediaRecorder.start(); } catch (e) { console.error('Exception while creating MediaRecorder:', e); @@ -41,7 +39,7 @@ export class AudioRecorderService { const now = new Date().getTime(); const filename = encodeURIComponent(`audio_${now}.webm`); return new File([blob], filename, { - lastModified: now, + lastModified: now }); } @@ -49,7 +47,7 @@ export class AudioRecorderService { this.requester = requester; const constraints = { audio: { - echoCancellation: {exact: true} + echoCancellation: { exact: true } } }; this.init(constraints); diff --git a/src/main/webapp/wise5/services/cRaterService.ts b/src/main/webapp/wise5/services/cRaterService.ts index 8ca189bbf0..e71adfcfd0 100644 --- a/src/main/webapp/wise5/services/cRaterService.ts +++ b/src/main/webapp/wise5/services/cRaterService.ts @@ -1,17 +1,12 @@ 'use strict'; -import { Injectable } from "@angular/core"; -import { HttpClient, HttpParams } from "@angular/common/http"; -import { ConfigService } from "./configService"; +import { Injectable } from '@angular/core'; +import { HttpClient, HttpParams } from '@angular/common/http'; +import { ConfigService } from './configService'; @Injectable() export class CRaterService { - constructor( - protected http: HttpClient, - protected ConfigService: ConfigService - ) { - - } + constructor(protected http: HttpClient, protected ConfigService: ConfigService) {} /** * Make a CRater request to score student data @@ -23,15 +18,18 @@ export class CRaterService { makeCRaterScoringRequest(cRaterItemId: string, cRaterResponseId: number, studentData: any) { const url = this.ConfigService.getCRaterRequestURL() + '/score'; const params = new HttpParams() - .set('itemId', cRaterItemId) - .set('responseId', cRaterResponseId + '') - .set('studentData', studentData); + .set('itemId', cRaterItemId) + .set('responseId', cRaterResponseId + '') + .set('studentData', studentData); const options = { params: params }; - return this.http.get(url, options).toPromise().then((response) => { - return response; - }); + return this.http + .get(url, options) + .toPromise() + .then((response) => { + return response; + }); } /** @@ -68,9 +66,10 @@ export class CRaterService { * 1. the enableCRater field is true * 2. there is no enableCRater field but there is a cRater object (this is for legacy purposes) */ - if ((component.enableCRater && component.cRater != null) || - (!component.hasOwnProperty('enableCRater') && component.cRater != null)) { - + if ( + (component.enableCRater && component.cRater != null) || + (!component.hasOwnProperty('enableCRater') && component.cRater != null) + ) { // get the score on value e.g. 'submit', 'save', 'change', or 'exit' return component.cRater.scoreOn; } @@ -209,10 +208,16 @@ export class CRaterService { * @param currentScore the score from the current submit * @returns the feedback text for the given previous score and current score */ - getMultipleAttemptCRaterFeedbackTextByScore(component: any, previousScore: any, - currentScore: any) { + getMultipleAttemptCRaterFeedbackTextByScore( + component: any, + previousScore: any, + currentScore: any + ) { const scoringRule = this.getMultipleAttemptCRaterScoringRuleByScore( - component, previousScore, currentScore); + component, + previousScore, + currentScore + ); if (scoringRule != null) { return scoringRule.feedbackText; } @@ -227,8 +232,11 @@ export class CRaterService { * @param currentScore the score from the current submit * @returns the scoring rule for the given previous score and current score */ - getMultipleAttemptCRaterScoringRuleByScore(component: any, previousScore: any, - currentScore: any) { + getMultipleAttemptCRaterScoringRuleByScore( + component: any, + previousScore: any, + currentScore: any + ) { if (component != null && previousScore != null && currentScore != null) { const cRater = component.cRater; if (cRater != null) { @@ -245,9 +253,10 @@ export class CRaterService { const previousScoreMatch = scoreSequence[0]; const currentScoreMatch = scoreSequence[1]; - if (previousScore.toString().match("[" + previousScoreMatch + "]") && - currentScore.toString().match("[" + currentScoreMatch + "]")) { - + if ( + previousScore.toString().match('[' + previousScoreMatch + ']') && + currentScore.toString().match('[' + currentScoreMatch + ']') + ) { /* * the previous score and current score match the * expected scores so we have found the rule we want @@ -273,9 +282,12 @@ export class CRaterService { const params = new HttpParams().set('itemId', itemId); const options = { params: params - }; - return this.http.get(url, options).toPromise().then((response: any) => { - return response.isAvailable; - }); + }; + return this.http + .get(url, options) + .toPromise() + .then((response: any) => { + return response.isAvailable; + }); } } diff --git a/src/main/webapp/wise5/services/configService.ts b/src/main/webapp/wise5/services/configService.ts index 3afd8dcc0c..23e6764cb3 100644 --- a/src/main/webapp/wise5/services/configService.ts +++ b/src/main/webapp/wise5/services/configService.ts @@ -8,8 +8,7 @@ import { HttpClient } from '@angular/common/http'; export class ConfigService { public config: any = null; - constructor(private upgrade: UpgradeModule, private http: HttpClient) { - } + constructor(private upgrade: UpgradeModule, private http: HttpClient) {} setConfig(config) { this.config = config; @@ -18,53 +17,56 @@ export class ConfigService { } retrieveConfig(configURL) { - return this.http.get(configURL).toPromise().then((configJSON: any) => { - this.setTimestampDiff(configJSON); + return this.http + .get(configURL) + .toPromise() + .then((configJSON: any) => { + this.setTimestampDiff(configJSON); - let constraints = true; + let constraints = true; - const absURL = document.location.href; + const absURL = document.location.href; - if (configJSON.mode === 'preview') { - // constraints can only be disabled using the url in preview mode + if (configJSON.mode === 'preview') { + // constraints can only be disabled using the url in preview mode - // regex to match constraints=false in the url - const constraintsRegEx = new RegExp('constraints=false', 'gi'); + // regex to match constraints=false in the url + const constraintsRegEx = new RegExp('constraints=false', 'gi'); - if (absURL != null && absURL.match(constraintsRegEx)) { - // the url contains constraints=false - constraints = false; + if (absURL != null && absURL.match(constraintsRegEx)) { + // the url contains constraints=false + constraints = false; + } } - } - // set the constraints value into the config so we can access it later - configJSON.constraints = constraints; + // set the constraints value into the config so we can access it later + configJSON.constraints = constraints; - // regex to match showProjectPath=true in the url - const showProjectPathRegEx = new RegExp('showProjectPath=true', 'gi'); + // regex to match showProjectPath=true in the url + const showProjectPathRegEx = new RegExp('showProjectPath=true', 'gi'); - if (absURL != null && absURL.match(showProjectPathRegEx)) { - // the url contains showProjectPath=true - const host = location.origin; - const projectURL = configJSON.projectURL; - const projectPath = host + projectURL; - console.log(projectPath); - } + if (absURL != null && absURL.match(showProjectPathRegEx)) { + // the url contains showProjectPath=true + const host = location.origin; + const projectURL = configJSON.projectURL; + const projectPath = host + projectURL; + console.log(projectPath); + } - configJSON.isRunActive = this.calculateIsRunActive(configJSON); + configJSON.isRunActive = this.calculateIsRunActive(configJSON); - this.setConfig(configJSON); + this.setConfig(configJSON); - if (this.isPreview()) { - const myUserInfo = this.getMyUserInfo(); - if (myUserInfo != null) { - // set the workgroup id to a random integer between 1 and 100 - myUserInfo.workgroupId = Math.floor(100 * Math.random()) + 1; + if (this.isPreview()) { + const myUserInfo = this.getMyUserInfo(); + if (myUserInfo != null) { + // set the workgroup id to a random integer between 1 and 100 + myUserInfo.workgroupId = Math.floor(100 * Math.random()) + 1; + } } - } - return configJSON; - }); + return configJSON; + }); } setTimestampDiff(configJSON) { @@ -392,11 +394,18 @@ export class ConfigService { getWorkgroupsByPeriod(periodId) { const workgroupsInPeriod = []; const myUserInfo = this.getMyUserInfo(); - if (this.isStudent() && this.upgrade.$injector.get('UtilService').isMatchingPeriods(myUserInfo.periodId, periodId)) { + if ( + this.isStudent() && + this.upgrade.$injector.get('UtilService').isMatchingPeriods(myUserInfo.periodId, periodId) + ) { workgroupsInPeriod.push(myUserInfo); } for (const classmateUserInfo of this.getClassmateUserInfos()) { - if (this.upgrade.$injector.get('UtilService').isMatchingPeriods(classmateUserInfo.periodId, periodId)) { + if ( + this.upgrade.$injector + .get('UtilService') + .isMatchingPeriods(classmateUserInfo.periodId, periodId) + ) { workgroupsInPeriod.push(classmateUserInfo); } } @@ -910,12 +919,16 @@ export class ConfigService { } getFormattedStartDate() { - return this.upgrade.$injector.get('UtilService').convertMillisecondsToFormattedDateTime(this.getStartDate()); + return this.upgrade.$injector + .get('UtilService') + .convertMillisecondsToFormattedDateTime(this.getStartDate()); } getFormattedEndDate() { if (this.getEndDate() != null) { - return this.upgrade.$injector.get('UtilService').convertMillisecondsToFormattedDateTime(this.getEndDate()); + return this.upgrade.$injector + .get('UtilService') + .convertMillisecondsToFormattedDateTime(this.getEndDate()); } return ''; } diff --git a/src/main/webapp/wise5/services/milestoneService.ts b/src/main/webapp/wise5/services/milestoneService.ts index 31584e1f24..7b0551d011 100644 --- a/src/main/webapp/wise5/services/milestoneService.ts +++ b/src/main/webapp/wise5/services/milestoneService.ts @@ -28,8 +28,7 @@ export class MilestoneService { private ProjectService: ProjectService, private TeacherDataService: TeacherDataService, private UtilService: UtilService - ) { - } + ) {} getTranslation(key: string, args: any = null) { if (args == null) { @@ -42,7 +41,7 @@ export class MilestoneService { getProjectMilestones() { const achievements = this.ProjectService.getAchievements(); if (achievements.isEnabled) { - return achievements.items.filter(achievement => { + return achievements.items.filter((achievement) => { return ['milestone', 'milestoneReport'].includes(achievement.type); }); } @@ -50,7 +49,7 @@ export class MilestoneService { } getProjectMilestoneReports() { - return this.getProjectMilestones().filter(milestone => { + return this.getProjectMilestones().filter((milestone) => { return milestone.type === 'milestoneReport'; }); } @@ -91,8 +90,7 @@ export class MilestoneService { } insertMilestoneCompletion(milestone: any) { - const achievementIdToStudentAchievements = - this.AchievementService.getAchievementIdToStudentAchievementsMappings(); + const achievementIdToStudentAchievements = this.AchievementService.getAchievementIdToStudentAchievementsMappings(); const studentAchievements = achievementIdToStudentAchievements[milestone.id]; const workgroupIdsCompleted = []; const achievementTimes = []; @@ -371,9 +369,7 @@ export class MilestoneService { } getPossibleScores(aggregateData: any) { - return Object.keys(aggregateData.counts) - .map(Number) - .sort(); + return Object.keys(aggregateData.counts).map(Number).sort(); } isPercentThresholdSatisfied(satisfyCriterion: any, aggregateData: any, sum: number) { @@ -398,8 +394,12 @@ export class MilestoneService { return components; } - calculateAggregateAutoScores(nodeId: string, componentId: string, periodId: number, - reportSettings: any) { + calculateAggregateAutoScores( + nodeId: string, + componentId: string, + periodId: number, + reportSettings: any + ) { const aggregate = {}; const scoreAnnotations = this.AnnotationService.getAllLatestScoreAnnotations( nodeId, @@ -429,8 +429,11 @@ export class MilestoneService { return aggregate; } - mergeAutoScoreAndTeacherScore(autoScoreAnnotation: any, teacherScoreAnnotation: any, - reportSettings: any) { + mergeAutoScoreAndTeacherScore( + autoScoreAnnotation: any, + teacherScoreAnnotation: any, + reportSettings: any + ) { if (autoScoreAnnotation.data.scores) { for (const subScore of autoScoreAnnotation.data.scores) { if (subScore.id === 'ki') { @@ -618,87 +621,86 @@ export class MilestoneService { `; - this.upgrade.$injector.get('$mdDialog') - .show({ - parent: angular.element(document.body), - template: template, - ariaLabel: title, - fullscreen: true, - multiple: true, - targetEvent: $event, - clickOutsideToClose: true, - escapeToClose: true, - locals: { - $event: $event, - milestone: milestone, - hideStudentWork: hideStudentWork - }, - controller: [ - '$scope', - '$state', - '$mdDialog', - 'milestone', - '$event', - 'TeacherDataService', - function DialogController( - $scope, - $state, - $mdDialog, - milestone, - $event, - TeacherDataService - ) { - $scope.milestone = milestone; - $scope.hideStudentWork = hideStudentWork; - $scope.event = $event; - $scope.close = function() { - $scope.saveMilestoneClosedEvent(); - $mdDialog.hide(); - }; - $scope.edit = function() { - $mdDialog.hide({ - milestone: $scope.milestone, - action: 'edit', - $event: $event - }); - }; - $scope.onShowWorkgroup = function(workgroup: any) { - $scope.saveMilestoneClosedEvent(); - $mdDialog.hide(); - TeacherDataService.setCurrentWorkgroup(workgroup); - $state.go('root.nodeProgress'); - }; - $scope.onVisitNodeGrading = function() { - $mdDialog.hide(); - }; - $scope.saveMilestoneOpenedEvent = function() { - $scope.saveMilestoneEvent('MilestoneOpened'); - }; - $scope.saveMilestoneClosedEvent = function() { - $scope.saveMilestoneEvent('MilestoneClosed'); - }; - $scope.saveMilestoneEvent = function(event: any) { - const context = 'ClassroomMonitor', - nodeId = null, - componentId = null, - componentType = null, - category = 'Navigation', - data = { milestoneId: $scope.milestone.id }, - projectId = null; - TeacherDataService.saveEvent( - context, - nodeId, - componentId, - componentType, - category, - event, - data, - projectId - ); - }; - $scope.saveMilestoneOpenedEvent(); - } - ] - }); + this.upgrade.$injector.get('$mdDialog').show({ + parent: angular.element(document.body), + template: template, + ariaLabel: title, + fullscreen: true, + multiple: true, + targetEvent: $event, + clickOutsideToClose: true, + escapeToClose: true, + locals: { + $event: $event, + milestone: milestone, + hideStudentWork: hideStudentWork + }, + controller: [ + '$scope', + '$state', + '$mdDialog', + 'milestone', + '$event', + 'TeacherDataService', + function DialogController( + $scope, + $state, + $mdDialog, + milestone, + $event, + TeacherDataService + ) { + $scope.milestone = milestone; + $scope.hideStudentWork = hideStudentWork; + $scope.event = $event; + $scope.close = function () { + $scope.saveMilestoneClosedEvent(); + $mdDialog.hide(); + }; + $scope.edit = function () { + $mdDialog.hide({ + milestone: $scope.milestone, + action: 'edit', + $event: $event + }); + }; + $scope.onShowWorkgroup = function (workgroup: any) { + $scope.saveMilestoneClosedEvent(); + $mdDialog.hide(); + TeacherDataService.setCurrentWorkgroup(workgroup); + $state.go('root.nodeProgress'); + }; + $scope.onVisitNodeGrading = function () { + $mdDialog.hide(); + }; + $scope.saveMilestoneOpenedEvent = function () { + $scope.saveMilestoneEvent('MilestoneOpened'); + }; + $scope.saveMilestoneClosedEvent = function () { + $scope.saveMilestoneEvent('MilestoneClosed'); + }; + $scope.saveMilestoneEvent = function (event: any) { + const context = 'ClassroomMonitor', + nodeId = null, + componentId = null, + componentType = null, + category = 'Navigation', + data = { milestoneId: $scope.milestone.id }, + projectId = null; + TeacherDataService.saveEvent( + context, + nodeId, + componentId, + componentType, + category, + event, + data, + projectId + ); + }; + $scope.saveMilestoneOpenedEvent(); + } + ] + }); } } diff --git a/src/main/webapp/wise5/services/nodeService.ts b/src/main/webapp/wise5/services/nodeService.ts index 8194f971e8..cfc35313fb 100644 --- a/src/main/webapp/wise5/services/nodeService.ts +++ b/src/main/webapp/wise5/services/nodeService.ts @@ -20,13 +20,11 @@ export class NodeService { private doneRenderingComponentSource: Subject = new Subject(); public doneRenderingComponent$ = this.doneRenderingComponentSource.asObservable(); private componentShowSubmitButtonValueChangedSource: Subject = new Subject(); - public componentShowSubmitButtonValueChanged$: Observable = - this.componentShowSubmitButtonValueChangedSource.asObservable(); + public componentShowSubmitButtonValueChanged$: Observable = this.componentShowSubmitButtonValueChangedSource.asObservable(); private showRubricSource: Subject = new Subject(); public showRubric$: Observable = this.showRubricSource.asObservable(); private siblingComponentStudentDataChangedSource: Subject = new Subject(); - public siblingComponentStudentDataChanged$: Observable = - this.siblingComponentStudentDataChangedSource.asObservable(); + public siblingComponentStudentDataChanged$: Observable = this.siblingComponentStudentDataChangedSource.asObservable(); private starterStateRequestSource: Subject = new Subject(); public starterStateRequest$: Observable = this.starterStateRequestSource.asObservable(); private starterStateResponseSource: Subject = new Subject(); @@ -174,7 +172,7 @@ export class NodeService { } goToNextNode() { - return this.getNextNodeId().then(nextNodeId => { + return this.getNextNodeId().then((nextNodeId) => { if (nextNodeId != null) { const mode = this.ConfigService.getMode(); this.DataService.endCurrentNodeAndSetCurrentNodeByNodeId(nextNodeId); @@ -260,26 +258,28 @@ export class NodeService { ); if (parentTransitionLogic != null) { parentHasTransitionLogic = true; - this.chooseTransition(parentGroupId, parentTransitionLogic).then(transition => { - if (transition != null) { - const transitionToNodeId = transition.to; - if (this.ProjectService.isGroupNode(transitionToNodeId)) { - const startId = this.ProjectService.getGroupStartId(transitionToNodeId); - if (startId == null || startId == '') { - nextNodeId = transitionToNodeId; + this.chooseTransition(parentGroupId, parentTransitionLogic).then( + (transition) => { + if (transition != null) { + const transitionToNodeId = transition.to; + if (this.ProjectService.isGroupNode(transitionToNodeId)) { + const startId = this.ProjectService.getGroupStartId(transitionToNodeId); + if (startId == null || startId == '') { + nextNodeId = transitionToNodeId; + } else { + nextNodeId = startId; + } } else { - nextNodeId = startId; + nextNodeId = transitionToNodeId; } - } else { - nextNodeId = transitionToNodeId; } + resolve(nextNodeId); } - resolve(nextNodeId); - }); + ); } } } else { - this.chooseTransition(currentNodeId, transitionLogic).then(transition => { + this.chooseTransition(currentNodeId, transitionLogic).then((transition) => { resolve(transition.to); }); } @@ -296,7 +296,7 @@ export class NodeService { * @return a promise that will return the next node id */ goToNextNodeWithWork() { - this.getNextNodeIdWithWork().then(nextNodeId => { + this.getNextNodeIdWithWork().then((nextNodeId) => { if (nextNodeId) { this.DataService.endCurrentNodeAndSetCurrentNodeByNodeId(nextNodeId); } @@ -478,7 +478,7 @@ export class NodeService { nodeId: toNodeId, nodeTitle: this.ProjectService.getNodePositionAndTitleByNodeId(toNodeId), transition: availableTransition - } + }; paths.push(path); } const dialogRef = this.dialog.open(ChooseBranchPathDialogComponent, { @@ -488,7 +488,7 @@ export class NodeService { }, disableClose: true }); - dialogRef.afterClosed().subscribe(result => { + dialogRef.afterClosed().subscribe((result) => { resolve(result); }); } @@ -532,8 +532,11 @@ export class NodeService { }); const availableTransitions = this.getAvailableTransitions(transitionLogic.transitions); const transitionResult = this.getTransitionResultByNodeId(nodeId); - if (this.ConfigService.isPreview() && availableTransitions.length > 1 && - transitionResult == null) { + if ( + this.ConfigService.isPreview() && + availableTransitions.length > 1 && + transitionResult == null + ) { this.setChooseTransitionPromise(nodeId, promise); } return promise; @@ -543,8 +546,7 @@ export class NodeService { const availableTransitions = []; for (const transition of transitions) { const criteria = transition.criteria; - if (criteria == null || - (criteria != null && this.DataService.evaluateCriterias(criteria))) { + if (criteria == null || (criteria != null && this.DataService.evaluateCriterias(criteria))) { availableTransitions.push(transition); } } @@ -583,7 +585,7 @@ export class NodeService { let transition, fromNodeId, toNodeId; if (alreadyBranched) { if (canChangePath) { - this.chooseTransition(nodeId, transitionLogic).then(transition => { + this.chooseTransition(nodeId, transitionLogic).then((transition) => { if (transition != null) { fromNodeId = currentNode.id; toNodeId = transition.to; @@ -596,7 +598,7 @@ export class NodeService { } else { // student has not branched yet - this.chooseTransition(nodeId, transitionLogic).then(transition => { + this.chooseTransition(nodeId, transitionLogic).then((transition) => { if (transition != null) { fromNodeId = currentNode.id; toNodeId = transition.to; @@ -623,14 +625,7 @@ export class NodeService { fromNodeId: fromNodeId, toNodeId: toNodeId }; - this.DataService.saveVLEEvent( - nodeId, - componentId, - componentType, - category, - event, - eventData - ); + this.DataService.saveVLEEvent(nodeId, componentId, componentType, category, event, eventData); } evaluateTransitionLogicOn(event) { @@ -766,7 +761,7 @@ export class NodeService { '$mdDialog', function DialogController($scope, $mdDialog) { // display the rubric in a new tab - $scope.openInNewWindow = function() { + $scope.openInNewWindow = function () { // open a new tab let w = window.open('', '_blank'); @@ -826,7 +821,7 @@ export class NodeService { this.starterStateResponseSource.next(args); } - showRubric(id: string) : void { + showRubric(id: string): void { this.showRubricSource.next(id); } } diff --git a/src/main/webapp/wise5/services/notebookService.ts b/src/main/webapp/wise5/services/notebookService.ts index 9c60850761..cae2efc6fa 100644 --- a/src/main/webapp/wise5/services/notebookService.ts +++ b/src/main/webapp/wise5/services/notebookService.ts @@ -1,14 +1,14 @@ 'use strict'; -import { Injectable } from "@angular/core"; -import { UpgradeModule } from "@angular/upgrade/static"; +import { Injectable } from '@angular/core'; +import { UpgradeModule } from '@angular/upgrade/static'; import { HttpClient, HttpHeaders } from '@angular/common/http'; -import { ConfigService } from "./configService"; -import { ProjectService } from "./projectService"; -import { StudentAssetService } from "./studentAssetService"; -import { StudentDataService } from "./studentDataService"; +import { ConfigService } from './configService'; +import { ProjectService } from './projectService'; +import { StudentAssetService } from './studentAssetService'; +import { StudentDataService } from './studentDataService'; import { UtilService } from './utilService'; -import { Subject } from "rxjs"; +import { Subject } from 'rxjs'; @Injectable() export class NotebookService { @@ -74,17 +74,20 @@ export class NotebookService { private showReportAnnotationsSource: Subject = new Subject(); public showReportAnnotations$ = this.showReportAnnotationsSource.asObservable(); - constructor(private upgrade: UpgradeModule, - public http: HttpClient, - private ConfigService: ConfigService, - private ProjectService: ProjectService, - private StudentAssetService: StudentAssetService, - private StudentDataService: StudentDataService, - private UtilService: UtilService) { - this.notebookItemAnnotationReceivedSubscription = - this.StudentDataService.notebookItemAnnotationReceived$.subscribe((args: any) => { - this.notebookItemAnnotationReceivedSource.next(args); - }); + constructor( + private upgrade: UpgradeModule, + public http: HttpClient, + private ConfigService: ConfigService, + private ProjectService: ProjectService, + private StudentAssetService: StudentAssetService, + private StudentDataService: StudentDataService, + private UtilService: UtilService + ) { + this.notebookItemAnnotationReceivedSubscription = this.StudentDataService.notebookItemAnnotationReceived$.subscribe( + (args: any) => { + this.notebookItemAnnotationReceivedSource.next(args); + } + ); } ngOnDestroy() { @@ -96,7 +99,7 @@ export class NotebookService { } getStudentNotebookConfig() { - return Object.assign(this.config, this.ProjectService.project.notebook); + return Object.assign(this.config, this.ProjectService.project.notebook); } getTeacherNotebookConfig() { @@ -107,39 +110,54 @@ export class NotebookService { this.broadcastEditNote({ itemId: itemId, ev: ev }); } - addNote(file, text = null, studentWorkIds = null, - isEditTextEnabled = true, isFileUploadEnabled = true) { + addNote( + file, + text = null, + studentWorkIds = null, + isEditTextEnabled = true, + isFileUploadEnabled = true + ) { this.broadcastAddNote({ - file: file, - text: text, - studentWorkIds: studentWorkIds, - isEditTextEnabled: isEditTextEnabled, - isFileUploadEnabled: isFileUploadEnabled + file: file, + text: text, + studentWorkIds: studentWorkIds, + isEditTextEnabled: isEditTextEnabled, + isFileUploadEnabled: isFileUploadEnabled }); } deleteNote(note) { - const noteCopy = {...note}; + const noteCopy = { ...note }; const clientTime = Date.parse(new Date().toString()); noteCopy.clientDeleteTime = clientTime; return this.updateNote(noteCopy, clientTime); } reviveNote(note) { - const noteCopy = {...note}; + const noteCopy = { ...note }; noteCopy.clientDeleteTime = null; return this.updateNote(noteCopy); } updateNote(note, clientSaveTime = Date.parse(new Date().toString())) { note.id = null; // set to null so we're creating a new notebook item - return this.saveNotebookItem(note.id, note.nodeId, note.localNotebookItemId, - note.type, note.title, note.content, note.groups, - clientSaveTime, note.clientDeleteTime); + return this.saveNotebookItem( + note.id, + note.nodeId, + note.localNotebookItemId, + note.type, + note.title, + note.content, + note.groups, + clientSaveTime, + note.clientDeleteTime + ); } - getLatestNotebookItemByLocalNotebookItemId(localNotebookItemId, - workgroupId = this.ConfigService.getWorkgroupId()) { + getLatestNotebookItemByLocalNotebookItemId( + localNotebookItemId, + workgroupId = this.ConfigService.getWorkgroupId() + ) { const notebookByWorkgroup = this.getNotebookByWorkgroup(workgroupId); if (notebookByWorkgroup != null) { const allNotebookItems = [...notebookByWorkgroup.allItems].reverse(); @@ -152,8 +170,10 @@ export class NotebookService { return null; } - getLatestNotebookReportItemByReportId(reportId, - workgroupId = this.ConfigService.getWorkgroupId()) { + getLatestNotebookReportItemByReportId( + reportId, + workgroupId = this.ConfigService.getWorkgroupId() + ) { return this.getLatestNotebookItemByLocalNotebookItemId(reportId, workgroupId); } @@ -199,18 +219,21 @@ export class NotebookService { } isNotebookEnabled(type: string = 'notebook') { - return this.ProjectService.project[type] != null && - this.ProjectService.project[type].enabled; + return this.ProjectService.project[type] != null && this.ProjectService.project[type].enabled; } isStudentNoteEnabled() { - return this.ProjectService.project.notebook != null && - this.ProjectService.project.notebook.itemTypes.note.enabled; + return ( + this.ProjectService.project.notebook != null && + this.ProjectService.project.notebook.itemTypes.note.enabled + ); } isStudentNoteClippingEnabled() { - return this.isStudentNoteEnabled() && - this.ProjectService.project.notebook.itemTypes.note.enableClipping; + return ( + this.isStudentNoteEnabled() && + this.ProjectService.project.notebook.itemTypes.note.enableClipping + ); } retrieveNotebookItems(workgroupId = null) { @@ -236,10 +259,13 @@ export class NotebookService { if (workgroupId != null) { url += `/workgroup/${workgroupId}`; } - return this.http.get(url).toPromise().then(resultData => { - const notebookItems = resultData; - return this.handleRetrieveNotebookItems(notebookItems); - }); + return this.http + .get(url) + .toPromise() + .then((resultData) => { + const notebookItems = resultData; + return this.handleRetrieveNotebookItems(notebookItems); + }); } handleRetrieveNotebookItems(notebookItems) { @@ -247,18 +273,19 @@ export class NotebookService { for (let notebookItem of notebookItems) { try { if (notebookItem.studentAssetId != null) { - notebookItem.studentAsset = - this.StudentAssetService.getAssetById(notebookItem.studentAssetId); + notebookItem.studentAsset = this.StudentAssetService.getAssetById( + notebookItem.studentAssetId + ); } else if (notebookItem.studentWorkId != null) { - notebookItem.studentWork = - this.StudentDataService.getStudentWorkByStudentWorkId(notebookItem.studentWorkId); + notebookItem.studentWork = this.StudentDataService.getStudentWorkByStudentWorkId( + notebookItem.studentWorkId + ); } else { notebookItem.content = JSON.parse(notebookItem.content); } const workgroupId = notebookItem.workgroupId; this.addToNotebooksByWorgkroup(notebookItem, workgroupId); - } catch (e) { - } + } catch (e) {} } this.groupNotebookItems(); return this.notebooksByWorkgroup; @@ -280,7 +307,7 @@ export class NotebookService { if (this.notebooksByWorkgroup.hasOwnProperty(workgroupId)) { const notebookByWorkgroup = this.notebooksByWorkgroup[workgroupId]; notebookByWorkgroup.items = {}; - notebookByWorkgroup.deletedItems = {}; // reset deleted items + notebookByWorkgroup.deletedItems = {}; // reset deleted items for (let ni = 0; ni < notebookByWorkgroup.allItems.length; ni++) { const notebookItem = notebookByWorkgroup.allItems[ni]; const notebookItemLocalNotebookItemId = notebookItem.localNotebookItemId; @@ -294,15 +321,19 @@ export class NotebookService { // If it's deleted, then move the entire item array to deletedItems for (let notebookItemLocalNotebookItemIdKey in notebookByWorkgroup.items) { if (notebookByWorkgroup.items.hasOwnProperty(notebookItemLocalNotebookItemIdKey)) { - const allRevisionsForThisLocalNotebookItemId = - notebookByWorkgroup.items[notebookItemLocalNotebookItemIdKey]; + const allRevisionsForThisLocalNotebookItemId = + notebookByWorkgroup.items[notebookItemLocalNotebookItemIdKey]; if (allRevisionsForThisLocalNotebookItemId != null) { - const lastRevision = allRevisionsForThisLocalNotebookItemId[allRevisionsForThisLocalNotebookItemId.length - 1]; + const lastRevision = + allRevisionsForThisLocalNotebookItemId[ + allRevisionsForThisLocalNotebookItemId.length - 1 + ]; if (lastRevision != null && lastRevision.serverDeleteTime != null) { // the last revision for this was deleted, // so move the entire note (with all its revisions) to deletedItems - notebookByWorkgroup.deletedItems[notebookItemLocalNotebookItemIdKey] = - allRevisionsForThisLocalNotebookItemId; + notebookByWorkgroup.deletedItems[ + notebookItemLocalNotebookItemIdKey + ] = allRevisionsForThisLocalNotebookItemId; delete notebookByWorkgroup.items[notebookItemLocalNotebookItemIdKey]; } } @@ -354,36 +385,73 @@ export class NotebookService { } const options = { params: params - } - return this.http.get(url, options).toPromise().then(resultData => { - const publicNotebookItemsForGroup = resultData; - return this.handleRetrievePublicNotebookItems(publicNotebookItemsForGroup, group); - }); + }; + return this.http + .get(url, options) + .toPromise() + .then((resultData) => { + const publicNotebookItemsForGroup = resultData; + return this.handleRetrievePublicNotebookItems(publicNotebookItemsForGroup, group); + }); } handleRetrievePublicNotebookItems(publicNotebookItemsForGroup, group) { for (let publicNotebookItemForGroup of publicNotebookItemsForGroup) { - publicNotebookItemForGroup.content = - JSON.parse(publicNotebookItemForGroup.content); + publicNotebookItemForGroup.content = JSON.parse(publicNotebookItemForGroup.content); } this.publicNotebookItems[group] = publicNotebookItemsForGroup; this.broadcastPublicNotebookItemsRetrieved({ publicNotebookItems: this.publicNotebookItems }); return this.publicNotebookItems; } - saveNotebookItem(notebookItemId, nodeId, localNotebookItemId, type, title, content, groups = [], - clientSaveTime = null, clientDeleteTime = null) { + saveNotebookItem( + notebookItemId, + nodeId, + localNotebookItemId, + type, + title, + content, + groups = [], + clientSaveTime = null, + clientDeleteTime = null + ) { if (this.ConfigService.isPreview()) { - return this.savePreviewNotebookItem(notebookItemId, nodeId, localNotebookItemId, type, title, - content, groups, clientSaveTime, clientDeleteTime); + return this.savePreviewNotebookItem( + notebookItemId, + nodeId, + localNotebookItemId, + type, + title, + content, + groups, + clientSaveTime, + clientDeleteTime + ); } else { - return this.doSaveNotebookItem(notebookItemId, nodeId, localNotebookItemId, type, title, - content, groups, clientDeleteTime); + return this.doSaveNotebookItem( + notebookItemId, + nodeId, + localNotebookItemId, + type, + title, + content, + groups, + clientDeleteTime + ); } } - savePreviewNotebookItem(notebookItemId, nodeId, localNotebookItemId, type, title, content, groups, - clientSaveTime, clientDeleteTime) { + savePreviewNotebookItem( + notebookItemId, + nodeId, + localNotebookItemId, + type, + title, + content, + groups, + clientSaveTime, + clientDeleteTime + ) { return new Promise((resolve, reject) => { const notebookItem = { content: content, @@ -403,15 +471,24 @@ export class NotebookService { this.addToNotebooksByWorgkroup(notebookItem, workgroupId); this.groupNotebookItems(); this.StudentDataService.updateNodeStatuses(); - this.broadcastNotebookUpdated( - { notebook: this.notebooksByWorkgroup[workgroupId], notebookItem: notebookItem } - ); + this.broadcastNotebookUpdated({ + notebook: this.notebooksByWorkgroup[workgroupId], + notebookItem: notebookItem + }); resolve(notebookItem); }); } - doSaveNotebookItem(notebookItemId, nodeId, localNotebookItemId, type, title, content, groups, - clientDeleteTime) { + doSaveNotebookItem( + notebookItemId, + nodeId, + localNotebookItemId, + type, + title, + content, + groups, + clientDeleteTime + ) { const params = { workgroupId: this.ConfigService.getWorkgroupId(), notebookItemId: notebookItemId, @@ -427,10 +504,11 @@ export class NotebookService { if (this.ConfigService.getMode() !== 'classroomMonitor') { params['periodId'] = this.ConfigService.getPeriodId(); } - const headers = new HttpHeaders({'Content-Type': 'application/x-www-form-urlencoded'}); - return this.http.post(this.ConfigService.getNotebookURL(), $.param(params), - { headers: headers }).toPromise().then( - resultData => { + const headers = new HttpHeaders({ 'Content-Type': 'application/x-www-form-urlencoded' }); + return this.http + .post(this.ConfigService.getNotebookURL(), $.param(params), { headers: headers }) + .toPromise() + .then((resultData) => { const notebookItem = resultData; this.handleSaveNotebookItem(notebookItem); return resultData; @@ -447,12 +525,10 @@ export class NotebookService { this.updatePrivateNotebookItem(notebookItem, workgroupId); } this.StudentDataService.updateNodeStatuses(); - this.broadcastNotebookUpdated( - { - notebook: this.notebooksByWorkgroup[workgroupId], - notebookItem: notebookItem - } - ); + this.broadcastNotebookUpdated({ + notebook: this.notebooksByWorkgroup[workgroupId], + notebookItem: notebookItem + }); } } @@ -476,9 +552,11 @@ export class NotebookService { workgroupId: this.ConfigService.getWorkgroupId(), clientSaveTime: Date.parse(new Date().toString()) }; - const headers = new HttpHeaders({'Content-Type': 'application/x-www-form-urlencoded'}); - return this.http.post(url, $.param(params), { headers: headers }).toPromise().then( - resultData => { + const headers = new HttpHeaders({ 'Content-Type': 'application/x-www-form-urlencoded' }); + return this.http + .post(url, $.param(params), { headers: headers }) + .toPromise() + .then((resultData) => { const notebookItem = resultData; return this.handleNewNotebookItem(notebookItem); }); @@ -491,20 +569,30 @@ export class NotebookService { this.notebooksByWorkgroup[workgroupId].allItems.push(notebookItem); this.groupNotebookItems(); this.StudentDataService.updateNodeStatuses(); - this.broadcastNotebookUpdated( - { notebook: this.notebooksByWorkgroup[workgroupId], notebookItem: notebookItem } - ); + this.broadcastNotebookUpdated({ + notebook: this.notebooksByWorkgroup[workgroupId], + notebookItem: notebookItem + }); return notebookItem; } saveNotebookToggleEvent(isOpen, currentNode) { - const nodeId = null, componentId = null, componentType = null, category = 'Notebook'; + const nodeId = null, + componentId = null, + componentType = null, + category = 'Notebook'; const eventData = { curentNodeId: currentNode == null ? null : currentNode.id }; const event = isOpen ? 'notebookOpened' : 'notebookClosed'; - this.StudentDataService.saveVLEEvent(nodeId, componentId, componentType, category, event, - eventData); + this.StudentDataService.saveVLEEvent( + nodeId, + componentId, + componentType, + category, + event, + eventData + ); } broadcastAddNote(args: any) { @@ -538,5 +626,4 @@ export class NotebookService { broadcastShowReportAnnotations() { this.showReportAnnotationsSource.next(); } - -} \ No newline at end of file +} diff --git a/src/main/webapp/wise5/services/notificationService.ts b/src/main/webapp/wise5/services/notificationService.ts index d4f427bb07..0a16c51279 100644 --- a/src/main/webapp/wise5/services/notificationService.ts +++ b/src/main/webapp/wise5/services/notificationService.ts @@ -1,12 +1,12 @@ import * as angular from 'angular'; -import { Injectable } from "@angular/core"; -import { UpgradeModule } from "@angular/upgrade/static"; -import { HttpClient, HttpParams } from "@angular/common/http"; -import { ConfigService } from "./configService"; -import { ProjectService } from "./projectService"; -import { UtilService } from "./utilService"; -import { Notification } from "../../site/src/app/domain/notification"; -import { Observable, Subject } from "rxjs"; +import { Injectable } from '@angular/core'; +import { UpgradeModule } from '@angular/upgrade/static'; +import { HttpClient, HttpParams } from '@angular/common/http'; +import { ConfigService } from './configService'; +import { ProjectService } from './projectService'; +import { UtilService } from './utilService'; +import { Notification } from '../../site/src/app/domain/notification'; +import { Observable, Subject } from 'rxjs'; @Injectable() export class NotificationService { @@ -18,16 +18,17 @@ export class NotificationService { private setIsJSONValidSource: Subject = new Subject(); public setIsJSONValid$: Observable = this.setIsJSONValidSource.asObservable(); private serverConnectionStatusSource: Subject = new Subject(); - public serverConnectionStatus$: Observable = - this.serverConnectionStatusSource.asObservable(); + public serverConnectionStatus$: Observable = this.serverConnectionStatusSource.asObservable(); private viewCurrentAmbientNotificationSource: Subject = new Subject(); - public viewCurrentAmbientNotification$: Observable = - this.viewCurrentAmbientNotificationSource.asObservable(); + public viewCurrentAmbientNotification$: Observable = this.viewCurrentAmbientNotificationSource.asObservable(); - constructor(private upgrade: UpgradeModule, private http: HttpClient, - private ConfigService: ConfigService, private ProjectService: ProjectService, - private UtilService: UtilService) { - } + constructor( + private upgrade: UpgradeModule, + private http: HttpClient, + private ConfigService: ConfigService, + private ProjectService: ProjectService, + private UtilService: UtilService + ) {} /** * Creates a new notification object @@ -41,8 +42,18 @@ export class NotificationService { * @param groupId id that groups multiple notifications together * @returns newly created notification object */ - createNewNotification(runId, periodId, notificationType, nodeId, componentId, - fromWorkgroupId, toWorkgroupId, message, data = null, groupId = null): Notification { + createNewNotification( + runId, + periodId, + notificationType, + nodeId, + componentId, + fromWorkgroupId, + toWorkgroupId, + message, + data = null, + groupId = null + ): Notification { const nodePosition = this.ProjectService.getNodePositionById(nodeId); const nodePositionAndTitle = this.ProjectService.getNodePositionAndTitleByNodeId(nodeId); const component = this.ProjectService.getComponentByNodeIdAndComponentId(nodeId, componentId); @@ -70,7 +81,6 @@ export class NotificationService { }); } - retrieveNotifications() { if (this.ConfigService.isPreview()) { this.notifications = []; @@ -79,22 +89,26 @@ export class NotificationService { const options: any = {}; if (this.ConfigService.getMode() === 'studentRun') { options.params = new HttpParams() - .set('periodId', this.ConfigService.getPeriodId()) - .set('toWorkgroupId', this.ConfigService.getWorkgroupId()); + .set('periodId', this.ConfigService.getPeriodId()) + .set('toWorkgroupId', this.ConfigService.getWorkgroupId()); } - return this.http.get(this.ConfigService.getNotificationURL(), options).toPromise() - .then((notifications: any) => { - this.notifications = notifications; - this.notifications.map((notification: Notification) => { - this.setNotificationNodePositionAndTitle(notification); + return this.http + .get(this.ConfigService.getNotificationURL(), options) + .toPromise() + .then((notifications: any) => { + this.notifications = notifications; + this.notifications.map((notification: Notification) => { + this.setNotificationNodePositionAndTitle(notification); + }); + return this.notifications; }); - return this.notifications; - }); } setNotificationNodePositionAndTitle(notification: Notification) { notification.nodePosition = this.ProjectService.getNodePositionById(notification.nodeId); - notification.nodePositionAndTitle = this.ProjectService.getNodePositionAndTitleByNodeId(notification.nodeId); + notification.nodePositionAndTitle = this.ProjectService.getNodePositionAndTitleByNodeId( + notification.nodeId + ); } sendNotificationForScore(notificationForScore) { @@ -103,7 +117,7 @@ export class NotificationService { const fromWorkgroupId = this.ConfigService.getWorkgroupId(); const runId = this.ConfigService.getRunId(); const periodId = this.ConfigService.getPeriodId(); - const notificationGroupId = runId + "_" + this.UtilService.generateKey(10); // links student and teacher notifications together + const notificationGroupId = runId + '_' + this.UtilService.generateKey(10); // links student and teacher notifications together const notificationData: any = {}; if (notificationForScore.isAmbient) { notificationData.isAmbient = true; @@ -112,31 +126,63 @@ export class NotificationService { notificationData.dismissCode = notificationForScore.dismissCode; } if (notificationForScore.isNotifyStudent) { - this.sendNotificationToUser(notificationForScore.notificationMessageToStudent, - fromWorkgroupId, notificationForScore, runId, periodId, notificationType, - this.ConfigService.getWorkgroupId(), notificationData, notificationGroupId) - .then((notification) => { - this.addNotification(notification); - }); + this.sendNotificationToUser( + notificationForScore.notificationMessageToStudent, + fromWorkgroupId, + notificationForScore, + runId, + periodId, + notificationType, + this.ConfigService.getWorkgroupId(), + notificationData, + notificationGroupId + ).then((notification) => { + this.addNotification(notification); + }); } if (notificationForScore.isNotifyTeacher) { - this.sendNotificationToUser(notificationForScore.notificationMessageToTeacher, - fromWorkgroupId, notificationForScore, runId, periodId, notificationType, - this.ConfigService.getTeacherWorkgroupId(), notificationData, notificationGroupId); + this.sendNotificationToUser( + notificationForScore.notificationMessageToTeacher, + fromWorkgroupId, + notificationForScore, + runId, + periodId, + notificationType, + this.ConfigService.getTeacherWorkgroupId(), + notificationData, + notificationGroupId + ); } } } - private sendNotificationToUser(notificationMessageTemplate: string, fromWorkgroupId: number, - notificationForScore: any, runId: number, periodId: any, notificationType: string, - toWorkgroupId: number, notificationData: any, notificationGroupId: string) { + private sendNotificationToUser( + notificationMessageTemplate: string, + fromWorkgroupId: number, + notificationForScore: any, + runId: number, + periodId: any, + notificationType: string, + toWorkgroupId: number, + notificationData: any, + notificationGroupId: string + ) { const notificationMessage = notificationMessageTemplate - .replace('{{username}}', this.ConfigService.getUsernameByWorkgroupId(fromWorkgroupId)) - .replace('{{score}}', notificationForScore.score) - .replace('{{dismissCode}}', notificationForScore.dismissCode); - const notification = this.createNewNotification(runId, periodId, notificationType, - notificationForScore.nodeId, notificationForScore.componentId, - fromWorkgroupId, toWorkgroupId, notificationMessage, notificationData, notificationGroupId); + .replace('{{username}}', this.ConfigService.getUsernameByWorkgroupId(fromWorkgroupId)) + .replace('{{score}}', notificationForScore.score) + .replace('{{dismissCode}}', notificationForScore.dismissCode); + const notification = this.createNewNotification( + runId, + periodId, + notificationType, + notificationForScore.nodeId, + notificationForScore.componentId, + fromWorkgroupId, + toWorkgroupId, + notificationMessage, + notificationData, + notificationGroupId + ); return this.saveNotificationToServer(notification); } @@ -144,10 +190,12 @@ export class NotificationService { if (this.ConfigService.isPreview()) { return this.pretendServerRequest(notification); } else { - return this.http.post(this.ConfigService.getNotificationURL(), notification).toPromise() - .then((notification: Notification) => { - return notification; - }); + return this.http + .post(this.ConfigService.getNotificationURL(), notification) + .toPromise() + .then((notification: Notification) => { + return notification; + }); } } @@ -156,10 +204,12 @@ export class NotificationService { return this.pretendServerRequest(notification); } notification.timeDismissed = Date.parse(new Date().toString()); - return this.http.post(`${this.ConfigService.getNotificationURL()}/dismiss`, notification) - .toPromise().then((notification: Notification) => { - this.addNotification(notification); - }); + return this.http + .post(`${this.ConfigService.getNotificationURL()}/dismiss`, notification) + .toPromise() + .then((notification: Notification) => { + this.addNotification(notification); + }); } pretendServerRequest(notification) { @@ -176,11 +226,9 @@ export class NotificationService { let notifications = this.notifications; for (const p in args) { if (args.hasOwnProperty(p) && args[p] !== null) { - notifications = notifications.filter( - notification => { - return (notification[p] === args[p]); - } - ); + notifications = notifications.filter((notification) => { + return notification[p] === args[p]; + }); } } return notifications; diff --git a/src/main/webapp/wise5/services/projectService.ts b/src/main/webapp/wise5/services/projectService.ts index 6a3cb97be1..0263543dbc 100644 --- a/src/main/webapp/wise5/services/projectService.ts +++ b/src/main/webapp/wise5/services/projectService.ts @@ -39,11 +39,9 @@ export class ProjectService { private errorSavingProjectSource: Subject = new Subject(); public errorSavingProject$: Observable = this.errorSavingProjectSource.asObservable(); private notAllowedToEditThisProjectSource: Subject = new Subject(); - public notAllowedToEditThisProject$: Observable = - this.notAllowedToEditThisProjectSource.asObservable(); + public notAllowedToEditThisProject$: Observable = this.notAllowedToEditThisProjectSource.asObservable(); private notLoggedInProjectNotSavedSource: Subject = new Subject(); - public notLoggedInProjectNotSaved$: Observable = - this.notLoggedInProjectNotSavedSource.asObservable(); + public notLoggedInProjectNotSaved$: Observable = this.notLoggedInProjectNotSavedSource.asObservable(); private projectChangedSource: Subject = new Subject(); public projectChanged$: Observable = this.projectChangedSource.asObservable(); private projectSavedSource: Subject = new Subject(); @@ -426,7 +424,7 @@ export class ProjectService { getGroupNodesIdToOrder() { const idToOrder = {}; - const onlyGroupNodes = Object.entries(this.idToOrder).filter(item => { + const onlyGroupNodes = Object.entries(this.idToOrder).filter((item) => { return this.isGroupNode(item[0]); }); for (const [key, value] of onlyGroupNodes) { @@ -532,7 +530,7 @@ export class ProjectService { '(\'|"|\\\\\'|\\\\")', 'gi' ), - matchedString => { + (matchedString) => { /* * once found, we prepend the contentBaseURL + "assets/" to the string within the quotes * and keep everything else the same. @@ -626,7 +624,7 @@ export class ProjectService { return matchedString.replace( 'img', `img ng-click=\\"this.$ctrl.ProjectService.broadcastSnipImage(` + - `{ target: $event.target, componentId: '${componentId}' })\\"` + `{ target: $event.target, componentId: '${componentId}' })\\"` ); }); } @@ -892,7 +890,7 @@ export class ProjectService { * the target ids show up in the project. */ constraintsComparatorGenerator(orderedNodeIds) { - return function(constraintA, constraintB) { + return function (constraintA, constraintB) { let constraintAIndex = orderedNodeIds.indexOf(constraintA.targetId); let constraintBIndex = orderedNodeIds.indexOf(constraintB.targetId); if (constraintAIndex < constraintBIndex) { @@ -1106,11 +1104,14 @@ export class ProjectService { if (projectURL == null) { return null; } - const headers = new HttpHeaders().set("cache-control", "no-cache"); - return this.http.get(projectURL, {headers: headers}).toPromise().then(projectJSON => { - this.setProject(projectJSON); - return projectJSON; - }); + const headers = new HttpHeaders().set('cache-control', 'no-cache'); + return this.http + .get(projectURL, { headers: headers }) + .toPromise() + .then((projectJSON) => { + this.setProject(projectJSON); + return projectJSON; + }); } /** @@ -1119,12 +1120,18 @@ export class ProjectService { * @return a promise to return the project JSON */ retrieveProjectById(projectId) { - return this.http.get(`/author/config/${projectId}`).toPromise().then((configJSON: any) => { - return this.http.get(configJSON.projectURL).toPromise().then((projectJSON: any) => { - projectJSON.previewProjectURL = configJSON.previewProjectURL; - return projectJSON; + return this.http + .get(`/author/config/${projectId}`) + .toPromise() + .then((configJSON: any) => { + return this.http + .get(configJSON.projectURL) + .toPromise() + .then((projectJSON: any) => { + projectJSON.previewProjectURL = configJSON.previewProjectURL; + return projectJSON; + }); }); - }); } /** @@ -1138,12 +1145,18 @@ export class ProjectService { } this.broadcastSavingProject(); this.cleanupBeforeSave(); - this.project.metadata.authors = this.getUniqueAuthors(this.addCurrentUserToAuthors( - this.getAuthors())); - return this.http.post(this.ConfigService.getConfigParam('saveProjectURL'), - angular.toJson(this.project, false)).toPromise().then((response: any) => { - this.handleSaveProjectResponse(response); - }); + this.project.metadata.authors = this.getUniqueAuthors( + this.addCurrentUserToAuthors(this.getAuthors()) + ); + return this.http + .post( + this.ConfigService.getConfigParam('saveProjectURL'), + angular.toJson(this.project, false) + ) + .toPromise() + .then((response: any) => { + this.handleSaveProjectResponse(response); + }); } getAuthors(): any[] { @@ -1198,10 +1211,10 @@ export class ProjectService { * objects. */ cleanupBeforeSave() { - this.getActiveNodes().forEach(activeNode => { + this.getActiveNodes().forEach((activeNode) => { this.cleanupNode(activeNode); }); - this.getInactiveNodes().forEach(inactiveNode => { + this.getInactiveNodes().forEach((inactiveNode) => { this.cleanupNode(inactiveNode); }); } @@ -1228,7 +1241,7 @@ export class ProjectService { if (node.components != null) { // activity node does not have components but step node does - node.components.forEach(component => { + node.components.forEach((component) => { this.cleanupComponent(component); }); } @@ -2614,7 +2627,7 @@ export class ProjectService { * @returns an array with all the node ids */ getNodeIds() { - return this.applicationNodes.map(node => { + return this.applicationNodes.map((node) => { return node.id; }); } @@ -2624,7 +2637,7 @@ export class ProjectService { * @returns an array with all the inactive node ids */ getInactiveNodeIds() { - return this.project.inactiveNodes.map(node => { + return this.project.inactiveNodes.map((node) => { return node.id; }); } @@ -2972,8 +2985,11 @@ export class ProjectService { for (let transitionCopy of transitionsCopy) { if (!this.isTransitionExist(transitions, transitionCopy)) { const toNodeId = transitionCopy.to; - if (this.isApplicationNode(node.id) && this.isGroupNode(toNodeId) && - this.hasGroupStartId(toNodeId)) { + if ( + this.isApplicationNode(node.id) && + this.isGroupNode(toNodeId) && + this.hasGroupStartId(toNodeId) + ) { this.addToTransition(node, this.getGroupStartId(toNodeId)); } else { transitions.splice(insertIndex, 0, transitionCopy); @@ -3000,8 +3016,11 @@ export class ProjectService { } } - if (transitions.length === 0 && parentIdOfNodeToRemove != 'group0' && - parentIdOfNodeToRemove != this.getParentGroupId(node.id)) { + if ( + transitions.length === 0 && + parentIdOfNodeToRemove != 'group0' && + parentIdOfNodeToRemove != this.getParentGroupId(node.id) + ) { /* * the from node no longer has any transitions so we will make it transition to the * parent of the node we are removing @@ -3435,16 +3454,24 @@ export class ProjectService { */ getActionMessage(action) { if (action === 'makeAllNodesAfterThisNotVisitable') { - return this.upgrade.$injector.get('$filter')('translate')('allStepsAfterThisOneWillNotBeVisitableUntil'); + return this.upgrade.$injector.get('$filter')('translate')( + 'allStepsAfterThisOneWillNotBeVisitableUntil' + ); } if (action === 'makeAllNodesAfterThisNotVisible') { - return this.upgrade.$injector.get('$filter')('translate')('allStepsAfterThisOneWillNotBeVisibleUntil'); + return this.upgrade.$injector.get('$filter')('translate')( + 'allStepsAfterThisOneWillNotBeVisibleUntil' + ); } if (action === 'makeAllOtherNodesNotVisitable') { - return this.upgrade.$injector.get('$filter')('translate')('allOtherStepsWillNotBeVisitableUntil'); + return this.upgrade.$injector.get('$filter')('translate')( + 'allOtherStepsWillNotBeVisitableUntil' + ); } if (action === 'makeAllOtherNodesNotVisible') { - return this.upgrade.$injector.get('$filter')('translate')('allOtherStepsWillNotBeVisibleUntil'); + return this.upgrade.$injector.get('$filter')('translate')( + 'allOtherStepsWillNotBeVisibleUntil' + ); } if (action === 'makeThisNodeNotVisitable') { return this.upgrade.$injector.get('$filter')('translate')('thisStepWillNotBeVisitableUntil'); @@ -3472,19 +3499,26 @@ export class ProjectService { const nodeId = params.nodeId; if (nodeId != null) { const nodeTitle = this.getNodePositionAndTitleByNodeId(nodeId); - message += this.upgrade.$injector.get('$filter')('translate')('completeNodeTitle', { nodeTitle: nodeTitle }); + message += this.upgrade.$injector.get('$filter')('translate')('completeNodeTitle', { + nodeTitle: nodeTitle + }); } } else if (name === 'isVisited') { const nodeId = params.nodeId; if (nodeId != null) { const nodeTitle = this.getNodePositionAndTitleByNodeId(nodeId); - message += this.upgrade.$injector.get('$filter')('translate')('visitNodeTitle', { nodeTitle: nodeTitle }); + message += this.upgrade.$injector.get('$filter')('translate')('visitNodeTitle', { + nodeTitle: nodeTitle + }); } } else if (name === 'isCorrect') { const nodeId = params.nodeId; if (nodeId != null) { const nodeTitle = this.getNodePositionAndTitleByNodeId(nodeId); - message += this.upgrade.$injector.get('$filter')('translate')('correctlyAnswerNodeTitle', { nodeTitle: nodeTitle }); + message += this.upgrade.$injector.get('$filter')('translate')( + 'correctlyAnswerNodeTitle', + { nodeTitle: nodeTitle } + ); } } else if (name === 'score') { const nodeId = params.nodeId; @@ -3499,10 +3533,13 @@ export class ProjectService { if (scores != null) { scoresString = scores.join(', '); } - message += this.upgrade.$injector.get('$filter')('translate')('obtainAScoreOfXOnNodeTitle', { - score: scoresString, - nodeTitle: nodeTitle - }); + message += this.upgrade.$injector.get('$filter')('translate')( + 'obtainAScoreOfXOnNodeTitle', + { + score: scoresString, + nodeTitle: nodeTitle + } + ); } else if (name === 'choiceChosen') { const nodeId = params.nodeId; const componentId = params.componentId; @@ -3549,37 +3586,50 @@ export class ProjectService { if (nodeId != null) { const requiredNumberOfWords = params.requiredNumberOfWords; const nodeTitle = this.getNodePositionAndTitleByNodeId(nodeId); - message += this.upgrade.$injector.get('$filter')('translate')('writeXNumberOfWordsOnNodeTitle', { - requiredNumberOfWords: requiredNumberOfWords, - nodeTitle: nodeTitle - }); + message += this.upgrade.$injector.get('$filter')('translate')( + 'writeXNumberOfWordsOnNodeTitle', + { + requiredNumberOfWords: requiredNumberOfWords, + nodeTitle: nodeTitle + } + ); } } else if (name === 'isVisible') { const nodeId = params.nodeId; if (nodeId != null) { const nodeTitle = this.getNodePositionAndTitleByNodeId(nodeId); - message += this.upgrade.$injector.get('$filter')('translate')('nodeTitleIsVisible', { nodeTitle: nodeTitle }); + message += this.upgrade.$injector.get('$filter')('translate')('nodeTitleIsVisible', { + nodeTitle: nodeTitle + }); } } else if (name === 'isVisitable') { const nodeId = params.nodeId; if (nodeId != null) { const nodeTitle = this.getNodePositionAndTitleByNodeId(nodeId); - message += this.upgrade.$injector.get('$filter')('translate')('nodeTitleIsVisitable', { nodeTitle: nodeTitle }); + message += this.upgrade.$injector.get('$filter')('translate')('nodeTitleIsVisitable', { + nodeTitle: nodeTitle + }); } } else if (name === 'addXNumberOfNotesOnThisStep') { const nodeId = params.nodeId; const requiredNumberOfNotes = params.requiredNumberOfNotes; const nodeTitle = this.getNodePositionAndTitleByNodeId(nodeId); if (requiredNumberOfNotes == 1) { - message += this.upgrade.$injector.get('$filter')('translate')('addXNumberOfNotesOnThisStepSingular', { - requiredNumberOfNotes: requiredNumberOfNotes, - nodeTitle: nodeTitle - }); + message += this.upgrade.$injector.get('$filter')('translate')( + 'addXNumberOfNotesOnThisStepSingular', + { + requiredNumberOfNotes: requiredNumberOfNotes, + nodeTitle: nodeTitle + } + ); } else { - message += this.upgrade.$injector.get('$filter')('translate')('addXNumberOfNotesOnThisStepPlural', { - requiredNumberOfNotes: requiredNumberOfNotes, - nodeTitle: nodeTitle - }); + message += this.upgrade.$injector.get('$filter')('translate')( + 'addXNumberOfNotesOnThisStepPlural', + { + requiredNumberOfNotes: requiredNumberOfNotes, + nodeTitle: nodeTitle + } + ); } } else if (name === 'fillXNumberOfRows') { const requiredNumberOfFilledRows = params.requiredNumberOfFilledRows; @@ -4540,7 +4590,7 @@ export class ProjectService { * up in the project. */ nodeIdsComparatorGenerator(orderedNodeIds) { - return function(nodeIdA, nodeIdB) { + return function (nodeIdA, nodeIdB) { let nodeIdAIndex = orderedNodeIds.indexOf(nodeIdA); let nodeIdBIndex = orderedNodeIds.indexOf(nodeIdB); if (nodeIdAIndex < nodeIdBIndex) { @@ -5258,7 +5308,7 @@ export class ProjectService { return this.http .get(this.ConfigService.getConfigParam('featuredProjectIconsURL')) .toPromise() - .then(data => { + .then((data) => { return data; }); } @@ -5274,15 +5324,16 @@ export class ProjectService { } setProjectIcon(projectIcon, isCustom) { - return this.http.post(this.ConfigService.getConfigParam('projectIconURL'), { - projectId: this.ConfigService.getProjectId(), - projectIcon: projectIcon, - isCustom: isCustom - }) - .toPromise() - .then(result => { - return result; - }); + return this.http + .post(this.ConfigService.getConfigParam('projectIconURL'), { + projectId: this.ConfigService.getProjectId(), + projectIcon: projectIcon, + isCustom: isCustom + }) + .toPromise() + .then((result) => { + return result; + }); } replaceNode(nodeId, node) { @@ -5301,9 +5352,7 @@ export class ProjectService { } } - replaceComponent(nodeId, componentId, component) { - - } + replaceComponent(nodeId, componentId, component) {} retrieveScript(scriptFilename) { const deferred = this.upgrade.$injector.get('$q').defer(); @@ -5311,21 +5360,15 @@ export class ProjectService { return deferred.promise; } - getGlobalAnnotationGroupByScore(component, previousScore, currentScore) { - - } + getGlobalAnnotationGroupByScore(component, previousScore, currentScore) {} - getNotificationByScore(component, previousScore, currentScore) { - - } + getNotificationByScore(component, previousScore, currentScore) {} isConnectedComponent(nodeId, componentId, connectedComponentId): boolean { return false; } - getConnectedComponentParams(componentContent, componentId) { - - } + getConnectedComponentParams(componentContent, componentId) {} getTags() { let tags = []; diff --git a/src/main/webapp/wise5/services/sessionService.ts b/src/main/webapp/wise5/services/sessionService.ts index 7acdf9c3ee..6bd5c24a7d 100644 --- a/src/main/webapp/wise5/services/sessionService.ts +++ b/src/main/webapp/wise5/services/sessionService.ts @@ -3,7 +3,7 @@ import { Injectable } from '@angular/core'; import { UpgradeModule } from '@angular/upgrade/static'; import { HttpClient } from '@angular/common/http'; -import { ConfigService } from "./configService"; +import { ConfigService } from './configService'; import { Observable, Subject } from 'rxjs'; @Injectable() @@ -25,8 +25,7 @@ export class SessionService { protected upgrade: UpgradeModule, protected http: HttpClient, protected ConfigService: ConfigService - ) { - } + ) {} calculateIntervals(sessionTimeout): any { const forceLogoutAfterWarningInterval: number = Math.min( @@ -42,9 +41,7 @@ export class SessionService { goHome() { this.broadcastExit(); - this.upgrade.$injector.get('$location').url( - this.ConfigService.getConfigParam('userType') - ); + this.upgrade.$injector.get('$location').url(this.ConfigService.getConfigParam('userType')); } broadcastExit() { @@ -56,8 +53,9 @@ export class SessionService { } initializeSession() { - const intervals: any = - this.calculateIntervals(this.ConfigService.getConfigParam('sessionTimeout')); + const intervals: any = this.calculateIntervals( + this.ConfigService.getConfigParam('sessionTimeout') + ); this.showWarningInterval = intervals.showWarningInterval; this.forceLogoutAfterWarningInterval = intervals.forceLogoutAfterWarningInterval; this.checkMouseEventInterval = this.convertMinutesToMilliseconds(1); @@ -100,7 +98,7 @@ export class SessionService { checkForLogout() { if (this.isInactiveLongEnoughToForceLogout()) { - this.checkIfSessionIsActive().subscribe(isSessionActive => { + this.checkIfSessionIsActive().subscribe((isSessionActive) => { if (!isSessionActive) { this.forceLogOut(); } @@ -111,10 +109,7 @@ export class SessionService { } isActiveWithinLastMinute(): boolean { - return ( - new Date().getTime() - this.lastActivityTimestamp < - this.convertMinutesToMilliseconds(1) - ); + return new Date().getTime() - this.lastActivityTimestamp < this.convertMinutesToMilliseconds(1); } isInactiveLongEnoughToForceLogout(): boolean { @@ -164,7 +159,7 @@ export class SessionService { } renewSession() { - this.checkIfSessionIsActive().subscribe(isSessionActive => { + this.checkIfSessionIsActive().subscribe((isSessionActive) => { if (!isSessionActive) { this.logOut(); } diff --git a/src/main/webapp/wise5/services/studentAssetService.ts b/src/main/webapp/wise5/services/studentAssetService.ts index 09429e1899..56000c8bb8 100644 --- a/src/main/webapp/wise5/services/studentAssetService.ts +++ b/src/main/webapp/wise5/services/studentAssetService.ts @@ -14,9 +14,11 @@ export class StudentAssetService { private showStudentAssetsSource: Subject = new Subject(); public showStudentAssets$: Observable = this.showStudentAssetsSource.asObservable(); - constructor(private upgrade: UpgradeModule, private http: HttpClient, - private ConfigService: ConfigService) { - } + constructor( + private upgrade: UpgradeModule, + private http: HttpClient, + private ConfigService: ConfigService + ) {} getAssetById(assetId) { for (const asset of this.allAssets) { @@ -34,37 +36,44 @@ export class StudentAssetService { deferred.resolve(this.allAssets); return deferred.promise; } else { - return this.http.get( - `${this.ConfigService.getStudentAssetsURL()}/${this.ConfigService.getWorkgroupId()}`) - .toPromise().then((assets: any) => { - this.allAssets = []; - const studentUploadsBaseURL = this.ConfigService.getStudentUploadsBaseURL(); - for (const asset of assets) { - if (!asset.isReferenced && asset.serverDeleteTime == null && - asset.fileName !== '.DS_Store') { - asset.url = studentUploadsBaseURL + asset.filePath; - if (this.isImage(asset)) { - asset.type = 'image'; - asset.iconURL = asset.url; - } else if (this.isAudio(asset)) { - asset.type = 'audio'; - asset.iconURL = 'wise5/vle/notebook/audio.png'; - } else { - asset.type = 'file'; - asset.iconURL = 'wise5/vle/notebook/file.png'; + return this.http + .get(`${this.ConfigService.getStudentAssetsURL()}/${this.ConfigService.getWorkgroupId()}`) + .toPromise() + .then((assets: any) => { + this.allAssets = []; + const studentUploadsBaseURL = this.ConfigService.getStudentUploadsBaseURL(); + for (const asset of assets) { + if ( + !asset.isReferenced && + asset.serverDeleteTime == null && + asset.fileName !== '.DS_Store' + ) { + asset.url = studentUploadsBaseURL + asset.filePath; + if (this.isImage(asset)) { + asset.type = 'image'; + asset.iconURL = asset.url; + } else if (this.isAudio(asset)) { + asset.type = 'audio'; + asset.iconURL = 'wise5/vle/notebook/audio.png'; + } else { + asset.type = 'file'; + asset.iconURL = 'wise5/vle/notebook/file.png'; + } + this.allAssets.push(asset); } - this.allAssets.push(asset); } - } - return this.allAssets; - }); + return this.allAssets; + }); } } getAssetContent(asset) { - return this.http.get(asset.url).toPromise().then(response => { - return response; - }); + return this.http + .get(asset.url) + .toPromise() + .then((response) => { + return response; + }); } hasSuffix(assetURL, suffixes) { @@ -92,8 +101,8 @@ export class StudentAssetService { if (this.ConfigService.isPreview()) { return this.upgrade.$injector.get('$q')((resolve, reject) => { const reader = new FileReader(); - reader.onload = (theFile => { - return e => { + reader.onload = ((theFile) => { + return (e) => { let fileSrc = e.target.result; let fileName = theFile.name; @@ -118,45 +127,53 @@ export class StudentAssetService { }); } else { const deferred = this.upgrade.$injector.get('$q').defer(); - this.upgrade.$injector.get('Upload').upload({ - url: this.ConfigService.getStudentAssetsURL(), - fields: { - runId: this.ConfigService.getRunId(), - workgroupId: this.ConfigService.getWorkgroupId(), - periodId: this.ConfigService.getPeriodId(), - clientSaveTime: Date.parse(new Date().toString()) - }, - file: file - }) - .success((asset, status, headers, config) => { - if (asset === 'error') { - alert(this.upgrade.$injector.get('$filter')('translate')('THERE_WAS_AN_ERROR_UPLOADING')); - } else { - const studentUploadsBaseURL = this.ConfigService.getStudentUploadsBaseURL(); - asset.url = studentUploadsBaseURL + asset.filePath; - if (this.isImage(asset)) { - asset.type = 'image'; - asset.iconURL = asset.url; - } else if (this.isAudio(asset)) { - asset.type = 'audio'; - asset.iconURL = 'wise5/themes/default/images/audio.png'; + this.upgrade.$injector + .get('Upload') + .upload({ + url: this.ConfigService.getStudentAssetsURL(), + fields: { + runId: this.ConfigService.getRunId(), + workgroupId: this.ConfigService.getWorkgroupId(), + periodId: this.ConfigService.getPeriodId(), + clientSaveTime: Date.parse(new Date().toString()) + }, + file: file + }) + .success((asset, status, headers, config) => { + if (asset === 'error') { + alert( + this.upgrade.$injector.get('$filter')('translate')('THERE_WAS_AN_ERROR_UPLOADING') + ); } else { - asset.type = 'file'; - asset.iconURL = 'wise5/themes/default/images/file.png'; + const studentUploadsBaseURL = this.ConfigService.getStudentUploadsBaseURL(); + asset.url = studentUploadsBaseURL + asset.filePath; + if (this.isImage(asset)) { + asset.type = 'image'; + asset.iconURL = asset.url; + } else if (this.isAudio(asset)) { + asset.type = 'audio'; + asset.iconURL = 'wise5/themes/default/images/audio.png'; + } else { + asset.type = 'file'; + asset.iconURL = 'wise5/themes/default/images/file.png'; + } + this.allAssets.push(asset); + deferred.resolve(asset); } - this.allAssets.push(asset); - deferred.resolve(asset); - } - }) - .error((asset, status, headers, config) => { - alert(this.upgrade.$injector.get('$filter')('translate')('THERE_WAS_AN_ERROR_UPLOADING_YOU_MIGHT_HAVE_REACHED_LIMIT')); - }); + }) + .error((asset, status, headers, config) => { + alert( + this.upgrade.$injector.get('$filter')('translate')( + 'THERE_WAS_AN_ERROR_UPLOADING_YOU_MIGHT_HAVE_REACHED_LIMIT' + ) + ); + }); return deferred.promise; } } uploadAssets(files) { - const promises = files.map(file => { + const promises = files.map((file) => { return this.upgrade.$injector.get('Upload').upload({ url: this.ConfigService.getStudentAssetsURL(), fields: { @@ -166,7 +183,7 @@ export class StudentAssetService { clientSaveTime: Date.parse(new Date().toString()) }, file: file - }) + }); }); return this.upgrade.$injector.get('$q').all(promises); } @@ -178,33 +195,34 @@ export class StudentAssetService { return resolve(studentAsset); }); } else { - return this.http.post(`${this.ConfigService.getStudentAssetsURL()}/copy`, - { - studentAssetId: studentAsset.id, - workgroupId: this.ConfigService.getWorkgroupId(), - periodId: this.ConfigService.getPeriodId(), - clientSaveTime: Date.parse(new Date().toString()) - }) - .toPromise().then((copiedAsset: any) => { - if (copiedAsset != null) { - const studentUploadsBaseURL = this.ConfigService.getStudentUploadsBaseURL(); - if (copiedAsset.isReferenced && copiedAsset.fileName !== '.DS_Store') { - copiedAsset.url = studentUploadsBaseURL + copiedAsset.filePath; - if (this.isImage(copiedAsset)) { - copiedAsset.type = 'image'; - copiedAsset.iconURL = copiedAsset.url; - } else if (this.isAudio(copiedAsset)) { - copiedAsset.type = 'audio'; - copiedAsset.iconURL = 'wise5/vle/notebook/audio.png'; - } else { - copiedAsset.type = 'file'; - copiedAsset.iconURL = 'wise5/vle/notebook/file.png'; + return this.http + .post(`${this.ConfigService.getStudentAssetsURL()}/copy`, { + studentAssetId: studentAsset.id, + workgroupId: this.ConfigService.getWorkgroupId(), + periodId: this.ConfigService.getPeriodId(), + clientSaveTime: Date.parse(new Date().toString()) + }) + .toPromise() + .then((copiedAsset: any) => { + if (copiedAsset != null) { + const studentUploadsBaseURL = this.ConfigService.getStudentUploadsBaseURL(); + if (copiedAsset.isReferenced && copiedAsset.fileName !== '.DS_Store') { + copiedAsset.url = studentUploadsBaseURL + copiedAsset.filePath; + if (this.isImage(copiedAsset)) { + copiedAsset.type = 'image'; + copiedAsset.iconURL = copiedAsset.url; + } else if (this.isAudio(copiedAsset)) { + copiedAsset.type = 'audio'; + copiedAsset.iconURL = 'wise5/vle/notebook/audio.png'; + } else { + copiedAsset.type = 'file'; + copiedAsset.iconURL = 'wise5/vle/notebook/file.png'; + } + return copiedAsset; } - return copiedAsset; } - } - return null; - }); + return null; + }); } } @@ -221,11 +239,13 @@ export class StudentAssetService { httpParams = httpParams.set('periodId', this.ConfigService.getPeriodId()); httpParams = httpParams.set('clientDeleteTime', `${Date.parse(new Date().toString())}`); const options = { params: httpParams }; - return this.http.delete(`${this.ConfigService.getStudentAssetsURL()}/delete`, options) - .toPromise().then(() => { - this.allAssets.splice(this.allAssets.indexOf(studentAsset), 1); - return studentAsset; - }); + return this.http + .delete(`${this.ConfigService.getStudentAssetsURL()}/delete`, options) + .toPromise() + .then(() => { + this.allAssets.splice(this.allAssets.indexOf(studentAsset), 1); + return studentAsset; + }); } } diff --git a/src/main/webapp/wise5/services/studentDataService.ts b/src/main/webapp/wise5/services/studentDataService.ts index fec3ea2f99..1c9a297701 100644 --- a/src/main/webapp/wise5/services/studentDataService.ts +++ b/src/main/webapp/wise5/services/studentDataService.ts @@ -1,20 +1,19 @@ 'use strict'; -import { Injectable } from "@angular/core"; -import { ConfigService } from "./configService"; -import { AnnotationService } from "./annotationService"; -import { ProjectService } from "./projectService"; -import { UtilService } from "./utilService"; -import { UpgradeModule } from "@angular/upgrade/static"; -import { HttpClient, HttpParams } from "@angular/common/http"; +import { Injectable } from '@angular/core'; +import { ConfigService } from './configService'; +import { AnnotationService } from './annotationService'; +import { ProjectService } from './projectService'; +import { UtilService } from './utilService'; +import { UpgradeModule } from '@angular/upgrade/static'; +import { HttpClient, HttpParams } from '@angular/common/http'; import * as angular from 'angular'; -import { TagService } from "./tagService"; -import { DataService } from "../../site/src/app/services/data.service"; -import { Observable, Subject } from "rxjs"; +import { TagService } from './tagService'; +import { DataService } from '../../site/src/app/services/data.service'; +import { Observable, Subject } from 'rxjs'; @Injectable() export class StudentDataService extends DataService { - dummyStudentWorkId: number = 1; maxScore: any = null; nodeStatuses: any = {}; @@ -29,55 +28,55 @@ export class StudentDataService extends DataService { }; visitedNodesHistory = []; criteriaFunctionNameToFunction = { - branchPathTaken: criteria => { + branchPathTaken: (criteria) => { return this.evaluateBranchPathTakenCriteria(criteria); }, - isVisible: criteria => { + isVisible: (criteria) => { return this.evaluateIsVisibleCriteria(criteria); }, - isVisitable: criteria => { + isVisitable: (criteria) => { return this.evaluateIsVisitableCriteria(criteria); }, - isVisited: criteria => { + isVisited: (criteria) => { return this.evaluateIsVisitedCriteria(criteria); }, - isVisitedAfter: criteria => { + isVisitedAfter: (criteria) => { return this.evaluateIsVisitedAfterCriteria(criteria); }, - isRevisedAfter: criteria => { + isRevisedAfter: (criteria) => { return this.evaluateIsRevisedAfterCriteria(criteria); }, - isVisitedAndRevisedAfter: criteria => { + isVisitedAndRevisedAfter: (criteria) => { return this.evaluateIsVisitedAndRevisedAfterCriteria(criteria); }, - isCompleted: criteria => { + isCompleted: (criteria) => { return this.evaluateIsCompletedCriteria(criteria); }, - isCorrect: criteria => { + isCorrect: (criteria) => { return this.evaluateIsCorrectCriteria(criteria); }, - choiceChosen: criteria => { + choiceChosen: (criteria) => { return this.evaluateChoiceChosenCriteria(criteria); }, - score: criteria => { + score: (criteria) => { return this.evaluateScoreCriteria(criteria); }, - teacherRemoval: criteria => { + teacherRemoval: (criteria) => { return this.evaluateTeacherRemovalCriteria(criteria); }, - usedXSubmits: criteria => { + usedXSubmits: (criteria) => { return this.evaluateUsedXSubmitsCriteria(criteria); }, - wroteXNumberOfWords: criteria => { + wroteXNumberOfWords: (criteria) => { return this.evaluateNumberOfWordsWrittenCriteria(criteria); }, - addXNumberOfNotesOnThisStep: criteria => { + addXNumberOfNotesOnThisStep: (criteria) => { return this.evaluateAddXNumberOfNotesOnThisStepCriteria(criteria); }, - fillXNumberOfRows: criteria => { + fillXNumberOfRows: (criteria) => { return this.evaluateFillXNumberOfRowsCriteria(criteria); }, - hasTag: criteria => { + hasTag: (criteria) => { return this.evaluateHasTagCriteria(criteria); } }; @@ -91,13 +90,11 @@ export class StudentDataService extends DataService { private componentDirtySource: Subject = new Subject(); public componentDirty$: Observable = this.componentDirtySource.asObservable(); private componentSaveTriggeredSource: Subject = new Subject(); - public componentSaveTriggered$: Observable = - this.componentSaveTriggeredSource.asObservable(); + public componentSaveTriggered$: Observable = this.componentSaveTriggeredSource.asObservable(); private componentSubmitDirtySource: Subject = new Subject(); public componentSubmitDirty$: Observable = this.componentSubmitDirtySource.asObservable(); private componentSubmitTriggeredSource: Subject = new Subject(); - public componentSubmitTriggered$: Observable = - this.componentSubmitTriggeredSource.asObservable(); + public componentSubmitTriggered$: Observable = this.componentSubmitTriggeredSource.asObservable(); private notebookItemAnnotationReceivedSource: Subject = new Subject(); public notebookItemAnnotationReceived$ = this.notebookItemAnnotationReceivedSource.asObservable(); private pauseScreenSource: Subject = new Subject(); @@ -105,21 +102,21 @@ export class StudentDataService extends DataService { private componentStudentDataSource: Subject = new Subject(); public componentStudentData$: Observable = this.componentStudentDataSource.asObservable(); private studentWorkSavedToServerSource: Subject = new Subject(); - public studentWorkSavedToServer$: Observable = - this.studentWorkSavedToServerSource.asObservable(); + public studentWorkSavedToServer$: Observable = this.studentWorkSavedToServerSource.asObservable(); private navItemIsExpandedSource: Subject = new Subject(); public navItemIsExpanded$: Observable = this.navItemIsExpandedSource.asObservable(); private nodeStatusesChangedSource: Subject = new Subject(); public nodeStatusesChanged$: Observable = this.nodeStatusesChangedSource.asObservable(); constructor( - upgrade: UpgradeModule, - public http: HttpClient, - private AnnotationService: AnnotationService, - private ConfigService: ConfigService, - ProjectService: ProjectService, - private TagService: TagService, - private UtilService: UtilService) { + upgrade: UpgradeModule, + public http: HttpClient, + private AnnotationService: AnnotationService, + private ConfigService: ConfigService, + ProjectService: ProjectService, + private TagService: TagService, + private UtilService: UtilService + ) { super(upgrade, ProjectService); } @@ -134,9 +131,9 @@ export class StudentDataService extends DataService { handleNodeStatusesChanged() { this.AnnotationService.calculateActiveGlobalAnnotationGroups(); const globalAnnotationGroups = this.AnnotationService.getActiveGlobalAnnotationGroups(); - globalAnnotationGroups.map(globalAnnotationGroup => { + globalAnnotationGroups.map((globalAnnotationGroup) => { const globalAnnotations = globalAnnotationGroup.annotations; - globalAnnotations.map(globalAnnotation => { + globalAnnotations.map((globalAnnotation) => { if (globalAnnotation.data != null && globalAnnotation.data.isGlobal) { this.processGlobalAnnotation(globalAnnotation); } @@ -203,19 +200,21 @@ export class StudentDataService extends DataService { retrieveStudentDataForSignedInStudent() { const params = new HttpParams() - .set('runId', this.ConfigService.getRunId()) - .set('workgroupId', this.ConfigService.getWorkgroupId() + '') - .set('getStudentWork', true + '') - .set('getEvents', true + '') - .set('getAnnotations', true + '') - .set('toWorkgroupId', this.ConfigService.getWorkgroupId()); + .set('runId', this.ConfigService.getRunId()) + .set('workgroupId', this.ConfigService.getWorkgroupId() + '') + .set('getStudentWork', true + '') + .set('getEvents', true + '') + .set('getAnnotations', true + '') + .set('toWorkgroupId', this.ConfigService.getWorkgroupId()); const options = { params: params }; - return this.http.get(this.ConfigService.getConfigParam('studentDataURL'), options).toPromise() - .then(resultData => { - return this.handleStudentDataResponse(resultData); - }); + return this.http + .get(this.ConfigService.getConfigParam('studentDataURL'), options) + .toPromise() + .then((resultData) => { + return this.handleStudentDataResponse(resultData); + }); } handleStudentDataResponse(resultData) { @@ -244,10 +243,12 @@ export class StudentDataService extends DataService { const options = { params: params }; - return this.http.get(this.ConfigService.getConfigParam('runStatusURL'), options).toPromise() - .then((runStatus: any) => { - this.runStatus = runStatus; - }); + return this.http + .get(this.ConfigService.getConfigParam('runStatusURL'), options) + .toPromise() + .then((runStatus: any) => { + this.runStatus = runStatus; + }); } } @@ -289,7 +290,7 @@ export class StudentDataService extends DataService { for (const group of groups) { group.depth = this.ProjectService.getNodeDepth(group.id, 0); } - groups.sort(function(a, b) { + groups.sort(function (a, b) { return b.depth - a.depth; }); for (const group of groups) { @@ -818,7 +819,9 @@ export class StudentDataService extends DataService { saveComponentEvent(component, category, event, data) { if (component == null || category == null || event == null) { alert( - this.upgrade.$injector.get('$filter')('translate')('STUDENT_DATA_SERVICE_SAVE_COMPONENT_EVENT_COMPONENT_CATEGORY_EVENT_ERROR') + this.upgrade.$injector.get('$filter')('translate')( + 'STUDENT_DATA_SERVICE_SAVE_COMPONENT_EVENT_COMPONENT_CATEGORY_EVENT_ERROR' + ) ); return; } @@ -839,7 +842,11 @@ export class StudentDataService extends DataService { saveVLEEvent(nodeId, componentId, componentType, category, event, data) { if (category == null || event == null) { - alert(this.upgrade.$injector.get('$filter')('translate')('STUDENT_DATA_SERVICE_SAVE_VLE_EVENT_CATEGORY_EVENT_ERROR')); + alert( + this.upgrade.$injector.get('$filter')('translate')( + 'STUDENT_DATA_SERVICE_SAVE_VLE_EVENT_CATEGORY_EVENT_ERROR' + ) + ); return; } const context = 'VLE'; @@ -904,15 +911,17 @@ export class StudentDataService extends DataService { events: angular.toJson(events), annotations: angular.toJson(annotations) }; - return this.http.post(this.ConfigService.getConfigParam('studentDataURL'), params).toPromise() - .then( - (resultData: any) => { - return this.handleSaveToServerSuccess(resultData); - }, - (resultData: any) => { - return this.handleSaveToServerError(); - } - ); + return this.http + .post(this.ConfigService.getConfigParam('studentDataURL'), params) + .toPromise() + .then( + (resultData: any) => { + return this.handleSaveToServerSuccess(resultData); + }, + (resultData: any) => { + return this.handleSaveToServerError(); + } + ); } } @@ -1089,15 +1098,17 @@ export class StudentDataService extends DataService { workgroupId: workgroupId, status: angular.toJson(studentStatusJSON) }; - return this.http.post(this.ConfigService.getStudentStatusURL(), studentStatusParams) - .toPromise().then( - result => { - return true; - }, - result => { - return false; - } - ); + return this.http + .post(this.ConfigService.getStudentStatusURL(), studentStatusParams) + .toPromise() + .then( + (result) => { + return true; + }, + (result) => { + return false; + } + ); } } @@ -1540,64 +1551,70 @@ export class StudentDataService extends DataService { getClassmateStudentWork(nodeId, componentId, periodId) { let params = new HttpParams() - .set('runId', this.ConfigService.getRunId()) - .set('nodeId', nodeId + '') - .set('componentId', componentId + '') - .set('getStudentWork', true + '') - .set('getEvents', false + '') - .set('getAnnotations', false + '') - .set('onlyGetLatest', true + ''); + .set('runId', this.ConfigService.getRunId()) + .set('nodeId', nodeId + '') + .set('componentId', componentId + '') + .set('getStudentWork', true + '') + .set('getEvents', false + '') + .set('getAnnotations', false + '') + .set('onlyGetLatest', true + ''); if (periodId != null) { params = params.set('periodId', periodId); } const options = { params: params }; - return this.http.get(this.ConfigService.getConfigParam('studentDataURL'), options).toPromise() - .then((resultData: any) => { - return resultData.studentWorkList; - }); + return this.http + .get(this.ConfigService.getConfigParam('studentDataURL'), options) + .toPromise() + .then((resultData: any) => { + return resultData.studentWorkList; + }); } getClassmateScores(nodeId, componentId, periodId) { let params = new HttpParams() - .set('runId', this.ConfigService.getRunId()) - .set('nodeId', nodeId + '') - .set('componentId', componentId + '') - .set('getStudentWork', false + '') - .set('getEvents', false + '') - .set('getAnnotations', true + '') - .set('onlyGetLatest', false + ''); + .set('runId', this.ConfigService.getRunId()) + .set('nodeId', nodeId + '') + .set('componentId', componentId + '') + .set('getStudentWork', false + '') + .set('getEvents', false + '') + .set('getAnnotations', true + '') + .set('onlyGetLatest', false + ''); if (periodId != null) { params = params.set('periodId', periodId); } const options = { params: params }; - return this.http.get(this.ConfigService.getConfigParam('studentDataURL'), options).toPromise() - .then((resultData: any) => { - return resultData.annotations; - }); + return this.http + .get(this.ConfigService.getConfigParam('studentDataURL'), options) + .toPromise() + .then((resultData: any) => { + return resultData.annotations; + }); } getStudentWorkById(id) { const params = new HttpParams() - .set('runId', this.ConfigService.getRunId()) - .set('id', id + '') - .set('getStudentWork', true + '') - .set('getEvents', false + '') - .set('getAnnotations', false + '') - .set('onlyGetLatest', true + ''); + .set('runId', this.ConfigService.getRunId()) + .set('id', id + '') + .set('getStudentWork', true + '') + .set('getEvents', false + '') + .set('getAnnotations', false + '') + .set('onlyGetLatest', true + ''); const options = { params: params }; - return this.http.get(this.ConfigService.getConfigParam('studentDataURL'), options).toPromise() - .then((resultData: any) => { - if (resultData != null && resultData.studentWorkList.length > 0) { - return resultData.studentWorkList[0]; - } - return null; - }); + return this.http + .get(this.ConfigService.getConfigParam('studentDataURL'), options) + .toPromise() + .then((resultData: any) => { + if (resultData != null && resultData.studentWorkList.length > 0) { + return resultData.studentWorkList[0]; + } + return null; + }); } /** @@ -1641,6 +1658,6 @@ export class StudentDataService extends DataService { this.deleteKeyPressedSource.next(); } setNavItemExpanded(nodeId: string, isExpanded: boolean) { - this.navItemIsExpandedSource.next({nodeId: nodeId, isExpanded: isExpanded}); + this.navItemIsExpandedSource.next({ nodeId: nodeId, isExpanded: isExpanded }); } } diff --git a/src/main/webapp/wise5/services/studentStatusService.ts b/src/main/webapp/wise5/services/studentStatusService.ts index 9df3c2ef87..ac1dfa4183 100644 --- a/src/main/webapp/wise5/services/studentStatusService.ts +++ b/src/main/webapp/wise5/services/studentStatusService.ts @@ -1,9 +1,9 @@ -import { Injectable } from "@angular/core"; -import { HttpClient } from "@angular/common/http"; -import { AnnotationService } from "./annotationService"; -import { ConfigService } from "./configService"; -import { ProjectService } from "./projectService"; -import { Observable, Subject } from "rxjs"; +import { Injectable } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { AnnotationService } from './annotationService'; +import { ConfigService } from './configService'; +import { ProjectService } from './projectService'; +import { Observable, Subject } from 'rxjs'; @Injectable() export class StudentStatusService { @@ -11,21 +11,26 @@ export class StudentStatusService { private studentStatusReceivedSource: Subject = new Subject(); public studentStatusReceived$: Observable = this.studentStatusReceivedSource.asObservable(); - constructor(private http: HttpClient, private AnnotationService: AnnotationService, - private ConfigService: ConfigService, private ProjectService: ProjectService) { - } + constructor( + private http: HttpClient, + private AnnotationService: AnnotationService, + private ConfigService: ConfigService, + private ProjectService: ProjectService + ) {} retrieveStudentStatuses() { this.studentStatuses = []; - return this.http.get(`/api/teacher/run/${this.ConfigService.getRunId()}/student-status`) - .toPromise().then((studentStatuses: any) => { - for (const studentStatus of studentStatuses) { - const parsedStatus = JSON.parse(studentStatus.status) - parsedStatus.postTimestamp = studentStatus.timestamp; - this.studentStatuses.push(parsedStatus); - } - return this.studentStatuses; - }); + return this.http + .get(`/api/teacher/run/${this.ConfigService.getRunId()}/student-status`) + .toPromise() + .then((studentStatuses: any) => { + for (const studentStatus of studentStatuses) { + const parsedStatus = JSON.parse(studentStatus.status); + parsedStatus.postTimestamp = studentStatus.timestamp; + this.studentStatuses.push(parsedStatus); + } + return this.studentStatuses; + }); } getStudentStatuses() { @@ -188,7 +193,11 @@ export class StudentStatusService { if (!this.ProjectService.isGroupNode(descendantId)) { let descendantStatus = nodeStatuses[descendantId]; - if (descendantStatus && descendantStatus.isVisible && this.ProjectService.nodeHasWork(descendantId)) { + if ( + descendantStatus && + descendantStatus.isVisible && + this.ProjectService.nodeHasWork(descendantId) + ) { numTotal++; if (descendantStatus.isCompleted) { @@ -220,7 +229,8 @@ export class StudentStatusService { * check whether we should include the node in the calculation * i.e. either includeNonWorkNodes is true or the node has student work */ - let includeNode = !excludeNonWorkNodes || this.ProjectService.nodeHasWork(nodeId); + let includeNode = + !excludeNonWorkNodes || this.ProjectService.nodeHasWork(nodeId); if (includeNode) { numTotal++; @@ -240,7 +250,7 @@ export class StudentStatusService { } // generate the percentage number rounded down to the nearest integer - let completionPercentage = (numTotal > 0 ? Math.floor(100 * numCompleted / numTotal) : 0); + let completionPercentage = numTotal > 0 ? Math.floor((100 * numCompleted) / numTotal) : 0; return { completedItems: numCompleted, @@ -284,7 +294,7 @@ export class StudentStatusService { if (numStudentsWithScore !== 0) { // calculate the average score for this node rounded down to the nearest hundredth - averageScore = Math.floor(100 * studentScoreSum / numStudentsWithScore) / 100; + averageScore = Math.floor((100 * studentScoreSum) / numStudentsWithScore) / 100; } return averageScore; diff --git a/src/main/webapp/wise5/services/studentWebSocketService.ts b/src/main/webapp/wise5/services/studentWebSocketService.ts index 528b5aff13..362657de60 100644 --- a/src/main/webapp/wise5/services/studentWebSocketService.ts +++ b/src/main/webapp/wise5/services/studentWebSocketService.ts @@ -1,13 +1,13 @@ 'use strict'; -import { Injectable } from "@angular/core"; -import { UpgradeModule } from "@angular/upgrade/static"; -import { AnnotationService } from "./annotationService"; -import { ConfigService } from "./configService"; -import { TagService } from "./tagService"; -import { StudentDataService } from "./studentDataService"; -import { NotificationService } from "./notificationService"; -import { ProjectService } from "./projectService"; +import { Injectable } from '@angular/core'; +import { UpgradeModule } from '@angular/upgrade/static'; +import { AnnotationService } from './annotationService'; +import { ConfigService } from './configService'; +import { TagService } from './tagService'; +import { StudentDataService } from './studentDataService'; +import { NotificationService } from './notificationService'; +import { ProjectService } from './projectService'; import * as angular from 'angular'; @Injectable() @@ -16,72 +16,80 @@ export class StudentWebSocketService { periodId: any; workgroupId: number; - constructor(private upgrade: UpgradeModule, private AnnotationService: AnnotationService, - private ConfigService: ConfigService, private NotificationService: NotificationService, - private ProjectService: ProjectService, private StudentDataService: StudentDataService, - private TagService: TagService) { - } + constructor( + private upgrade: UpgradeModule, + private AnnotationService: AnnotationService, + private ConfigService: ConfigService, + private NotificationService: NotificationService, + private ProjectService: ProjectService, + private StudentDataService: StudentDataService, + private TagService: TagService + ) {} initialize() { this.runId = this.ConfigService.getRunId(); this.periodId = this.ConfigService.getPeriodId(); this.workgroupId = this.ConfigService.getWorkgroupId(); this.upgrade.$injector.get('$stomp').setDebug((args) => { - this.upgrade.$injector.get('$log').debug(args) + this.upgrade.$injector.get('$log').debug(args); }); try { - this.upgrade.$injector.get('$stomp').connect(this.ConfigService.getWebSocketURL()) - .then((frame) => { - this.subscribeToClassroomTopic(); - this.subscribeToWorkgroupTopic(); - }); - } catch(e) { + this.upgrade.$injector + .get('$stomp') + .connect(this.ConfigService.getWebSocketURL()) + .then((frame) => { + this.subscribeToClassroomTopic(); + this.subscribeToWorkgroupTopic(); + }); + } catch (e) { console.log(e); } } subscribeToClassroomTopic() { - this.upgrade.$injector.get('$stomp').subscribe( - `/topic/classroom/${this.runId}/${this.periodId}`, (message, headers, res) => { - if (message.type === 'pause') { - this.StudentDataService.pauseScreen(true); - } else if (message.type === 'unpause') { - this.StudentDataService.pauseScreen(false); - } else if (message.type === 'studentWork') { - const studentWork = JSON.parse(message.content); - this.StudentDataService.broadcastStudentWorkReceived(studentWork); - } else if (message.type === 'annotation') { - const annotation = JSON.parse(message.content); - this.AnnotationService.broadcastAnnotationReceived({ annotation: annotation }); - } else if (message.type === "goToNode") { - this.goToStep(message.content); - } else if (message.type === 'node') { - this.updateNode(message.content); - } - }); + this.upgrade.$injector + .get('$stomp') + .subscribe(`/topic/classroom/${this.runId}/${this.periodId}`, (message, headers, res) => { + if (message.type === 'pause') { + this.StudentDataService.pauseScreen(true); + } else if (message.type === 'unpause') { + this.StudentDataService.pauseScreen(false); + } else if (message.type === 'studentWork') { + const studentWork = JSON.parse(message.content); + this.StudentDataService.broadcastStudentWorkReceived(studentWork); + } else if (message.type === 'annotation') { + const annotation = JSON.parse(message.content); + this.AnnotationService.broadcastAnnotationReceived({ annotation: annotation }); + } else if (message.type === 'goToNode') { + this.goToStep(message.content); + } else if (message.type === 'node') { + this.updateNode(message.content); + } + }); } subscribeToWorkgroupTopic() { - this.upgrade.$injector.get('$stomp').subscribe(`/topic/workgroup/${this.workgroupId}`, - (message, headers, res) => { - if (message.type === 'notification') { - const notification = JSON.parse(message.content); - this.NotificationService.addNotification(notification); - } else if (message.type === 'annotation') { - const annotationData = JSON.parse(message.content); - this.AnnotationService.addOrUpdateAnnotation(annotationData); - this.StudentDataService.handleAnnotationReceived(annotationData); - } else if (message.type === 'tagsToWorkgroup') { - const tags = JSON.parse(message.content); - this.TagService.setTags(tags); - this.upgrade.$injector.get('StudentDataService').updateNodeStatuses(); - this.upgrade.$injector.get('NodeService').evaluateTransitionLogic() - } else if (message.type === 'goToNode') { - this.goToStep(message.content); - } else if (message.type === 'goToNextNode') { - this.goToNextStep(); - } - }); + this.upgrade.$injector + .get('$stomp') + .subscribe(`/topic/workgroup/${this.workgroupId}`, (message, headers, res) => { + if (message.type === 'notification') { + const notification = JSON.parse(message.content); + this.NotificationService.addNotification(notification); + } else if (message.type === 'annotation') { + const annotationData = JSON.parse(message.content); + this.AnnotationService.addOrUpdateAnnotation(annotationData); + this.StudentDataService.handleAnnotationReceived(annotationData); + } else if (message.type === 'tagsToWorkgroup') { + const tags = JSON.parse(message.content); + this.TagService.setTags(tags); + this.upgrade.$injector.get('StudentDataService').updateNodeStatuses(); + this.upgrade.$injector.get('NodeService').evaluateTransitionLogic(); + } else if (message.type === 'goToNode') { + this.goToStep(message.content); + } else if (message.type === 'goToNextNode') { + this.goToNextStep(); + } + }); } goToStep(nodeId) { @@ -89,11 +97,12 @@ export class StudentWebSocketService { } goToNextStep() { - this.upgrade.$injector.get('NodeService').getNextNodeId().then(nextNodeId => { - this.StudentDataService.endCurrentNodeAndSetCurrentNodeByNodeId( - nextNodeId - ); - }); + this.upgrade.$injector + .get('NodeService') + .getNextNodeId() + .then((nextNodeId) => { + this.StudentDataService.endCurrentNodeAndSetCurrentNodeByNodeId(nextNodeId); + }); } updateNode(nodeString: string) { diff --git a/src/main/webapp/wise5/services/tagService.ts b/src/main/webapp/wise5/services/tagService.ts index f48ca4bc1c..8228a548ed 100644 --- a/src/main/webapp/wise5/services/tagService.ts +++ b/src/main/webapp/wise5/services/tagService.ts @@ -13,11 +13,10 @@ export class TagService { tags: any[] = []; constructor( - protected http: HttpClient, - protected ConfigService: ConfigService, - protected ProjectService: ProjectService) { - - } + protected http: HttpClient, + protected ConfigService: ConfigService, + protected ProjectService: ProjectService + ) {} setTags(tags: any[]) { this.tags = tags; @@ -38,18 +37,21 @@ export class TagService { } retrieveRunTags() { - return this.http.get(`/api/tag/run/${this.ConfigService.getRunId()}`).pipe(map((data: any) => { - this.tags = data; - return data; - })); + return this.http.get(`/api/tag/run/${this.ConfigService.getRunId()}`).pipe( + map((data: any) => { + this.tags = data; + return data; + }) + ); } retrieveStudentTags() { - return this.http.get(`/api/tag/workgroup/${this.ConfigService.getWorkgroupId()}`) - .pipe(map((data: any) => { - this.tags = data; - return data; - })); + return this.http.get(`/api/tag/workgroup/${this.ConfigService.getWorkgroupId()}`).pipe( + map((data: any) => { + this.tags = data; + return data; + }) + ); } getNextAvailableTag() { diff --git a/src/main/webapp/wise5/services/teacherDataService.ts b/src/main/webapp/wise5/services/teacherDataService.ts index a635c28ba7..96fba369ea 100644 --- a/src/main/webapp/wise5/services/teacherDataService.ts +++ b/src/main/webapp/wise5/services/teacherDataService.ts @@ -1,19 +1,18 @@ 'use strict'; -import { UpgradeModule } from "@angular/upgrade/static"; -import { HttpClient, HttpParams } from "@angular/common/http"; -import { AnnotationService } from "./annotationService"; -import { ConfigService } from "./configService"; -import { UtilService } from "./utilService"; -import { TeacherProjectService } from "./teacherProjectService"; -import { TeacherWebSocketService } from "./teacherWebSocketService"; -import { Injectable } from "@angular/core"; -import { DataService } from "../../site/src/app/services/data.service"; -import { Observable, Subject, Subscription } from "rxjs"; +import { UpgradeModule } from '@angular/upgrade/static'; +import { HttpClient, HttpParams } from '@angular/common/http'; +import { AnnotationService } from './annotationService'; +import { ConfigService } from './configService'; +import { UtilService } from './utilService'; +import { TeacherProjectService } from './teacherProjectService'; +import { TeacherWebSocketService } from './teacherWebSocketService'; +import { Injectable } from '@angular/core'; +import { DataService } from '../../site/src/app/services/data.service'; +import { Observable, Subject, Subscription } from 'rxjs'; @Injectable() export class TeacherDataService extends DataService { - studentData: any; $rootScope: any; currentPeriod = null; @@ -31,8 +30,7 @@ export class TeacherDataService extends DataService { private currentPeriodChangedSource: Subject = new Subject(); public currentPeriodChanged$: Observable = this.currentPeriodChangedSource.asObservable(); private currentWorkgroupChangedSource: Subject = new Subject(); - public currentWorkgroupChanged$: Observable = - this.currentWorkgroupChangedSource.asObservable(); + public currentWorkgroupChanged$: Observable = this.currentWorkgroupChangedSource.asObservable(); constructor( upgrade: UpgradeModule, @@ -51,21 +49,24 @@ export class TeacherDataService extends DataService { }; if (this.upgrade.$injector != null) { - this.annotationSavedToServerSubscription = - this.AnnotationService.annotationSavedToServer$.subscribe(({ annotation }) => { - this.handleAnnotationReceived(annotation); - }); + this.annotationSavedToServerSubscription = this.AnnotationService.annotationSavedToServer$.subscribe( + ({ annotation }) => { + this.handleAnnotationReceived(annotation); + } + ); - this.newAnnotationReceivedSubscription = this.TeacherWebSocketService.newAnnotationReceived$ - .subscribe(({ annotation }) => { - this.handleAnnotationReceived(annotation); - }); + this.newAnnotationReceivedSubscription = this.TeacherWebSocketService.newAnnotationReceived$.subscribe( + ({ annotation }) => { + this.handleAnnotationReceived(annotation); + } + ); - this.newStudentWorkReceivedSubscription = this.TeacherWebSocketService.newStudentWorkReceived$ - .subscribe(({ studentWork }) => { - this.addOrUpdateComponentState(studentWork); - this.broadcastStudentWorkReceived({ studentWork: studentWork }); - }); + this.newStudentWorkReceivedSubscription = this.TeacherWebSocketService.newStudentWorkReceived$.subscribe( + ({ studentWork }) => { + this.addOrUpdateComponentState(studentWork); + this.broadcastStudentWorkReceived({ studentWork: studentWork }); + } + ); } } @@ -150,10 +151,10 @@ export class TeacherDataService extends DataService { retrieveStudentDataExport(selectedNodes) { let params = new HttpParams() - .set('runId', this.ConfigService.getRunId()) - .set('getStudentWork', 'true') - .set('getEvents', 'false') - .set('getAnnotations', 'true'); + .set('runId', this.ConfigService.getRunId()) + .set('getStudentWork', 'true') + .set('getEvents', 'false') + .set('getAnnotations', 'true'); if (selectedNodes != null) { for (const selectedNode of selectedNodes) { params = params.append('components', JSON.stringify(selectedNode)); @@ -164,44 +165,52 @@ export class TeacherDataService extends DataService { retrieveEventsExport(includeStudentEvents, includeTeacherEvents, includeNames) { const params = new HttpParams() - .set('runId', this.ConfigService.getRunId()) - .set('getStudentWork', 'false') - .set('getAnnotations', 'false') - .set('getEvents', 'false') - .set('includeStudentEvents', includeStudentEvents + '') - .set('includeTeacherEvents', includeTeacherEvents + '') - .set('includeNames', includeNames + ''); + .set('runId', this.ConfigService.getRunId()) + .set('getStudentWork', 'false') + .set('getAnnotations', 'false') + .set('getEvents', 'false') + .set('includeStudentEvents', includeStudentEvents + '') + .set('includeTeacherEvents', includeTeacherEvents + '') + .set('includeNames', includeNames + ''); const options = { params: params }; const url = this.ConfigService.getConfigParam('runDataExportURL') + '/events'; - return this.http.get(url, options).toPromise().then((data: any) => { - return this.handleStudentDataResponse(data); - }); + return this.http + .get(url, options) + .toPromise() + .then((data: any) => { + return this.handleStudentDataResponse(data); + }); } retrieveNotebookExport(exportType) { const params = new HttpParams().set('exportType', exportType); const options = { params: params }; - return this.http.get(`/teacher/notebook/run/${this.ConfigService.getRunId()}`, options) - .toPromise().then((data: any) => { - return data; - }); + return this.http + .get(`/teacher/notebook/run/${this.ConfigService.getRunId()}`, options) + .toPromise() + .then((data: any) => { + return data; + }); } retrieveNotificationsExport() { const url = this.getExportURL(this.ConfigService.getRunId(), 'notifications'); - return this.http.get(url).toPromise().then((data: any) => { - return data; - }); + return this.http + .get(url) + .toPromise() + .then((data: any) => { + return data; + }); } retrieveOneWorkgroupPerRowExport(selectedNodes) { let params = new HttpParams() - .set('runId', this.ConfigService.getRunId()) - .set('getStudentWork', 'true') - .set('getEvents', 'true') - .set('getAnnotations', 'true'); + .set('runId', this.ConfigService.getRunId()) + .set('getStudentWork', 'true') + .set('getEvents', 'true') + .set('getAnnotations', 'true'); if (selectedNodes != null) { for (const selectedNode of selectedNodes) { params = params.append('components', JSON.stringify(selectedNode)); @@ -212,17 +221,17 @@ export class TeacherDataService extends DataService { retrieveStudentAssetsExport() { window.location.href = this.getExportURL(this.ConfigService.getRunId(), 'studentAssets'); - return new Promise(resolve => { + return new Promise((resolve) => { resolve([]); }); } retrieveRawDataExport(selectedNodes) { let params = new HttpParams() - .set('runId', this.ConfigService.getRunId()) - .set('getStudentWork', 'true') - .set('getEvents', 'true') - .set('getAnnotations', 'true'); + .set('runId', this.ConfigService.getRunId()) + .set('getStudentWork', 'true') + .set('getEvents', 'true') + .set('getAnnotations', 'true'); if (selectedNodes != null) { for (const selectedNode of selectedNodes) { params = params.append('components', JSON.stringify(selectedNode)); @@ -232,8 +241,15 @@ export class TeacherDataService extends DataService { } saveEvent(context, nodeId, componentId, componentType, category, event, data) { - const newEvent = this.createEvent(context, nodeId, componentId, componentType, category, event, - data); + const newEvent = this.createEvent( + context, + nodeId, + componentId, + componentType, + category, + event, + data + ); const events = [newEvent]; let body = new HttpParams().set('events', JSON.stringify(events)); body = this.addCommonParams(body); @@ -241,9 +257,12 @@ export class TeacherDataService extends DataService { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }; const url = this.ConfigService.getConfigParam('teacherDataURL'); - return this.http.post(url, body, options).toPromise().then((data: any) => { - return data.events; - }); + return this.http + .post(url, body, options) + .toPromise() + .then((data: any) => { + return data.events; + }); } addCommonParams(params) { @@ -299,10 +318,10 @@ export class TeacherDataService extends DataService { retrieveStudentDataByNodeId(nodeId) { let params = new HttpParams() - .set('runId', this.ConfigService.getRunId()) - .set('getStudentWork', 'true') - .set('getAnnotations', 'false') - .set('getEvents', 'false'); + .set('runId', this.ConfigService.getRunId()) + .set('getStudentWork', 'true') + .set('getAnnotations', 'false') + .set('getEvents', 'false'); const components = this.getAllRelatedComponents(nodeId); for (const component of components) { params = params.append('components', JSON.stringify(component)); @@ -311,8 +330,9 @@ export class TeacherDataService extends DataService { } getAllRelatedComponents(nodeId) { - const components = (this.ProjectService) - .getNodeIdsAndComponentIds(nodeId); + const components = (this.ProjectService).getNodeIdsAndComponentIds( + nodeId + ); return components.concat(this.getConnectedComponentsIfNecessary(components)); } @@ -342,37 +362,37 @@ export class TeacherDataService extends DataService { retrieveStudentDataByWorkgroupId(workgroupId) { const params = new HttpParams() - .set('runId', this.ConfigService.getRunId()) - .set('workgroupId', workgroupId) - .set('toWorkgroupId', workgroupId) - .set('getStudentWork', 'true') - .set('getEvents', 'false') - .set('getAnnotations', 'false'); + .set('runId', this.ConfigService.getRunId()) + .set('workgroupId', workgroupId) + .set('toWorkgroupId', workgroupId) + .set('getStudentWork', 'true') + .set('getEvents', 'false') + .set('getAnnotations', 'false'); return this.retrieveStudentData(params); } retrieveAnnotations() { const params = new HttpParams() - .set('runId', this.ConfigService.getRunId()) - .set('getStudentWork', 'false') - .set('getEvents', 'false') - .set('getAnnotations', 'true'); + .set('runId', this.ConfigService.getRunId()) + .set('getStudentWork', 'false') + .set('getEvents', 'false') + .set('getAnnotations', 'true'); return this.retrieveStudentData(params); } retrieveLatestStudentDataByNodeIdAndComponentIdAndPeriodId(nodeId, componentId, periodId) { let params = new HttpParams() - .set('runId', this.ConfigService.getRunId()) - .set('nodeId', nodeId) - .set('componentId', componentId) - .set('getStudentWork', 'true') - .set('getEvents', 'false') - .set('getAnnotations', 'false') - .set('onlyGetLatest', 'true'); + .set('runId', this.ConfigService.getRunId()) + .set('nodeId', nodeId) + .set('componentId', componentId) + .set('getStudentWork', 'true') + .set('getEvents', 'false') + .set('getAnnotations', 'false') + .set('onlyGetLatest', 'true'); if (periodId != null) { params = params.set('periodId', periodId); } - return this.retrieveStudentData(params).then(result => { + return this.retrieveStudentData(params).then((result) => { return result.studentWorkList; }); } @@ -382,9 +402,12 @@ export class TeacherDataService extends DataService { const options = { params: params }; - return this.http.get(url, options).toPromise().then((data: any) => { - return this.handleStudentDataResponse(data); - }); + return this.http + .get(url, options) + .toPromise() + .then((data: any) => { + return this.handleStudentDataResponse(data); + }); } handleStudentDataResponse(resultData) { @@ -566,10 +589,13 @@ export class TeacherDataService extends DataService { params: params, headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }; - return this.http.get(url, options).toPromise().then((data: any) => { - this.runStatus = data; - this.initializePeriods(); - }); + return this.http + .get(url, options) + .toPromise() + .then((data: any) => { + this.runStatus = data; + this.initializePeriods(); + }); } getComponentStatesByWorkgroupId(workgroupId) { @@ -768,7 +794,7 @@ export class TeacherDataService extends DataService { getAnnotationsByNodeIdAndPeriodId(nodeId, periodId) { const annotationsByNodeId = this.studentData.annotationsByNodeId[nodeId]; if (annotationsByNodeId != null) { - return annotationsByNodeId.filter(annotation => { + return annotationsByNodeId.filter((annotation) => { return this.UtilService.isMatchingPeriods(annotation.periodId, periodId); }); } else { @@ -960,20 +986,22 @@ export class TeacherDataService extends DataService { } sendRunStatusThenHandlePauseScreen(periodId, isPaused) { - this.sendRunStatus().toPromise().then(() => { - if (isPaused) { - this.TeacherWebSocketService.pauseScreens(periodId); - } else { - this.TeacherWebSocketService.unPauseScreens(periodId); - } - }); + this.sendRunStatus() + .toPromise() + .then(() => { + if (isPaused) { + this.TeacherWebSocketService.pauseScreens(periodId); + } else { + this.TeacherWebSocketService.unPauseScreens(periodId); + } + }); } sendRunStatus() { const url = this.ConfigService.getConfigParam('runStatusURL'); const body = new HttpParams() - .set('runId', this.ConfigService.getConfigParam('runId')) - .set('status', JSON.stringify(this.runStatus)); + .set('runId', this.ConfigService.getConfigParam('runId')) + .set('status', JSON.stringify(this.runStatus)); const options = { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }; diff --git a/src/main/webapp/wise5/services/teacherProjectService.ts b/src/main/webapp/wise5/services/teacherProjectService.ts index 0ff2116faa..40d4eaeaf3 100644 --- a/src/main/webapp/wise5/services/teacherProjectService.ts +++ b/src/main/webapp/wise5/services/teacherProjectService.ts @@ -12,7 +12,6 @@ import { SessionService } from './sessionService'; @Injectable() export class TeacherProjectService extends ProjectService { - private componentChangedSource: Subject = new Subject(); public componentChanged$: Observable = this.componentChangedSource.asObservable(); private nodeChangedSource: Subject = new Subject(); @@ -23,11 +22,12 @@ export class TeacherProjectService extends ProjectService { public scrollToBottomOfPage$ = this.scrollToBottomOfPageSource.asObservable(); constructor( - protected upgrade: UpgradeModule, - protected http: HttpClient, - protected ConfigService: ConfigService, - protected SessionService: SessionService, - protected UtilService: UtilService) { + protected upgrade: UpgradeModule, + protected http: HttpClient, + protected ConfigService: ConfigService, + protected SessionService: SessionService, + protected UtilService: UtilService + ) { super(upgrade, http, ConfigService, SessionService, UtilService); } @@ -168,9 +168,14 @@ export class TeacherProjectService extends ProjectService { } notifyAuthorProjectBeginEnd(projectId, isBegin) { - return this.http.post( - `${this.ConfigService.getConfigParam('notifyAuthoringBeginEndURL')}/${projectId}/${isBegin}`, - null).toPromise(); + return this.http + .post( + `${this.ConfigService.getConfigParam( + 'notifyAuthoringBeginEndURL' + )}/${projectId}/${isBegin}`, + null + ) + .toPromise(); } notifyAuthorProjectBegin(projectId) { @@ -193,10 +198,10 @@ export class TeacherProjectService extends ProjectService { } copyProject(projectId) { - return this.http.post(`${this.ConfigService.getConfigParam('copyProjectURL')}/${projectId}`, - null) + return this.http + .post(`${this.ConfigService.getConfigParam('copyProjectURL')}/${projectId}`, null) .toPromise() - .then(newProject => { + .then((newProject) => { return newProject; }); } @@ -208,14 +213,14 @@ export class TeacherProjectService extends ProjectService { */ registerNewProject(projectName, projectJSONString) { return this.http - .post(this.ConfigService.getConfigParam('registerNewProjectURL'), { - projectName: projectName, - projectJSONString: projectJSONString - }) - .toPromise() - .then( newProjectId => { - return newProjectId; - }); + .post(this.ConfigService.getConfigParam('registerNewProjectURL'), { + projectName: projectName, + projectJSONString: projectJSONString + }) + .toPromise() + .then((newProjectId) => { + return newProjectId; + }); } /** @@ -452,82 +457,82 @@ export class TeacherProjectService extends ProjectService { * new name and change all the references in the steps to use the new * name. */ - return this.http.post(this.ConfigService.getConfigParam('importStepsURL'), - { - steps: angular.toJson(selectedNodes), - fromProjectId: fromProjectId, - toProjectId: toProjectId - }) - .toPromise() - .then((selectedNodes: any) => { - const inactiveNodes = this.getInactiveNodes(); - const newNodes = []; - const newNodeIds = []; - for (const selectedNode of selectedNodes) { - const tempNode = this.UtilService.makeCopyOfJSONObject(selectedNode); - if (this.isNodeIdUsed(tempNode.id)) { - const nextAvailableNodeId = this.getNextAvailableNodeId(newNodeIds); - tempNode.id = nextAvailableNodeId; - } - const tempComponents = tempNode.components; - for (const tempComponent of tempComponents) { - if (this.isComponentIdUsed(tempComponent.id)) { - // we are already using the component id so we will need to change it - tempComponent.id = this.getUnusedComponentId(); + return this.http + .post(this.ConfigService.getConfigParam('importStepsURL'), { + steps: angular.toJson(selectedNodes), + fromProjectId: fromProjectId, + toProjectId: toProjectId + }) + .toPromise() + .then((selectedNodes: any) => { + const inactiveNodes = this.getInactiveNodes(); + const newNodes = []; + const newNodeIds = []; + for (const selectedNode of selectedNodes) { + const tempNode = this.UtilService.makeCopyOfJSONObject(selectedNode); + if (this.isNodeIdUsed(tempNode.id)) { + const nextAvailableNodeId = this.getNextAvailableNodeId(newNodeIds); + tempNode.id = nextAvailableNodeId; } + const tempComponents = tempNode.components; + for (const tempComponent of tempComponents) { + if (this.isComponentIdUsed(tempComponent.id)) { + // we are already using the component id so we will need to change it + tempComponent.id = this.getUnusedComponentId(); + } + } + tempNode.constraints = []; + newNodes.push(tempNode); + newNodeIds.push(tempNode.id); } - tempNode.constraints = []; - newNodes.push(tempNode); - newNodeIds.push(tempNode.id); - } - if (nodeIdToInsertInsideOrAfter == null) { - /* - * the place to put the new node has not been specified so we - * will place it in the inactive steps section - */ - - /* - * Insert the node after the last inactive node. If there - * are no inactive nodes it will just be placed in the - * inactive nodes section. In the latter case we do this by - * setting nodeIdToInsertInsideOrAfter to 'inactiveSteps'. - */ - if (inactiveNodes != null && inactiveNodes.length > 0) { - nodeIdToInsertInsideOrAfter = inactiveNodes[inactiveNodes.length - 1]; - } else { - nodeIdToInsertInsideOrAfter = 'inactiveSteps'; - } - } + if (nodeIdToInsertInsideOrAfter == null) { + /* + * the place to put the new node has not been specified so we + * will place it in the inactive steps section + */ - for (const newNode of newNodes) { - if (this.isGroupNode(nodeIdToInsertInsideOrAfter)) { - this.createNodeInside(newNode, nodeIdToInsertInsideOrAfter); - } else { - this.createNodeAfter(newNode, nodeIdToInsertInsideOrAfter); + /* + * Insert the node after the last inactive node. If there + * are no inactive nodes it will just be placed in the + * inactive nodes section. In the latter case we do this by + * setting nodeIdToInsertInsideOrAfter to 'inactiveSteps'. + */ + if (inactiveNodes != null && inactiveNodes.length > 0) { + nodeIdToInsertInsideOrAfter = inactiveNodes[inactiveNodes.length - 1]; + } else { + nodeIdToInsertInsideOrAfter = 'inactiveSteps'; + } } - /* - * Update the nodeIdToInsertInsideOrAfter so that when we are - * importing multiple steps, the steps get placed in the correct - * order. - * - * Example - * We are importing nodeA and nodeB and want to place them after - * nodeX. Therefore we want the order to be - * - * nodeX - * nodeA - * nodeB - * - * This means after we add nodeA, we must update - * nodeIdToInsertInsideOrAfter to be nodeA so that when we add - * nodeB, it will be placed after nodeA. - */ - nodeIdToInsertInsideOrAfter = newNode.id; - } - return newNodes; - }); + for (const newNode of newNodes) { + if (this.isGroupNode(nodeIdToInsertInsideOrAfter)) { + this.createNodeInside(newNode, nodeIdToInsertInsideOrAfter); + } else { + this.createNodeAfter(newNode, nodeIdToInsertInsideOrAfter); + } + + /* + * Update the nodeIdToInsertInsideOrAfter so that when we are + * importing multiple steps, the steps get placed in the correct + * order. + * + * Example + * We are importing nodeA and nodeB and want to place them after + * nodeX. Therefore we want the order to be + * + * nodeX + * nodeA + * nodeB + * + * This means after we add nodeA, we must update + * nodeIdToInsertInsideOrAfter to be nodeA so that when we add + * nodeB, it will be placed after nodeA. + */ + nodeIdToInsertInsideOrAfter = newNode.id; + } + return newNodes; + }); } /** @@ -868,28 +873,28 @@ export class TeacherProjectService extends ProjectService { * new name and change all the references in the steps to use the new * name. */ - return this.http.post(this.ConfigService.getConfigParam('importStepsURL'), - { + return this.http + .post(this.ConfigService.getConfigParam('importStepsURL'), { steps: angular.toJson(newComponents), fromProjectId: importProjectId, toProjectId: this.ConfigService.getConfigParam('projectId') }) .toPromise() .then((newComponents: any) => { - const node = this.getNodeById(nodeId); - let insertPosition = 0; - if (insertAfterComponentId == null) { - insertPosition = 0; - } else { - insertPosition = - this.getComponentPositionByNodeIdAndComponentId(nodeId, insertAfterComponentId) + 1; - } - for (const newComponent of newComponents) { - node.components.splice(insertPosition, 0, newComponent); - insertPosition += 1; - } - return newComponents; - }); + const node = this.getNodeById(nodeId); + let insertPosition = 0; + if (insertAfterComponentId == null) { + insertPosition = 0; + } else { + insertPosition = + this.getComponentPositionByNodeIdAndComponentId(nodeId, insertAfterComponentId) + 1; + } + for (const newComponent of newComponents) { + node.components.splice(insertPosition, 0, newComponent); + insertPosition += 1; + } + return newComponents; + }); } /** @@ -1201,14 +1206,14 @@ export class TeacherProjectService extends ProjectService { return this.http .get(this.ConfigService.getConfigParam('getLibraryProjectsURL')) .toPromise() - .then(projects => { + .then((projects) => { return projects; }); } sortAndFilterUniqueLibraryProjects(libraryProjects) { const flatProjectList = libraryProjects - .map(grade => { + .map((grade) => { return grade.children; }) .flat(); @@ -1289,22 +1294,26 @@ export class TeacherProjectService extends ProjectService { action: 'makeThisNodeNotVisitable', targetId: node.id, removalConditional: 'any', - removalCriteria: [{ - 'name': 'teacherRemoval', - 'params': { - periodId: periodId + removalCriteria: [ + { + name: 'teacherRemoval', + params: { + periodId: periodId + } } - }] + ] }; this.addConstraintToNode(node, lockConstraint); } removeTeacherRemovalConstraint(node: any, periodId: number) { - node.constraints = node.constraints.filter(constraint => { - return !(constraint.action === 'makeThisNodeNotVisitable' && - constraint.targetId === node.id && - constraint.removalCriteria[0].name === 'teacherRemoval' && - constraint.removalCriteria[0].params.periodId === periodId); + node.constraints = node.constraints.filter((constraint) => { + return !( + constraint.action === 'makeThisNodeNotVisitable' && + constraint.targetId === node.id && + constraint.removalCriteria[0].name === 'teacherRemoval' && + constraint.removalCriteria[0].params.periodId === periodId + ); }); } @@ -1324,5 +1333,4 @@ export class TeacherProjectService extends ProjectService { escapeToClose: true }); } - } diff --git a/src/main/webapp/wise5/services/teacherWebSocketService.ts b/src/main/webapp/wise5/services/teacherWebSocketService.ts index 8c1e27398f..f6a898228d 100644 --- a/src/main/webapp/wise5/services/teacherWebSocketService.ts +++ b/src/main/webapp/wise5/services/teacherWebSocketService.ts @@ -1,31 +1,30 @@ 'use strict'; -import { Injectable } from "@angular/core"; -import { ConfigService } from "./configService"; -import { StudentStatusService } from "./studentStatusService"; -import { UpgradeModule } from "@angular/upgrade/static"; -import { NotificationService } from "./notificationService"; -import { Observable, Subject } from "rxjs"; -import { AchievementService } from "./achievementService"; +import { Injectable } from '@angular/core'; +import { ConfigService } from './configService'; +import { StudentStatusService } from './studentStatusService'; +import { UpgradeModule } from '@angular/upgrade/static'; +import { NotificationService } from './notificationService'; +import { Observable, Subject } from 'rxjs'; +import { AchievementService } from './achievementService'; @Injectable() export class TeacherWebSocketService { - runId: number; rootScope: any; stomp: any; private newAnnotationReceivedSource: Subject = new Subject(); public newAnnotationReceived$: Observable = this.newAnnotationReceivedSource.asObservable(); private newStudentWorkReceivedSource: Subject = new Subject(); - public newStudentWorkReceived$: Observable = - this.newStudentWorkReceivedSource.asObservable(); + public newStudentWorkReceived$: Observable = this.newStudentWorkReceivedSource.asObservable(); constructor( - private upgrade: UpgradeModule, - private AchievementService: AchievementService, - private ConfigService: ConfigService, - private NotificationService: NotificationService, - private StudentStatusService: StudentStatusService) { + private upgrade: UpgradeModule, + private AchievementService: AchievementService, + private ConfigService: ConfigService, + private NotificationService: NotificationService, + private StudentStatusService: StudentStatusService + ) { if (this.upgrade.$injector != null) { this.initializeStomp(); } @@ -50,11 +49,13 @@ export class TeacherWebSocketService { initialize() { this.runId = this.ConfigService.getRunId(); try { - this.getStomp().connect(this.ConfigService.getWebSocketURL()).then((frame) => { - this.subscribeToTeacherTopic(); - this.subscribeToTeacherWorkgroupTopic(); - }); - } catch(e) { + this.getStomp() + .connect(this.ConfigService.getWebSocketURL()) + .then((frame) => { + this.subscribeToTeacherTopic(); + this.subscribeToTeacherWorkgroupTopic(); + }); + } catch (e) { console.log(e); } } @@ -63,17 +64,17 @@ export class TeacherWebSocketService { this.getStomp().subscribe(`/topic/teacher/${this.runId}`, (message, headers, res) => { if (message.type === 'studentWork') { const studentWork = JSON.parse(message.content); - this.broadcastNewStudentWorkReceived({studentWork: studentWork}); + this.broadcastNewStudentWorkReceived({ studentWork: studentWork }); } else if (message.type === 'studentStatus') { const status = JSON.parse(message.content); this.StudentStatusService.setStudentStatus(status); - this.StudentStatusService.broadcastStudentStatusReceived({studentStatus: status}); + this.StudentStatusService.broadcastStudentStatusReceived({ studentStatus: status }); } else if (message.type === 'newStudentAchievement') { const achievement = JSON.parse(message.content); - this.AchievementService.broadcastNewStudentAchievement({studentAchievement: achievement}); + this.AchievementService.broadcastNewStudentAchievement({ studentAchievement: achievement }); } else if (message.type === 'annotation') { const annotationData = JSON.parse(message.content); - this.broadcastNewAnnotationReceived({annotation: annotationData}); + this.broadcastNewAnnotationReceived({ annotation: annotationData }); } }); } @@ -87,12 +88,14 @@ export class TeacherWebSocketService { } subscribeToTeacherWorkgroupTopic() { - this.getStomp().subscribe(`/topic/workgroup/${this.ConfigService.getWorkgroupId()}`, - (message, headers, res) => { - if (message.type === 'notification') { - this.NotificationService.addNotification(JSON.parse(message.content)); + this.getStomp().subscribe( + `/topic/workgroup/${this.ConfigService.getWorkgroupId()}`, + (message, headers, res) => { + if (message.type === 'notification') { + this.NotificationService.addNotification(JSON.parse(message.content)); + } } - }); + ); } pauseScreens(periodId) { diff --git a/src/main/webapp/wise5/services/utilService.ts b/src/main/webapp/wise5/services/utilService.ts index 659aeda6a4..34bc10a4f7 100644 --- a/src/main/webapp/wise5/services/utilService.ts +++ b/src/main/webapp/wise5/services/utilService.ts @@ -8,12 +8,46 @@ import '../lib/jquery/jquery-global'; @Injectable() export class UtilService { componentTypeToLabel = {}; - CHARS = [ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', - 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' + CHARS = [ + 'a', + 'b', + 'c', + 'd', + 'e', + 'f', + 'g', + 'h', + 'i', + 'j', + 'k', + 'l', + 'm', + 'n', + 'o', + 'p', + 'q', + 'r', + 's', + 't', + 'u', + 'v', + 'w', + 'x', + 'y', + 'z', + '0', + '1', + '2', + '3', + '4', + '5', + '6', + '7', + '8', + '9' ]; - constructor(private upgrade: UpgradeModule) { - } + constructor(private upgrade: UpgradeModule) {} broadcastEventInRootScope(event, data = {}) { this.upgrade.$injector.get('$rootScope').$broadcast(event, data); @@ -62,10 +96,7 @@ export class UtilService { let byteString; if (dataURI.split(',')[0].indexOf('base64') >= 0) byteString = atob(dataURI.split(',')[1]); else byteString = unescape(dataURI.split(',')[1]); - const mimeString = dataURI - .split(',')[0] - .split(':')[1] - .split(';')[0]; + const mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0]; const ia = new Uint8Array(byteString.length); for (let i = 0; i < byteString.length; i++) { ia[i] = byteString.charCodeAt(i); @@ -277,7 +308,13 @@ export class UtilService { let newElement = null; if (type == 'link') { newElement = - "' + linkText + ''; + "' + + linkText + + ''; } else if (type == 'button') { newElement = " + {{filteredOpenEducationalResources.length}} Item(s) Found +
+ +
+
+ + + {{openEducationalResource.metadata.title}} + +
+
+ + + check_circle + + + + Info + + + +
+
+
+ No results found +
+
+ +
+ + URL + + +
+
+ + Width (px) (optional) + + + + Height (px) + + + + +
diff --git a/src/main/webapp/wise5/components/outsideURL/outside-url-authoring/outside-url-authoring.component.scss b/src/main/webapp/wise5/components/outsideURL/outside-url-authoring/outside-url-authoring.component.scss new file mode 100644 index 0000000000..7d40d10950 --- /dev/null +++ b/src/main/webapp/wise5/components/outsideURL/outside-url-authoring/outside-url-authoring.component.scss @@ -0,0 +1,78 @@ +label.mat-slide-toggle-label { + display: flex; +} + +.show-oer-toggle { + margin-bottom: 15px; +} + +.select-oer-container { + width: 100%; +} + +p.select-oer-instructions { + font-size: 14px; + font-weight: 500; + letter-spacing: .01em; + line-height: 24px; +} + +.topic-filter-container { + max-width: 250px; + margin-right: 20px; +} + +.search-container { + max-width: 250px; + margin-right: 20px; +} + +.clear-button { + margin-right: 15px; +} + +.oer-cards-container { + height: 600px; + overflow-y: auto; + padding: 8px; + margin-bottom: 16px; +} + +.oer-card { + margin: 6px; +} + +.oer-card-title { + font-size: 14px; +} + +.oer-card-image { + margin-left: -16px; + margin-right: -16px; + position: relative; + padding-top: 60%; + background-size: cover; + background-position: center; +} + +.oer-card-actions-spacer { + flex: 1 1 auto; +} + +.oer-info-link { + margin: 0 16px; +} + +.no-results-message-container { + width: 100%; + margin-top: 20px; +} + +.url-input-container { + width: 100%; +} + +.dimension-input-container { + margin-right: 20px; + width: 150px; +} \ No newline at end of file diff --git a/src/main/webapp/wise5/components/outsideURL/outside-url-authoring/outside-url-authoring.component.ts b/src/main/webapp/wise5/components/outsideURL/outside-url-authoring/outside-url-authoring.component.ts new file mode 100644 index 0000000000..5bd81b665f --- /dev/null +++ b/src/main/webapp/wise5/components/outsideURL/outside-url-authoring/outside-url-authoring.component.ts @@ -0,0 +1,146 @@ +import { Component, ViewEncapsulation } from '@angular/core'; +import { Subject, Subscription } from 'rxjs'; +import { debounceTime, distinctUntilChanged } from 'rxjs/operators'; +import { ComponentAuthoring } from '../../../authoringTool/components/component-authoring.component'; +import { ConfigService } from '../../../services/configService'; +import { NodeService } from '../../../services/nodeService'; +import { TeacherProjectService } from '../../../services/teacherProjectService'; +import { OutsideURLService } from '../outsideURLService'; + +@Component({ + selector: 'outside-url-authoring', + templateUrl: 'outside-url-authoring.component.html', + styleUrls: ['outside-url-authoring.component.scss'], + encapsulation: ViewEncapsulation.None +}) +export class OutsideUrlAuthoring extends ComponentAuthoring { + isShowOERs: boolean; + allOpenEducationalResources: any[]; + filteredOpenEducationalResources: any[]; + outsideURLIFrameId: string; + subjects: any[]; + searchText: string; + selectedSubjects: any[]; + urlChanged: Subject = new Subject(); + widthChanged: Subject = new Subject(); + heightChanged: Subject = new Subject(); + urlChangedSubscription: Subscription; + widthChangedSubscription: Subscription; + heightChangedSubscription: Subscription; + + constructor( + protected ConfigService: ConfigService, + protected NodeService: NodeService, + protected OutsideURLService: OutsideURLService, + protected ProjectService: TeacherProjectService + ) { + super(ConfigService, NodeService, ProjectService); + } + + ngOnInit() { + super.ngOnInit(); + this.outsideURLIFrameId = 'outsideResource_' + this.componentId; + this.isShowOERs = this.componentContent.url === ''; + this.subjects = [ + { + value: 'Earth and Space Sciences', + label: $localize`Earth and Space Sciences` + }, + { + value: 'Life Sciences', + label: $localize`Life Sciences` + }, + { + value: 'Physical Sciences', + label: $localize`Physical Sciences` + }, + { + value: 'Engineering, Technology, and Applications of Science', + label: $localize`Engineering, Technology, and Applications of Science` + } + ]; + this.searchText = ''; + this.selectedSubjects = []; + this.OutsideURLService.getOpenEducationalResources().then((openEducationalResources: any) => { + this.allOpenEducationalResources = openEducationalResources.sort((a, b) => + a.metadata.title > b.metadata.title ? 1 : -1 + ); + this.filteredOpenEducationalResources = this.allOpenEducationalResources; + }); + this.urlChangedSubscription = this.urlChanged + .pipe(debounceTime(1000), distinctUntilChanged()) + .subscribe((url: string) => { + this.authoringComponentContent.url = url; + this.authoringComponentContent.info = null; + this.componentChanged(); + }); + this.widthChangedSubscription = this.widthChanged + .pipe(debounceTime(1000), distinctUntilChanged()) + .subscribe((width: string) => { + this.componentChanged(); + }); + this.heightChangedSubscription = this.heightChanged + .pipe(debounceTime(1000), distinctUntilChanged()) + .subscribe((height: string) => { + this.componentChanged(); + }); + } + + ngOnDestroy() { + this.urlChangedSubscription.unsubscribe(); + this.widthChangedSubscription.unsubscribe(); + this.heightChangedSubscription.unsubscribe(); + } + + chooseOpenEducationalResource(openEducationalResource: any): void { + this.authoringComponentContent.url = openEducationalResource.url; + this.authoringComponentContent.info = openEducationalResource.info; + this.componentChanged(); + } + + isResourceSelected(resourceUrl: string): boolean { + return resourceUrl === this.authoringComponentContent.url; + } + + reloadResource(): void { + const iframe: any = document.getElementById(this.outsideURLIFrameId); + iframe.src = ''; + iframe.src = this.authoringComponentContent.url; + } + + clearFilters(): void { + this.searchText = ''; + this.selectedSubjects = []; + this.searchFieldChanged(); + } + + searchFieldChanged(): void { + this.filteredOpenEducationalResources = this.allOpenEducationalResources.filter((oer) => { + if (!this.isTextMatch(this.searchText, JSON.stringify(oer))) { + return false; + } + if (this.isAnySubjectChosen()) { + return this.isSubjectMatch(this.selectedSubjects, oer); + } else { + return true; + } + }); + } + + isTextMatch(searchText: string, testText: string): boolean { + return testText.toLowerCase().indexOf(searchText.toLowerCase()) !== -1; + } + + isAnySubjectChosen(): boolean { + return this.selectedSubjects.length > 0; + } + + isSubjectMatch(selectedSubjects: any[], resource: any): boolean { + for (const subject of selectedSubjects) { + if (resource.metadata.subjects.includes(subject)) { + return true; + } + } + return false; + } +} diff --git a/src/main/webapp/wise5/components/outsideURL/outsideURLAuthoringComponentModule.ts b/src/main/webapp/wise5/components/outsideURL/outsideURLAuthoringComponentModule.ts index 56cd213690..16ab2813fb 100644 --- a/src/main/webapp/wise5/components/outsideURL/outsideURLAuthoringComponentModule.ts +++ b/src/main/webapp/wise5/components/outsideURL/outsideURLAuthoringComponentModule.ts @@ -3,13 +3,16 @@ import * as angular from 'angular'; import { downgradeComponent, downgradeInjectable } from '@angular/upgrade/static'; import { OutsideURLService } from './outsideURLService'; -import OutsideURLAuthoring from './outsideURLAuthoring'; import { EditOutsideUrlAdvancedComponent } from './edit-outside-url-advanced/edit-outside-url-advanced.component'; +import { OutsideUrlAuthoring } from './outside-url-authoring/outside-url-authoring.component'; const outsideURLAuthoringComponentModule = angular .module('outsideURLAuthoringComponentModule', []) .service('OutsideURLService', downgradeInjectable(OutsideURLService)) - .component('outsideUrlAuthoring', OutsideURLAuthoring) + .directive( + 'outsideUrlAuthoring', + downgradeComponent({ component: OutsideUrlAuthoring }) as angular.IDirectiveFactory + ) .directive( 'editOutsideUrlAdvanced', downgradeComponent({ component: EditOutsideUrlAdvancedComponent }) as angular.IDirectiveFactory From 798fa6e3d14fdd40740bc49e314038e229e58178 Mon Sep 17 00:00:00 2001 From: Geoffrey Kwan Date: Fri, 8 Jan 2021 15:55:44 -0500 Subject: [PATCH 05/26] Removed old Outside URL authoring files. #2763 --- .../components/outsideURL/authoring.html | 106 ------------ .../outsideURL/outsideURLAuthoring.ts | 152 ------------------ 2 files changed, 258 deletions(-) delete mode 100644 src/main/webapp/wise5/components/outsideURL/authoring.html delete mode 100644 src/main/webapp/wise5/components/outsideURL/outsideURLAuthoring.ts diff --git a/src/main/webapp/wise5/components/outsideURL/authoring.html b/src/main/webapp/wise5/components/outsideURL/authoring.html deleted file mode 100644 index bb49793333..0000000000 --- a/src/main/webapp/wise5/components/outsideURL/authoring.html +++ /dev/null @@ -1,106 +0,0 @@ - - - {{ ::'outsideURL.showOERs' | translate }} - - -
-

{{ ::'outsideURL.OERInfo' | translate }}

-
-
- - - - - - {{subject.label}} - - - - -
-
- - - - - clear - {{ ::'outsideURL.clearAll' | translate }} - -
-
-
-
- - - - {{openEducationalResource.metadata.title}} - - -
- - - check_circle - - - {{ ::'outsideURL.info' | translate }} - - {{ ::'select' | translate }} - - -
-
-

{{ ::'outsideURL.noResults' | translate }}

-
-
- - - - -
- - - - - - - - - - - {{ ::'outsideURL.reloadResource' | translate }} - refresh - -
diff --git a/src/main/webapp/wise5/components/outsideURL/outsideURLAuthoring.ts b/src/main/webapp/wise5/components/outsideURL/outsideURLAuthoring.ts deleted file mode 100644 index 2b8edf63c6..0000000000 --- a/src/main/webapp/wise5/components/outsideURL/outsideURLAuthoring.ts +++ /dev/null @@ -1,152 +0,0 @@ -'use strict'; - -import { Directive } from '@angular/core'; -import { EditComponentController } from '../../authoringTool/components/editComponentController'; -import { OutsideURLService } from './outsideURLService'; - -@Directive() -class OutsideURLAuthoringController extends EditComponentController { - height: string; - info: string; - isShowOERs: boolean; - openEducationalResources: any[]; - outsideURLIFrameId: string; - subjects: any[]; - searchText: string; - selectedSubjects: any[]; - url: string; - width: string; - - static $inject = [ - '$filter', - '$sce', - 'ConfigService', - 'NodeService', - 'NotificationService', - 'OutsideURLService', - 'ProjectAssetService', - 'ProjectService', - 'UtilService' - ]; - - constructor( - $filter, - private $sce: any, - ConfigService, - NodeService, - NotificationService, - private OutsideURLService: OutsideURLService, - ProjectAssetService, - ProjectService, - UtilService - ) { - super( - $filter, - ConfigService, - NodeService, - NotificationService, - ProjectAssetService, - ProjectService, - UtilService - ); - } - - $onInit() { - super.$onInit(); - this.$translate = this.$filter('translate'); - this.isShowOERs = this.componentContent.url === ''; - this.subjects = [ - { - value: 'Earth and Space Sciences', - label: this.$translate('outsideURL.ESS') - }, - { - value: 'Life Sciences', - label: this.$translate('outsideURL.LS') - }, - { - value: 'Physical Sciences', - label: this.$translate('outsideURL.PS') - }, - { - value: 'Engineering, Technology, and Applications of Science', - label: this.$translate('outsideURL.ETS') - } - ]; - this.searchText = ''; - this.selectedSubjects = []; - this.OutsideURLService.getOpenEducationalResources().then((openEducationalResources: any) => { - this.openEducationalResources = openEducationalResources.sort((a, b) => - a.metadata.title > b.metadata.title ? 1 : -1 - ); - }); - } - - setURL(url) { - if (url == null || url === '') { - this.url = ' '; - } else { - this.url = this.$sce.trustAsResourceUrl(url); - } - } - - setInfo(info) { - if (info == null || info === '') { - this.info = this.url; - } else { - this.info = this.$sce.trustAsResourceUrl(info); - } - } - - setWidthAndHeight(width, height) { - this.width = width ? width + 'px' : '100%'; - this.height = height ? height + 'px' : '600px'; - } - - urlInputChanged() { - this.authoringComponentContent.info = null; - this.componentChanged(); - } - - populateOpenEducationalResourceURL(openEducationalResource) { - this.authoringComponentContent.url = openEducationalResource.url; - this.authoringComponentContent.info = openEducationalResource.info; - this.componentChanged(); - } - - isResourceSelected(resourceUrl) { - return resourceUrl === this.authoringComponentContent.url; - } - - reloadResource() { - const iframe: any = document.getElementById(this.outsideURLIFrameId); - iframe.src = ''; - iframe.src = this.authoringComponentContent.url; - } - - isSubjectMatch(resource) { - for (const subject of this.selectedSubjects) { - if (resource.metadata.subjects.includes(subject)) { - return true; - } - } - return false; - } - - clearFilters() { - this.searchText = ''; - this.selectedSubjects = []; - } -} - -const OutsideURLAuthoring = { - bindings: { - nodeId: '@', - componentId: '@' - }, - controller: OutsideURLAuthoringController, - controllerAs: 'outsideURLController', - templateUrl: 'wise5/components/outsideURL/authoring.html' -}; - -export default OutsideURLAuthoring; From a31c1d98e0ee5807b30845320ee5e64983f0248c Mon Sep 17 00:00:00 2001 From: Hiroki Terashima Date: Mon, 11 Jan 2021 12:02:50 -0800 Subject: [PATCH 06/26] Refactored MilestoneService and tests. Reduced number of duplicate code for better readability. #2872 --- .../src/app/services/milestoneService.spec.ts | 924 +++++++++--------- .../webapp/wise5/services/milestoneService.ts | 204 ++-- src/main/webapp/wise5/services/utilService.ts | 24 + 3 files changed, 522 insertions(+), 630 deletions(-) diff --git a/src/main/webapp/site/src/app/services/milestoneService.spec.ts b/src/main/webapp/site/src/app/services/milestoneService.spec.ts index 1b604bcc80..a9d7ec91b9 100644 --- a/src/main/webapp/site/src/app/services/milestoneService.spec.ts +++ b/src/main/webapp/site/src/app/services/milestoneService.spec.ts @@ -1,5 +1,4 @@ import * as angular from 'angular'; -import * as moment from 'moment'; import { MilestoneService } from '../../../../wise5/services/milestoneService'; import { TestBed } from '@angular/core/testing'; import { HttpClientTestingModule } from '@angular/common/http/testing'; @@ -98,18 +97,8 @@ describe('MilestoneService', () => { chooseTemplate(); isTemplateMatch(); isTemplateCriterionSatisfied(); - isPercentOfScoresGreaterThan(); - getGreaterThanSum(); - isPercentOfScoresGreaterThanOrEqualTo(); - getGreaterThanOrEqualToSum(); - isPercentOfScoresLessThan(); - getLessThanSum(); - isPercentOfScoresLessThanOrEqualTo(); - getLessThanOrEqualToSum(); - isPercentOfScoresEqualTo(); - getEqualToSum(); - isPercentOfScoresNotEqualTo(); - getNotEqualToSum(); + isPercentOfScoresSatisfiesComparator(); + getComparatorSum(); getAggregateData(); getPossibleScores(); isPercentThresholdSatisfied(); @@ -122,7 +111,6 @@ describe('MilestoneService', () => { getPossibleScoreValueCounts(); processMilestoneGraphsAndData(); setReportAvailable(); - clearTempFields(); }); function createScoreCounts(counts: any[]) { @@ -715,479 +703,466 @@ function isTemplateCriterionSatisfied() { }); } -function isPercentOfScoresGreaterThan() { - describe('isPercentOfScoresGreaterThan()', () => { - it('should check is percent of scores greater than false', () => { - const satisfyCriterion = createSatisfyCriteria( - 'node1', - 'component1', - 'ki', - 'percentOfScoresGreaterThan', - 3, - 50 - ); - const aggregateAutoScores = { - component1: { - ki: { - counts: createScoreCounts([10, 10, 10, 10, 10]), - scoreCount: 50 - } - } - }; - expect(service.isPercentOfScoresGreaterThan(satisfyCriterion, aggregateAutoScores)).toEqual( - false - ); - }); - it('should check is percent of scores greater than true', () => { - const satisfyCriterion = createSatisfyCriteria( - 'node1', - 'component1', - 'ki', - 'percentOfScoresGreaterThan', - 2, - 50 - ); - const aggregateAutoScores = { - component1: { - ki: { - counts: createScoreCounts([10, 10, 10, 10, 10]), - scoreCount: 50 - } - } - }; - expect(service.isPercentOfScoresGreaterThan(satisfyCriterion, aggregateAutoScores)).toEqual( - true - ); - }); +function isPercentOfScoresSatisfiesComparator() { + describe('isPercentOfScoresSatisfiesComparator()', () => { + isPercentOfScoresGreaterThan(); + isPercentOfScoresGreaterThanOrEqualTo(); + isPercentOfScoresLessThan(); + isPercentOfScoresLessThanOrEqualTo(); + isPercentOfScoresEqualTo(); + isPercentOfScoresNotEqualTo(); }); } -function getGreaterThanSum() { - describe('getGreaterThanSum()', () => { - it('should get greater than sum with score 1', () => { - const satisfyCriterion = { value: 1 }; - const aggregateData = { - counts: createScoreCounts([10, 20, 30, 40, 50]) - }; - const possibleScores = [1, 2, 3, 4, 5]; - expect(service.getGreaterThanSum(satisfyCriterion, aggregateData, possibleScores)).toEqual( - 140 - ); - }); - it('should get greater than sum with score 2', () => { - const satisfyCriterion = { value: 2 }; - const aggregateData = { - counts: createScoreCounts([10, 20, 30, 40, 50]) - }; - const possibleScores = [1, 2, 3, 4, 5]; - expect(service.getGreaterThanSum(satisfyCriterion, aggregateData, possibleScores)).toEqual( - 120 - ); - }); - it('should get greater than sum with score 3', () => { - const satisfyCriterion = { value: 3 }; - const aggregateData = { - counts: createScoreCounts([10, 20, 30, 40, 50]) - }; - const possibleScores = [1, 2, 3, 4, 5]; - expect(service.getGreaterThanSum(satisfyCriterion, aggregateData, possibleScores)).toEqual( - 90 - ); - }); - it('should get greater than sum with score 4', () => { - const satisfyCriterion = { value: 4 }; - const aggregateData = { - counts: createScoreCounts([10, 20, 30, 40, 50]) - }; - const possibleScores = [1, 2, 3, 4, 5]; - expect(service.getGreaterThanSum(satisfyCriterion, aggregateData, possibleScores)).toEqual( - 50 - ); - }); +function isPercentOfScoresGreaterThan() { + const aggregateAutoScores = { + component1: { + ki: { + counts: createScoreCounts([10, 10, 10, 10, 10]), + scoreCount: 50 + } + } + }; + it('should check is percent of scores greater than false', () => { + const satisfyCriterion = createSatisfyCriteria( + 'node1', + 'component1', + 'ki', + 'percentOfScoresGreaterThan', + 3, + 50 + ); + expect( + service.isPercentOfScoresSatisfiesComparator( + satisfyCriterion, + aggregateAutoScores, + utilService.greaterThan + ) + ).toEqual(false); + }); + it('should check is percent of scores greater than true', () => { + const satisfyCriterion = createSatisfyCriteria( + 'node1', + 'component1', + 'ki', + 'percentOfScoresGreaterThan', + 2, + 50 + ); + expect( + service.isPercentOfScoresSatisfiesComparator( + satisfyCriterion, + aggregateAutoScores, + utilService.greaterThan + ) + ).toEqual(true); }); } function isPercentOfScoresGreaterThanOrEqualTo() { - describe('isPercentOfScoresGreaterThanOrEqualTo()', () => { - it('should check is percent of scores greater than or equal to false', () => { - const satisfyCriterion = createSatisfyCriteria( - 'node1', - 'component1', - 'ki', - 'percentOfScoresGreaterThanOrEqualTo', - 4, - 50 - ); - const aggregateAutoScores = { - component1: { - ki: { - counts: createScoreCounts([10, 10, 10, 10, 10]), - scoreCount: 50 - } - } - }; - expect( - service.isPercentOfScoresGreaterThanOrEqualTo(satisfyCriterion, aggregateAutoScores) - ).toEqual(false); - }); - it('should check is percent of scores greater than or equal to true', () => { - const satisfyCriterion = createSatisfyCriteria( - 'node1', - 'component1', - 'ki', - 'percentOfScoresGreaterThanOrEqualTo', - 3, - 50 - ); - const aggregateAutoScores = { - component1: { - ki: { - counts: createScoreCounts([10, 10, 10, 10, 10]), - scoreCount: 50 - } - } - }; - expect( - service.isPercentOfScoresGreaterThanOrEqualTo(satisfyCriterion, aggregateAutoScores) - ).toEqual(true); - }); + const aggregateAutoScores = { + component1: { + ki: { + counts: createScoreCounts([10, 10, 10, 10, 10]), + scoreCount: 50 + } + } + }; + it('should check is percent of scores greater than or equal to false', () => { + const satisfyCriterion = createSatisfyCriteria( + 'node1', + 'component1', + 'ki', + 'percentOfScoresGreaterThanOrEqualTo', + 4, + 50 + ); + expect( + service.isPercentOfScoresSatisfiesComparator( + satisfyCriterion, + aggregateAutoScores, + utilService.greaterThanEqualTo + ) + ).toEqual(false); }); -} - -function getGreaterThanOrEqualToSum() { - describe('getGreaterThanOrEqualToSum()', () => { - it('should get greater than or equal to sum with score 1', () => { - const satisfyCriterion = { value: 1 }; - const aggregateData = { - counts: createScoreCounts([10, 20, 30, 40, 50]) - }; - const possibleScores = [1, 2, 3, 4, 5]; - expect( - service.getGreaterThanOrEqualToSum(satisfyCriterion, aggregateData, possibleScores) - ).toEqual(150); - }); - it('should get greater than or equal to sum with score 2', () => { - const satisfyCriterion = { value: 2 }; - const aggregateData = { - counts: createScoreCounts([10, 20, 30, 40, 50]) - }; - const possibleScores = [1, 2, 3, 4, 5]; - expect( - service.getGreaterThanOrEqualToSum(satisfyCriterion, aggregateData, possibleScores) - ).toEqual(140); - }); - it('should get greater than or equal to sum with score 3', () => { - const satisfyCriterion = { value: 3 }; - const aggregateData = { - counts: createScoreCounts([10, 20, 30, 40, 50]) - }; - const possibleScores = [1, 2, 3, 4, 5]; - expect( - service.getGreaterThanOrEqualToSum(satisfyCriterion, aggregateData, possibleScores) - ).toEqual(120); - }); - it('should get greater than or equal to sum with score 4', () => { - const satisfyCriterion = { value: 4 }; - const aggregateData = { - counts: createScoreCounts([10, 20, 30, 40, 50]) - }; - const possibleScores = [1, 2, 3, 4, 5]; - expect( - service.getGreaterThanOrEqualToSum(satisfyCriterion, aggregateData, possibleScores) - ).toEqual(90); - }); - it('should get greater than or equal to sum with score 5', () => { - const satisfyCriterion = { value: 5 }; - const aggregateData = { - counts: createScoreCounts([10, 20, 30, 40, 50]) - }; - const possibleScores = [1, 2, 3, 4, 5]; - expect( - service.getGreaterThanOrEqualToSum(satisfyCriterion, aggregateData, possibleScores) - ).toEqual(50); - }); + it('should check is percent of scores greater than or equal to true', () => { + const satisfyCriterion = createSatisfyCriteria( + 'node1', + 'component1', + 'ki', + 'percentOfScoresGreaterThanOrEqualTo', + 3, + 50 + ); + expect( + service.isPercentOfScoresSatisfiesComparator( + satisfyCriterion, + aggregateAutoScores, + utilService.greaterThanEqualTo + ) + ).toEqual(true); }); } function isPercentOfScoresLessThan() { - describe('isPercentOfScoresLessThan()', () => { - it('should check is percent of scores less than false', () => { - const satisfyCriterion = createSatisfyCriteria( - 'node1', - 'component1', - 'ki', - 'percentOfScoresLessThan', - 3, - 50 - ); - const aggregateAutoScores = { - component1: { - ki: { - counts: createScoreCounts([10, 10, 10, 10, 10]), - scoreCount: 50 - } - } - }; - expect(service.isPercentOfScoresLessThan(satisfyCriterion, aggregateAutoScores)).toEqual( - false - ); - }); - it('should check is percent of scores less than true', () => { - const satisfyCriterion = createSatisfyCriteria( - 'node1', - 'component1', - 'ki', - 'percentOfScoresLessThan', - 4, - 50 - ); - const aggregateAutoScores = { - component1: { - ki: { - counts: createScoreCounts([10, 10, 10, 10, 10]), - scoreCount: 50 - } - } - }; - expect(service.isPercentOfScoresLessThan(satisfyCriterion, aggregateAutoScores)).toEqual( - true - ); - }); + const aggregateAutoScores = { + component1: { + ki: { + counts: createScoreCounts([10, 10, 10, 10, 10]), + scoreCount: 50 + } + } + }; + it('should check is percent of scores less than false', () => { + const satisfyCriterion = createSatisfyCriteria( + 'node1', + 'component1', + 'ki', + 'percentOfScoresLessThan', + 3, + 50 + ); + expect( + service.isPercentOfScoresSatisfiesComparator( + satisfyCriterion, + aggregateAutoScores, + utilService.lessThan + ) + ).toEqual(false); + }); + it('should check is percent of scores less than true', () => { + const satisfyCriterion = createSatisfyCriteria( + 'node1', + 'component1', + 'ki', + 'percentOfScoresLessThan', + 4, + 50 + ); + expect( + service.isPercentOfScoresSatisfiesComparator( + satisfyCriterion, + aggregateAutoScores, + utilService.lessThan + ) + ).toEqual(true); }); } -function getLessThanSum() { - describe('getLessThanSum()', () => { - it('should get less than sum with score 2', () => { - const satisfyCriterion = { value: 2 }; - const aggregateData = { - counts: createScoreCounts([10, 20, 30, 40, 50]) - }; - const possibleScores = [1, 2, 3, 4, 5]; - expect(service.getLessThanSum(satisfyCriterion, aggregateData, possibleScores)).toEqual(10); - }); - it('should get less than sum with score 3', () => { - const satisfyCriterion = { value: 3 }; - const aggregateData = { - counts: createScoreCounts([10, 20, 30, 40, 50]) - }; - const possibleScores = [1, 2, 3, 4, 5]; - expect(service.getLessThanSum(satisfyCriterion, aggregateData, possibleScores)).toEqual(30); - }); - it('should get less than sum with score 4', () => { - const satisfyCriterion = { value: 4 }; - const aggregateData = { - counts: createScoreCounts([10, 20, 30, 40, 50]) - }; - const possibleScores = [1, 2, 3, 4, 5]; - expect(service.getLessThanSum(satisfyCriterion, aggregateData, possibleScores)).toEqual(60); - }); - it('should get less than sum with score 5', () => { - const satisfyCriterion = { value: 5 }; - const aggregateData = { - counts: createScoreCounts([10, 20, 30, 40, 50]) - }; - const possibleScores = [1, 2, 3, 4, 5]; - expect(service.getLessThanSum(satisfyCriterion, aggregateData, possibleScores)).toEqual(100); - }); +function isPercentOfScoresLessThanOrEqualTo() { + const aggregateAutoScores = { + component1: { + ki: { + counts: createScoreCounts([10, 10, 10, 10, 10]), + scoreCount: 50 + } + } + }; + it('should check is percent of scores less than or equal to false', () => { + const satisfyCriterion = createSatisfyCriteria( + 'node1', + 'component1', + 'ki', + 'percentOfScoresLessThanOrEqualTo', + 2, + 50 + ); + expect( + service.isPercentOfScoresSatisfiesComparator( + satisfyCriterion, + aggregateAutoScores, + utilService.lessThanEqualTo + ) + ).toEqual(false); + }); + it('should check is percent of scores less than or equal to true', () => { + const satisfyCriterion = createSatisfyCriteria( + 'node1', + 'component1', + 'ki', + 'percentOfScoresLessThanOrEqualTo', + 3, + 50 + ); + expect( + service.isPercentOfScoresSatisfiesComparator( + satisfyCriterion, + aggregateAutoScores, + utilService.lessThanEqualTo + ) + ).toEqual(true); }); } -function isPercentOfScoresLessThanOrEqualTo() { - describe('isPercentOfScoresLessThanOrEqualTo()', () => { - it('should check is percent of scores less than or equal to false', () => { - const satisfyCriterion = createSatisfyCriteria( - 'node1', - 'component1', - 'ki', - 'percentOfScoresLessThanOrEqualTo', - 2, - 50 - ); - const aggregateAutoScores = { - component1: { - ki: { - counts: createScoreCounts([10, 10, 10, 10, 10]), - scoreCount: 50 - } +function isPercentOfScoresEqualTo() { + const satisfyCriterion = createSatisfyCriteria( + 'node1', + 'component1', + 'ki', + 'percentOfScoresEqualTo', + 3, + 50 + ); + it('should check is percent of scores equal to false', () => { + const aggregateAutoScores = { + component1: { + ki: { + counts: createScoreCounts([10, 10, 10, 10, 10]), + scoreCount: 50 } - }; - expect( - service.isPercentOfScoresLessThanOrEqualTo(satisfyCriterion, aggregateAutoScores) - ).toEqual(false); - }); - it('should check is percent of scores less than or equal to true', () => { - const satisfyCriterion = createSatisfyCriteria( - 'node1', - 'component1', - 'ki', - 'percentOfScoresLessThanOrEqualTo', - 3, - 50 - ); - const aggregateAutoScores = { - component1: { - ki: { - counts: createScoreCounts([10, 10, 10, 10, 10]), - scoreCount: 50 - } + } + }; + expect( + service.isPercentOfScoresSatisfiesComparator( + satisfyCriterion, + aggregateAutoScores, + utilService.equalTo + ) + ).toEqual(false); + }); + it('should check is percent of scores equal to true', () => { + const aggregateAutoScores = { + component1: { + ki: { + counts: createScoreCounts([10, 0, 10, 0, 0]), + scoreCount: 20 } - }; - expect( - service.isPercentOfScoresLessThanOrEqualTo(satisfyCriterion, aggregateAutoScores) - ).toEqual(true); - }); + } + }; + expect( + service.isPercentOfScoresSatisfiesComparator( + satisfyCriterion, + aggregateAutoScores, + utilService.equalTo + ) + ).toEqual(true); }); } -function getLessThanOrEqualToSum() { - describe('getLessThanOrEqualToSum()', () => { - it('should get less than or equal to sum with score 1', () => { - const satisfyCriterion = { value: 1 }; - const aggregateData = { - counts: createScoreCounts([10, 20, 30, 40, 50]) - }; - const possibleScores = [1, 2, 3, 4, 5]; - expect( - service.getLessThanOrEqualToSum(satisfyCriterion, aggregateData, possibleScores) - ).toEqual(10); - }); - it('should get less than or equal to sum with score 2', () => { - const satisfyCriterion = { value: 2 }; - const aggregateData = { - counts: createScoreCounts([10, 20, 30, 40, 50]) - }; - const possibleScores = [1, 2, 3, 4, 5]; - expect( - service.getLessThanOrEqualToSum(satisfyCriterion, aggregateData, possibleScores) - ).toEqual(30); - }); - it('should get less than or equal to sum with score 3', () => { - const satisfyCriterion = { value: 3 }; - const aggregateData = { - counts: createScoreCounts([10, 20, 30, 40, 50]) - }; - const possibleScores = [1, 2, 3, 4, 5]; - expect( - service.getLessThanOrEqualToSum(satisfyCriterion, aggregateData, possibleScores) - ).toEqual(60); - }); - it('should get less than or equal to sum with score 4', () => { - const satisfyCriterion = { value: 4 }; - const aggregateData = { - counts: createScoreCounts([10, 20, 30, 40, 50]) - }; - const possibleScores = [1, 2, 3, 4, 5]; - expect( - service.getLessThanOrEqualToSum(satisfyCriterion, aggregateData, possibleScores) - ).toEqual(100); - }); - it('should get less than or equal to sum with score 5', () => { - const satisfyCriterion = { value: 5 }; - const aggregateData = { - counts: createScoreCounts([10, 20, 30, 40, 50]) - }; - const possibleScores = [1, 2, 3, 4, 5]; - expect( - service.getLessThanOrEqualToSum(satisfyCriterion, aggregateData, possibleScores) - ).toEqual(150); - }); +function isPercentOfScoresNotEqualTo() { + it('should return true when percent of scores equal to value are less than threshold', () => { + expect( + service.isPercentOfScoresSatisfiesComparator( + satisfyCriterionSample, + aggregateAutoScoresSample, + utilService.notEqualTo + ) + ).toEqual(true); + }); + it('should return true when percent of scores equal to value meet threshold', () => { + const aggregateAutoScores = angular.copy(aggregateAutoScoresSample); + aggregateAutoScores.xfns1g7pga.ki.counts = { 1: 1, 2: 0, 3: 2, 4: 0, 5: 0 }; + expect( + service.isPercentOfScoresSatisfiesComparator( + satisfyCriterionSample, + aggregateAutoScores, + utilService.notEqualTo + ) + ).toEqual(false); }); } -function isPercentOfScoresEqualTo() { - describe('isPercentOfScoresEqualTo()', () => { - it('should check is percent of scores equal to false', () => { - const satisfyCriterion = createSatisfyCriteria( - 'node1', - 'component1', - 'ki', - 'percentOfScoresEqualTo', - 3, - 50 - ); - const aggregateAutoScores = { - component1: { - ki: { - counts: createScoreCounts([10, 10, 10, 10, 10]), - scoreCount: 50 - } - } - }; - expect(service.isPercentOfScoresEqualTo(satisfyCriterion, aggregateAutoScores)).toEqual( - false - ); - }); - it('should check is percent of scores equal to true', () => { - const satisfyCriterion = createSatisfyCriteria( - 'node1', - 'component1', - 'ki', - 'percentOfScoresEqualTo', - 3, - 50 - ); - const aggregateAutoScores = { - component1: { - ki: { - counts: createScoreCounts([10, 0, 10, 0, 0]), - scoreCount: 20 - } - } - }; - expect(service.isPercentOfScoresEqualTo(satisfyCriterion, aggregateAutoScores)).toEqual(true); - }); +function getComparatorSum() { + describe('getComparatorSum()', () => { + getGreaterThanSum(); + getGreaterThanOrEqualToSum(); + getLessThanSum(); + getEqualToSum(); + getNotEqualToSum(); }); } -function getEqualToSum() { - describe('getEqualToSum()', () => { - it('should equal to sum', () => { - const satisfyCriterion = { value: 3 }; - const aggregateData = { - counts: createScoreCounts([10, 20, 30, 40, 50]) - }; - const possibleScores = [1, 2, 3, 4, 5]; - expect(service.getEqualToSum(satisfyCriterion, aggregateData, possibleScores)).toEqual(30); - }); +function getGreaterThanSum() { + const aggregateData = { + counts: createScoreCounts([10, 20, 30, 40, 50]) + }; + it('should get greater than sum with score 1', () => { + const satisfyCriterion = { value: 1 }; + expect( + service.getComparatorSum( + satisfyCriterion, + aggregateData, + possibleScoresKi, + utilService.greaterThan + ) + ).toEqual(140); + }); + it('should get greater than sum with score 2', () => { + const satisfyCriterion = { value: 2 }; + expect( + service.getComparatorSum( + satisfyCriterion, + aggregateData, + possibleScoresKi, + utilService.greaterThan + ) + ).toEqual(120); + }); + it('should get greater than sum with score 3', () => { + const satisfyCriterion = { value: 3 }; + expect( + service.getComparatorSum( + satisfyCriterion, + aggregateData, + possibleScoresKi, + utilService.greaterThan + ) + ).toEqual(90); + }); + it('should get greater than sum with score 4', () => { + const satisfyCriterion = { value: 4 }; + expect( + service.getComparatorSum( + satisfyCriterion, + aggregateData, + possibleScoresKi, + utilService.greaterThan + ) + ).toEqual(50); }); } -function isPercentOfScoresNotEqualTo() { - describe('isPercentOfScoresNotEqualTo()', () => { - it('should return true when percent of scores equal to value are less than threshold', () => { - const result = service.isPercentOfScoresNotEqualTo( - satisfyCriterionSample, - aggregateAutoScoresSample - ); - expect(result).toBeTruthy(); - }); - it('should return true when percent of scores equal to value meet threshold', () => { - const aggregateAutoScores = angular.copy(aggregateAutoScoresSample); - aggregateAutoScores.xfns1g7pga.ki.counts = { 1: 1, 2: 0, 3: 2, 4: 0, 5: 0 }; - const result = service.isPercentOfScoresNotEqualTo( - satisfyCriterionSample, - aggregateAutoScores - ); - expect(result).toBeFalsy(); - }); +function getGreaterThanOrEqualToSum() { + const aggregateData = { + counts: createScoreCounts([10, 20, 30, 40, 50]) + }; + it('should get greater than or equal to sum with score 1', () => { + const satisfyCriterion = { value: 1 }; + expect( + service.getComparatorSum( + satisfyCriterion, + aggregateData, + possibleScoresKi, + utilService.greaterThanEqualTo + ) + ).toEqual(150); + }); + it('should get greater than or equal to sum with score 2', () => { + const satisfyCriterion = { value: 2 }; + expect( + service.getComparatorSum( + satisfyCriterion, + aggregateData, + possibleScoresKi, + utilService.greaterThanEqualTo + ) + ).toEqual(140); + }); + it('should get greater than or equal to sum with score 3', () => { + const satisfyCriterion = { value: 3 }; + expect( + service.getComparatorSum( + satisfyCriterion, + aggregateData, + possibleScoresKi, + utilService.greaterThanEqualTo + ) + ).toEqual(120); + }); + it('should get greater than or equal to sum with score 4', () => { + const satisfyCriterion = { value: 4 }; + expect( + service.getComparatorSum( + satisfyCriterion, + aggregateData, + possibleScoresKi, + utilService.greaterThanEqualTo + ) + ).toEqual(90); + }); + it('should get greater than or equal to sum with score 5', () => { + const satisfyCriterion = { value: 5 }; + expect( + service.getComparatorSum( + satisfyCriterion, + aggregateData, + possibleScoresKi, + utilService.greaterThanEqualTo + ) + ).toEqual(50); }); } -function getNotEqualToSum() { - describe('getNotEqualToSum()', () => { +function getLessThanSum() { + const aggregateData = { + counts: createScoreCounts([10, 20, 30, 40, 50]) + }; + it('should get less than sum with score 2', () => { + const satisfyCriterion = { value: 2 }; + expect( + service.getComparatorSum( + satisfyCriterion, + aggregateData, + possibleScoresKi, + utilService.lessThan + ) + ).toEqual(10); + }); + it('should get less than sum with score 3', () => { + const satisfyCriterion = { value: 3 }; + expect( + service.getComparatorSum( + satisfyCriterion, + aggregateData, + possibleScoresKi, + utilService.lessThan + ) + ).toEqual(30); + }); + it('should get less than sum with score 4', () => { + const satisfyCriterion = { value: 4 }; + expect( + service.getComparatorSum( + satisfyCriterion, + aggregateData, + possibleScoresKi, + utilService.lessThan + ) + ).toEqual(60); + }); + it('should get less than sum with score 5', () => { + const satisfyCriterion = { value: 5 }; + expect( + service.getComparatorSum( + satisfyCriterion, + aggregateData, + possibleScoresKi, + utilService.lessThan + ) + ).toEqual(100); + }); +} + +function getEqualToSum() { + it('should return the sum of scores equal to value', () => { + const satisfyCriterion = { value: 3 }; const aggregateData = { - counts: { 1: 2, 2: 0, 3: 1, 4: 0, 5: 0 }, - scoreCount: 3 + counts: createScoreCounts([10, 20, 30, 40, 50]) }; - it('should return the sum of scores not equal to value', () => { - const result = service.getNotEqualToSum( - satisfyCriterionSample, + expect( + service.getComparatorSum( + satisfyCriterion, aggregateData, - possibleScoresKi - ); - expect(result).toBe(2); - }); + possibleScoresKi, + utilService.equalTo + ) + ).toEqual(30); + }); +} + +function getNotEqualToSum() { + const aggregateData = { + counts: { 1: 2, 2: 0, 3: 1, 4: 0, 5: 0 }, + scoreCount: 3 + }; + it('should return the sum of scores not equal to value', () => { + const result = service.getComparatorSum( + satisfyCriterionSample, + aggregateData, + possibleScoresKi, + utilService.notEqualTo + ); + expect(result).toBe(2); }); } @@ -1225,7 +1200,12 @@ function isPercentThresholdSatisfied() { } }; const aggregateData = service.getAggregateData(satisfyCriterionSample, aggregateAutoScores); - const sum = service.getEqualToSum(satisfyCriterionSample, aggregateData, possibleScoresKi); + const sum = service.getComparatorSum( + satisfyCriterionSample, + aggregateData, + possibleScoresKi, + utilService.equalTo + ); const result = service.isPercentThresholdSatisfied( satisfyCriterionSample, aggregateData, @@ -1238,7 +1218,12 @@ function isPercentThresholdSatisfied() { satisfyCriterionSample, aggregateAutoScoresSample ); - const sum = service.getEqualToSum(satisfyCriterionSample, aggregateData, possibleScoresKi); + const sum = service.getComparatorSum( + satisfyCriterionSample, + aggregateData, + possibleScoresKi, + utilService.equalTo + ); const result = service.isPercentThresholdSatisfied( satisfyCriterionSample, aggregateData, @@ -1453,40 +1438,3 @@ function setReportAvailable() { }); }); } - -function clearTempFields() { - describe('clearTempFields()', () => { - it('should clear temp fields', () => { - const projectAchievements = [ - { - items: [{}], - workgroups: [1], - numberOfStudentsCompleted: 2, - numberOfStudentsInRun: 4, - percentageCompleted: 50, - generatedReport: 'report', - generatedRecommendations: 'recommendations', - nodeId: 'node1', - componentId: 'component1', - isReportAvailable: true - } - ]; - spyOn(projectService, 'getAchievementItems').and.returnValue(projectAchievements); - service.clearTempFields(); - const projectAchievement = projectAchievements[0]; - expect(projectAchievement.items).toBeUndefined(); - expect(projectAchievement.workgroups).toBeUndefined(); - expect(projectAchievement.numberOfStudentsCompleted).toBeUndefined(); - expect(projectAchievement.numberOfStudentsInRun).toBeUndefined(); - expect(projectAchievement.percentageCompleted).toBeUndefined(); - expect(projectAchievement.generatedReport).toBeUndefined(); - expect(projectAchievement.generatedRecommendations).toBeUndefined(); - expect(projectAchievement.nodeId).toBeUndefined(); - expect(projectAchievement.componentId).toBeUndefined(); - expect(projectAchievement.isReportAvailable).toBeUndefined(); - expect(service.workgroupsStorage[0]).toEqual([1]); - expect(service.numberOfStudentsCompletedStorage[0]).toEqual(2); - expect(service.percentageCompletedStorage[0]).toEqual(50); - }); - }); -} diff --git a/src/main/webapp/wise5/services/milestoneService.ts b/src/main/webapp/wise5/services/milestoneService.ts index 7b0551d011..47580d6e77 100644 --- a/src/main/webapp/wise5/services/milestoneService.ts +++ b/src/main/webapp/wise5/services/milestoneService.ts @@ -19,6 +19,50 @@ export class MilestoneService { projectMilestones: any[]; workgroupIds: any[]; workgroupsStorage: any[] = []; + satisfyCriteriaFuncNameToFunc = { + percentOfScoresGreaterThan: (satisfyCriterion: any, aggregateAutoScores: any) => { + return this.isPercentOfScoresSatisfiesComparator( + satisfyCriterion, + aggregateAutoScores, + this.UtilService.greaterThan + ); + }, + percentOfScoresGreaterThanOrEqualTo: (satisfyCriterion: any, aggregateAutoScores: any) => { + return this.isPercentOfScoresSatisfiesComparator( + satisfyCriterion, + aggregateAutoScores, + this.UtilService.greaterThanEqualTo + ); + }, + percentOfScoresLessThan: (satisfyCriterion: any, aggregateAutoScores: any) => { + return this.isPercentOfScoresSatisfiesComparator( + satisfyCriterion, + aggregateAutoScores, + this.UtilService.lessThan + ); + }, + percentOfScoresLessThanOrEqualTo: (satisfyCriterion: any, aggregateAutoScores: any) => { + return this.isPercentOfScoresSatisfiesComparator( + satisfyCriterion, + aggregateAutoScores, + this.UtilService.lessThanEqualTo + ); + }, + percentOfScoresEqualTo: (satisfyCriterion: any, aggregateAutoScores: any) => { + return this.isPercentOfScoresSatisfiesComparator( + satisfyCriterion, + aggregateAutoScores, + this.UtilService.equalTo + ); + }, + percentOfScoresNotEqualTo: (satisfyCriterion: any, aggregateAutoScores: any) => { + return this.isPercentOfScoresSatisfiesComparator( + satisfyCriterion, + aggregateAutoScores, + this.UtilService.notEqualTo + ); + } + }; constructor( private upgrade: UpgradeModule, @@ -244,125 +288,41 @@ export class MilestoneService { } isTemplateCriterionSatisfied(satisfyCriterion: any, aggregateAutoScores: any) { - if (satisfyCriterion.function === 'percentOfScoresGreaterThan') { - return this.isPercentOfScoresGreaterThan(satisfyCriterion, aggregateAutoScores); - } else if (satisfyCriterion.function === 'percentOfScoresGreaterThanOrEqualTo') { - return this.isPercentOfScoresGreaterThanOrEqualTo(satisfyCriterion, aggregateAutoScores); - } else if (satisfyCriterion.function === 'percentOfScoresLessThan') { - return this.isPercentOfScoresLessThan(satisfyCriterion, aggregateAutoScores); - } else if (satisfyCriterion.function === 'percentOfScoresLessThanOrEqualTo') { - return this.isPercentOfScoresLessThanOrEqualTo(satisfyCriterion, aggregateAutoScores); - } else if (satisfyCriterion.function === 'percentOfScoresEqualTo') { - return this.isPercentOfScoresEqualTo(satisfyCriterion, aggregateAutoScores); - } else if (satisfyCriterion.function === 'percentOfScoresNotEqualTo') { - return this.isPercentOfScoresNotEqualTo(satisfyCriterion, aggregateAutoScores); - } else if (satisfyCriterion.function === 'default') { + if (satisfyCriterion.function === 'default') { return true; } + return this.satisfyCriteriaFuncNameToFunc[satisfyCriterion.function]( + satisfyCriterion, + aggregateAutoScores + ); } - isPercentOfScoresGreaterThan(satisfyCriterion: any, aggregateAutoScores: any) { - const aggregateData = this.getAggregateData(satisfyCriterion, aggregateAutoScores); - const possibleScores = this.getPossibleScores(aggregateData); - const sum = this.getGreaterThanSum(satisfyCriterion, aggregateData, possibleScores); - return this.isPercentThresholdSatisfied(satisfyCriterion, aggregateData, sum); - } - - getGreaterThanSum(satisfyCriterion: any, aggregateData: any, possibleScores: number[]) { - let sum = 0; - for (const possibleScore of possibleScores) { - if (possibleScore > satisfyCriterion.value) { - sum += aggregateData.counts[possibleScore]; - } - } - return sum; - } - - isPercentOfScoresGreaterThanOrEqualTo(satisfyCriterion: any, aggregateAutoScores: any) { - const aggregateData = this.getAggregateData(satisfyCriterion, aggregateAutoScores); - const possibleScores = this.getPossibleScores(aggregateData); - const sum = this.getGreaterThanOrEqualToSum(satisfyCriterion, aggregateData, possibleScores); - return this.isPercentThresholdSatisfied(satisfyCriterion, aggregateData, sum); - } - - getGreaterThanOrEqualToSum(satisfyCriterion: any, aggregateData: any, possibleScores: number[]) { - let sum = 0; - for (const possibleScore of possibleScores) { - if (possibleScore >= satisfyCriterion.value) { - sum += aggregateData.counts[possibleScore]; - } - } - return sum; - } - - isPercentOfScoresLessThan(satisfyCriterion: any, aggregateAutoScores: any) { - const aggregateData = this.getAggregateData(satisfyCriterion, aggregateAutoScores); - const possibleScores = this.getPossibleScores(aggregateData); - const sum = this.getLessThanSum(satisfyCriterion, aggregateData, possibleScores); - return this.isPercentThresholdSatisfied(satisfyCriterion, aggregateData, sum); - } - - getLessThanSum(satisfyCriterion: any, aggregateData: any, possibleScores: number[]) { - let sum = 0; - for (const possibleScore of possibleScores) { - if (possibleScore < satisfyCriterion.value) { - sum += aggregateData.counts[possibleScore]; - } - } - return sum; - } - - isPercentOfScoresLessThanOrEqualTo(satisfyCriterion: any, aggregateAutoScores: any) { - const aggregateData = this.getAggregateData(satisfyCriterion, aggregateAutoScores); - const possibleScores = this.getPossibleScores(aggregateData); - const sum = this.getLessThanOrEqualToSum(satisfyCriterion, aggregateData, possibleScores); - return this.isPercentThresholdSatisfied(satisfyCriterion, aggregateData, sum); - } - - getLessThanOrEqualToSum(satisfyCriterion: any, aggregateData: any, possibleScores: number[]) { - let sum = 0; - for (const possibleScore of possibleScores) { - if (possibleScore <= satisfyCriterion.value) { - sum += aggregateData.counts[possibleScore]; - } - } - return sum; - } - - isPercentOfScoresEqualTo(satisfyCriterion: any, aggregateAutoScores: any) { - const aggregateData = this.getAggregateData(satisfyCriterion, aggregateAutoScores); - const possibleScores = this.getPossibleScores(aggregateData); - const sum = this.getEqualToSum(satisfyCriterion, aggregateData, possibleScores); - return this.isPercentThresholdSatisfied(satisfyCriterion, aggregateData, sum); - } - - getEqualToSum(satisfyCriterion: any, aggregateData: any, possibleScores: number[]) { + getComparatorSum( + satisfyCriterion: any, + aggregateData: any, + possibleScores: number[], + comparator: any + ): number { let sum = 0; for (const possibleScore of possibleScores) { - if (possibleScore === satisfyCriterion.value) { + if (comparator(possibleScore, satisfyCriterion.value)) { sum += aggregateData.counts[possibleScore]; } } return sum; } - isPercentOfScoresNotEqualTo(satisfyCriterion: any, aggregateAutoScores: any) { + isPercentOfScoresSatisfiesComparator( + satisfyCriterion: any, + aggregateAutoScores: any, + comparator: any + ) { const aggregateData = this.getAggregateData(satisfyCriterion, aggregateAutoScores); const possibleScores = this.getPossibleScores(aggregateData); - const sum = this.getNotEqualToSum(satisfyCriterion, aggregateData, possibleScores); + const sum = this.getComparatorSum(satisfyCriterion, aggregateData, possibleScores, comparator); return this.isPercentThresholdSatisfied(satisfyCriterion, aggregateData, sum); } - getNotEqualToSum(satisfyCriterion: any, aggregateData: any, possibleScores: number[]) { - let sum = 0; - for (const possibleScore of possibleScores) { - if (possibleScore !== satisfyCriterion.value) { - sum += aggregateData.counts[possibleScore]; - } - } - return sum; - } - getAggregateData(satisfyCriterion: any, aggregateAutoScores: any) { const component = aggregateAutoScores[satisfyCriterion.componentId]; return component[satisfyCriterion.targetVariable]; @@ -550,46 +510,6 @@ export class MilestoneService { projectAchievement.isReportAvailable = reportAvailable; } - deleteMilestone(milestone: any) { - const projectAchievements = this.ProjectService.getAchievementItems(); - let index = -1; - for (let i = 0; i < projectAchievements.length; i++) { - if (projectAchievements[i].id === milestone.id) { - index = i; - break; - } - } - - if (index > -1) { - projectAchievements.splice(index, 1); - this.saveProject(); - } - } - - saveProject() { - this.clearTempFields(); - this.ProjectService.saveProject(); - } - - clearTempFields() { - const projectAchievements = this.ProjectService.getAchievementItems(); - for (const projectAchievement of projectAchievements) { - this.workgroupsStorage.push(projectAchievement.workgroups); - this.numberOfStudentsCompletedStorage.push(projectAchievement.numberOfStudentsCompleted); - this.percentageCompletedStorage.push(projectAchievement.percentageCompleted); - delete projectAchievement.items; - delete projectAchievement.workgroups; - delete projectAchievement.numberOfStudentsCompleted; - delete projectAchievement.numberOfStudentsInRun; - delete projectAchievement.percentageCompleted; - delete projectAchievement.generatedReport; - delete projectAchievement.generatedRecommendations; - delete projectAchievement.nodeId; - delete projectAchievement.componentId; - delete projectAchievement.isReportAvailable; - } - } - showMilestoneDetails(milestone: any, $event: any, hideStudentWork: boolean = false) { const title = this.getTranslation('MILESTONE_DETAILS_TITLE', { name: milestone.name diff --git a/src/main/webapp/wise5/services/utilService.ts b/src/main/webapp/wise5/services/utilService.ts index 34bc10a4f7..76448c9688 100644 --- a/src/main/webapp/wise5/services/utilService.ts +++ b/src/main/webapp/wise5/services/utilService.ts @@ -807,6 +807,30 @@ export class UtilService { } return arrData; } + + greaterThanEqualTo(a: number, b: number): boolean { + return a >= b; + } + + greaterThan(a: number, b: number): boolean { + return a > b; + } + + lessThanEqualTo(a: number, b: number): boolean { + return a <= b; + } + + lessThan(a: number, b: number): boolean { + return a < b; + } + + equalTo(a: number, b: number): boolean { + return a === b; + } + + notEqualTo(a: number, b: number): boolean { + return a !== b; + } } declare global { From 07d76240fe80eb401fe11c05d617dc3e80258b70 Mon Sep 17 00:00:00 2001 From: Geoffrey Kwan Date: Tue, 12 Jan 2021 11:53:58 -0500 Subject: [PATCH 07/26] Moved teacher related code from ProjectService to TeacherProjectService. #2676 --- ...e-new-component-location.component.spec.ts | 16 +- ...choose-new-component-location.component.ts | 32 +- .../choose-branch-path-dialog.component.ts | 2 - .../src/app/services/projectService.spec.ts | 647 +--- .../src/app/services/spaceService.spec.ts | 18 +- .../services/teacherProjectService.spec.ts | 639 +++- .../node/editRubric/edit-rubric.component.ts | 11 +- .../wise5/components/componentController.ts | 28 +- .../conceptMap/conceptMapService.ts | 17 +- .../wise5/components/label/labelService.ts | 7 +- .../wise5/services/notificationService.ts | 10 +- .../webapp/wise5/services/projectService.ts | 2593 ++--------------- .../webapp/wise5/services/spaceService.ts | 8 +- .../wise5/services/teacherProjectService.ts | 2192 +++++++++++++- 14 files changed, 3114 insertions(+), 3106 deletions(-) diff --git a/src/main/webapp/site/src/app/authoring-tool/add-component/choose-new-component-location/choose-new-component-location.component.spec.ts b/src/main/webapp/site/src/app/authoring-tool/add-component/choose-new-component-location/choose-new-component-location.component.spec.ts index 6a872a7325..bf37d2f8b4 100644 --- a/src/main/webapp/site/src/app/authoring-tool/add-component/choose-new-component-location/choose-new-component-location.component.spec.ts +++ b/src/main/webapp/site/src/app/authoring-tool/add-component/choose-new-component-location/choose-new-component-location.component.spec.ts @@ -2,8 +2,8 @@ import { HttpClientTestingModule } from '@angular/common/http/testing'; import { TestBed } from '@angular/core/testing'; import { UpgradeModule } from '@angular/upgrade/static'; import { ConfigService } from '../../../../../../wise5/services/configService'; -import { ProjectService } from '../../../../../../wise5/services/projectService'; import { TeacherDataService } from '../../../../../../wise5/services/teacherDataService'; +import { TeacherProjectService } from '../../../../../../wise5/services/teacherProjectService'; import { UtilService } from '../../../../../../wise5/services/utilService'; import { ChooseNewComponentLocation } from './choose-new-component-location.component'; @@ -42,7 +42,7 @@ class MockUpgradeModule { } let component: ChooseNewComponentLocation; -let projectService: ProjectService; +let teacherProjectService: TeacherProjectService; describe('ChooseNewComponentLocation', () => { beforeEach(() => { @@ -51,14 +51,14 @@ describe('ChooseNewComponentLocation', () => { providers: [ ChooseNewComponentLocation, ConfigService, - { provide: ProjectService, useClass: MockProjectService }, + { provide: TeacherProjectService, useClass: MockProjectService }, { provide: TeacherDataService, useClass: MockTeacherDataService }, { provide: UpgradeModule, useClass: MockUpgradeModule }, UtilService ] }); component = TestBed.inject(ChooseNewComponentLocation); - projectService = TestBed.inject(ProjectService); + teacherProjectService = TestBed.inject(TeacherProjectService); }); it('should have nodeId and components set after initialization', () => { @@ -68,10 +68,10 @@ describe('ChooseNewComponentLocation', () => { }); it('insertComponentAfter() should create a new component and save the project', () => { - spyOn(projectService, 'createComponent').and.returnValue({ id: 'comp3', type: 'Discussion' }); - spyOn(projectService, 'saveProject').and.returnValue(new Promise(() => {})); + spyOn(teacherProjectService, 'createComponent').and.returnValue({ id: 'comp3', type: 'Discussion' }); + spyOn(teacherProjectService, 'saveProject').and.returnValue(new Promise(() => {})); component.insertComponentAfter('comp2'); - expect(projectService.createComponent).toHaveBeenCalled(); - expect(projectService.saveProject).toHaveBeenCalled(); + expect(teacherProjectService.createComponent).toHaveBeenCalled(); + expect(teacherProjectService.saveProject).toHaveBeenCalled(); }); }); diff --git a/src/main/webapp/site/src/app/authoring-tool/add-component/choose-new-component-location/choose-new-component-location.component.ts b/src/main/webapp/site/src/app/authoring-tool/add-component/choose-new-component-location/choose-new-component-location.component.ts index 28c4d45a2f..0957a3f594 100644 --- a/src/main/webapp/site/src/app/authoring-tool/add-component/choose-new-component-location/choose-new-component-location.component.ts +++ b/src/main/webapp/site/src/app/authoring-tool/add-component/choose-new-component-location/choose-new-component-location.component.ts @@ -1,9 +1,9 @@ import { UtilService } from '../../../../../../wise5/services/utilService'; -import { ProjectService } from '../../../../../../wise5/services/projectService'; import { TeacherDataService } from '../../../../../../wise5/services/teacherDataService'; import { ConfigService } from '../../../../../../wise5/services/configService'; import { Component } from '@angular/core'; import { UpgradeModule } from '@angular/upgrade/static'; +import { TeacherProjectService } from '../../../../../../wise5/services/teacherProjectService'; @Component({ selector: 'choose-new-component-location', @@ -16,14 +16,14 @@ export class ChooseNewComponentLocation { constructor( private upgrade: UpgradeModule, private ConfigService: ConfigService, - private ProjectService: ProjectService, + private TeacherProjectService: TeacherProjectService, private TeacherDataService: TeacherDataService, private UtilService: UtilService ) {} ngOnInit() { this.nodeId = this.TeacherDataService.getCurrentNodeId(); - this.components = this.ProjectService.getComponentsByNodeId(this.nodeId); + this.components = this.TeacherProjectService.getComponentsByNodeId(this.nodeId); } getComponentTypeLabel(componentType) { @@ -35,20 +35,18 @@ export class ChooseNewComponentLocation { } insertComponentAfter(insertAfterComponentId = null) { - const newComponent = this.ProjectService.createComponent( + const newComponent = this.TeacherProjectService.createComponent( this.nodeId, this.upgrade.$injector.get('$stateParams').componentType, insertAfterComponentId ); - this.ProjectService.saveProject().then(() => { + this.TeacherProjectService.saveProject().then(() => { this.saveAddComponentEvent(newComponent); - this.upgrade.$injector - .get('$state') - .go('root.at.project.node', { - projectId: this.ConfigService.getProjectId(), - nodeId: this.nodeId, - newComponents: [newComponent] - }); + this.upgrade.$injector.get('$state').go('root.at.project.node', { + projectId: this.ConfigService.getProjectId(), + nodeId: this.nodeId, + newComponents: [newComponent] + }); }); } @@ -74,11 +72,9 @@ export class ChooseNewComponentLocation { } cancel() { - this.upgrade.$injector - .get('$state') - .go('root.at.project.node', { - projectId: this.ConfigService.getProjectId(), - nodeId: this.nodeId - }); + this.upgrade.$injector.get('$state').go('root.at.project.node', { + projectId: this.ConfigService.getProjectId(), + nodeId: this.nodeId + }); } } diff --git a/src/main/webapp/site/src/app/preview/modules/choose-branch-path-dialog/choose-branch-path-dialog.component.ts b/src/main/webapp/site/src/app/preview/modules/choose-branch-path-dialog/choose-branch-path-dialog.component.ts index ee4d17f34f..72acab6952 100644 --- a/src/main/webapp/site/src/app/preview/modules/choose-branch-path-dialog/choose-branch-path-dialog.component.ts +++ b/src/main/webapp/site/src/app/preview/modules/choose-branch-path-dialog/choose-branch-path-dialog.component.ts @@ -1,7 +1,5 @@ import { Component, OnInit, Inject } from '@angular/core'; import { MAT_DIALOG_DATA } from '@angular/material/dialog'; -import { NodeService } from '../../../../../../wise5/services/nodeService'; -import { ProjectService } from '../../../../../../wise5/services/projectService'; @Component({ selector: 'app-choose-branch-path-dialog', diff --git a/src/main/webapp/site/src/app/services/projectService.spec.ts b/src/main/webapp/site/src/app/services/projectService.spec.ts index 1342c12d57..bc96161e41 100644 --- a/src/main/webapp/site/src/app/services/projectService.spec.ts +++ b/src/main/webapp/site/src/app/services/projectService.spec.ts @@ -7,7 +7,6 @@ import { UtilService } from '../../../../wise5/services/utilService'; import demoProjectJSON_import from './sampleData/curriculum/Demo.project.json'; import oneBranchTwoPathsProjectJSON_import from './sampleData/curriculum/OneBranchTwoPaths.project.json'; import scootersProjectJSON_import from './sampleData/curriculum/SelfPropelledVehiclesChallenge.project.json'; -import { getAuthServiceConfigs } from '../app.module'; import twoStepsProjectJSON_import from './sampleData/curriculum/TwoSteps.project.json'; import { SessionService } from '../../../../wise5/services/sessionService'; const projectIdDefault = 1; @@ -47,52 +46,20 @@ describe('ProjectService', () => { shouldNotReplaceAssetPathsInHtmlComponentContent(); shouldRetrieveProjectWhenConfigProjectURLIsValid(); shouldNotRetrieveProjectWhenConfigProjectURLIsUndefined(); - shouldSaveProject(); - shouldHandleSaveProjectResponse(); - shouldNotSaveProjectWhenTheUserDoesNotHavePermissionToEditTheProject(); shouldGetDefaultThemePathWhenThemeIsNotDefinedInTheProject(); shouldGetProjectThemePathWhenThemeIsDefinedInTheProject(); shouldReturnTheStartNodeOfTheProject(); shouldReturnTheNodeByNodeId(); shouldReturnTheNodeTitleByNodeId(); - getNextAvailableNodeId(); - shouldReturnTheNextAvailableGroupId(); - shouldReturnTheGroupIdsInTheProject(); - getNodeIds(); - getInactiveNodeIds(); shouldGetTheComponentByNodeIdAndComponentId(); shouldGetTheComponentPositionByNodeIdAndComonentId(); shouldGetTheComponentsByNodeId(); - shouldReturnTheMaxScoreOfTheProject(); - shouldNotAddSpaceIfItDoesExist(); - shouldAddSpaceIfItDoesntExist(); - shouldRemoveSpaces(); shouldCheckOrderBetweenStepGroupAndStepGroup(); - shouldRemoveTransitionsGoingOutOfGroupInChildNodesOfGroup(); - removeNodeFromGroup(); shouldIdentifyBranchStartAndMergePoints(); shouldGetPaths(); - insertNodeAfterInTransitions(); - shouldNotBeAbleToInsertANodeAfterAnotherNodeWhenTheyAreDifferentTypes(); - shouldBeAbleToInsertAStepNodeInsideAGroupNode(); - shouldBeAbleToInsertAGroupNodeInsideAGroupNode(); - shouldNotBeAbleToInsertAStepNodeInsideAStepNode(); - shouldDeleteAStepFromTheProject(); - shouldDeleteAnInactiveStepFromTheProject(); - shouldDeleteAStepThatIsTheStartIdOfTheProject(); - shouldDeleteAStepThatIsTheLastStepOfTheProject(); - shouldDeleteAStepThatIsTheStartIdOfAnAactivityThatIsNotTheFirstActivity(); - shouldDeleteTheFirstActivityFromTheProject(); - shouldDeleteAnActivityInTheMiddleOfTheProject(); - shouldDeleteTheLastActivityFromTheProject(); calculateNodeOrder(); getGroupNodesIdToOrder(); - getUniqueAuthors(); - deleteActivityWithBranching(); - deleteTheLastStepInAnActivity(); - deleteAllStepsInAnActivity(); getTags(); - addCurrentUserToAuthors_CM_shouldAddUserInfo(); getAllPaths(); consolidatePaths(); getParentGroup(); @@ -141,7 +108,7 @@ describe('ProjectService', () => { }); function createNormalSpy() { - spyOn(configService, 'getConfigParam').and.callFake((param) => { + spyOn(configService, 'getConfigParam').and.callFake(param => { if (param === 'projectBaseURL') { return projectBaseURL; } else if (param === 'projectURL') { @@ -195,8 +162,10 @@ function shouldNotReplaceAssetPathsInHtmlComponentContent() { function shouldRetrieveProjectWhenConfigProjectURLIsValid() { it('should retrieve project when Config.projectURL is valid', () => { - spyOn(configService, 'getConfigParam').withArgs('projectURL').and.returnValue(projectURL); - service.retrieveProject().then((response) => { + spyOn(configService, 'getConfigParam') + .withArgs('projectURL') + .and.returnValue(projectURL); + service.retrieveProject().then(response => { expect(response).toEqual(scootersProjectJSON); }); http.expectOne(projectURL); @@ -212,77 +181,6 @@ function shouldNotRetrieveProjectWhenConfigProjectURLIsUndefined() { }); } -function shouldSaveProject() { - it('should save project', () => { - spyOn(configService, 'getConfigParam') - .withArgs('canEditProject') - .and.returnValue(true) - .withArgs('saveProjectURL') - .and.returnValue(saveProjectURL) - .withArgs('mode') - .and.returnValue('authoring') - .withArgs('userInfo') - .and.returnValue({}); - spyOn(configService, 'getProjectId').and.returnValue(projectIdDefault); - spyOn(configService, 'getMyUserInfo').and.returnValue({ id: 1 }); - service.setProject(scootersProjectJSON); - service.saveProject(); - expect(configService.getConfigParam).toHaveBeenCalledWith('saveProjectURL'); - http.expectOne(saveProjectURL); - }); -} - -function shouldHandleSaveProjectResponse() { - it('should broadcast project saved', () => { - shouldHandleSaveProjectResponseSuccessHelper('broadcastProjectSaved'); - }); - it('should broadcast not logged in project not saved', () => { - shouldHandleSaveProjectResponseErrorHelper( - 'notSignedIn', - 'broadcastNotLoggedInProjectNotSaved' - ); - }); - it('should broadcast not allowed to edit this project', () => { - shouldHandleSaveProjectResponseErrorHelper( - 'notAllowedToEditThisProject', - 'broadcastNotAllowedToEditThisProject' - ); - }); - it('should broadcast error saving project', () => { - shouldHandleSaveProjectResponseErrorHelper('errorSavingProject', 'broadcastErrorSavingProject'); - }); -} - -function shouldHandleSaveProjectResponseSuccessHelper(functionName: any) { - shouldHandleSaveProjectResponseHelper('success', '', functionName); -} - -function shouldHandleSaveProjectResponseErrorHelper(messageCode: string, functionName: any) { - shouldHandleSaveProjectResponseHelper('error', messageCode, functionName); -} - -function shouldHandleSaveProjectResponseHelper( - status: string, - messageCode: string, - functionName: any -) { - const response = { - status: status, - messageCode: messageCode - }; - spyOn(service, functionName).and.callFake(() => {}); - service.handleSaveProjectResponse(response); - expect(service[functionName]).toHaveBeenCalled(); -} - -function shouldNotSaveProjectWhenTheUserDoesNotHavePermissionToEditTheProject() { - it('should not save project when the user does not have permission to edit the project', () => { - service.setProject(scootersProjectJSON); - spyOn(configService, 'getConfigParam').withArgs('canEditProject').and.returnValue(false); - expect(service.saveProject()).toEqual(null); - }); -} - function shouldGetDefaultThemePathWhenThemeIsNotDefinedInTheProject() { it('should get default theme path when theme is not defined in the project', () => { spyOn(configService, 'getConfigParam').and.returnValue(wiseBaseURL); @@ -341,93 +239,6 @@ function shouldReturnTheNodeTitleByNodeId() { }); } -function getNextAvailableNodeId() { - describe('getNextAvailableNodeId', () => { - it('should return the next available node id', () => { - createNormalSpy(); - service.setProject(scootersProjectJSON); - expect(service.getNextAvailableNodeId()).toEqual('node43'); - expect(service.getNextAvailableNodeId(['node43'])).toEqual('node44'); - expect(service.getNextAvailableNodeId(['node43', 'node44'])).toEqual('node45'); - }); - }); -} - -function shouldReturnTheNextAvailableGroupId() { - it('should return the next available group id', () => { - createNormalSpy(); - service.setProject(scootersProjectJSON); - const nextGroupIdExpected = 'group7'; - const nextGroupIdActual = service.getNextAvailableGroupId(); - expect(nextGroupIdActual).toEqual(nextGroupIdExpected); - }); -} - -function shouldReturnTheGroupIdsInTheProject() { - it('should return the group ids in the project', () => { - createNormalSpy(); - service.setProject(scootersProjectJSON); - const groupIdsExpected = ['group0', 'group1', 'group2', 'group3', 'group4', 'group5', 'group6']; - const groupIdsActual = service.getGroupIds(); - expect(groupIdsActual).toEqual(groupIdsExpected); - }); -} - -function getNodeIds() { - describe('getNodeIds', () => { - it('should return the node ids in the project', () => { - service.setProject(scootersProjectJSON); - const nodeIdsExpected = [ - 'node1', - 'node2', - 'node3', - 'node4', - 'node5', - 'node6', - 'node7', - 'node9', - 'node12', - 'node13', - 'node14', - 'node18', - 'node19', - 'node21', - 'node22', - 'node23', - 'node24', - 'node25', - 'node26', - 'node27', - 'node28', - 'node29', - 'node30', - 'node31', - 'node40', - 'node32', - 'node33', - 'node34', - 'node35', - 'node36', - 'node37', - 'node38', - 'node39', - 'nodeWithNoComponents' - ]; - const nodeIdsActual = service.getNodeIds(); - expect(nodeIdsActual).toEqual(nodeIdsExpected); - }); - }); -} - -function getInactiveNodeIds() { - describe('getInactiveNodeIds', () => { - it('should return the inactive nodes in the project', () => { - service.setProject(scootersProjectJSON); - expect(service.getInactiveNodeIds()).toEqual(['node41', 'node42']); - }); - }); -} - function shouldGetTheComponentByNodeIdAndComponentId() { it('should get the component by node id and component id', () => { service.setProject(scootersProjectJSON); @@ -517,74 +328,6 @@ function shouldGetTheComponentsByNodeId() { }); } -function shouldReturnTheMaxScoreOfTheProject() { - it('should return the max score of the project', () => { - service.setProject(demoProjectJSON); - const demoProjectMaxScoreActual = service.getMaxScore(); - expect(demoProjectMaxScoreActual).toBeNull(); - service.setProject(scootersProjectJSON); - const scootersProjectMaxScoreExpected = 18; - const scootersProjectMaxScoreActual = service.getMaxScore(); - expect(scootersProjectMaxScoreActual).toEqual(scootersProjectMaxScoreExpected); - }); -} - -function shouldNotAddSpaceIfItDoesExist() { - it('should not add space if it does exist', () => { - service.setProject(scootersProjectJSON); - const spaces = service.getSpaces(); - expect(spaces.length).toEqual(2); - const space = { - id: 'public', - name: 'Public', - isPublic: true, - isShowInNotebook: true - }; - service.addSpace(space); - expect(spaces.length).toEqual(2); - expect(spaces[0].id).toEqual('public'); - expect(spaces[1].id).toEqual('ideasAboutGlobalClimateChange'); - }); -} - -function shouldAddSpaceIfItDoesntExist() { - it("should add space if it doesn't exist", () => { - service.setProject(scootersProjectJSON); - const spaces = service.getSpaces(); - expect(spaces.length).toEqual(2); - const space = { - id: 'newSpace', - name: 'New Space to share your thoughts', - isPublic: true, - isShowInNotebook: false - }; - service.addSpace(space); - expect(spaces.length).toEqual(3); - expect(spaces[0].id).toEqual('public'); - expect(spaces[1].id).toEqual('ideasAboutGlobalClimateChange'); - expect(spaces[2].id).toEqual('newSpace'); - }); -} - -function shouldRemoveSpaces() { - let spaces; - describe('removeSpace', () => { - beforeEach(() => { - service.setProject(demoProjectJSON); - spaces = service.getSpaces(); - expect(spaces.length).toEqual(1); - }); - it('should not remove a space that does not exist', () => { - service.removeSpace('public'); - expect(spaces.length).toEqual(1); - }); - it('should remove a space that does exist', () => { - service.removeSpace('sharePictures'); - expect(spaces.length).toEqual(0); - }); - }); -} - function shouldCheckOrderBetweenStepGroupAndStepGroup() { it('should check order between step/group and step/group', () => { service.setProject(demoProjectJSON); @@ -599,44 +342,6 @@ function shouldCheckOrderBetweenStepGroupAndStepGroup() { }); } -function shouldRemoveTransitionsGoingOutOfGroupInChildNodesOfGroup() { - it('should remove transitions going out of group in child nodes of group', () => { - service.setProject(demoProjectJSON); - expect(service.getTransitionsByFromNodeId('node18').length).toEqual(1); - expect(service.getTransitionsByFromNodeId('node19').length).toEqual(1); - service.removeTransitionsOutOfGroup('group1'); - expect(service.getTransitionsByFromNodeId('node18').length).toEqual(1); - expect(service.getTransitionsByFromNodeId('node19').length).toEqual(0); - }); -} - -function expectChildNodeIdLength(nodeId, expectedLength) { - expect(service.getChildNodeIdsById(nodeId).length).toEqual(expectedLength); -} - -function expectGroupStartId(groupId, expectedStartNodeId) { - expect(service.getGroupStartId(groupId)).toEqual(expectedStartNodeId); -} - -function removeNodeFromGroup() { - it('should remove node from group', () => { - service.setProject(demoProjectJSON); - expectChildNodeIdLength('group1', 19); - const group1 = service.getNodeById('group1'); - service.removeNodeIdFromGroup(group1, 'node3'); - expectChildNodeIdLength('group1', 18); - service.removeNodeIdFromGroup(group1, 'node4'); - expectChildNodeIdLength('group1', 17); - expectGroupStartId('group1', 'node1'); - service.removeNodeIdFromGroup(group1, 'node1'); - expectChildNodeIdLength('group1', 16); - expectGroupStartId('group1', 'node2'); - service.removeNodeIdFromGroup(group1, 'node2'); - expectChildNodeIdLength('group1', 15); - expectGroupStartId('group1', 'node3'); - }); -} - function shouldIdentifyBranchStartAndMergePoints() { it('should identify branch start point', () => { service.setProject(demoProjectJSON); @@ -648,7 +353,7 @@ function shouldIdentifyBranchStartAndMergePoints() { } function expectFunctionCallToReturnValue(func, nodeIdArray, expectedValue) { - nodeIdArray.forEach((nodeId) => { + nodeIdArray.forEach(nodeId => { expect(service[func](nodeId)).toEqual(expectedValue); }); } @@ -678,187 +383,6 @@ function expectPaths(paths, nodeId, expectedPath) { expect(subPath).toEqual(expectedPath); } -function insertNodeAfterInTransitions() { - it('should be able to insert a step node after another step node', () => { - expectInsertNodeAfterInTransition('node1', 'node2'); - }); - it('should be able to insert an activity node after another activity node', () => { - expectInsertNodeAfterInTransition('group1', 'group2'); - }); -} - -function expectInsertNodeAfterInTransition(nodeIdBefore, nodeIdAfter) { - service.setProject(demoProjectJSON); - expect( - service.nodeHasTransitionToNodeId(service.getNodeById(nodeIdBefore), nodeIdAfter) - ).toBeTruthy(); - service.insertNodeAfterInTransitions(service.getNodeById(nodeIdBefore), nodeIdAfter); - expect( - service.nodeHasTransitionToNodeId(service.getNodeById(nodeIdBefore), nodeIdAfter) - ).toBeFalsy(); - expect( - service.nodeHasTransitionToNodeId(service.getNodeById(nodeIdAfter), nodeIdBefore) - ).toBeTruthy(); -} - -function shouldNotBeAbleToInsertANodeAfterAnotherNodeWhenTheyAreDifferentTypes() { - it('should not be able to insert a node after another node when they are different types', () => { - service.setProject(demoProjectJSON); - expect(() => { - service.insertNodeAfterInTransitions(service.getNodeById('node1'), 'group2'); - }).toThrow('Error: insertNodeAfterInTransitions() nodes are not the same type'); - }); -} - -function shouldBeAbleToInsertAStepNodeInsideAGroupNode() { - it('should be able to insert a step node inside an group node', () => { - service.setProject(demoProjectJSON); - const node1 = service.getNodeById('node1'); - const node19 = service.getNodeById('node19'); - expect(service.nodeHasTransitionToNodeId(node1, 'node2')).toBeTruthy(); - expect(service.nodeHasTransitionToNodeId(node1, 'node20')).toBeFalsy(); - expect(service.nodeHasTransitionToNodeId(node19, 'node20')).toBeTruthy(); - expect(service.nodeHasTransitionToNodeId(node19, 'node1')).toBeFalsy(); - service.insertNodeInsideOnlyUpdateTransitions('node1', 'group2'); - expect(service.nodeHasTransitionToNodeId(node1, 'node20')).toBeTruthy(); - expect(service.nodeHasTransitionToNodeId(node1, 'node2')).toBeFalsy(); - expect(service.nodeHasTransitionToNodeId(node19, 'node1')).toBeTruthy(); - expect(service.nodeHasTransitionToNodeId(node19, 'node20')).toBeFalsy(); - }); -} - -function shouldBeAbleToInsertAGroupNodeInsideAGroupNode() { - it('should be able to insert a group node inside a group node', () => { - service.setProject(demoProjectJSON); - const group1 = service.getNodeById('group1'); - const group2 = service.getNodeById('group2'); - expect(service.nodeHasTransitionToNodeId(group1, 'group2')).toBeTruthy(); - expect(service.nodeHasTransitionToNodeId(group2, 'group1')).toBeFalsy(); - service.insertNodeInsideOnlyUpdateTransitions('group2', 'group0'); - expect(service.nodeHasTransitionToNodeId(group2, 'group1')).toBeTruthy(); - /* - * the transition from group1 to group2 still remains because it is usually - * removed by calling removeNodeIdFromTransitions() but we don't call it here - */ - expect(service.nodeHasTransitionToNodeId(group1, 'group2')).toBeTruthy(); - }); -} - -function shouldNotBeAbleToInsertAStepNodeInsideAStepNode() { - it('should not be able to insert a step node inside a step node', () => { - service.setProject(demoProjectJSON); - expect(() => { - service.insertNodeInsideOnlyUpdateTransitions('node1', 'node2'); - }).toThrow('Error: insertNodeInsideOnlyUpdateTransitions() second parameter must be a group'); - }); -} - -function shouldDeleteAStepFromTheProject() { - it('should delete a step from the project', () => { - service.setProject(demoProjectJSON); - expect(service.getNodes().length).toEqual(54); - expect(service.getNodeById('node5')).not.toBeNull(); - expect(service.nodeHasTransitionToNodeId(service.getNodeById('node4'), 'node5')).toBeTruthy(); - expect(service.nodeHasTransitionToNodeId(service.getNodeById('node5'), 'node6')).toBeTruthy(); - expect(service.getNodesWithTransitionToNodeId('node6').length).toEqual(1); - service.deleteNode('node5'); - expect(service.getNodes().length).toEqual(53); - expect(service.getNodeById('node5')).toBeNull(); - expect(service.nodeHasTransitionToNodeId(service.getNodeById('node4'), 'node6')).toBeTruthy(); - expect(service.getNodesWithTransitionToNodeId('node6').length).toEqual(1); - }); -} - -function shouldDeleteAnInactiveStepFromTheProject() { - it('should delete an inactive step from the project', () => { - service.setProject(demoProjectJSON); - expect(service.getInactiveNodes().length).toEqual(1); - expect(service.getNodeById('node789')).not.toBeNull(); - service.deleteNode('node789'); - expect(service.getInactiveNodes().length).toEqual(0); - expect(service.getNodeById('node789')).toBeNull(); - }); -} - -function shouldDeleteAStepThatIsTheStartIdOfTheProject() { - it('should delete a step that is the start id of the project', () => { - service.setProject(demoProjectJSON); - expect(service.getStartNodeId()).toEqual('node1'); - expect(service.getNodesWithTransitionToNodeId('node2').length).toEqual(1); - service.deleteNode('node1'); - expect(service.getStartNodeId()).toEqual('node2'); - expect(service.getNodesWithTransitionToNodeId('node2').length).toEqual(0); - }); -} - -function shouldDeleteAStepThatIsTheLastStepOfTheProject() { - it('should delete a step that is the last step of the project', () => { - service.setProject(demoProjectJSON); - expect(service.getTransitionsByFromNodeId('node802').length).toEqual(1); - expect( - service.nodeHasTransitionToNodeId(service.getNodeById('node802'), 'node803') - ).toBeTruthy(); - service.deleteNode('node803'); - expect(service.getTransitionsByFromNodeId('node802').length).toEqual(0); - expect( - service.nodeHasTransitionToNodeId(service.getNodeById('node802'), 'node803') - ).toBeFalsy(); - }); -} - -function shouldDeleteAStepThatIsTheStartIdOfAnAactivityThatIsNotTheFirstActivity() { - it('should delete a step that is the start id of an activity that is not the first activity', () => { - service.setProject(demoProjectJSON); - expect(service.getGroupStartId('group2')).toEqual('node20'); - expect(service.nodeHasTransitionToNodeId(service.getNodeById('node19'), 'node20')).toBeTruthy(); - expect(service.nodeHasTransitionToNodeId(service.getNodeById('node20'), 'node21')).toBeTruthy(); - service.deleteNode('node20'); - expect(service.getGroupStartId('group2')).toEqual('node21'); - expect(service.nodeHasTransitionToNodeId(service.getNodeById('node19'), 'node21')).toBeTruthy(); - }); -} - -function shouldDeleteTheFirstActivityFromTheProject() { - it('should delete the first activity from the project', () => { - service.setProject(demoProjectJSON); - expect(service.getGroupStartId('group0')).toEqual('group1'); - expect(service.getStartNodeId()).toEqual('node1'); - expect(service.getNodes().length).toEqual(54); - expect(service.getNodesWithTransitionToNodeId('node20').length).toEqual(1); - service.deleteNode('group1'); - expect(service.getNodeById('group1')).toBeNull(); - expect(service.getGroupStartId('group0')).toEqual('group2'); - expect(service.getStartNodeId()).toEqual('node20'); - expect(service.getNodes().length).toEqual(34); - expect(service.getNodesWithTransitionToNodeId('node20').length).toEqual(0); - }); -} - -function shouldDeleteAnActivityInTheMiddleOfTheProject() { - it('should delete an activity that is in the middle of the project', () => { - service.setProject(demoProjectJSON); - expect(service.nodeHasTransitionToNodeId(service.getNodeById('group2'), 'group3')).toBeTruthy(); - expect(service.getNodes().length).toEqual(54); - service.deleteNode('group3'); - expect(service.nodeHasTransitionToNodeId(service.getNodeById('group2'), 'group3')).toBeFalsy(); - expect(service.nodeHasTransitionToNodeId(service.getNodeById('group2'), 'group4')).toBeTruthy(); - expect(service.getNodes().length).toEqual(51); - }); -} - -function shouldDeleteTheLastActivityFromTheProject() { - it('should delete the last activity from the project', () => { - service.setProject(demoProjectJSON); - expect(service.nodeHasTransitionToNodeId(service.getNodeById('group4'), 'group5')).toBeTruthy(); - expect(service.getTransitionsByFromNodeId('group4').length).toEqual(1); - expect(service.getNodes().length).toEqual(54); - service.deleteNode('group5'); - expect(service.nodeHasTransitionToNodeId(service.getNodeById('group4'), 'group5')).toBeFalsy(); - expect(service.getTransitionsByFromNodeId('group4').length).toEqual(0); - expect(service.getNodes().length).toEqual(48); - }); -} - function calculateNodeOrder() { describe('calculateNodeOrder', () => { it('should calculate the node order', () => { @@ -895,131 +419,6 @@ function getGroupNodesIdToOrder() { }); } -function getUniqueAuthors() { - describe('getUniqueAuthors', () => { - it('should get unique authors when there are no authors', () => { - const authors = []; - const uniqueAuthors = service.getUniqueAuthors(authors); - expect(uniqueAuthors.length).toEqual(0); - }); - - it('should get unique authors when there is one author', () => { - const authors = [{ id: 1, firstName: 'a', lastName: 'a' }]; - const uniqueAuthors = service.getUniqueAuthors(authors); - expect(uniqueAuthors.length).toEqual(1); - expect(uniqueAuthors[0].id).toEqual(1); - expect(uniqueAuthors[0].firstName).toEqual('a'); - expect(uniqueAuthors[0].lastName).toEqual('a'); - }); - - it('should get unique authors when there are multiple duplicates', () => { - const authors = [ - { id: 1, firstName: 'a', lastName: 'a' }, - { id: 2, firstName: 'b', lastName: 'b' }, - { id: 1, firstName: 'a', lastName: 'a' }, - { id: 3, firstName: 'c', lastName: 'c' }, - { id: 3, firstName: 'c', lastName: 'c' }, - { id: 1, firstName: 'a', lastName: 'a' } - ]; - const uniqueAuthors = service.getUniqueAuthors(authors); - expect(uniqueAuthors.length).toEqual(3); - expect(uniqueAuthors[0].id).toEqual(1); - expect(uniqueAuthors[0].firstName).toEqual('a'); - expect(uniqueAuthors[0].lastName).toEqual('a'); - expect(uniqueAuthors[1].id).toEqual(2); - expect(uniqueAuthors[1].firstName).toEqual('b'); - expect(uniqueAuthors[1].lastName).toEqual('b'); - expect(uniqueAuthors[2].id).toEqual(3); - expect(uniqueAuthors[2].firstName).toEqual('c'); - expect(uniqueAuthors[2].lastName).toEqual('c'); - }); - - it('should get unique authors when there are no duplicates', () => { - const authors = [ - { id: 1, firstName: 'a', lastName: 'a' }, - { id: 2, firstName: 'b', lastName: 'b' }, - { id: 3, firstName: 'c', lastName: 'c' } - ]; - const uniqueAuthors = service.getUniqueAuthors(authors); - expect(uniqueAuthors.length).toEqual(3); - expect(uniqueAuthors[0].id).toEqual(1); - expect(uniqueAuthors[0].firstName).toEqual('a'); - expect(uniqueAuthors[0].lastName).toEqual('a'); - expect(uniqueAuthors[1].id).toEqual(2); - expect(uniqueAuthors[1].firstName).toEqual('b'); - expect(uniqueAuthors[1].lastName).toEqual('b'); - expect(uniqueAuthors[2].id).toEqual(3); - expect(uniqueAuthors[2].firstName).toEqual('c'); - expect(uniqueAuthors[2].lastName).toEqual('c'); - }); - }); -} - -function deleteActivityWithBranching() { - it(`should delete an activity with branching and is also the first activity in the project - and properly set the project start node id`, () => { - service.setProject(demoProjectJSON); - expect(service.getStartNodeId()).toEqual('node1'); - service.deleteNode('group1'); - expect(service.getStartNodeId()).toEqual('node20'); - }); - - it(`should delete an activity in the middle of the project with branching and properly remove - transitions from remaining steps`, () => { - service.setProject(demoProjectJSON); - const node19 = service.getNodeById('node19'); - const node19Transitions = node19.transitionLogic.transitions; - expect(node19Transitions.length).toEqual(1); - expect(node19Transitions[0].to).toEqual('node20'); - service.deleteNode('group2'); - expect(node19Transitions.length).toEqual(1); - expect(node19Transitions[0].to).toEqual('node790'); - }); - - it(`should delete an activity at the end of the project with branching and properly remove - transitions from remaining steps`, () => { - service.setProject(demoProjectJSON); - const node798 = service.getNodeById('node798'); - const node798Transitions = node798.transitionLogic.transitions; - expect(node798Transitions.length).toEqual(1); - expect(node798Transitions[0].to).toEqual('node799'); - service.deleteNode('group5'); - expect(node798Transitions.length).toEqual(0); - }); -} - -function deleteTheLastStepInAnActivity() { - it(`should delete the last step in an activity in the middle of the project and set previous - step to transition to the first step of the next activity`, () => { - service.setProject(demoProjectJSON); - const node790Transitions = service.getTransitionsByFromNodeId('node790'); - expect(node790Transitions.length).toEqual(1); - expect( - service.nodeHasTransitionToNodeId(service.getNodeById('node790'), 'node791') - ).toBeTruthy(); - service.deleteNode('node791'); - expect(node790Transitions.length).toEqual(1); - expect( - service.nodeHasTransitionToNodeId(service.getNodeById('node790'), 'node792') - ).toBeTruthy(); - }); -} - -function deleteAllStepsInAnActivity() { - it(`should delete all steps in an activity in the middle of the project and set previous step - to transition to activity`, () => { - service.setProject(demoProjectJSON); - const node34 = service.getNodeById('node34'); - const node34Transitions = node34.transitionLogic.transitions; - expect(node34Transitions.length).toEqual(1); - expect(node34Transitions[0].to).toEqual('node790'); - service.deleteNode('node790'); - service.deleteNode('node791'); - expect(node34Transitions.length).toEqual(1); - expect(node34Transitions[0].to).toEqual('group3'); - }); -} - function getTags() { it('should get tags from the project', () => { service.setProject(demoProjectJSON); @@ -1030,37 +429,22 @@ function getTags() { }); } -function addCurrentUserToAuthors_CM_shouldAddUserInfo() { - it('should add current user to authors in CM mode', () => { - spyOn(configService, 'getMyUserInfo').and.returnValue({ - userIds: [1], - firstName: 'wise', - lastName: 'panda', - username: 'wisepanda' - }); - spyOn(configService, 'isClassroomMonitor').and.returnValue(true); - const authors = service.addCurrentUserToAuthors([]); - expect(authors.length).toEqual(1); - expect(authors[0].id).toEqual(1); - }); -} - function getAllPaths() { describe('getAllPaths()', () => { - it ('should get all paths in a unit with no branches', () => { + it('should get all paths in a unit with no branches', () => { service.setProject(twoStepsProjectJSON); const allPaths = service.getAllPaths([], service.getStartNodeId(), true); expect(allPaths.length).toEqual(1); expect(allPaths[0]).toEqual(['group1', 'node1', 'node2']); }); - it ('should get all paths in a unit with a branch with two paths', () => { + it('should get all paths in a unit with a branch with two paths', () => { service.setProject(oneBranchTwoPathsProjectJSON); const allPaths = service.getAllPaths([], service.getStartNodeId(), true); expect(allPaths.length).toEqual(2); expect(allPaths[0]).toEqual(['group1', 'node1', 'node2', 'node3', 'node4', 'node8']); expect(allPaths[1]).toEqual(['group1', 'node1', 'node2', 'node5', 'node6', 'node7', 'node8']); }); - it ('should get all paths in a unit starting with a node in a branch path', () => { + it('should get all paths in a unit starting with a node in a branch path', () => { service.setProject(oneBranchTwoPathsProjectJSON); const allPaths1 = service.getAllPaths(['group1', 'node1', 'node2'], 'node3', true); expect(allPaths1.length).toEqual(1); @@ -1078,8 +462,17 @@ function consolidatePaths() { service.setProject(oneBranchTwoPathsProjectJSON); const allPaths = service.getAllPaths([], service.getStartNodeId(), true); const consolidatedPaths = service.consolidatePaths(allPaths); - expect(consolidatedPaths).toEqual(['group1', 'node1', 'node2', 'node3', 'node4', 'node5', - 'node6', 'node7', 'node8']); + expect(consolidatedPaths).toEqual([ + 'group1', + 'node1', + 'node2', + 'node3', + 'node4', + 'node5', + 'node6', + 'node7', + 'node8' + ]); }); }); } diff --git a/src/main/webapp/site/src/app/services/spaceService.spec.ts b/src/main/webapp/site/src/app/services/spaceService.spec.ts index 6111bcc4c2..f56f145f29 100644 --- a/src/main/webapp/site/src/app/services/spaceService.spec.ts +++ b/src/main/webapp/site/src/app/services/spaceService.spec.ts @@ -1,21 +1,21 @@ import { TestBed } from '@angular/core/testing'; import { UpgradeModule } from '@angular/upgrade/static'; -import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; import { ConfigService } from '../../../../wise5/services/configService'; -import { ProjectService } from '../../../../wise5/services/projectService'; import { SpaceService } from '../../../../wise5/services/spaceService'; import { UtilService } from '../../../../wise5/services/utilService'; import { SessionService } from '../../../../wise5/services/sessionService'; +import { TeacherProjectService } from '../../../../wise5/services/teacherProjectService'; let service: SpaceService; -let projectService: ProjectService; +let teacherProjectService: TeacherProjectService; describe('SpaceService', () => { beforeEach(() => { TestBed.configureTestingModule({ imports: [HttpClientTestingModule, UpgradeModule], - providers: [ConfigService, ProjectService, SessionService, SpaceService, UtilService] + providers: [ConfigService, TeacherProjectService, SessionService, SpaceService, UtilService] }); - projectService = TestBed.get(ProjectService); + teacherProjectService = TestBed.get(TeacherProjectService); service = TestBed.get(SpaceService); }); createSpace(); @@ -48,9 +48,9 @@ function addSpace() { const name = 'Public'; const isPublic = true; const isShowInNotebook = true; - spyOn(projectService, 'addSpace'); + spyOn(teacherProjectService, 'addSpace'); service.addSpace(id, name, isPublic, isShowInNotebook); - expect(projectService.addSpace).toHaveBeenCalledWith({ + expect(teacherProjectService.addSpace).toHaveBeenCalledWith({ id: id, name: name, isPublic: isPublic, @@ -63,9 +63,9 @@ function addSpace() { function removeSpace() { describe('removeSpace()', () => { it('should remove a space', () => { - spyOn(projectService, 'removeSpace'); + spyOn(teacherProjectService, 'removeSpace'); service.removeSpace('public'); - expect(projectService.removeSpace).toHaveBeenCalledWith('public'); + expect(teacherProjectService.removeSpace).toHaveBeenCalledWith('public'); }); }); } diff --git a/src/main/webapp/site/src/app/services/teacherProjectService.spec.ts b/src/main/webapp/site/src/app/services/teacherProjectService.spec.ts index f28443b587..28b22cd33a 100644 --- a/src/main/webapp/site/src/app/services/teacherProjectService.spec.ts +++ b/src/main/webapp/site/src/app/services/teacherProjectService.spec.ts @@ -71,10 +71,56 @@ describe('TeacherProjectService', () => { shouldGetTheBranchLetter(); lockNode(); unlockNode(); + getNextAvailableNodeId(); + shouldReturnTheNextAvailableGroupId(); + shouldReturnTheGroupIdsInTheProject(); + shouldReturnTheMaxScoreOfTheProject(); + shouldNotAddSpaceIfItDoesExist(); + shouldAddSpaceIfItDoesntExist(); + shouldRemoveSpaces(); + shouldRemoveTransitionsGoingOutOfGroupInChildNodesOfGroup(); + removeNodeFromGroup(); + insertNodeAfterInTransitions(); + shouldNotBeAbleToInsertANodeAfterAnotherNodeWhenTheyAreDifferentTypes(); + shouldBeAbleToInsertAStepNodeInsideAGroupNode(); + shouldBeAbleToInsertAGroupNodeInsideAGroupNode(); + shouldNotBeAbleToInsertAStepNodeInsideAStepNode(); + shouldDeleteAStepFromTheProject(); + shouldDeleteAnInactiveStepFromTheProject(); + shouldDeleteAStepThatIsTheStartIdOfTheProject(); + shouldDeleteAStepThatIsTheLastStepOfTheProject(); + shouldDeleteAStepThatIsTheStartIdOfAnAactivityThatIsNotTheFirstActivity(); + shouldDeleteTheFirstActivityFromTheProject(); + shouldDeleteAnActivityInTheMiddleOfTheProject(); + shouldDeleteTheLastActivityFromTheProject(); + getUniqueAuthors(); + deleteActivityWithBranching(); + deleteTheLastStepInAnActivity(); + deleteAllStepsInAnActivity(); + addCurrentUserToAuthors_CM_shouldAddUserInfo(); + getNodeIds(); + getInactiveNodeIds(); + shouldHandleSaveProjectResponse(); + shouldNotSaveProjectWhenTheUserDoesNotHavePermissionToEditTheProject(); + shouldSaveProject(); }); +function createNormalSpy() { + spyOn(configService, 'getConfigParam').and.callFake(param => { + if (param === 'projectBaseURL') { + return projectBaseURL; + } else if (param === 'projectURL') { + return projectURL; + } else if (param === 'saveProjectURL') { + return saveProjectURL; + } else if (param === 'wiseBaseURL') { + return wiseBaseURL; + } + }); +} + function createConfigServiceGetConfigParamSpy() { - spyOn(configService, 'getConfigParam').and.callFake((param) => { + spyOn(configService, 'getConfigParam').and.callFake(param => { if (param === 'projectBaseURL') { return projectBaseURL; } else if (param === 'projectURL') { @@ -101,7 +147,7 @@ function registerNewProject() { scootersProjectJSONString ); http.expectOne(registerNewProjectURL).flush(newProjectIdExpected); - newProjectIdActual.then((result) => { + newProjectIdActual.then(result => { expect(result).toEqual(newProjectIdExpected); }); }); @@ -203,7 +249,7 @@ function getLibraryProjects() { createConfigServiceGetConfigParamSpy(); const result = service.getLibraryProjects(); http.expectOne(getLibraryProjectsURL).flush(libraryProjects); - result.then((projects) => { + result.then(projects => { expect(projects).toEqual(libraryProjects); }); }); @@ -329,3 +375,590 @@ function unlockNode() { }); }); } + +function getNextAvailableNodeId() { + describe('getNextAvailableNodeId', () => { + it('should return the next available node id', () => { + createNormalSpy(); + service.setProject(scootersProjectJSON); + expect(service.getNextAvailableNodeId()).toEqual('node43'); + expect(service.getNextAvailableNodeId(['node43'])).toEqual('node44'); + expect(service.getNextAvailableNodeId(['node43', 'node44'])).toEqual('node45'); + }); + }); +} + +function shouldReturnTheNextAvailableGroupId() { + it('should return the next available group id', () => { + createNormalSpy(); + service.setProject(scootersProjectJSON); + const nextGroupIdExpected = 'group7'; + const nextGroupIdActual = service.getNextAvailableGroupId(); + expect(nextGroupIdActual).toEqual(nextGroupIdExpected); + }); +} + +function shouldReturnTheGroupIdsInTheProject() { + it('should return the group ids in the project', () => { + createNormalSpy(); + service.setProject(scootersProjectJSON); + const groupIdsExpected = ['group0', 'group1', 'group2', 'group3', 'group4', 'group5', 'group6']; + const groupIdsActual = service.getGroupIds(); + expect(groupIdsActual).toEqual(groupIdsExpected); + }); +} + +function shouldReturnTheMaxScoreOfTheProject() { + it('should return the max score of the project', () => { + service.setProject(demoProjectJSON); + const demoProjectMaxScoreActual = service.getMaxScore(); + expect(demoProjectMaxScoreActual).toBeNull(); + service.setProject(scootersProjectJSON); + const scootersProjectMaxScoreExpected = 18; + const scootersProjectMaxScoreActual = service.getMaxScore(); + expect(scootersProjectMaxScoreActual).toEqual(scootersProjectMaxScoreExpected); + }); +} + +function shouldNotAddSpaceIfItDoesExist() { + it('should not add space if it does exist', () => { + service.setProject(scootersProjectJSON); + const spaces = service.getSpaces(); + expect(spaces.length).toEqual(2); + const space = { + id: 'public', + name: 'Public', + isPublic: true, + isShowInNotebook: true + }; + service.addSpace(space); + expect(spaces.length).toEqual(2); + expect(spaces[0].id).toEqual('public'); + expect(spaces[1].id).toEqual('ideasAboutGlobalClimateChange'); + }); +} + +function shouldAddSpaceIfItDoesntExist() { + it("should add space if it doesn't exist", () => { + service.setProject(scootersProjectJSON); + const spaces = service.getSpaces(); + expect(spaces.length).toEqual(2); + const space = { + id: 'newSpace', + name: 'New Space to share your thoughts', + isPublic: true, + isShowInNotebook: false + }; + service.addSpace(space); + expect(spaces.length).toEqual(3); + expect(spaces[0].id).toEqual('public'); + expect(spaces[1].id).toEqual('ideasAboutGlobalClimateChange'); + expect(spaces[2].id).toEqual('newSpace'); + }); +} + +function shouldRemoveSpaces() { + let spaces; + describe('removeSpace', () => { + beforeEach(() => { + service.setProject(demoProjectJSON); + spaces = service.getSpaces(); + expect(spaces.length).toEqual(1); + }); + it('should not remove a space that does not exist', () => { + service.removeSpace('public'); + expect(spaces.length).toEqual(1); + }); + it('should remove a space that does exist', () => { + service.removeSpace('sharePictures'); + expect(spaces.length).toEqual(0); + }); + }); +} + +function shouldRemoveTransitionsGoingOutOfGroupInChildNodesOfGroup() { + it('should remove transitions going out of group in child nodes of group', () => { + service.setProject(demoProjectJSON); + expect(service.getTransitionsByFromNodeId('node18').length).toEqual(1); + expect(service.getTransitionsByFromNodeId('node19').length).toEqual(1); + service.removeTransitionsOutOfGroup('group1'); + expect(service.getTransitionsByFromNodeId('node18').length).toEqual(1); + expect(service.getTransitionsByFromNodeId('node19').length).toEqual(0); + }); +} + +function expectChildNodeIdLength(nodeId, expectedLength) { + expect(service.getChildNodeIdsById(nodeId).length).toEqual(expectedLength); +} + +function expectGroupStartId(groupId, expectedStartNodeId) { + expect(service.getGroupStartId(groupId)).toEqual(expectedStartNodeId); +} + +function removeNodeFromGroup() { + it('should remove node from group', () => { + service.setProject(demoProjectJSON); + expectChildNodeIdLength('group1', 19); + const group1 = service.getNodeById('group1'); + service.removeNodeIdFromGroup(group1, 'node3'); + expectChildNodeIdLength('group1', 18); + service.removeNodeIdFromGroup(group1, 'node4'); + expectChildNodeIdLength('group1', 17); + expectGroupStartId('group1', 'node1'); + service.removeNodeIdFromGroup(group1, 'node1'); + expectChildNodeIdLength('group1', 16); + expectGroupStartId('group1', 'node2'); + service.removeNodeIdFromGroup(group1, 'node2'); + expectChildNodeIdLength('group1', 15); + expectGroupStartId('group1', 'node3'); + }); +} + +function expectInsertNodeAfterInTransition(nodeIdBefore, nodeIdAfter) { + service.setProject(demoProjectJSON); + expect( + service.nodeHasTransitionToNodeId(service.getNodeById(nodeIdBefore), nodeIdAfter) + ).toBeTruthy(); + service.insertNodeAfterInTransitions(service.getNodeById(nodeIdBefore), nodeIdAfter); + expect( + service.nodeHasTransitionToNodeId(service.getNodeById(nodeIdBefore), nodeIdAfter) + ).toBeFalsy(); + expect( + service.nodeHasTransitionToNodeId(service.getNodeById(nodeIdAfter), nodeIdBefore) + ).toBeTruthy(); +} + +function shouldNotBeAbleToInsertANodeAfterAnotherNodeWhenTheyAreDifferentTypes() { + it('should not be able to insert a node after another node when they are different types', () => { + service.setProject(demoProjectJSON); + expect(() => { + service.insertNodeAfterInTransitions(service.getNodeById('node1'), 'group2'); + }).toThrow('Error: insertNodeAfterInTransitions() nodes are not the same type'); + }); +} + +function shouldBeAbleToInsertAStepNodeInsideAGroupNode() { + it('should be able to insert a step node inside an group node', () => { + service.setProject(demoProjectJSON); + const node1 = service.getNodeById('node1'); + const node19 = service.getNodeById('node19'); + expect(service.nodeHasTransitionToNodeId(node1, 'node2')).toBeTruthy(); + expect(service.nodeHasTransitionToNodeId(node1, 'node20')).toBeFalsy(); + expect(service.nodeHasTransitionToNodeId(node19, 'node20')).toBeTruthy(); + expect(service.nodeHasTransitionToNodeId(node19, 'node1')).toBeFalsy(); + service.insertNodeInsideOnlyUpdateTransitions('node1', 'group2'); + expect(service.nodeHasTransitionToNodeId(node1, 'node20')).toBeTruthy(); + expect(service.nodeHasTransitionToNodeId(node1, 'node2')).toBeFalsy(); + expect(service.nodeHasTransitionToNodeId(node19, 'node1')).toBeTruthy(); + expect(service.nodeHasTransitionToNodeId(node19, 'node20')).toBeFalsy(); + }); +} + +function shouldBeAbleToInsertAGroupNodeInsideAGroupNode() { + it('should be able to insert a group node inside a group node', () => { + service.setProject(demoProjectJSON); + const group1 = service.getNodeById('group1'); + const group2 = service.getNodeById('group2'); + expect(service.nodeHasTransitionToNodeId(group1, 'group2')).toBeTruthy(); + expect(service.nodeHasTransitionToNodeId(group2, 'group1')).toBeFalsy(); + service.insertNodeInsideOnlyUpdateTransitions('group2', 'group0'); + expect(service.nodeHasTransitionToNodeId(group2, 'group1')).toBeTruthy(); + /* + * the transition from group1 to group2 still remains because it is usually + * removed by calling removeNodeIdFromTransitions() but we don't call it here + */ + expect(service.nodeHasTransitionToNodeId(group1, 'group2')).toBeTruthy(); + }); +} + +function shouldNotBeAbleToInsertAStepNodeInsideAStepNode() { + it('should not be able to insert a step node inside a step node', () => { + service.setProject(demoProjectJSON); + expect(() => { + service.insertNodeInsideOnlyUpdateTransitions('node1', 'node2'); + }).toThrow('Error: insertNodeInsideOnlyUpdateTransitions() second parameter must be a group'); + }); +} + +function shouldDeleteAStepFromTheProject() { + it('should delete a step from the project', () => { + service.setProject(demoProjectJSON); + expect(service.getNodes().length).toEqual(54); + expect(service.getNodeById('node5')).not.toBeNull(); + expect(service.nodeHasTransitionToNodeId(service.getNodeById('node4'), 'node5')).toBeTruthy(); + expect(service.nodeHasTransitionToNodeId(service.getNodeById('node5'), 'node6')).toBeTruthy(); + expect(service.getNodesWithTransitionToNodeId('node6').length).toEqual(1); + service.deleteNode('node5'); + expect(service.getNodes().length).toEqual(53); + expect(service.getNodeById('node5')).toBeNull(); + expect(service.nodeHasTransitionToNodeId(service.getNodeById('node4'), 'node6')).toBeTruthy(); + expect(service.getNodesWithTransitionToNodeId('node6').length).toEqual(1); + }); +} + +function shouldDeleteAnInactiveStepFromTheProject() { + it('should delete an inactive step from the project', () => { + service.setProject(demoProjectJSON); + expect(service.getInactiveNodes().length).toEqual(1); + expect(service.getNodeById('node789')).not.toBeNull(); + service.deleteNode('node789'); + expect(service.getInactiveNodes().length).toEqual(0); + expect(service.getNodeById('node789')).toBeNull(); + }); +} + +function shouldDeleteAStepThatIsTheStartIdOfTheProject() { + it('should delete a step that is the start id of the project', () => { + service.setProject(demoProjectJSON); + expect(service.getStartNodeId()).toEqual('node1'); + expect(service.getNodesWithTransitionToNodeId('node2').length).toEqual(1); + service.deleteNode('node1'); + expect(service.getStartNodeId()).toEqual('node2'); + expect(service.getNodesWithTransitionToNodeId('node2').length).toEqual(0); + }); +} + +function shouldDeleteAStepThatIsTheLastStepOfTheProject() { + it('should delete a step that is the last step of the project', () => { + service.setProject(demoProjectJSON); + expect(service.getTransitionsByFromNodeId('node802').length).toEqual(1); + expect( + service.nodeHasTransitionToNodeId(service.getNodeById('node802'), 'node803') + ).toBeTruthy(); + service.deleteNode('node803'); + expect(service.getTransitionsByFromNodeId('node802').length).toEqual(0); + expect( + service.nodeHasTransitionToNodeId(service.getNodeById('node802'), 'node803') + ).toBeFalsy(); + }); +} + +function shouldDeleteAStepThatIsTheStartIdOfAnAactivityThatIsNotTheFirstActivity() { + it('should delete a step that is the start id of an activity that is not the first activity', () => { + service.setProject(demoProjectJSON); + expect(service.getGroupStartId('group2')).toEqual('node20'); + expect(service.nodeHasTransitionToNodeId(service.getNodeById('node19'), 'node20')).toBeTruthy(); + expect(service.nodeHasTransitionToNodeId(service.getNodeById('node20'), 'node21')).toBeTruthy(); + service.deleteNode('node20'); + expect(service.getGroupStartId('group2')).toEqual('node21'); + expect(service.nodeHasTransitionToNodeId(service.getNodeById('node19'), 'node21')).toBeTruthy(); + }); +} + +function shouldDeleteTheFirstActivityFromTheProject() { + it('should delete the first activity from the project', () => { + service.setProject(demoProjectJSON); + expect(service.getGroupStartId('group0')).toEqual('group1'); + expect(service.getStartNodeId()).toEqual('node1'); + expect(service.getNodes().length).toEqual(54); + expect(service.getNodesWithTransitionToNodeId('node20').length).toEqual(1); + service.deleteNode('group1'); + expect(service.getNodeById('group1')).toBeNull(); + expect(service.getGroupStartId('group0')).toEqual('group2'); + expect(service.getStartNodeId()).toEqual('node20'); + expect(service.getNodes().length).toEqual(34); + expect(service.getNodesWithTransitionToNodeId('node20').length).toEqual(0); + }); +} + +function shouldDeleteAnActivityInTheMiddleOfTheProject() { + it('should delete an activity that is in the middle of the project', () => { + service.setProject(demoProjectJSON); + expect(service.nodeHasTransitionToNodeId(service.getNodeById('group2'), 'group3')).toBeTruthy(); + expect(service.getNodes().length).toEqual(54); + service.deleteNode('group3'); + expect(service.nodeHasTransitionToNodeId(service.getNodeById('group2'), 'group3')).toBeFalsy(); + expect(service.nodeHasTransitionToNodeId(service.getNodeById('group2'), 'group4')).toBeTruthy(); + expect(service.getNodes().length).toEqual(51); + }); +} + +function shouldDeleteTheLastActivityFromTheProject() { + it('should delete the last activity from the project', () => { + service.setProject(demoProjectJSON); + expect(service.nodeHasTransitionToNodeId(service.getNodeById('group4'), 'group5')).toBeTruthy(); + expect(service.getTransitionsByFromNodeId('group4').length).toEqual(1); + expect(service.getNodes().length).toEqual(54); + service.deleteNode('group5'); + expect(service.nodeHasTransitionToNodeId(service.getNodeById('group4'), 'group5')).toBeFalsy(); + expect(service.getTransitionsByFromNodeId('group4').length).toEqual(0); + expect(service.getNodes().length).toEqual(48); + }); +} + +function insertNodeAfterInTransitions() { + it('should be able to insert a step node after another step node', () => { + expectInsertNodeAfterInTransition('node1', 'node2'); + }); + it('should be able to insert an activity node after another activity node', () => { + expectInsertNodeAfterInTransition('group1', 'group2'); + }); +} + +function getUniqueAuthors() { + describe('getUniqueAuthors', () => { + it('should get unique authors when there are no authors', () => { + const authors = []; + const uniqueAuthors = service.getUniqueAuthors(authors); + expect(uniqueAuthors.length).toEqual(0); + }); + + it('should get unique authors when there is one author', () => { + const authors = [{ id: 1, firstName: 'a', lastName: 'a' }]; + const uniqueAuthors = service.getUniqueAuthors(authors); + expect(uniqueAuthors.length).toEqual(1); + expect(uniqueAuthors[0].id).toEqual(1); + expect(uniqueAuthors[0].firstName).toEqual('a'); + expect(uniqueAuthors[0].lastName).toEqual('a'); + }); + + it('should get unique authors when there are multiple duplicates', () => { + const authors = [ + { id: 1, firstName: 'a', lastName: 'a' }, + { id: 2, firstName: 'b', lastName: 'b' }, + { id: 1, firstName: 'a', lastName: 'a' }, + { id: 3, firstName: 'c', lastName: 'c' }, + { id: 3, firstName: 'c', lastName: 'c' }, + { id: 1, firstName: 'a', lastName: 'a' } + ]; + const uniqueAuthors = service.getUniqueAuthors(authors); + expect(uniqueAuthors.length).toEqual(3); + expect(uniqueAuthors[0].id).toEqual(1); + expect(uniqueAuthors[0].firstName).toEqual('a'); + expect(uniqueAuthors[0].lastName).toEqual('a'); + expect(uniqueAuthors[1].id).toEqual(2); + expect(uniqueAuthors[1].firstName).toEqual('b'); + expect(uniqueAuthors[1].lastName).toEqual('b'); + expect(uniqueAuthors[2].id).toEqual(3); + expect(uniqueAuthors[2].firstName).toEqual('c'); + expect(uniqueAuthors[2].lastName).toEqual('c'); + }); + + it('should get unique authors when there are no duplicates', () => { + const authors = [ + { id: 1, firstName: 'a', lastName: 'a' }, + { id: 2, firstName: 'b', lastName: 'b' }, + { id: 3, firstName: 'c', lastName: 'c' } + ]; + const uniqueAuthors = service.getUniqueAuthors(authors); + expect(uniqueAuthors.length).toEqual(3); + expect(uniqueAuthors[0].id).toEqual(1); + expect(uniqueAuthors[0].firstName).toEqual('a'); + expect(uniqueAuthors[0].lastName).toEqual('a'); + expect(uniqueAuthors[1].id).toEqual(2); + expect(uniqueAuthors[1].firstName).toEqual('b'); + expect(uniqueAuthors[1].lastName).toEqual('b'); + expect(uniqueAuthors[2].id).toEqual(3); + expect(uniqueAuthors[2].firstName).toEqual('c'); + expect(uniqueAuthors[2].lastName).toEqual('c'); + }); + }); +} + +function deleteActivityWithBranching() { + it(`should delete an activity with branching and is also the first activity in the project + and properly set the project start node id`, () => { + service.setProject(demoProjectJSON); + expect(service.getStartNodeId()).toEqual('node1'); + service.deleteNode('group1'); + expect(service.getStartNodeId()).toEqual('node20'); + }); + + it(`should delete an activity in the middle of the project with branching and properly remove + transitions from remaining steps`, () => { + service.setProject(demoProjectJSON); + const node19 = service.getNodeById('node19'); + const node19Transitions = node19.transitionLogic.transitions; + expect(node19Transitions.length).toEqual(1); + expect(node19Transitions[0].to).toEqual('node20'); + service.deleteNode('group2'); + expect(node19Transitions.length).toEqual(1); + expect(node19Transitions[0].to).toEqual('node790'); + }); + + it(`should delete an activity at the end of the project with branching and properly remove + transitions from remaining steps`, () => { + service.setProject(demoProjectJSON); + const node798 = service.getNodeById('node798'); + const node798Transitions = node798.transitionLogic.transitions; + expect(node798Transitions.length).toEqual(1); + expect(node798Transitions[0].to).toEqual('node799'); + service.deleteNode('group5'); + expect(node798Transitions.length).toEqual(0); + }); +} + +function deleteTheLastStepInAnActivity() { + it(`should delete the last step in an activity in the middle of the project and set previous + step to transition to the first step of the next activity`, () => { + service.setProject(demoProjectJSON); + const node790Transitions = service.getTransitionsByFromNodeId('node790'); + expect(node790Transitions.length).toEqual(1); + expect( + service.nodeHasTransitionToNodeId(service.getNodeById('node790'), 'node791') + ).toBeTruthy(); + service.deleteNode('node791'); + expect(node790Transitions.length).toEqual(1); + expect( + service.nodeHasTransitionToNodeId(service.getNodeById('node790'), 'node792') + ).toBeTruthy(); + }); +} + +function deleteAllStepsInAnActivity() { + it(`should delete all steps in an activity in the middle of the project and set previous step + to transition to activity`, () => { + service.setProject(demoProjectJSON); + const node34 = service.getNodeById('node34'); + const node34Transitions = node34.transitionLogic.transitions; + expect(node34Transitions.length).toEqual(1); + expect(node34Transitions[0].to).toEqual('node790'); + service.deleteNode('node790'); + service.deleteNode('node791'); + expect(node34Transitions.length).toEqual(1); + expect(node34Transitions[0].to).toEqual('group3'); + }); +} + +function addCurrentUserToAuthors_CM_shouldAddUserInfo() { + it('should add current user to authors in CM mode', () => { + spyOn(configService, 'getMyUserInfo').and.returnValue({ + userIds: [1], + firstName: 'wise', + lastName: 'panda', + username: 'wisepanda' + }); + spyOn(configService, 'isClassroomMonitor').and.returnValue(true); + const authors = service.addCurrentUserToAuthors([]); + expect(authors.length).toEqual(1); + expect(authors[0].id).toEqual(1); + }); +} + +function getNodeIds() { + describe('getNodeIds', () => { + it('should return the node ids in the project', () => { + service.setProject(scootersProjectJSON); + const nodeIdsExpected = [ + 'node1', + 'node2', + 'node3', + 'node4', + 'node5', + 'node6', + 'node7', + 'node9', + 'node12', + 'node13', + 'node14', + 'node18', + 'node19', + 'node21', + 'node22', + 'node23', + 'node24', + 'node25', + 'node26', + 'node27', + 'node28', + 'node29', + 'node30', + 'node31', + 'node40', + 'node32', + 'node33', + 'node34', + 'node35', + 'node36', + 'node37', + 'node38', + 'node39', + 'nodeWithNoComponents' + ]; + const nodeIdsActual = service.getNodeIds(); + expect(nodeIdsActual).toEqual(nodeIdsExpected); + }); + }); +} + +function getInactiveNodeIds() { + describe('getInactiveNodeIds', () => { + it('should return the inactive nodes in the project', () => { + service.setProject(scootersProjectJSON); + expect(service.getInactiveNodeIds()).toEqual(['node41', 'node42']); + }); + }); +} + +function shouldHandleSaveProjectResponse() { + it('should broadcast project saved', () => { + shouldHandleSaveProjectResponseSuccessHelper('broadcastProjectSaved'); + }); + it('should broadcast not logged in project not saved', () => { + shouldHandleSaveProjectResponseErrorHelper( + 'notSignedIn', + 'broadcastNotLoggedInProjectNotSaved' + ); + }); + it('should broadcast not allowed to edit this project', () => { + shouldHandleSaveProjectResponseErrorHelper( + 'notAllowedToEditThisProject', + 'broadcastNotAllowedToEditThisProject' + ); + }); + it('should broadcast error saving project', () => { + shouldHandleSaveProjectResponseErrorHelper('errorSavingProject', 'broadcastErrorSavingProject'); + }); +} + +function shouldHandleSaveProjectResponseSuccessHelper(functionName: any) { + shouldHandleSaveProjectResponseHelper('success', '', functionName); +} + +function shouldHandleSaveProjectResponseErrorHelper(messageCode: string, functionName: any) { + shouldHandleSaveProjectResponseHelper('error', messageCode, functionName); +} + +function shouldHandleSaveProjectResponseHelper( + status: string, + messageCode: string, + functionName: any +) { + const response = { + status: status, + messageCode: messageCode + }; + spyOn(service, functionName).and.callFake(() => {}); + service.handleSaveProjectResponse(response); + expect(service[functionName]).toHaveBeenCalled(); +} + +function shouldNotSaveProjectWhenTheUserDoesNotHavePermissionToEditTheProject() { + it('should not save project when the user does not have permission to edit the project', () => { + service.setProject(scootersProjectJSON); + spyOn(configService, 'getConfigParam') + .withArgs('canEditProject') + .and.returnValue(false); + expect(service.saveProject()).toEqual(null); + }); +} + +function shouldSaveProject() { + it('should save project', () => { + spyOn(configService, 'getConfigParam') + .withArgs('canEditProject') + .and.returnValue(true) + .withArgs('saveProjectURL') + .and.returnValue(saveProjectURL) + .withArgs('mode') + .and.returnValue('authoring') + .withArgs('userInfo') + .and.returnValue({}); + spyOn(configService, 'getProjectId').and.returnValue(projectIdDefault); + spyOn(configService, 'getMyUserInfo').and.returnValue({ id: 1 }); + service.setProject(scootersProjectJSON); + service.saveProject(); + expect(configService.getConfigParam).toHaveBeenCalledWith('saveProjectURL'); + http.expectOne(saveProjectURL); + }); +} diff --git a/src/main/webapp/wise5/authoringTool/node/editRubric/edit-rubric.component.ts b/src/main/webapp/wise5/authoringTool/node/editRubric/edit-rubric.component.ts index 28a3a2fc23..889ea59d12 100644 --- a/src/main/webapp/wise5/authoringTool/node/editRubric/edit-rubric.component.ts +++ b/src/main/webapp/wise5/authoringTool/node/editRubric/edit-rubric.component.ts @@ -1,7 +1,6 @@ -import { ProjectAssetService } from '../../../../site/src/app/services/projectAssetService'; import { ConfigService } from '../../../services/configService'; -import { ProjectService } from '../../../services/projectService'; import { TeacherDataService } from '../../../services/teacherDataService'; +import { TeacherProjectService } from '../../../services/teacherProjectService'; import { UtilService } from '../../../services/utilService'; class EditRubricComponentController { @@ -20,22 +19,22 @@ class EditRubricComponentController { constructor( private $state: any, private ConfigService: ConfigService, - private ProjectService: ProjectService, + private TeacherProjectService: TeacherProjectService, private TeacherDataService: TeacherDataService, private UtilService: UtilService ) {} $onInit(): void { this.nodeId = this.TeacherDataService.getCurrentNodeId(); - this.node = this.ProjectService.getNodeById(this.nodeId); - this.rubric = this.ProjectService.replaceAssetPaths(this.node.rubric); + this.node = this.TeacherProjectService.getNodeById(this.nodeId); + this.rubric = this.TeacherProjectService.replaceAssetPaths(this.node.rubric); } rubricChanged(): void { let html = this.ConfigService.removeAbsoluteAssetPaths(this.rubric); html = this.UtilService.insertWISELinks(html); this.node.rubric = html; - this.ProjectService.saveProject(); + this.TeacherProjectService.saveProject(); } goBack(): void { diff --git a/src/main/webapp/wise5/components/componentController.ts b/src/main/webapp/wise5/components/componentController.ts index df26de6fc6..5dfac72bea 100644 --- a/src/main/webapp/wise5/components/componentController.ts +++ b/src/main/webapp/wise5/components/componentController.ts @@ -230,12 +230,12 @@ class ComponentController { } initializeScopeGetComponentState(scope, childControllerName) { - scope.getComponentState = (isSubmit) => { + scope.getComponentState = isSubmit => { const deferred = this.$q.defer(); const childController = scope[childControllerName]; if (this.hasDirtyWorkToSendToParent(childController, isSubmit)) { const action = this.getDirtyWorkToSendToParentAction(childController, isSubmit); - childController.createComponentState(action).then((componentState) => { + childController.createComponentState(action).then(componentState => { deferred.resolve(componentState); }); } else { @@ -460,7 +460,7 @@ class ComponentController { * data has changed. */ createComponentStateAndBroadcast(action) { - this.createComponentState(action).then((componentState) => { + this.createComponentState(action).then(componentState => { this.emitComponentStudentDataChanged(componentState); }); } @@ -670,7 +670,7 @@ class ComponentController { } importWorkByStudentWorkId(studentWorkId) { - this.StudentDataService.getStudentWorkById(studentWorkId).then((componentState) => { + this.StudentDataService.getStudentWorkById(studentWorkId).then(componentState => { if (componentState != null) { this.setStudentWork(componentState); this.setParentStudentWorkIdToCurrentStudentWork(studentWorkId); @@ -751,7 +751,7 @@ class ComponentController { } attachStudentAsset(studentAsset) { - return this.StudentAssetService.copyAssetForReference(studentAsset).then((copiedAsset) => { + return this.StudentAssetService.copyAssetForReference(studentAsset).then(copiedAsset => { const attachment = { studentAssetId: copiedAsset.id, iconURL: copiedAsset.iconURL, @@ -829,7 +829,7 @@ class ComponentController { this.audioRecordedSubscription = this.AudioRecorderService.audioRecorded$.subscribe( ({ requester, audioFile }) => { if (requester === `${this.nodeId}-${this.componentId}`) { - this.StudentAssetService.uploadAsset(audioFile).then((studentAsset) => { + this.StudentAssetService.uploadAsset(audioFile).then(studentAsset => { this.attachStudentAsset(studentAsset).then(() => { this.StudentAssetService.deleteAsset(studentAsset); }); @@ -871,7 +871,7 @@ class ComponentController { $scope.nodeId = nodeId; $scope.componentId = componentId; $scope.componentState = componentState; - $scope.closeDialog = function () { + $scope.closeDialog = function() { $mdDialog.hide(); }; } @@ -882,14 +882,12 @@ class ComponentController { if (componentState.nodeId == nodeId && componentState.componentId == componentId) { setTimeout(() => { const componentService = this.$injector.get(componentState.componentType + 'Service'); - componentService - .generateImageFromRenderedComponentState(componentState) - .then((image) => { - clearTimeout(destroyDoneRenderingComponentListenerTimeout); - doneRenderingComponentSubscription.unsubscribe(); - deferred.resolve(image); - this.$mdDialog.hide(); - }); + componentService.generateImageFromRenderedComponentState(componentState).then(image => { + clearTimeout(destroyDoneRenderingComponentListenerTimeout); + doneRenderingComponentSubscription.unsubscribe(); + deferred.resolve(image); + this.$mdDialog.hide(); + }); }, 1000); } } diff --git a/src/main/webapp/wise5/components/conceptMap/conceptMapService.ts b/src/main/webapp/wise5/components/conceptMap/conceptMapService.ts index 564898b6ab..791c1ab87b 100644 --- a/src/main/webapp/wise5/components/conceptMap/conceptMapService.ts +++ b/src/main/webapp/wise5/components/conceptMap/conceptMapService.ts @@ -1,6 +1,7 @@ 'use strict'; import * as angular from 'angular'; +import SVG from 'svg.js'; import { ComponentService } from '../componentService'; import { ConfigService } from '../../services/configService'; import { StudentAssetService } from '../../services/studentAssetService'; @@ -775,7 +776,7 @@ export class ConceptMapService extends ComponentService { let svgString = svgElement.innerHTML; // find all the images in the svg and replace them with Base64 images - this.getHrefToBase64ImageReplacements(svgString, true).then((images) => { + this.getHrefToBase64ImageReplacements(svgString, true).then(images => { /* * Loop through all the image objects. Each object contains * an image href and a Base64 image. @@ -825,7 +826,7 @@ export class ConceptMapService extends ComponentService { const thisUtilService = this.UtilService; // the function that is called after the image is fully loaded - image.onload = (event) => { + image.onload = event => { // get the image that was loaded const image: any = event.target; @@ -841,13 +842,13 @@ export class ConceptMapService extends ComponentService { const imageObject = thisUtilService.getImageObjectFromBase64String(base64Image); // create a student asset image - this.StudentAssetService.uploadAsset(imageObject).then((unreferencedAsset) => { + this.StudentAssetService.uploadAsset(imageObject).then(unreferencedAsset => { /* * make a copy of the unreferenced asset so that we * get a referenced asset */ this.StudentAssetService.copyAssetForReference(unreferencedAsset).then( - (referencedAsset) => { + referencedAsset => { if (referencedAsset != null) { /* * get the asset url @@ -969,7 +970,7 @@ export class ConceptMapService extends ComponentService { const ctx = myCanvas.getContext('2d'); // the function that is called after the image is fully loaded - image.onload = function (event) { + image.onload = function(event) { // get the image that was loaded const image: any = event.target; @@ -1146,7 +1147,7 @@ export class ConceptMapService extends ComponentService { let svgString = serializer.serializeToString(svgElement); // find all the images in the svg and replace them with Base64 images - this.getHrefToBase64ImageReplacements(svgString).then((images) => { + this.getHrefToBase64ImageReplacements(svgString).then(images => { /* * Loop through all the image objects. Each object contains * an image href and a Base64 image. @@ -1182,7 +1183,7 @@ export class ConceptMapService extends ComponentService { const image = new Image(); // the function that is called after the image is fully loaded - image.onload = (event) => { + image.onload = event => { // get the image that was loaded let image: any = event.target; @@ -1198,7 +1199,7 @@ export class ConceptMapService extends ComponentService { const imageObject = this.UtilService.getImageObjectFromBase64String(base64Image); // add the image to the student assets - this.StudentAssetService.uploadAsset(imageObject).then((asset) => { + this.StudentAssetService.uploadAsset(imageObject).then(asset => { resolve(asset); }); }; diff --git a/src/main/webapp/wise5/components/label/labelService.ts b/src/main/webapp/wise5/components/label/labelService.ts index d0e22e0e89..c0b7e43fc6 100644 --- a/src/main/webapp/wise5/components/label/labelService.ts +++ b/src/main/webapp/wise5/components/label/labelService.ts @@ -1,6 +1,7 @@ 'use strict'; import * as angular from 'angular'; +import SVG from 'svg.js'; import { ComponentService } from '../componentService'; import { StudentAssetService } from '../../services/studentAssetService'; import { Injectable } from '@angular/core'; @@ -287,7 +288,7 @@ export class LabelService extends ComponentService { const image = new Image(); const thisUtilService = this.UtilService; return new Promise((resolve, reject) => { - image.onload = (event) => { + image.onload = event => { const image: any = event.target; myCanvas.width = image.width; myCanvas.height = image.height; @@ -296,13 +297,13 @@ export class LabelService extends ComponentService { const imageObject = thisUtilService.getImageObjectFromBase64String(base64Image); // create a student asset image - this.StudentAssetService.uploadAsset(imageObject).then((unreferencedAsset) => { + this.StudentAssetService.uploadAsset(imageObject).then(unreferencedAsset => { /* * make a copy of the unreferenced asset so that we * get a referenced asset */ this.StudentAssetService.copyAssetForReference(unreferencedAsset).then( - (referencedAsset) => { + referencedAsset => { if (referencedAsset != null) { /* * get the asset url diff --git a/src/main/webapp/wise5/services/notificationService.ts b/src/main/webapp/wise5/services/notificationService.ts index 0a16c51279..9398cb35af 100644 --- a/src/main/webapp/wise5/services/notificationService.ts +++ b/src/main/webapp/wise5/services/notificationService.ts @@ -18,9 +18,11 @@ export class NotificationService { private setIsJSONValidSource: Subject = new Subject(); public setIsJSONValid$: Observable = this.setIsJSONValidSource.asObservable(); private serverConnectionStatusSource: Subject = new Subject(); - public serverConnectionStatus$: Observable = this.serverConnectionStatusSource.asObservable(); + public serverConnectionStatus$: Observable = + this.serverConnectionStatusSource.asObservable(); private viewCurrentAmbientNotificationSource: Subject = new Subject(); - public viewCurrentAmbientNotification$: Observable = this.viewCurrentAmbientNotificationSource.asObservable(); + public viewCurrentAmbientNotification$: Observable = + this.viewCurrentAmbientNotificationSource.asObservable(); constructor( private upgrade: UpgradeModule, @@ -136,7 +138,7 @@ export class NotificationService { this.ConfigService.getWorkgroupId(), notificationData, notificationGroupId - ).then((notification) => { + ).then(notification => { this.addNotification(notification); }); } @@ -226,7 +228,7 @@ export class NotificationService { let notifications = this.notifications; for (const p in args) { if (args.hasOwnProperty(p) && args[p] !== null) { - notifications = notifications.filter((notification) => { + notifications = notifications.filter(notification => { return notification[p] === args[p]; }); } diff --git a/src/main/webapp/wise5/services/projectService.ts b/src/main/webapp/wise5/services/projectService.ts index 46e6debd81..008fc36889 100644 --- a/src/main/webapp/wise5/services/projectService.ts +++ b/src/main/webapp/wise5/services/projectService.ts @@ -36,18 +36,8 @@ export class ProjectService { project: any; rootNode: any = null; transitions: any; - private errorSavingProjectSource: Subject = new Subject(); - public errorSavingProject$: Observable = this.errorSavingProjectSource.asObservable(); - private notAllowedToEditThisProjectSource: Subject = new Subject(); - public notAllowedToEditThisProject$: Observable = this.notAllowedToEditThisProjectSource.asObservable(); - private notLoggedInProjectNotSavedSource: Subject = new Subject(); - public notLoggedInProjectNotSaved$: Observable = this.notLoggedInProjectNotSavedSource.asObservable(); private projectChangedSource: Subject = new Subject(); public projectChanged$: Observable = this.projectChangedSource.asObservable(); - private projectSavedSource: Subject = new Subject(); - public projectSaved$: Observable = this.projectSavedSource.asObservable(); - private savingProjectSource: Subject = new Subject(); - public savingProject$: Observable = this.savingProjectSource.asObservable(); private snipImageSource: Subject = new Subject(); public snipImage$: Observable = this.snipImageSource.asObservable(); @@ -424,7 +414,7 @@ export class ProjectService { getGroupNodesIdToOrder() { const idToOrder = {}; - const onlyGroupNodes = Object.entries(this.idToOrder).filter((item) => { + const onlyGroupNodes = Object.entries(this.idToOrder).filter(item => { return this.isGroupNode(item[0]); }); for (const [key, value] of onlyGroupNodes) { @@ -530,7 +520,7 @@ export class ProjectService { '(\'|"|\\\\\'|\\\\")', 'gi' ), - (matchedString) => { + matchedString => { /* * once found, we prepend the contentBaseURL + "assets/" to the string within the quotes * and keep everything else the same. @@ -876,7 +866,7 @@ export class ProjectService { * the target ids show up in the project. */ constraintsComparatorGenerator(orderedNodeIds) { - return function (constraintA, constraintB) { + return function(constraintA, constraintB) { let constraintAIndex = orderedNodeIds.indexOf(constraintA.targetId); let constraintBIndex = orderedNodeIds.indexOf(constraintB.targetId); if (constraintAIndex < constraintBIndex) { @@ -1007,6 +997,16 @@ export class ProjectService { return transitionLogic.transitions; } + /** + * Get all the node ids from steps (not groups) + * @returns an array with all the node ids + */ + getNodeIds() { + return this.applicationNodes.map(node => { + return node.id; + }); + } + /** * Get nodes that have a transition to the given node id * @param toNodeId the node id @@ -1032,6 +1032,14 @@ export class ProjectService { return nodesByToNodeId; } + getActiveNodes() { + return this.project.nodes; + } + + getInactiveNodes() { + return this.project.inactiveNodes; + } + /** * Check if a node has a transition to the given nodeId. * @param node The node to check. @@ -1094,7 +1102,7 @@ export class ProjectService { return this.http .get(projectURL, { headers: headers }) .toPromise() - .then((projectJSON) => { + .then(projectJSON => { this.setProject(projectJSON); return projectJSON; }); @@ -1120,129 +1128,6 @@ export class ProjectService { }); } - /** - * Saves the project to Config.saveProjectURL and returns commit history promise. - * if Config.saveProjectURL or Config.projectId are undefined, does not save and returns null - */ - saveProject(): any { - if (!this.ConfigService.getConfigParam('canEditProject')) { - this.broadcastNotAllowedToEditThisProject(); - return null; - } - this.broadcastSavingProject(); - this.cleanupBeforeSave(); - this.project.metadata.authors = this.getUniqueAuthors( - this.addCurrentUserToAuthors(this.getAuthors()) - ); - return this.http - .post( - this.ConfigService.getConfigParam('saveProjectURL'), - angular.toJson(this.project, false) - ) - .toPromise() - .then((response: any) => { - this.handleSaveProjectResponse(response); - }); - } - - getAuthors(): any[] { - return this.project.metadata.authors ? this.project.metadata.authors : []; - } - - addCurrentUserToAuthors(authors: any[]): any[] { - let userInfo = this.ConfigService.getMyUserInfo(); - if (this.ConfigService.isClassroomMonitor()) { - userInfo = { - id: userInfo.userIds[0], - firstName: userInfo.firstName, - lastName: userInfo.lastName, - username: userInfo.username - }; - } - authors.push(userInfo); - return authors; - } - - getUniqueAuthors(authors = []): any[] { - const idToAuthor = {}; - const uniqueAuthors = []; - for (const author of authors) { - if (idToAuthor[author.id] == null) { - uniqueAuthors.push(author); - idToAuthor[author.id] = author; - } - } - return uniqueAuthors; - } - - handleSaveProjectResponse(response: any): any { - if (response.status === 'error') { - if (response.messageCode === 'notSignedIn') { - this.broadcastNotLoggedInProjectNotSaved(); - this.SessionService.forceLogOut(); - } else if (response.messageCode === 'notAllowedToEditThisProject') { - this.broadcastNotAllowedToEditThisProject(); - } else if (response.messageCode === 'errorSavingProject') { - this.broadcastErrorSavingProject(); - } - } else { - this.broadcastProjectSaved(); - } - return response; - } - - /** - * Perform any necessary cleanup before we save the project. - * For example we need to remove the checked field in the inactive node - * objects. - */ - cleanupBeforeSave() { - this.getActiveNodes().forEach((activeNode) => { - this.cleanupNode(activeNode); - }); - this.getInactiveNodes().forEach((inactiveNode) => { - this.cleanupNode(inactiveNode); - }); - } - - /** - * Remove any fields that are used temporarily for display purposes like when - * the project is loaded in the authoring tool and grading tool - * @param node The node object. - */ - cleanupNode(node) { - delete node.checked; - delete node.hasWork; - delete node.hasAlert; - delete node.hasNewAlert; - delete node.isVisible; - delete node.completionStatus; - delete node.score; - delete node.hasScore; - delete node.maxScore; - delete node.hasMaxScore; - delete node.scorePct; - delete node.order; - delete node.show; - - if (node.components != null) { - // activity node does not have components but step node does - node.components.forEach((component) => { - this.cleanupComponent(component); - }); - } - } - - /** - * Remove any fields that are used temporarily for display purposes like when - * the project is loaded in the authoring tool and grading tool - * @param component The component object. - */ - cleanupComponent(component) { - delete component.checked; - delete component.hasWork; - } - /** * Returns the theme path for the current project */ @@ -2176,1291 +2061,188 @@ export class ProjectService { } /** - * Insert the node after the given node id in the group's array of children ids - * @param nodeIdToInsert the node id we want to insert - * @param nodeIdToInsertAfter the node id we want to insert after - */ - insertNodeAfterInGroups(nodeIdToInsert, nodeIdToInsertAfter) { - const groupNodes = this.getGroupNodes(); - if (groupNodes != null) { - for (const group of groupNodes) { - this.insertNodeAfterInGroup(group, nodeIdToInsert, nodeIdToInsertAfter); - } - } - const inactiveGroupNodes = this.getInactiveGroupNodes(); - if (inactiveGroupNodes != null) { - for (const inactiveGroup of inactiveGroupNodes) { - this.insertNodeAfterInGroup(inactiveGroup, nodeIdToInsert, nodeIdToInsertAfter); - } - } - } - - /** - * Insert a node id in a group after another specific node id. - * @param group A group object. - * @param nodeIdToInsert The node id to insert. - * @param nodeIdToInsertAfter The node id to insert after. - * @returns {boolean} Whether we inserted the node id. - */ - insertNodeAfterInGroup(group, nodeIdToInsert, nodeIdToInsertAfter) { - const ids = group.ids; - if (ids != null) { - for (let i = 0; i < ids.length; i++) { - const id = ids[i]; - if (nodeIdToInsertAfter === id) { - ids.splice(i + 1, 0, nodeIdToInsert); - return true; - } - } - } - return false; - } - - /** - * Update the transitions to handle inserting a node after another node. - * The two nodes must either both be steps or both be activities. - * @param nodeToInsert the node to insert - * @param nodeIdToInsertAfter the node id to insert after - */ - insertNodeAfterInTransitions(nodeToInsert, nodeIdToInsertAfter) { - const nodeToInsertAfter = this.getNodeById(nodeIdToInsertAfter); - if (nodeToInsert.type != nodeToInsertAfter.type) { - throw 'Error: insertNodeAfterInTransitions() nodes are not the same type'; - } - if (nodeToInsertAfter.transitionLogic == null) { - nodeToInsertAfter.transitionLogic = { - transitions: [] - }; - } - if (nodeToInsert.transitionLogic == null) { - nodeToInsert.transitionLogic = { - transitions: [] - }; - } - if (this.isGroupNode(nodeToInsert.id)) { - this.updateChildrenTransitionsInAndOutOfGroup(nodeToInsert, nodeIdToInsertAfter); - } - this.copyTransitions(nodeToInsertAfter, nodeToInsert); - if (nodeToInsert.transitionLogic.transitions.length == 0) { - this.copyParentTransitions(nodeIdToInsertAfter, nodeToInsert); - } - const transitionObject = { - to: nodeToInsert.id - }; - nodeToInsertAfter.transitionLogic.transitions = [transitionObject]; - this.updateBranchPathTakenConstraints(nodeToInsert, nodeIdToInsertAfter); - } - - /* - * Copy the transitions from nodeId's parent and add to node's transitions. - * @param nodeId Copy the transition of this nodeId's parent. - * @param node The node to add transitions to. + * Get the max score for the node + * @param nodeId the node id which can be a step or an activity + * @returns the max score for the node which can be null or a number + * if null, author/teacher has not set a max score for the node */ - copyParentTransitions(nodeId, node) { - const parentGroupId = this.getParentGroupId(nodeId); - if (parentGroupId != 'group0') { - const parentTransitions = this.getTransitionsByFromNodeId(parentGroupId); - for (let parentTransition of parentTransitions) { - const newTransition = {}; - const toNodeId = parentTransition.to; - if (this.isGroupNode(toNodeId)) { - const startId = this.getGroupStartId(toNodeId); - if (startId == null || startId == '') { - (newTransition).to = toNodeId; + getMaxScoreForNode(nodeId) { + let maxScore = null; + if (!this.isGroupNode(nodeId)) { + const node = this.getNodeById(nodeId); + for (let component of node.components) { + const componentMaxScore = component.maxScore; + if (typeof componentMaxScore == 'number') { + if (maxScore == null) { + maxScore = componentMaxScore; } else { - (newTransition).to = startId; + maxScore += componentMaxScore; } } - node.transitionLogic.transitions.push(newTransition); } } - } - - copyTransitions(previousNode, node) { - const transitionsJSONString = angular.toJson(previousNode.transitionLogic.transitions); - const transitionsCopy = angular.fromJson(transitionsJSONString); - node.transitionLogic.transitions = transitionsCopy; + return maxScore; } /** - * If the previous node was in a branch path, we will also put the - * inserted node into the branch path. - * @param node The node that is in the branch path. - * @param nodeId The node we are adding to the branch path. + * Get the max score for a component + * @param nodeId get the max score from a component in this node + * @param componentId get the max score from this component */ - updateBranchPathTakenConstraints(node, nodeId) { - this.removeBranchPathTakenNodeConstraintsIfAny(node.id); - const branchPathTakenConstraints = this.getBranchPathTakenConstraintsByNodeId(nodeId); - for (let branchPathTakenConstraint of branchPathTakenConstraints) { - const newConstraint = { - id: this.getNextAvailableConstraintIdForNodeId(node.id), - action: branchPathTakenConstraint.action, - targetId: node.id, - removalCriteria: this.UtilService.makeCopyOfJSONObject( - branchPathTakenConstraint.removalCriteria - ) - }; - this.addConstraintToNode(node, newConstraint); + getMaxScoreForComponent(nodeId, componentId) { + const component = this.getComponentByNodeIdAndComponentId(nodeId, componentId); + if (component != null) { + return component.maxScore; } + return null; } /** - * Update a node's branchPathTaken constraint's fromNodeId and toNodeId - * @param node update the branch path taken constraints in this node - * @param currentFromNodeId the current from node id - * @param currentToNodeId the current to node id - * @param newFromNodeId the new from node id - * @param newToNodeId the new to node id + * Determine if a node id is a direct child of a group + * @param nodeId the node id + * @param groupId the group id */ - updateBranchPathTakenConstraint( - node, - currentFromNodeId, - currentToNodeId, - newFromNodeId, - newToNodeId - ) { - for (let constraint of node.constraints) { - for (let removalCriterion of constraint.removalCriteria) { - if (removalCriterion.name === 'branchPathTaken') { - const params = removalCriterion.params; - if (params.fromNodeId === currentFromNodeId && params.toNodeId === currentToNodeId) { - params.fromNodeId = newFromNodeId; - params.toNodeId = newToNodeId; - } - } - } - } + isNodeInGroup(nodeId, groupId) { + const group = this.getNodeById(groupId); + return group.ids.indexOf(nodeId) != -1; } /** - * Insert a node into a group - * @param nodeIdToInsert the node id to insert - * @param nodeIdToInsertInside the node id of the group we will insert into + * Get the first leaf node by traversing all the start ids + * until a leaf node id is found */ - insertNodeInsideInGroups(nodeIdToInsert, nodeIdToInsertInside) { - const group = this.getNodeById(nodeIdToInsertInside); - if (group != null) { - const ids = group.ids; - if (ids != null) { - ids.splice(0, 0, nodeIdToInsert); - group.startId = nodeIdToInsert; + getFirstLeafNodeId() { + let firstLeafNodeId = null; + const startGroupId = this.project.startGroupId; + let node = this.getNodeById(startGroupId); + let done = false; + + // loop until we have found a leaf node id or something went wrong + while (!done) { + if (node == null) { + done = true; + } else if (this.isGroupNode(node.id)) { + firstLeafNodeId = node.id; + node = this.getNodeById(node.startId); + } else if (this.isApplicationNode(node.id)) { + firstLeafNodeId = node.id; + done = true; + } else { + done = true; } } + return firstLeafNodeId; } /** - * Update the transitions to handle inserting a node as the first step in a group. - * @param nodeIdToInsert node id that we will insert - * @param nodeIdToInsertInside the node id of the group we are inserting into + * Get the message that describes how to disable the constraint + * @param nodeId the node the student is trying to go to + * @param constraint the constraint that is preventing the student + * from going to the node + * @returns the message to display to the student that describes how + * to disable the constraint */ - insertNodeInsideOnlyUpdateTransitions(nodeIdToInsert, nodeIdToInsertInside) { - if (!this.isGroupNode(nodeIdToInsertInside)) { - throw 'Error: insertNodeInsideOnlyUpdateTransitions() second parameter must be a group'; - } + getConstraintMessage(nodeId, constraint) { + let message = ''; - const nodeToInsert = this.getNodeById(nodeIdToInsert); - nodeToInsert.transitionLogic.transitions = []; - this.removeBranchPathTakenNodeConstraintsIfAny(nodeIdToInsert); + if (nodeId != null && constraint != null) { + // get the node title the student is trying to go to + const nodeTitle = this.getNodePositionAndTitleByNodeId(nodeId); - if (this.isGroupNode(nodeIdToInsert)) { - this.updateChildrenTransitionsInAndOutOfGroup(nodeToInsert); - } + const removalConditional = constraint.removalConditional; + const removalCriteria = constraint.removalCriteria; - /* - * the node will become the first node in the group. this means we need to update any nodes - * that point to the old start id and make them point to the node we are inserting. - */ - const group = this.getNodeById(nodeIdToInsertInside); - const startId = group.startId; - this.updateTransitionsToStartId(startId, nodeIdToInsert); - this.updateStepTransitionsToGroup(nodeIdToInsertInside, nodeIdToInsert); - this.createTransitionFromNodeToInsertToOldStartNode(startId, nodeToInsert); - const transitions = this.getTransitionsByFromNodeId(nodeIdToInsert); - if (transitions.length == 0) { - this.inheritParentTransitions(nodeIdToInsertInside, nodeToInsert); + if (removalCriteria != null) { + let criteriaMessages = ''; + for (let tempRemovalCriteria of removalCriteria) { + if (tempRemovalCriteria != null) { + // get the message that describes the criteria that needs to be satisfied + const criteriaMessage = this.getCriteriaMessage(tempRemovalCriteria); + + if (criteriaMessage != null && criteriaMessage != '') { + // separate criteria messages with a line break + if (criteriaMessages != '') { + criteriaMessages += '
'; + } + criteriaMessages += criteriaMessage; + } + } + } + message += criteriaMessages; + } } + return message; } /** - * Copy the transitions from the parent to the node we are inserting. - * @param nodeIdToInsertInside - * @param nodeToInsert + * Get the human readable description of the constraint. + * @param constraint The constraint object. + * @returns A human readable text string that describes the constraint. + * example + * 'All steps after this one will not be visitable until the student completes + * "3.7 Revise Your Bowls Explanation"' */ - inheritParentTransitions(nodeIdToInsertInside, nodeToInsert) { - const parentTransitions = this.getTransitionsByFromNodeId(nodeIdToInsertInside); - for (let parentTransition of parentTransitions) { - const toNodeId = parentTransition.to; - if (this.isGroupNode(toNodeId)) { - const nextGroup = this.getNodeById(toNodeId); - const startId = nextGroup.startId; - if (startId == null || startId == '') { - this.addToTransition(nodeToInsert, toNodeId); - } else { - this.addToTransition(nodeToInsert, startId); + getConstraintDescription(constraint) { + let message = ''; + for (const singleRemovalCriteria of constraint.removalCriteria) { + if (message != '') { + // this constraint has multiple removal criteria + if (constraint.removalConditional === 'any') { + message += ' or '; + } else if (constraint.removalConditional === 'all') { + message += ' and '; } - } else { - this.addToTransition(nodeToInsert, toNodeId); } + message += this.getCriteriaMessage(singleRemovalCriteria); } + return this.getActionMessage(constraint.action) + message; } - /* - * Create a transition from the node we are inserting to the node that - * was the start node. - * @param startId - * @param nodeToInsert + /** + * Get the constraint action as human readable text. + * @param action A constraint action. + * @return A human readable text string that describes the action + * example + * 'All steps after this one will not be visitable until ' */ - createTransitionFromNodeToInsertToOldStartNode(startId, nodeToInsert) { - const startNode = this.getNodeById(startId); - if (startNode != null) { - const transitions = this.getTransitionsByFromNodeId(nodeToInsert.id); - const transitionObject = { - to: startId - }; - transitions.push(transitionObject); + getActionMessage(action) { + if (action === 'makeAllNodesAfterThisNotVisitable') { + return this.upgrade.$injector.get('$filter')('translate')( + 'allStepsAfterThisOneWillNotBeVisitableUntil' + ); } - } - - /* - * Update all the transitions that point to the group and change - * them to point to the new start id - */ - updateStepTransitionsToGroup(nodeIdToInsertInside, nodeIdToInsert) { - const nodesThatTransitionToGroup = this.getNodesByToNodeId(nodeIdToInsertInside); - for (let nodeThatTransitionsToGroup of nodesThatTransitionToGroup) { - if (!this.isGroupNode(nodeThatTransitionsToGroup.id)) { - this.updateToTransition(nodeThatTransitionsToGroup, nodeIdToInsertInside, nodeIdToInsert); - } + if (action === 'makeAllNodesAfterThisNotVisible') { + return this.upgrade.$injector.get('$filter')('translate')( + 'allStepsAfterThisOneWillNotBeVisibleUntil' + ); } - } - - updateTransitionsToStartId(startId, nodeIdToInsert) { - const nodesThatTransitionToStartId = this.getNodesByToNodeId(startId); - for (let nodeThatTransitionToStartId of nodesThatTransitionToStartId) { - this.updateToTransition(nodeThatTransitionToStartId, startId, nodeIdToInsert); + if (action === 'makeAllOtherNodesNotVisitable') { + return this.upgrade.$injector.get('$filter')('translate')( + 'allOtherStepsWillNotBeVisitableUntil' + ); } - } - - /** - * Add a transition to a node - * @param node the node we are adding a transition to - * @param toNodeId the node id we going to transition to - * @param criteria (optional) a criteria object specifying - * what needs to be satisfied in order to use this transition - */ - addToTransition(node, toNodeId, criteria = null) { - if (node != null) { - if (node.transitionLogic == null) { - node.transitionLogic = {}; - } - if (node.transitionLogic.transitions == null) { - node.transitionLogic.transitions = []; - } - const transition = {}; - (transition).to = toNodeId; - if (criteria != null) { - (transition).criteria = criteria; - } - node.transitionLogic.transitions.push(transition); + if (action === 'makeAllOtherNodesNotVisible') { + return this.upgrade.$injector.get('$filter')('translate')( + 'allOtherStepsWillNotBeVisibleUntil' + ); + } + if (action === 'makeThisNodeNotVisitable') { + return this.upgrade.$injector.get('$filter')('translate')('thisStepWillNotBeVisitableUntil'); + } + if (action === 'makeThisNodeNotVisible') { + return this.upgrade.$injector.get('$filter')('translate')('thisStepWillNotBeVisibleUntil'); } } /** - * Update the to value of aa transition - * @param node the node to update - * @param oldToNodeId the previous to node id - * @param newToNodeId the new to node id + * Get the message that describes how to satisfy the criteria + * TODO: check if the criteria is satisfied + * @param criteria the criteria object that needs to be satisfied + * @returns the message to display to the student that describes how to + * satisfy the criteria */ - updateToTransition(node, oldToNodeId, newToNodeId) { - if (node != null) { - if (node.transitionLogic == null) { - node.transitionLogic = {}; - } - - if (node.transitionLogic.transitions == null) { - node.transitionLogic.transitions = []; - } - - const transitions = node.transitionLogic.transitions; - for (let transition of transitions) { - if (transition != null) { - const toNodeId = transition.to; - if (oldToNodeId === toNodeId) { - transition.to = newToNodeId; - } - } - } - } - } - - /** - * @param group The group object. - * @returns {Array} The nodes in the group that do not have transitions. - */ - getChildNodesWithoutTransitions(group) { - const lastNodes = []; - for (let childId of group.ids) { - const child = this.getNodeById(childId); - const transitionLogic = child.transitionLogic; - const transitions = transitionLogic.transitions; - if (transitions.length == 0) { - lastNodes.push(child); - } - } - return lastNodes; - } - - /** - * Get the next available group id - * @returns the next available group id - */ - getNextAvailableGroupId() { - const groupIds = this.getGroupIds(); - let largestGroupIdNumber = null; - for (let groupId of groupIds) { - // get the number from the group id e.g. the number of 'group2' would be 2 - let groupIdNumber = groupId.replace('group', ''); - - // make sure the number is an actual number - if (!isNaN(groupIdNumber)) { - groupIdNumber = parseInt(groupIdNumber); - - // update the largest group id number if necessary - if (largestGroupIdNumber == null) { - largestGroupIdNumber = groupIdNumber; - } else if (groupIdNumber > largestGroupIdNumber) { - largestGroupIdNumber = groupIdNumber; - } - } - } - - const nextAvailableGroupId = 'group' + (largestGroupIdNumber + 1); - return nextAvailableGroupId; - } - - /** - * Get all the group ids - * @returns an array with all the group ids - */ - getGroupIds() { - const groupIds = []; - const groupNodes = this.groupNodes; - for (let group of groupNodes) { - if (group != null) { - const groupId = group.id; - if (groupId != null) { - groupIds.push(groupId); - } - } - } - - const inactiveGroupNodes = this.getInactiveGroupNodes(); - for (let inactiveGroup of inactiveGroupNodes) { - if (inactiveGroup != null) { - const inactiveGroupId = inactiveGroup.id; - if (inactiveGroupId != null) { - groupIds.push(inactiveGroupId); - } - } - } - return groupIds; - } - - /** - * Get the next available node id - * @param nodeIdsToSkip (optional) An array of additional node ids to not - * use. This parameter is used in cases where we are creating multiple new - * nodes at once. - * Example - * We ask for two new node ids by calling getNextAvailableNodeId() twice. - * The first time it returns "node10". - * If we ask the second time without actually creating and adding node10, - * it will return "node10" again. If we provide "node10" in the - * nodeIdsToSkip, then getNextAvailableNodeId() will properly return to us - * "node11". - * @returns the next available node id - */ - getNextAvailableNodeId(nodeIdsToSkip = []) { - let largestNodeIdNumber = -1; - for (const nodeId of this.getNodeIds() - .concat(this.getInactiveNodeIds()) - .concat(nodeIdsToSkip)) { - const nodeIdNumber = parseInt(nodeId.replace('node', '')); - if (nodeIdNumber > largestNodeIdNumber) { - largestNodeIdNumber = nodeIdNumber; - } - } - return 'node' + (largestNodeIdNumber + 1); - } - - /** - * Get all the node ids from steps (not groups) - * @returns an array with all the node ids - */ - getNodeIds() { - return this.applicationNodes.map((node) => { - return node.id; - }); - } - - /** - * Get all the node ids from inactive steps - * @returns an array with all the inactive node ids - */ - getInactiveNodeIds() { - return this.project.inactiveNodes.map((node) => { - return node.id; - }); - } - - /** - * Copy the node with the specified nodeId - * @param nodeId the node id to copy - * @return copied node - */ - copyNode(nodeId) { - const node = this.getNodeById(nodeId); - const nodeCopy = this.UtilService.makeCopyOfJSONObject(node); - nodeCopy.id = this.getNextAvailableNodeId(); - nodeCopy.transitionLogic = {}; // clear transition logic - nodeCopy.constraints = []; // clear constraints - - const newComponentIds = []; - for (let component of nodeCopy.components) { - const newComponentId = this.getUnusedComponentId(newComponentIds); - newComponentIds.push(newComponentId); - component.id = newComponentId; - } - return nodeCopy; - } - - /** - * Delete a node from the project and update transitions. - * - * If we are deleting the project start node id, we will need to change it to the - * next logical node id that will be used as the project start. - * - * @param nodeId the node id to delete from the project. It can be a step or an activity. - */ - deleteNode(nodeId) { - const parentGroup = this.getParentGroup(nodeId); - if (parentGroup != null && parentGroup.startId === nodeId) { - this.setGroupStartIdToNextChildId(parentGroup); - } - if (this.isProjectStartNodeIdOrContainsProjectStartNodeId(nodeId)) { - this.updateProjectStartNodeIdToNextLogicalNode(nodeId); - } - if (this.isGroupNode(nodeId)) { - this.removeChildNodes(nodeId); - } - this.removeNodeIdFromTransitions(nodeId); - this.removeNodeIdFromGroups(nodeId); - this.removeNodeIdFromNodes(nodeId); - } - - updateProjectStartNodeIdToNextLogicalNode(nodeId) { - if (this.isGroupNode(nodeId)) { - this.updateProjectStartNodeIdToNextLogicalNodeForRemovingGroup(nodeId); - } else { - this.updateProjectStartNodeIdToNextLogicalNodeForRemovingStep(nodeId); - } - } - - /** - * Set the startNodeId of the specified group to the first node of the next group. - * If the next group doesn't have any nodes, startNodeId should point - * to the next group. - */ - updateProjectStartNodeIdToNextLogicalNodeForRemovingGroup(nodeId) { - const transitions = this.getTransitionsByFromNodeId(nodeId); - if (transitions.length == 0) { - this.setStartNodeId('group0'); - } else { - let nextNodeId = transitions[0].to; - if (this.isGroupNode(nextNodeId)) { - const nextGroupStartId = this.getGroupStartId(nextNodeId); - if (nextGroupStartId == null) { - this.setStartNodeId(nextNodeId); - } else { - this.setStartNodeId(nextGroupStartId); - } - } else { - this.setStartNodeId(nextNodeId); - } - } - } - - /** - * Set the startNodeId to the next node in the transitions. - * If there are no transitions, set it to the parent group of the node. - */ - updateProjectStartNodeIdToNextLogicalNodeForRemovingStep(nodeId) { - const transitions = this.getTransitionsByFromNodeId(nodeId); - const parentGroupId = this.getParentGroupId(nodeId); - if (transitions.length == 0) { - this.setStartNodeId(parentGroupId); - } else { - let nextNodeId = transitions[0].to; - if (this.isNodeInGroup(nextNodeId, parentGroupId)) { - this.setStartNodeId(nextNodeId); - } else { - this.setStartNodeId(this.getParentGroupId(nodeId)); - } - } - } - - setGroupStartIdToNextChildId(group) { - let hasSetNewStartId = false; - const transitions = this.getTransitionsByFromNodeId(group.startId); - if (transitions.length > 0) { - const transition = transitions[0]; - const toNodeId = transition.to; - if (this.isNodeInGroup(toNodeId, group.id)) { - group.startId = toNodeId; - hasSetNewStartId = true; - } - } - - if (!hasSetNewStartId) { - group.startId = ''; - } - } - - removeChildNodes(groupId) { - const group = this.getNodeById(groupId); - for (let i = 0; i < group.ids.length; i++) { - const childId = group.ids[i]; - this.removeNodeIdFromTransitions(childId); - this.removeNodeIdFromGroups(childId); - this.removeNodeIdFromNodes(childId); - i--; // so it won't skip the next element - } - } - - isProjectStartNodeIdOrContainsProjectStartNodeId(nodeId) { - return ( - this.getStartNodeId() === nodeId || - (this.isGroupNode(nodeId) && this.containsStartNodeId(nodeId)) - ); - } - - containsStartNodeId(groupId) { - const group = this.getNodeById(groupId); - const projectStartNodeId = this.getStartNodeId(); - for (let childId of group.ids) { - if (childId === projectStartNodeId) { - return true; - } - } - return false; - } - - /** - * Update the transitions to handle removing a node - * @param nodeId the node id to remove - */ - removeNodeIdFromTransitions(nodeId) { - const nodeToRemove = this.getNodeById(nodeId); - const nodesByToNodeId = this.getNodesByToNodeId(nodeId); - - const nodeToRemoveTransitionLogic = nodeToRemove.transitionLogic; - let nodeToRemoveTransitions = []; - - if (nodeToRemoveTransitionLogic != null && nodeToRemoveTransitionLogic.transitions != null) { - nodeToRemoveTransitions = nodeToRemoveTransitionLogic.transitions; - } - - const parentIdOfNodeToRemove = this.getParentGroupId(nodeId); - const parentGroup = this.getNodeById(parentIdOfNodeToRemove); - - // update the start id if we are removing the start node of a group - if (parentGroup != null) { - const parentGroupStartId = parentGroup.startId; - if (parentGroupStartId != null) { - if (parentGroupStartId === nodeId) { - // the node we are removing is the start node - - if (nodeToRemoveTransitions != null && nodeToRemoveTransitions.length > 0) { - for (let nodeToRemoveTransition of nodeToRemoveTransitions) { - if (nodeToRemoveTransition != null) { - const toNodeId = nodeToRemoveTransition.to; - if (toNodeId != null) { - /* - * we need to check that the to node id is in the - * same group. some transitions point to a node id - * in the next group which we would not want to use - * for the start id. - */ - if (this.getParentGroupId(toNodeId) == parentIdOfNodeToRemove) { - // set the new start id - parentGroup.startId = toNodeId; - } - } - } - } - } else { - // there are no transitions so we will have an empty start id - parentGroup.startId = ''; - } - } - } - } - - for (let n = 0; n < nodesByToNodeId.length; n++) { - const node = nodesByToNodeId[n]; - if (node != null) { - const parentIdOfFromNode = this.getParentGroupId(node.id); - const transitionLogic = node.transitionLogic; - - if (transitionLogic != null) { - const transitions = transitionLogic.transitions; - for (let t = 0; t < transitions.length; t++) { - const transition = transitions[t]; - if (nodeId === transition.to) { - // we have found the transition to the node we are removing - - // copy the transitions from the node we are removing - let transitionsCopy = angular.toJson(nodeToRemoveTransitions); - transitionsCopy = angular.fromJson(transitionsCopy); - - /* - * if the parent from group is different than the parent removing group - * remove transitions that are to a node in a different group than - * the parent removing group - */ - - if (parentIdOfFromNode != parentIdOfNodeToRemove) { - for (let tc = 0; tc < transitionsCopy.length; tc++) { - const tempTransition = transitionsCopy[tc]; - if (tempTransition != null) { - const tempToNodeId = tempTransition.to; - if (tempToNodeId != null) { - const parentIdOfToNode = this.getParentGroupId(tempToNodeId); - if (parentIdOfNodeToRemove != parentIdOfToNode) { - // remove the transition - transitionsCopy.splice(tc, 1); - tc--; - } - } - } - } - } - - if (this.isFirstNodeInBranchPath(nodeId)) { - /* - * Get the node ids that have a branchPathTaken - * constraint from the before node and to the node - * we are removing. If there are any, we need to - * update the branchPathTaken constraint with the - * next nodeId that comes after the node we are - * removing. - */ - const nodeIdsInBranch = this.getNodeIdsInBranch(node.id, nodeId); - - if (nodeIdsInBranch != null) { - for (let nodeIdInBranch of nodeIdsInBranch) { - const nodeInBranch = this.getNodeById(nodeIdInBranch); - for (let transitionCopy of transitionsCopy) { - if (transitionCopy != null) { - const currentFromNodeId = node.id; - const currentToNodeId = nodeId; - const newFromNodeId = node.id; - const newToNodeId = transitionCopy.to; - - /* - * change the branch path taken constraint by changing - * the toNodeId - */ - this.updateBranchPathTakenConstraint( - nodeInBranch, - currentFromNodeId, - currentToNodeId, - newFromNodeId, - newToNodeId - ); - } - } - } - } - } else if (this.isBranchPoint(nodeId)) { - /* - * get all the branches that have the node we - * are removing as the start point - */ - const branches = this.getBranchesByBranchStartPointNodeId(nodeId); - - for (let branch of branches) { - if (branch != null) { - /* - * get the branch paths. these paths do not - * contain the start point or merge point. - */ - const branchPaths = branch.branchPaths; - - if (branchPaths != null) { - for (let branchPath of branchPaths) { - if (branchPath != null) { - const currentFromNodeId = nodeId; - const currentToNodeId = branchPath[0]; - const newFromNodeId = node.id; - const newToNodeId = branchPath[0]; - for (let branchPathNodeId of branchPath) { - const branchPathNode = this.getNodeById(branchPathNodeId); - this.updateBranchPathTakenConstraint( - branchPathNode, - currentFromNodeId, - currentToNodeId, - newFromNodeId, - newToNodeId - ); - } - } - } - } - } - } - } - - // remove the transition to the node we are removing - transitions.splice(t, 1); - - if (transitionsCopy != null) { - let insertIndex = t; - - /* - * loop through all the transitions from the node we are removing - * and insert them into the transitions of the from node - * e.g. - * the node that comes before the node we are removing has these transitions - * "transitions": [ - * { - * "to": "node4" - * }, - * { - * "to": "node6" - * } - * ] - * - * we are removing node4. node4 has a transition to node5. - * - * the node that comes before the node we are removing now has these transitions - * "transitions": [ - * { - * "to": "node5" - * }, - * { - * "to": "node6" - * } - * ] - */ - for (let transitionCopy of transitionsCopy) { - if (!this.isTransitionExist(transitions, transitionCopy)) { - const toNodeId = transitionCopy.to; - if ( - this.isApplicationNode(node.id) && - this.isGroupNode(toNodeId) && - this.hasGroupStartId(toNodeId) - ) { - this.addToTransition(node, this.getGroupStartId(toNodeId)); - } else { - transitions.splice(insertIndex, 0, transitionCopy); - insertIndex++; - } - } - } - } - t--; - - // check if the node we are moving is a group - if (this.isGroupNode(nodeId)) { - /* - * we are moving a group so we need to update transitions that - * go into the group - */ - const groupIdWeAreMoving = nodeId; - const groupThatTransitionsToGroupWeAreMoving = node; - this.updateChildrenTransitionsIntoGroupWeAreMoving( - groupThatTransitionsToGroupWeAreMoving, - groupIdWeAreMoving - ); - } - } - } - - if ( - transitions.length === 0 && - parentIdOfNodeToRemove != 'group0' && - parentIdOfNodeToRemove != this.getParentGroupId(node.id) - ) { - /* - * the from node no longer has any transitions so we will make it transition to the - * parent of the node we are removing - */ - this.addToTransition(node, parentIdOfNodeToRemove); - } - - if (this.isBranchPoint(nodeId)) { - /* - * the node we are deleting is a branch point so we to - * copy the transition logic to the node that comes - * before it - */ - node.transitionLogic = this.UtilService.makeCopyOfJSONObject( - nodeToRemoveTransitionLogic - ); - - /* - * set the transitions for the node that comes before - * the one we are removing - */ - node.transitionLogic.transitions = transitions; - } - } - } - } - - if (nodeToRemoveTransitionLogic != null) { - nodeToRemoveTransitionLogic.transitions = []; - } - - if (this.isGroupNode(nodeId)) { - this.removeTransitionsOutOfGroup(nodeId); - } - } - - isTransitionExist(transitions: any[], transition: any) { - for (const tempTransition of transitions) { - if (tempTransition.from === transition.from && tempTransition.to === transition.to) { - return true; - } - } - return false; - } - - /** - * Remove the node id from all groups - * @param nodeId the node id to remove - */ - removeNodeIdFromGroups(nodeId) { - for (const group of this.getGroupNodes()) { - this.removeNodeIdFromGroup(group, nodeId); - } - for (const inactiveGroup of this.getInactiveGroupNodes()) { - this.removeNodeIdFromGroup(inactiveGroup, nodeId); - } - } - - /** - * Remove a node from a group. - * If the node is a start node of the group, update the group's start node to - * the next node in the group after removing. - * @param group The group to remove from. - * @param nodeId The node id to remove. - */ - removeNodeIdFromGroup(group, nodeId) { - const ids = group.ids; - for (let i = 0; i < ids.length; i++) { - const id = ids[i]; - if (id === nodeId) { - ids.splice(i, 1); - if (id === group.startId) { - this.shiftGroupStartNodeByOne(group); - } - } - } - } - - // TODO handle the case when the start node of the group is a branch point - shiftGroupStartNodeByOne(group) { - const transitionsFromStartNode = this.getTransitionsByFromNodeId(group.startId); - if (transitionsFromStartNode.length > 0) { - group.startId = transitionsFromStartNode[0].to; - } else { - group.startId = ''; - } - } - - /** - * Remove the node from the array of nodes - * @param nodeId the node id to remove - */ - removeNodeIdFromNodes(nodeId) { - const nodes = this.project.nodes; - for (let n = 0; n < nodes.length; n++) { - const node = nodes[n]; - if (node != null) { - if (nodeId === node.id) { - nodes.splice(n, 1); - } - } - } - - const inactiveNodes = this.project.inactiveNodes; - if (inactiveNodes != null) { - for (let i = 0; i < inactiveNodes.length; i++) { - const inactiveNode = inactiveNodes[i]; - if (inactiveNode != null) { - if (nodeId === inactiveNode.id) { - inactiveNodes.splice(i, 1); - } - } - } - } - - this.idToNode[nodeId] = null; - } - - /** - * Remove the node from the inactive nodes array - * @param nodeId the node to remove from the inactive nodes array - */ - removeNodeIdFromInactiveNodes(nodeId) { - const inactiveNodes = this.project.inactiveNodes; - if (inactiveNodes != null) { - for (let i = 0; i < inactiveNodes.length; i++) { - const inactiveNode = inactiveNodes[i]; - if (inactiveNode != null) { - const inactiveNodeId = inactiveNode.id; - if (inactiveNodeId === nodeId) { - inactiveNodes.splice(i, 1); - } - } - } - } - } - - /** - * Create a new component - * @param nodeId the node id to create the component in - * @param componentType the component type - * @param insertAfterComponentId Insert the new compnent after the given - * component id. If this argument is null, we will place the new component - * in the first position. - */ - createComponent(nodeId, componentType, insertAfterComponentId = null) { - const node = this.getNodeById(nodeId); - const service = this.upgrade.$injector.get(componentType + 'Service'); - const component = service.createComponent(); - if (service.componentHasWork()) { - if (node.showSaveButton == false) { - if (this.doesAnyComponentInNodeShowSubmitButton(node.id)) { - component.showSaveButton = true; - } else { - node.showSaveButton = true; - } - } - } - this.addComponentToNode(node, component, insertAfterComponentId); - return component; - } - - /** - * Returns true iff any component in the step generates work - * @param nodeId the node id - * @return whether any components in the step generates work - */ - doesAnyComponentHaveWork(nodeId) { - const node = this.getNodeById(nodeId); - for (const component of node.components) { - const service = this.upgrade.$injector.get(component.type + 'Service'); - if (service != null && service.componentHasWork()) { - return true; - } - } - return false; - } - - /** - * Check if any of the components in the node are showing their submit button. - * @param nodeId {string} The node id to check. - * @return {boolean} Whether any of the components in the node show their submit button. - */ - doesAnyComponentInNodeShowSubmitButton(nodeId) { - const node = this.getNodeById(nodeId); - for (const component of node.components) { - if (component.showSubmitButton == true) { - return true; - } - } - return false; - } - - /** - * Add the component to the node - * @param node the node - * @param component the component - * @param insertAfterComponentId Insert the component after this given - * component id. If this argument is null, we will place the new component - * in the first position. - */ - addComponentToNode(node, component, insertAfterComponentId) { - if (insertAfterComponentId == null) { - node.components.splice(0, 0, component); - } else { - // place the new component after the insertAfterComponentId - - // boolean flag for whether we have added the component yet - let added = false; - - const components = node.components; - for (let c = 0; c < components.length; c++) { - const tempComponent = components[c]; - if ( - tempComponent != null && - tempComponent.id != null && - tempComponent.id == insertAfterComponentId - ) { - /* - * we have found the component we want to add the new - * one after - */ - - components.splice(c + 1, 0, component); - added = true; - break; - } - } - - if (!added) { - /* - * the component has not been added yet so we will just add - * it at the end - */ - node.components.push(component); - } - } - } - - /** - * TODO: Deprecated, should be removed; replaced by getMaxScoreForWorkgroupId in - * StudentStatusService - * Get the max score for the project. If the project contains branches, we - * will only calculate the max score for a single path from the first node - * to the last node in the project. - * @returns the max score for the project or null if none of the components in the project - * has max scores. - */ - getMaxScore() { - let maxScore = null; - const startNodeId = this.getStartNodeId(); - - // get all the paths in the project - const allPaths = this.getAllPaths([], startNodeId); - - if (allPaths != null && allPaths.length > 0) { - const firstPath = allPaths[0]; - for (let nodeId of firstPath) { - const nodeMaxScore = this.getMaxScoreForNode(nodeId); - if (nodeMaxScore != null) { - if (maxScore == null) { - maxScore = nodeMaxScore; - } else { - maxScore += nodeMaxScore; - } - } - } - } - return maxScore; - } - - /** - * Get the max score for the node - * @param nodeId the node id which can be a step or an activity - * @returns the max score for the node which can be null or a number - * if null, author/teacher has not set a max score for the node - */ - getMaxScoreForNode(nodeId) { - let maxScore = null; - if (!this.isGroupNode(nodeId)) { - const node = this.getNodeById(nodeId); - for (let component of node.components) { - const componentMaxScore = component.maxScore; - if (typeof componentMaxScore == 'number') { - if (maxScore == null) { - maxScore = componentMaxScore; - } else { - maxScore += componentMaxScore; - } - } - } - } - return maxScore; - } - - /** - * Get the max score for a component - * @param nodeId get the max score from a component in this node - * @param componentId get the max score from this component - */ - getMaxScoreForComponent(nodeId, componentId) { - const component = this.getComponentByNodeIdAndComponentId(nodeId, componentId); - if (component != null) { - return component.maxScore; - } - return null; - } - - /** - * Set the max score for a component - * @param nodeId set the max score from a component in this node - * @param componentId set the max score from this component - * @param maxScore set it to this maxScore - */ - setMaxScoreForComponent(nodeId, componentId, maxScore) { - if (nodeId != null && componentId != null && maxScore != null && typeof maxScore === 'number') { - let component = this.getComponentByNodeIdAndComponentId(nodeId, componentId); - if (component != null) { - component.maxScore = maxScore; - } - } - } - - /** - * Determine if a node id is a direct child of a group - * @param nodeId the node id - * @param groupId the group id - */ - isNodeInGroup(nodeId, groupId) { - const group = this.getNodeById(groupId); - return group.ids.indexOf(nodeId) != -1; - } - - /** - * Get the first leaf node by traversing all the start ids - * until a leaf node id is found - */ - getFirstLeafNodeId() { - let firstLeafNodeId = null; - const startGroupId = this.project.startGroupId; - let node = this.getNodeById(startGroupId); - let done = false; - - // loop until we have found a leaf node id or something went wrong - while (!done) { - if (node == null) { - done = true; - } else if (this.isGroupNode(node.id)) { - firstLeafNodeId = node.id; - node = this.getNodeById(node.startId); - } else if (this.isApplicationNode(node.id)) { - firstLeafNodeId = node.id; - done = true; - } else { - done = true; - } - } - return firstLeafNodeId; - } - - /** - * Get the message that describes how to disable the constraint - * @param nodeId the node the student is trying to go to - * @param constraint the constraint that is preventing the student - * from going to the node - * @returns the message to display to the student that describes how - * to disable the constraint - */ - getConstraintMessage(nodeId, constraint) { - let message = ''; - - if (nodeId != null && constraint != null) { - // get the node title the student is trying to go to - const nodeTitle = this.getNodePositionAndTitleByNodeId(nodeId); - - const removalConditional = constraint.removalConditional; - const removalCriteria = constraint.removalCriteria; - - if (removalCriteria != null) { - let criteriaMessages = ''; - for (let tempRemovalCriteria of removalCriteria) { - if (tempRemovalCriteria != null) { - // get the message that describes the criteria that needs to be satisfied - const criteriaMessage = this.getCriteriaMessage(tempRemovalCriteria); - - if (criteriaMessage != null && criteriaMessage != '') { - // separate criteria messages with a line break - if (criteriaMessages != '') { - criteriaMessages += '
'; - } - criteriaMessages += criteriaMessage; - } - } - } - message += criteriaMessages; - } - } - return message; - } - - /** - * Get the human readable description of the constraint. - * @param constraint The constraint object. - * @returns A human readable text string that describes the constraint. - * example - * 'All steps after this one will not be visitable until the student completes - * "3.7 Revise Your Bowls Explanation"' - */ - getConstraintDescription(constraint) { - let message = ''; - for (const singleRemovalCriteria of constraint.removalCriteria) { - if (message != '') { - // this constraint has multiple removal criteria - if (constraint.removalConditional === 'any') { - message += ' or '; - } else if (constraint.removalConditional === 'all') { - message += ' and '; - } - } - message += this.getCriteriaMessage(singleRemovalCriteria); - } - return this.getActionMessage(constraint.action) + message; - } - - /** - * Get the constraint action as human readable text. - * @param action A constraint action. - * @return A human readable text string that describes the action - * example - * 'All steps after this one will not be visitable until ' - */ - getActionMessage(action) { - if (action === 'makeAllNodesAfterThisNotVisitable') { - return this.upgrade.$injector.get('$filter')('translate')( - 'allStepsAfterThisOneWillNotBeVisitableUntil' - ); - } - if (action === 'makeAllNodesAfterThisNotVisible') { - return this.upgrade.$injector.get('$filter')('translate')( - 'allStepsAfterThisOneWillNotBeVisibleUntil' - ); - } - if (action === 'makeAllOtherNodesNotVisitable') { - return this.upgrade.$injector.get('$filter')('translate')( - 'allOtherStepsWillNotBeVisitableUntil' - ); - } - if (action === 'makeAllOtherNodesNotVisible') { - return this.upgrade.$injector.get('$filter')('translate')( - 'allOtherStepsWillNotBeVisibleUntil' - ); - } - if (action === 'makeThisNodeNotVisitable') { - return this.upgrade.$injector.get('$filter')('translate')('thisStepWillNotBeVisitableUntil'); - } - if (action === 'makeThisNodeNotVisible') { - return this.upgrade.$injector.get('$filter')('translate')('thisStepWillNotBeVisibleUntil'); - } - } - - /** - * Get the message that describes how to satisfy the criteria - * TODO: check if the criteria is satisfied - * @param criteria the criteria object that needs to be satisfied - * @returns the message to display to the student that describes how to - * satisfy the criteria - */ - getCriteriaMessage(criteria) { - let message = ''; + getCriteriaMessage(criteria) { + let message = ''; if (criteria != null) { const name = criteria.name; @@ -3598,555 +2380,67 @@ export class ProjectService { 'addXNumberOfNotesOnThisStepPlural', { requiredNumberOfNotes: requiredNumberOfNotes, - nodeTitle: nodeTitle - } - ); - } - } else if (name === 'fillXNumberOfRows') { - const requiredNumberOfFilledRows = params.requiredNumberOfFilledRows; - const nodeId = params.nodeId; - const nodeTitle = this.getNodePositionAndTitleByNodeId(nodeId); - if (requiredNumberOfFilledRows == 1) { - message += this.upgrade.$injector.get('$filter')('translate')('youMustFillInXRow', { - requiredNumberOfFilledRows: requiredNumberOfFilledRows, - nodeTitle: nodeTitle - }); - } else { - message += this.upgrade.$injector.get('$filter')('translate')('youMustFillInXRows', { - requiredNumberOfFilledRows: requiredNumberOfFilledRows, - nodeTitle: nodeTitle - }); - } - } else if (name === 'teacherRemoval') { - message += this.upgrade.$injector.get('$filter')('translate')('waitForTeacherToUnlock'); - } - } - return message; - } - - /** - * Get the choices of a Multiple Choice component. - * @param nodeId The node id. - * @param componentId The component id. - * @return The choices from the component. - */ - getChoicesByNodeIdAndComponentId(nodeId, componentId) { - const component = this.getComponentByNodeIdAndComponentId(nodeId, componentId); - return component.choices; - } - - /** - * Get the choice text for the given choice ids of a multiple choice component. - * @param nodeId The node id of the component. - * @param componentId The component id of the component. - * @param choiceIds An array of choice ids. - * @return An array of choice text strings. - */ - getChoiceTextByNodeIdAndComponentId(nodeId, componentId, choiceIds) { - const choicesText = []; - for (const choice of this.getChoicesByNodeIdAndComponentId(nodeId, componentId)) { - if (choiceIds.indexOf(choice.id) != -1) { - choicesText.push(choice.text); - } - } - return choicesText; - } - - /** - * Get the start id of a group - * @param nodeId get the start id of this group - * @returns the start id of the group - */ - getGroupStartId(nodeId) { - return this.getNodeById(nodeId).startId; - } - - hasGroupStartId(nodeId) { - const startId = this.getGroupStartId(nodeId); - return startId != null && startId != ''; - } - - /** - * Update the transitions so that the fromGroup points to the newToGroup - * - * Before - * fromGroup -> oldToGroup -> newToGroup - * - * After - * fromGroup -> newToGroup - * oldToGroup becomes dangling and has no transitions to or from it - */ - updateTransitionsForExtractingGroup(fromGroupId, oldToGroupId, newToGroupId) { - /* - * make the transitions - * fromGroup -> newToGroup - */ - if (fromGroupId != null && oldToGroupId != null) { - const fromGroup = this.getNodeById(fromGroupId); - const oldToGroup = this.getNodeById(oldToGroupId); - let newToGroup = null; - let newToGroupStartId = null; - - if (newToGroupId != null) { - newToGroup = this.getNodeById(newToGroupId); - } - - if (newToGroup != null) { - newToGroupStartId = newToGroup.startId; - } - - if (fromGroup != null && oldToGroup != null) { - const childIds = fromGroup.ids; - - // update the children of the from group to point to the new to group - if (childIds != null) { - for (let childId of childIds) { - const child = this.getNodeById(childId); - const transitions = this.getTransitionsByFromNodeId(childId); - - if (transitions != null) { - for (let t = 0; t < transitions.length; t++) { - const transition = transitions[t]; - if (transition != null) { - const toNodeId = transition.to; - if (toNodeId === oldToGroupId) { - // the transition is to the group - if (newToGroupId == null && newToGroupStartId == null) { - // there is no new to group so we will remove the transition - transitions.splice(t, 1); - t--; - } else { - // make the transition point to the new to group - transition.to = newToGroupId; - } - } else if (this.isNodeInGroup(toNodeId, oldToGroupId)) { - // the transition is to a node in the group - if (newToGroupId == null && newToGroupStartId == null) { - // there is no new to group so we will remove the transition - transitions.splice(t, 1); - t--; - } else if (newToGroupStartId == null || newToGroupStartId == '') { - // make the transition point to the new to group - transition.to = newToGroupId; - } else { - // make the transition point to the new group start id - transition.to = newToGroupStartId; - } - } - } - } - } - } - } - } - } - - /* - * remove the transitions from the oldToGroup - */ - if (oldToGroupId != null && newToGroupId != null) { - const oldToGroup = this.getNodeById(oldToGroupId); - if (oldToGroup != null) { - const childIds = oldToGroup.ids; - - // remove the transitions from the old to group that point to the new to group - if (childIds != null) { - for (let childId of childIds) { - const child = this.getNodeById(childId); - const transitions = this.getTransitionsByFromNodeId(childId); - if (transitions != null) { - for (let t = 0; t < transitions.length; t++) { - const transition = transitions[t]; - if (transition != null) { - const toNodeId = transition.to; - if (toNodeId === newToGroupId) { - // the transition is to the group so we will remove it - transitions.splice(t, 1); - t--; - } else if (this.isNodeInGroup(toNodeId, newToGroupId)) { - // the transition is to a node in the group so we will remove it - transitions.splice(t, 1); - t--; - } - } - } - } - } - } - } - } - } - - /** - * Update the transitions so that the fromGroup points to the newToGroup - * - * Before - * fromGroup -> oldToGroup - * newToGroup is dangling and has no transitions to or from it - * - * After - * fromGroup -> newToGroup -> oldToGroup - */ - updateTransitionsForInsertingGroup(fromGroupId, oldToGroupIds, newToGroupId) { - let fromGroup = null; - let newToGroup = null; - if (fromGroupId != null) { - fromGroup = this.getNodeById(fromGroupId); - } - - if (newToGroupId != null) { - newToGroup = this.getNodeById(newToGroupId); - } - - /* - * make the transitions that point to the old group now point - * to the new group - * fromGroup -> newToGroup - */ - if (fromGroup != null && newToGroup != null) { - const childIds = fromGroup.ids; - const newToGroupStartId = newToGroup.startId; - if (childIds != null) { - for (let childId of childIds) { - const child = this.getNodeById(childId); - - // get the transitions from the child - const transitions = this.getTransitionsByFromNodeId(childId); - - if (transitions == null || transitions.length == 0) { - /* - * the child does not have any transitions so we will make it - * point to the new group - */ - if (newToGroupStartId == null || newToGroupStartId == '') { - this.addToTransition(child, newToGroupId); - } else { - this.addToTransition(child, newToGroupStartId); - } - } else if (transitions != null) { - for (let transition of transitions) { - if (transition != null) { - const toNodeId = transition.to; - if (oldToGroupIds != null) { - for (let oldToGroupId of oldToGroupIds) { - if (toNodeId === oldToGroupId) { - /* - * the transition is to the group so we will update the transition - * to the new group - */ - transition.to = newToGroupId; - } else if (this.isNodeInGroup(toNodeId, oldToGroupId)) { - /* - * the transition is to a node in the old group so we will update - * the transition to point to the new group - */ - if (newToGroupStartId == null || newToGroupStartId == '') { - transition.to = newToGroupId; - } else { - transition.to = newToGroupStartId; - } - } - } - } - } - } - } - } - } - } - - /* - * make the steps that do not have a transition now point to the old - * group - * newToGroup -> oldToGroup - */ - if (newToGroup != null) { - const childIds = newToGroup.ids; - if (childIds != null) { - for (let childId of childIds) { - const child = this.getNodeById(childId); - const transitions = this.getTransitionsByFromNodeId(childId); - - if (transitions == null || transitions.length == 0) { - if (oldToGroupIds != null) { - for (let oldToGroupId of oldToGroupIds) { - const oldToGroup = this.getNodeById(oldToGroupId); - if (oldToGroup != null) { - const oldToGroupStartId = oldToGroup.startId; - const transition = {}; - let toNodeId = ''; - if (oldToGroupStartId == null) { - // there is no start node id so we will just point to the group - toNodeId = oldToGroup; - } else { - // there is a start node id so we will point to it - toNodeId = oldToGroupStartId; - } - - // create the transition from the child to the old group - this.addToTransition(child, toNodeId); - } - } - } - } - } - } - } - } - - /** - * Update the child transitions because we are moving a group. We will - * update the transitions into and out of the group in the location - * we are extracting the group from and also in the location we are - * inserting the group into. - * @param node the group we are moving - * @param nodeId we will put the group after this node id - */ - updateChildrenTransitionsInAndOutOfGroup(node, nodeId = null) { - let transitionsBefore = null; - - // get the group nodes that point to the group we are moving - const previousGroupNodes = this.getGroupNodesByToNodeId(node.id); - - // get all the transitions from the group we are moving - const transitionsAfter = this.getTransitionsByFromNodeId(node.id); - - let extracted = false; - - /* - * extract the group we are moving by updating the transitions of the - * from group and the new to group. also remove the transitions from the - * group we are moving. - */ - - for (let previousGroupNode of previousGroupNodes) { - if (transitionsAfter == null || transitionsAfter.length == 0) { - // the group we are moving does not have any transitions - - /* - * remove the transitions to the group we are moving and make - * new transitions from the from group to the new to group - */ - this.updateTransitionsForExtractingGroup(previousGroupNode.id, node.id, null); - extracted = true; - } else { - // the group we are moving has transitions - - // make the previous group point to the new to group - for (let transitionAfter of transitionsAfter) { - if (transitionAfter != null) { - const toNodeId = transitionAfter.to; - - /* - * remove the transitions to the group we are moving and make - * new transitions from the from group to the new to group - */ - this.updateTransitionsForExtractingGroup(previousGroupNode.id, node.id, toNodeId); - extracted = true; - } - } - } - } - - if (!extracted) { - /* - * we have not removed the transitions yet because the group - * we are moving does not have any groups before it - */ - - if (transitionsAfter != null) { - // remove the transitions from the group we are moving - for (let transitionAfter of transitionsAfter) { - if (transitionAfter != null) { - const toNodeId = transitionAfter.to; - - // remove the transitions to the group we are moving - this.updateTransitionsForExtractingGroup(null, node.id, toNodeId); - extracted = true; - } - } - } - } - - let inserted = false; - - /* - * create the transitions from the from group to the group we are moving - * and the transitions from the group we are moving to the old to group - */ - if (nodeId != null) { - // get the transitions from the previous group to the next group - const transitionsAfter = this.getTransitionsByFromNodeId(nodeId); - - for (let transitionAfter of transitionsAfter) { - if (transitionAfter != null) { - const toNodeId = transitionAfter.to; - - /* - * create the transitions that traverse from the from group - * to the group we are moving. also create the transitions - * that traverse from the group we are moving to the old - * to group. - */ - this.updateTransitionsForInsertingGroup(nodeId, [toNodeId], node.id); - inserted = true; - } - } - } - - if (!inserted) { - /* - * we have not inserted the transitions yet because there were no - * previous group transitions - */ - - if (nodeId == null) { - /* - * the previous node id is null which means there was no previous - * group. this means the group we are inserting will become the - * first group. this happens when the group we are moving - * is moved inside the root (group0). - */ - - const startGroupId = this.getStartGroupId(); - - if (startGroupId != null) { - // get the start group for the whole project (group0) - const startGroup = this.getNodeById(startGroupId); - - if (startGroup != null) { - const firstGroupId = startGroup.startId; - - /* - * create the transitions that traverse from the group - * we are moving to the previous first activity. - */ - this.updateTransitionsForInsertingGroup(nodeId, [firstGroupId], node.id); - } - } - } else { - /* - * we have not inserted the group yet because the from group doesn't - * have a group after it - */ - - /* - * create the transitions that traverse from the from group - * to the group we are moving. - */ - this.updateTransitionsForInsertingGroup(nodeId, null, node.id); - } - } - } - - getActiveNodes() { - return this.project.nodes; - } - - getInactiveNodes() { - return this.project.inactiveNodes; - } - - /** - * Remove the node from the inactive nodes array - * @param nodeId the node to remove - * @returns the node that was removed - */ - removeNodeFromInactiveNodes(nodeId) { - let node = null; - if (nodeId != null) { - let parentGroup = this.getParentGroup(nodeId); - if (parentGroup != null) { - this.removeChildFromParent(nodeId); - } - - let inactiveNodes = this.project.inactiveNodes; - if (inactiveNodes != null) { - for (let i = 0; i < inactiveNodes.length; i++) { - let inactiveNode = inactiveNodes[i]; - if (inactiveNode != null) { - if (nodeId === inactiveNode.id) { - node = inactiveNode; - inactiveNodes.splice(i, 1); - break; + nodeTitle: nodeTitle } - } + ); + } + } else if (name === 'fillXNumberOfRows') { + const requiredNumberOfFilledRows = params.requiredNumberOfFilledRows; + const nodeId = params.nodeId; + const nodeTitle = this.getNodePositionAndTitleByNodeId(nodeId); + if (requiredNumberOfFilledRows == 1) { + message += this.upgrade.$injector.get('$filter')('translate')('youMustFillInXRow', { + requiredNumberOfFilledRows: requiredNumberOfFilledRows, + nodeTitle: nodeTitle + }); + } else { + message += this.upgrade.$injector.get('$filter')('translate')('youMustFillInXRows', { + requiredNumberOfFilledRows: requiredNumberOfFilledRows, + nodeTitle: nodeTitle + }); } + } else if (name === 'teacherRemoval') { + message += this.upgrade.$injector.get('$filter')('translate')('waitForTeacherToUnlock'); } - this.removeNodeFromInactiveStepNodes(nodeId); - this.removeNodeFromInactiveGroupNodes(nodeId); } - return node; + return message; } /** - * Remove the child node from the parent group. - * @param nodeId The child node to remove from the parent. + * Get the choices of a Multiple Choice component. + * @param nodeId The node id. + * @param componentId The component id. + * @return The choices from the component. */ - removeChildFromParent(nodeId) { - let parentGroup = this.getParentGroup(nodeId); - if (parentGroup != null) { - // Remove the child from the parent - for (let i = 0; i < parentGroup.ids.length; i++) { - let childId = parentGroup.ids[i]; - if (nodeId == childId) { - parentGroup.ids.splice(i, 1); - break; - } - } - if (nodeId == parentGroup.startId) { - /* - * The child we removed was the start id of the group so we - * will update the start id. - */ - let startIdUpdated = false; - let transitions = this.getTransitionsByFromNodeId(nodeId); - if ( - transitions != null && - transitions.length > 0 && - transitions[0] != null && - transitions[0].to != null - ) { - parentGroup.startId = transitions[0].to; - startIdUpdated = true; - } - if (!startIdUpdated && parentGroup.ids.length > 0) { - parentGroup.startId = parentGroup.ids[0]; - startIdUpdated = true; - } - if (!startIdUpdated) { - parentGroup.startId = ''; - } - } - } + getChoicesByNodeIdAndComponentId(nodeId, componentId) { + const component = this.getComponentByNodeIdAndComponentId(nodeId, componentId); + return component.choices; } /** - * Remove the node from the inactive step nodes array. - * @param nodeId The node id of the node we want to remove from the - * inactive step nodes array. + * Get the choice text for the given choice ids of a multiple choice component. + * @param nodeId The node id of the component. + * @param componentId The component id of the component. + * @param choiceIds An array of choice ids. + * @return An array of choice text strings. */ - removeNodeFromInactiveStepNodes(nodeId) { - for (let i = 0; i < this.inactiveStepNodes.length; i++) { - if (nodeId == this.inactiveStepNodes[i].id) { - this.inactiveStepNodes.splice(i, 1); - break; + getChoiceTextByNodeIdAndComponentId(nodeId, componentId, choiceIds) { + const choicesText = []; + for (const choice of this.getChoicesByNodeIdAndComponentId(nodeId, componentId)) { + if (choiceIds.indexOf(choice.id) != -1) { + choicesText.push(choice.text); } } + return choicesText; } /** - * Remove the node from the inactive group nodes array. - * @param nodeId The node id of the group we want to remove from the - * inactive group nodes array. + * Get the start id of a group + * @param nodeId get the start id of this group + * @returns the start id of the group */ - removeNodeFromInactiveGroupNodes(nodeId) { - for (let i = 0; i < this.inactiveGroupNodes.length; i++) { - if (nodeId == this.inactiveGroupNodes[i].id) { - this.inactiveGroupNodes.splice(i, 1); - break; - } - } + getGroupStartId(nodeId) { + return this.getNodeById(nodeId).startId; } /** @@ -4192,177 +2486,6 @@ export class ProjectService { return false; } - /** - * Move the node to the active nodes array. If the node is a group node, - * also move all of its children to active. - */ - moveToActive(node) { - if (!this.isActive(node.id)) { - this.removeNodeFromInactiveNodes(node.id); - this.addNode(node); - if (this.isGroupNode(node.id)) { - for (const childId of node.ids) { - this.addNode(this.removeNodeFromInactiveNodes(childId)); - } - } - } - } - - /** - * Add a group's cthild nodes to the inactive nodes. - * @param node The group node. - */ - addGroupChildNodesToInactive(node) { - for (const childId of node.ids) { - const childNode = this.getNodeById(childId); - this.project.inactiveNodes.push(childNode); - this.inactiveStepNodes.push(childNode); - } - } - - /** - * Remove transition from nodes in the specified group that go out of the group - * @param nodeId the group id - */ - removeTransitionsOutOfGroup(groupId) { - const group = this.getNodeById(groupId); - for (const childId of group.ids) { - const transitions = this.getTransitionsByFromNodeId(childId); - for (let t = 0; t < transitions.length; t++) { - const transition = transitions[t]; - const parentGroupId = this.getParentGroupId(transition.to); - if (parentGroupId != groupId) { - // this is a transition that goes out of the specified group - transitions.splice(t, 1); - t--; // so it won't skip the next element - } - } - } - } - - /* - * Update the step transitions that point into the group we are moving - * For example - * group1 has children node1 and node2 (node2 transitions to node3) - * group2 has children node3 and node4 (node4 transitions to node5) - * group3 has children node5 and node6 - * if we move group2 after group3 we will need to change the - * transition from node2 to node3 and make node2 transition to node5 - * the result will be - * group1 has children node1 and node2 (node2 transitions to node5) - * group3 has children node5 and node6 - * group2 has children node3 and node4 (node4 transitions to node5) - * note: the (node4 transition to node5) will be removed later - * when is called removeTransitionsOutOfGroup - * note: when group2 is added in a later function call, we will add - * the node6 to node3 transition - * @param groupThatTransitionsToGroupWeAreMoving the group object - * that transitions to the group we are moving. we may need to update - * the transitions of this group's children. - * @param groupIdWeAreMoving the group id of the group we are moving - */ - updateChildrenTransitionsIntoGroupWeAreMoving( - groupThatTransitionsToGroupWeAreMoving, - groupIdWeAreMoving - ) { - if (groupThatTransitionsToGroupWeAreMoving != null && groupIdWeAreMoving != null) { - const group = this.getNodeById(groupIdWeAreMoving); - if (group != null) { - // get all the nodes that have a transition to the node we are removing - const nodesByToNodeId = this.getNodesByToNodeId(groupIdWeAreMoving); - - // get the transitions of the node we are removing - const nodeToRemoveTransitionLogic = group.transitionLogic; - let nodeToRemoveTransitions = []; - - if ( - nodeToRemoveTransitionLogic != null && - nodeToRemoveTransitionLogic.transitions != null - ) { - nodeToRemoveTransitions = nodeToRemoveTransitionLogic.transitions; - } - - if (nodeToRemoveTransitions.length == 0) { - /* - * The group we are moving is the last group in the project - * and does not have any transitions. We will loop through - * all the nodes that transition into this group and remove - * those transitions. - */ - - // get child ids of the group that comes before the group we are moving - const childIds = groupThatTransitionsToGroupWeAreMoving.ids; - - if (childIds != null) { - for (let childId of childIds) { - const transitionsFromChild = this.getTransitionsByFromNodeId(childId); - if (transitionsFromChild != null) { - for (let tfc = 0; tfc < transitionsFromChild.length; tfc++) { - const transitionFromChild = transitionsFromChild[tfc]; - if (transitionFromChild != null) { - const toNodeId = transitionFromChild.to; - const toNodeIdParentGroupId = this.getParentGroupId(toNodeId); - - if (groupIdWeAreMoving === toNodeIdParentGroupId) { - // the transition is to a child in the group we are moving - transitionsFromChild.splice(tfc, 1); - - /* - * move the counter back one because we have just removed an - * element from the array - */ - tfc--; - } - } - } - } - } - } - } else if (nodeToRemoveTransitions.length > 0) { - // get the first group that comes after the group we are removing - const firstNodeToRemoveTransition = nodeToRemoveTransitions[0]; - const firstNodeToRemoveTransitionToNodeId = firstNodeToRemoveTransition.to; - - if (this.isGroupNode(firstNodeToRemoveTransitionToNodeId)) { - // get the group that comes after the group we are moving - const groupNode = this.getNodeById(firstNodeToRemoveTransitionToNodeId); - - // get child ids of the group that comes before the group we are moving - const childIds = groupThatTransitionsToGroupWeAreMoving.ids; - - if (childIds != null) { - for (let childId of childIds) { - const transitionsFromChild = this.getTransitionsByFromNodeId(childId); - if (transitionsFromChild != null) { - for (let transitionFromChild of transitionsFromChild) { - if (transitionFromChild != null) { - const toNodeId = transitionFromChild.to; - - // get the parent group id of the toNodeId - const toNodeIdParentGroupId = this.getParentGroupId(toNodeId); - - if (groupIdWeAreMoving === toNodeIdParentGroupId) { - // the transition is to a child in the group we are moving - - if (groupNode.startId == null) { - // change the transition to point to the after group - transitionFromChild.to = firstNodeToRemoveTransitionToNodeId; - } else { - // change the transition to point to the start id of the after group - transitionFromChild.to = groupNode.startId; - } - } - } - } - } - } - } - } - } - } - } - } - /** * Check if a node generates work by looking at all of its components * @param nodeId the node id @@ -4415,231 +2538,6 @@ export class ProjectService { return component.type; } - /** - * Get an unused component id - * @param componentIdsToSkip (optional) An array of additional component ids - * to skip. This is used when we are creating multiple new components. There - * is avery small chance that we create duplicate component ids that aren't - * already in the project. We avoid this problem by using this parameter. - * Example - * We want to create two new components. We first generate a new component - * id for the first new component for example "1234567890". Then we generate - * a new component id for the second new component and pass in - * ["1234567890"] as componentIdsToSkip because the new "1234567890" - * component hasn't actually been added to the project yet. - * @return a component id that isn't already being used in the project - */ - getUnusedComponentId(componentIdsToSkip = []) { - // we want to make an id with 10 characters - const idLength = 10; - - let newComponentId = this.UtilService.generateKey(idLength); - - // check if the component id is already used in the project - if (this.isComponentIdUsed(newComponentId)) { - /* - * the component id is already used in the project so we need to - * try generating another one - */ - let alreadyUsed = true; - - /* - * keep trying to generate a new component id until we have found - * one that isn't already being used - */ - while (!alreadyUsed) { - newComponentId = this.UtilService.generateKey(idLength); - - // check if the id is already being used in the project - alreadyUsed = this.isComponentIdUsed(newComponentId); - - if (componentIdsToSkip != null && componentIdsToSkip.indexOf(newComponentId) != -1) { - /* - * the new component is in the componentIdsToSkip so it has - * already been used - */ - alreadyUsed = true; - } - } - } - return newComponentId; - } - - /** - * Check if the component id is already being used in the project - * @param componentId check if this component id is already being used in - * the project - * @return whether the component id is already being used in the project - */ - isComponentIdUsed(componentId) { - for (const node of this.project.nodes.concat(this.project.inactiveNodes)) { - if (node.components != null) { - for (const component of node.components) { - if (componentId === component.id) { - return true; - } - } - } - } - return false; - } - - /** - * Get the next available constraint id for a node - * @param nodeId get the next available constraint id for this node - * e.g. node8Constraint2 - * @return the next available constraint id for the node - */ - getNextAvailableConstraintIdForNodeId(nodeId) { - let nextAvailableConstraintId = null; - if (nodeId != null) { - const usedConstraintIds = []; - const node = this.getNodeById(nodeId); - if (node != null) { - const constraints = node.constraints; - if (constraints != null) { - for (let constraint of constraints) { - if (constraint != null) { - const constraintId = constraint.id; - usedConstraintIds.push(constraintId); - } - } - } - } - - let foundNextAvailableConstraintId = false; - let counter = 1; - - while (!foundNextAvailableConstraintId) { - const potentialConstraintId = nodeId + 'Constraint' + counter; - if (usedConstraintIds.indexOf(potentialConstraintId) == -1) { - nextAvailableConstraintId = potentialConstraintId; - foundNextAvailableConstraintId = true; - } else { - counter++; - } - } - } - return nextAvailableConstraintId; - } - - /** - * Get the node ids in the branch by looking for nodes that have branch - * path taken constraints with the given fromNodeId and toNodeId - * @param fromNodeId the from node id - * @param toNodeId the to node id - * @return an array of nodes that are in the branch path - */ - getNodeIdsInBranch(fromNodeId, toNodeId) { - const nodeIdsInBranch = []; - for (const node of this.getNodes()) { - if (this.hasBranchPathTakenConstraint(node, fromNodeId, toNodeId)) { - nodeIdsInBranch.push(node.id); - } - } - this.orderNodeIds(nodeIdsInBranch); - return nodeIdsInBranch; - } - - /** - * Order the node ids so that they show up in the same order as in the - * project. - * @param constraints An array of node ids. - * @return An array of ordered node ids. - */ - orderNodeIds(nodeIds) { - let orderedNodeIds = this.getFlattenedProjectAsNodeIds(); - return nodeIds.sort(this.nodeIdsComparatorGenerator(orderedNodeIds)); - } - - /** - * Create the node ids comparator function that is used for sorting an - * array of node ids. - * @param orderedNodeIds An array of node ids in the order in which they - * show up in the project. - * @return A comparator that orders node ids in the order in which they show - * up in the project. - */ - nodeIdsComparatorGenerator(orderedNodeIds) { - return function (nodeIdA, nodeIdB) { - let nodeIdAIndex = orderedNodeIds.indexOf(nodeIdA); - let nodeIdBIndex = orderedNodeIds.indexOf(nodeIdB); - if (nodeIdAIndex < nodeIdBIndex) { - return -1; - } else if (nodeIdAIndex > nodeIdBIndex) { - return 1; - } - return 0; - }; - } - - /** - * Check if a node has a branch path taken constraint - * @param node the node to check - * @param fromNodeId the from node id of the branch path taken - * @param toNodeId the to node id of the branch path taken - * @return whether the node has a branch path taken constraint with the - * given from node id and to node id - */ - hasBranchPathTakenConstraint(node, fromNodeId, toNodeId) { - const constraints = node.constraints; - if (constraints != null) { - for (let constraint of constraints) { - for (let removalCriterion of constraint.removalCriteria) { - if (removalCriterion.name == 'branchPathTaken') { - const params = removalCriterion.params; - if (params.fromNodeId == fromNodeId && params.toNodeId == toNodeId) { - return true; - } - } - } - } - } - return false; - } - - /** - * Remove all branch path taken constraints from a node. - * @param nodeId Remove the constraints from this node. - */ - removeBranchPathTakenNodeConstraintsIfAny(nodeId) { - const node = this.getNodeById(nodeId); - const constraints = node.constraints; - if (constraints != null) { - for (let c = 0; c < constraints.length; c++) { - const constraint = constraints[c]; - const removalCriteria = constraint.removalCriteria; - for (let removalCriterion of removalCriteria) { - if (removalCriterion.name == 'branchPathTaken') { - constraints.splice(c, 1); - c--; // update the counter so we don't skip over the next element - } - } - } - } - } - - /** - * @param nodeId Get the branch path taken constraints from this node. - * @return {Array} An array of branch path taken constraints from the node. - */ - getBranchPathTakenConstraintsByNodeId(nodeId) { - const branchPathTakenConstraints = []; - const node = this.getNodeById(nodeId); - const constraints = node.constraints; - if (constraints != null) { - for (let constraint of constraints) { - for (let removalCriterion of constraint.removalCriteria) { - if (removalCriterion.name == 'branchPathTaken') { - branchPathTakenConstraints.push(constraint); - break; - } - } - } - } - return branchPathTakenConstraints; - } - getProjectRubric() { return this.project.rubric; } @@ -4655,24 +2553,6 @@ export class ProjectService { return transitions != null && transitions.length > 1; } - /** - * Check if a node is the first node in a branch path - * @param nodeId the node id - * @return whether the node is the first node in a branch path - */ - isFirstNodeInBranchPath(nodeId) { - for (const node of this.getNodes()) { - if (node.transitionLogic != null && node.transitionLogic.transitions != null) { - for (const transition of node.transitionLogic.transitions) { - if (transition.to === nodeId) { - return true; - } - } - } - } - return false; - } - /** * Check if the node is in any branch path * @param nodeId the node id of the node @@ -5222,16 +3102,6 @@ export class ProjectService { } } - addSpace(space) { - if (this.project.spaces == null) { - this.project.spaces = []; - } - if (!this.isSpaceExists(space.id)) { - this.project.spaces.push(space); - this.saveProject(); - } - } - isSpaceExists(id) { const spaces = this.getSpaces(); for (let space of spaces) { @@ -5242,17 +3112,6 @@ export class ProjectService { return false; } - removeSpace(id) { - let spaces = this.getSpaces(); - for (let s = 0; s < spaces.length; s++) { - if (spaces[s].id == id) { - spaces.splice(s, 1); - this.saveProject(); - return; - } - } - } - /** * Returns true iff the specified node and component has any registered * additionalProcessingFunctions @@ -5275,38 +3134,6 @@ export class ProjectService { return this.additionalProcessingFunctionsMap[`${nodeId}_${componentId}`]; } - getFeaturedProjectIcons() { - return this.http - .get(this.ConfigService.getConfigParam('featuredProjectIconsURL')) - .toPromise() - .then((data) => { - return data; - }); - } - - setFeaturedProjectIcon(projectIcon) { - const isCustom = false; - return this.setProjectIcon(projectIcon, isCustom); - } - - setCustomProjectIcon(projectIcon) { - const isCustom = true; - return this.setProjectIcon(projectIcon, isCustom); - } - - setProjectIcon(projectIcon, isCustom) { - return this.http - .post(this.ConfigService.getConfigParam('projectIconURL'), { - projectId: this.ConfigService.getProjectId(), - projectIcon: projectIcon, - isCustom: isCustom - }) - .toPromise() - .then((result) => { - return result; - }); - } - replaceNode(nodeId, node) { this.setIdToNode(nodeId, node); const nodes = this.getNodes(); @@ -5399,30 +3226,10 @@ export class ProjectService { } } - broadcastSavingProject() { - this.savingProjectSource.next(); - } - - broadcastErrorSavingProject() { - this.errorSavingProjectSource.next(); - } - - broadcastNotAllowedToEditThisProject() { - this.notAllowedToEditThisProjectSource.next(); - } - - broadcastNotLoggedInProjectNotSaved() { - this.notLoggedInProjectNotSavedSource.next(); - } - broadcastProjectChanged() { this.projectChangedSource.next(); } - broadcastProjectSaved() { - this.projectSavedSource.next(); - } - broadcastSnipImage(args: any) { this.snipImageSource.next(args); } diff --git a/src/main/webapp/wise5/services/spaceService.ts b/src/main/webapp/wise5/services/spaceService.ts index 7880238bb7..9eb8a06e0a 100644 --- a/src/main/webapp/wise5/services/spaceService.ts +++ b/src/main/webapp/wise5/services/spaceService.ts @@ -1,11 +1,11 @@ 'use strict'; import { Injectable } from '@angular/core'; -import { ProjectService } from './projectService'; +import { TeacherProjectService } from './teacherProjectService'; @Injectable() export class SpaceService { - constructor(private ProjectService: ProjectService) {} + constructor(private TeacherProjectService: TeacherProjectService) {} createSpace( id: string, @@ -22,10 +22,10 @@ export class SpaceService { } addSpace(id: string, name: string, isPublic: boolean = true, isShowInNotebook: boolean = true) { - this.ProjectService.addSpace(this.createSpace(id, name, isPublic, isShowInNotebook)); + this.TeacherProjectService.addSpace(this.createSpace(id, name, isPublic, isShowInNotebook)); } removeSpace(id: string) { - this.ProjectService.removeSpace(id); + this.TeacherProjectService.removeSpace(id); } } diff --git a/src/main/webapp/wise5/services/teacherProjectService.ts b/src/main/webapp/wise5/services/teacherProjectService.ts index 40d4eaeaf3..0e861ce396 100644 --- a/src/main/webapp/wise5/services/teacherProjectService.ts +++ b/src/main/webapp/wise5/services/teacherProjectService.ts @@ -1,8 +1,8 @@ 'use strict'; import * as angular from 'angular'; import * as $ from 'jquery'; -import { ProjectService } from '../services/projectService'; import { ConfigService } from '../services/configService'; +import { ProjectService } from './projectService'; import { UtilService } from '../services/utilService'; import { Injectable } from '@angular/core'; import { UpgradeModule } from '@angular/upgrade/static'; @@ -20,6 +20,20 @@ export class TeacherProjectService extends ProjectService { public refreshProject$ = this.refreshProjectSource.asObservable(); private scrollToBottomOfPageSource: Subject = new Subject(); public scrollToBottomOfPage$ = this.scrollToBottomOfPageSource.asObservable(); + private errorSavingProjectSource: Subject = new Subject(); + public errorSavingProject$: Observable = this.errorSavingProjectSource.asObservable(); + private notAllowedToEditThisProjectSource: Subject = new Subject(); + public notAllowedToEditThisProject$: Observable< + any + > = this.notAllowedToEditThisProjectSource.asObservable(); + private notLoggedInProjectNotSavedSource: Subject = new Subject(); + public notLoggedInProjectNotSaved$: Observable< + any + > = this.notLoggedInProjectNotSavedSource.asObservable(); + private projectSavedSource: Subject = new Subject(); + public projectSaved$: Observable = this.projectSavedSource.asObservable(); + private savingProjectSource: Subject = new Subject(); + public savingProject$: Observable = this.savingProjectSource.asObservable(); constructor( protected upgrade: UpgradeModule, @@ -201,7 +215,7 @@ export class TeacherProjectService extends ProjectService { return this.http .post(`${this.ConfigService.getConfigParam('copyProjectURL')}/${projectId}`, null) .toPromise() - .then((newProject) => { + .then(newProject => { return newProject; }); } @@ -218,7 +232,7 @@ export class TeacherProjectService extends ProjectService { projectJSONString: projectJSONString }) .toPromise() - .then((newProjectId) => { + .then(newProjectId => { return newProjectId; }); } @@ -1206,14 +1220,14 @@ export class TeacherProjectService extends ProjectService { return this.http .get(this.ConfigService.getConfigParam('getLibraryProjectsURL')) .toPromise() - .then((projects) => { + .then(projects => { return projects; }); } sortAndFilterUniqueLibraryProjects(libraryProjects) { const flatProjectList = libraryProjects - .map((grade) => { + .map(grade => { return grade.children; }) .flat(); @@ -1307,7 +1321,7 @@ export class TeacherProjectService extends ProjectService { } removeTeacherRemovalConstraint(node: any, periodId: number) { - node.constraints = node.constraints.filter((constraint) => { + node.constraints = node.constraints.filter(constraint => { return !( constraint.action === 'makeThisNodeNotVisitable' && constraint.targetId === node.id && @@ -1333,4 +1347,2170 @@ export class TeacherProjectService extends ProjectService { escapeToClose: true }); } + + /** + * Saves the project to Config.saveProjectURL and returns commit history promise. + * if Config.saveProjectURL or Config.projectId are undefined, does not save and returns null + */ + saveProject(): any { + if (!this.ConfigService.getConfigParam('canEditProject')) { + this.broadcastNotAllowedToEditThisProject(); + return null; + } + this.broadcastSavingProject(); + this.cleanupBeforeSave(); + this.project.metadata.authors = this.getUniqueAuthors( + this.addCurrentUserToAuthors(this.getAuthors()) + ); + return this.http + .post( + this.ConfigService.getConfigParam('saveProjectURL'), + angular.toJson(this.project, false) + ) + .toPromise() + .then((response: any) => { + this.handleSaveProjectResponse(response); + }); + } + + getAuthors(): any[] { + return this.project.metadata.authors ? this.project.metadata.authors : []; + } + + addCurrentUserToAuthors(authors: any[]): any[] { + let userInfo = this.ConfigService.getMyUserInfo(); + if (this.ConfigService.isClassroomMonitor()) { + userInfo = { + id: userInfo.userIds[0], + firstName: userInfo.firstName, + lastName: userInfo.lastName, + username: userInfo.username + }; + } + authors.push(userInfo); + return authors; + } + + getUniqueAuthors(authors = []): any[] { + const idToAuthor = {}; + const uniqueAuthors = []; + for (const author of authors) { + if (idToAuthor[author.id] == null) { + uniqueAuthors.push(author); + idToAuthor[author.id] = author; + } + } + return uniqueAuthors; + } + + handleSaveProjectResponse(response: any): any { + if (response.status === 'error') { + if (response.messageCode === 'notSignedIn') { + this.broadcastNotLoggedInProjectNotSaved(); + this.SessionService.forceLogOut(); + } else if (response.messageCode === 'notAllowedToEditThisProject') { + this.broadcastNotAllowedToEditThisProject(); + } else if (response.messageCode === 'errorSavingProject') { + this.broadcastErrorSavingProject(); + } + } else { + this.broadcastProjectSaved(); + } + return response; + } + + /** + * Perform any necessary cleanup before we save the project. + * For example we need to remove the checked field in the inactive node + * objects. + */ + cleanupBeforeSave() { + this.getActiveNodes().forEach(activeNode => { + this.cleanupNode(activeNode); + }); + this.getInactiveNodes().forEach(inactiveNode => { + this.cleanupNode(inactiveNode); + }); + } + + /** + * Remove any fields that are used temporarily for display purposes like when + * the project is loaded in the authoring tool and grading tool + * @param node The node object. + */ + cleanupNode(node) { + delete node.checked; + delete node.hasWork; + delete node.hasAlert; + delete node.hasNewAlert; + delete node.isVisible; + delete node.completionStatus; + delete node.score; + delete node.hasScore; + delete node.maxScore; + delete node.hasMaxScore; + delete node.scorePct; + delete node.order; + delete node.show; + + if (node.components != null) { + // activity node does not have components but step node does + node.components.forEach(component => { + this.cleanupComponent(component); + }); + } + } + + /** + * Remove any fields that are used temporarily for display purposes like when + * the project is loaded in the authoring tool and grading tool + * @param component The component object. + */ + cleanupComponent(component) { + delete component.checked; + delete component.hasWork; + } + + /** + * Insert the node after the given node id in the group's array of children ids + * @param nodeIdToInsert the node id we want to insert + * @param nodeIdToInsertAfter the node id we want to insert after + */ + insertNodeAfterInGroups(nodeIdToInsert, nodeIdToInsertAfter) { + const groupNodes = this.getGroupNodes(); + if (groupNodes != null) { + for (const group of groupNodes) { + this.insertNodeAfterInGroup(group, nodeIdToInsert, nodeIdToInsertAfter); + } + } + const inactiveGroupNodes = this.getInactiveGroupNodes(); + if (inactiveGroupNodes != null) { + for (const inactiveGroup of inactiveGroupNodes) { + this.insertNodeAfterInGroup(inactiveGroup, nodeIdToInsert, nodeIdToInsertAfter); + } + } + } + + /** + * Insert a node id in a group after another specific node id. + * @param group A group object. + * @param nodeIdToInsert The node id to insert. + * @param nodeIdToInsertAfter The node id to insert after. + * @returns {boolean} Whether we inserted the node id. + */ + insertNodeAfterInGroup(group, nodeIdToInsert, nodeIdToInsertAfter) { + const ids = group.ids; + if (ids != null) { + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + if (nodeIdToInsertAfter === id) { + ids.splice(i + 1, 0, nodeIdToInsert); + return true; + } + } + } + return false; + } + + /** + * Update the transitions to handle inserting a node after another node. + * The two nodes must either both be steps or both be activities. + * @param nodeToInsert the node to insert + * @param nodeIdToInsertAfter the node id to insert after + */ + insertNodeAfterInTransitions(nodeToInsert, nodeIdToInsertAfter) { + const nodeToInsertAfter = this.getNodeById(nodeIdToInsertAfter); + if (nodeToInsert.type != nodeToInsertAfter.type) { + throw 'Error: insertNodeAfterInTransitions() nodes are not the same type'; + } + if (nodeToInsertAfter.transitionLogic == null) { + nodeToInsertAfter.transitionLogic = { + transitions: [] + }; + } + if (nodeToInsert.transitionLogic == null) { + nodeToInsert.transitionLogic = { + transitions: [] + }; + } + if (this.isGroupNode(nodeToInsert.id)) { + this.updateChildrenTransitionsInAndOutOfGroup(nodeToInsert, nodeIdToInsertAfter); + } + this.copyTransitions(nodeToInsertAfter, nodeToInsert); + if (nodeToInsert.transitionLogic.transitions.length == 0) { + this.copyParentTransitions(nodeIdToInsertAfter, nodeToInsert); + } + const transitionObject = { + to: nodeToInsert.id + }; + nodeToInsertAfter.transitionLogic.transitions = [transitionObject]; + this.updateBranchPathTakenConstraints(nodeToInsert, nodeIdToInsertAfter); + } + + /* + * Copy the transitions from nodeId's parent and add to node's transitions. + * @param nodeId Copy the transition of this nodeId's parent. + * @param node The node to add transitions to. + */ + copyParentTransitions(nodeId, node) { + const parentGroupId = this.getParentGroupId(nodeId); + if (parentGroupId != 'group0') { + const parentTransitions = this.getTransitionsByFromNodeId(parentGroupId); + for (let parentTransition of parentTransitions) { + const newTransition = {}; + const toNodeId = parentTransition.to; + if (this.isGroupNode(toNodeId)) { + const startId = this.getGroupStartId(toNodeId); + if (startId == null || startId == '') { + (newTransition).to = toNodeId; + } else { + (newTransition).to = startId; + } + } + node.transitionLogic.transitions.push(newTransition); + } + } + } + + copyTransitions(previousNode, node) { + const transitionsJSONString = angular.toJson(previousNode.transitionLogic.transitions); + const transitionsCopy = angular.fromJson(transitionsJSONString); + node.transitionLogic.transitions = transitionsCopy; + } + + /** + * If the previous node was in a branch path, we will also put the + * inserted node into the branch path. + * @param node The node that is in the branch path. + * @param nodeId The node we are adding to the branch path. + */ + updateBranchPathTakenConstraints(node, nodeId) { + this.removeBranchPathTakenNodeConstraintsIfAny(node.id); + const branchPathTakenConstraints = this.getBranchPathTakenConstraintsByNodeId(nodeId); + for (let branchPathTakenConstraint of branchPathTakenConstraints) { + const newConstraint = { + id: this.getNextAvailableConstraintIdForNodeId(node.id), + action: branchPathTakenConstraint.action, + targetId: node.id, + removalCriteria: this.UtilService.makeCopyOfJSONObject( + branchPathTakenConstraint.removalCriteria + ) + }; + this.addConstraintToNode(node, newConstraint); + } + } + + /** + * Update a node's branchPathTaken constraint's fromNodeId and toNodeId + * @param node update the branch path taken constraints in this node + * @param currentFromNodeId the current from node id + * @param currentToNodeId the current to node id + * @param newFromNodeId the new from node id + * @param newToNodeId the new to node id + */ + updateBranchPathTakenConstraint( + node, + currentFromNodeId, + currentToNodeId, + newFromNodeId, + newToNodeId + ) { + for (let constraint of node.constraints) { + for (let removalCriterion of constraint.removalCriteria) { + if (removalCriterion.name === 'branchPathTaken') { + const params = removalCriterion.params; + if (params.fromNodeId === currentFromNodeId && params.toNodeId === currentToNodeId) { + params.fromNodeId = newFromNodeId; + params.toNodeId = newToNodeId; + } + } + } + } + } + + /** + * Insert a node into a group + * @param nodeIdToInsert the node id to insert + * @param nodeIdToInsertInside the node id of the group we will insert into + */ + insertNodeInsideInGroups(nodeIdToInsert, nodeIdToInsertInside) { + const group = this.getNodeById(nodeIdToInsertInside); + if (group != null) { + const ids = group.ids; + if (ids != null) { + ids.splice(0, 0, nodeIdToInsert); + group.startId = nodeIdToInsert; + } + } + } + + /** + * Update the transitions to handle inserting a node as the first step in a group. + * @param nodeIdToInsert node id that we will insert + * @param nodeIdToInsertInside the node id of the group we are inserting into + */ + insertNodeInsideOnlyUpdateTransitions(nodeIdToInsert, nodeIdToInsertInside) { + if (!this.isGroupNode(nodeIdToInsertInside)) { + throw 'Error: insertNodeInsideOnlyUpdateTransitions() second parameter must be a group'; + } + + const nodeToInsert = this.getNodeById(nodeIdToInsert); + nodeToInsert.transitionLogic.transitions = []; + this.removeBranchPathTakenNodeConstraintsIfAny(nodeIdToInsert); + + if (this.isGroupNode(nodeIdToInsert)) { + this.updateChildrenTransitionsInAndOutOfGroup(nodeToInsert); + } + + /* + * the node will become the first node in the group. this means we need to update any nodes + * that point to the old start id and make them point to the node we are inserting. + */ + const group = this.getNodeById(nodeIdToInsertInside); + const startId = group.startId; + this.updateTransitionsToStartId(startId, nodeIdToInsert); + this.updateStepTransitionsToGroup(nodeIdToInsertInside, nodeIdToInsert); + this.createTransitionFromNodeToInsertToOldStartNode(startId, nodeToInsert); + const transitions = this.getTransitionsByFromNodeId(nodeIdToInsert); + if (transitions.length == 0) { + this.inheritParentTransitions(nodeIdToInsertInside, nodeToInsert); + } + } + + /** + * Copy the transitions from the parent to the node we are inserting. + * @param nodeIdToInsertInside + * @param nodeToInsert + */ + inheritParentTransitions(nodeIdToInsertInside, nodeToInsert) { + const parentTransitions = this.getTransitionsByFromNodeId(nodeIdToInsertInside); + for (let parentTransition of parentTransitions) { + const toNodeId = parentTransition.to; + if (this.isGroupNode(toNodeId)) { + const nextGroup = this.getNodeById(toNodeId); + const startId = nextGroup.startId; + if (startId == null || startId == '') { + this.addToTransition(nodeToInsert, toNodeId); + } else { + this.addToTransition(nodeToInsert, startId); + } + } else { + this.addToTransition(nodeToInsert, toNodeId); + } + } + } + + /* + * Create a transition from the node we are inserting to the node that + * was the start node. + * @param startId + * @param nodeToInsert + */ + createTransitionFromNodeToInsertToOldStartNode(startId, nodeToInsert) { + const startNode = this.getNodeById(startId); + if (startNode != null) { + const transitions = this.getTransitionsByFromNodeId(nodeToInsert.id); + const transitionObject = { + to: startId + }; + transitions.push(transitionObject); + } + } + + /* + * Update all the transitions that point to the group and change + * them to point to the new start id + */ + updateStepTransitionsToGroup(nodeIdToInsertInside, nodeIdToInsert) { + const nodesThatTransitionToGroup = this.getNodesByToNodeId(nodeIdToInsertInside); + for (let nodeThatTransitionsToGroup of nodesThatTransitionToGroup) { + if (!this.isGroupNode(nodeThatTransitionsToGroup.id)) { + this.updateToTransition(nodeThatTransitionsToGroup, nodeIdToInsertInside, nodeIdToInsert); + } + } + } + + updateTransitionsToStartId(startId, nodeIdToInsert) { + const nodesThatTransitionToStartId = this.getNodesByToNodeId(startId); + for (let nodeThatTransitionToStartId of nodesThatTransitionToStartId) { + this.updateToTransition(nodeThatTransitionToStartId, startId, nodeIdToInsert); + } + } + + /** + * Add a transition to a node + * @param node the node we are adding a transition to + * @param toNodeId the node id we going to transition to + * @param criteria (optional) a criteria object specifying + * what needs to be satisfied in order to use this transition + */ + addToTransition(node, toNodeId, criteria = null) { + if (node != null) { + if (node.transitionLogic == null) { + node.transitionLogic = {}; + } + if (node.transitionLogic.transitions == null) { + node.transitionLogic.transitions = []; + } + const transition = {}; + (transition).to = toNodeId; + if (criteria != null) { + (transition).criteria = criteria; + } + node.transitionLogic.transitions.push(transition); + } + } + + /** + * Update the to value of aa transition + * @param node the node to update + * @param oldToNodeId the previous to node id + * @param newToNodeId the new to node id + */ + updateToTransition(node, oldToNodeId, newToNodeId) { + if (node != null) { + if (node.transitionLogic == null) { + node.transitionLogic = {}; + } + + if (node.transitionLogic.transitions == null) { + node.transitionLogic.transitions = []; + } + + const transitions = node.transitionLogic.transitions; + for (let transition of transitions) { + if (transition != null) { + const toNodeId = transition.to; + if (oldToNodeId === toNodeId) { + transition.to = newToNodeId; + } + } + } + } + } + + /** + * Get the next available group id + * @returns the next available group id + */ + getNextAvailableGroupId() { + const groupIds = this.getGroupIds(); + let largestGroupIdNumber = null; + for (let groupId of groupIds) { + // get the number from the group id e.g. the number of 'group2' would be 2 + let groupIdNumber = groupId.replace('group', ''); + + // make sure the number is an actual number + if (!isNaN(groupIdNumber)) { + groupIdNumber = parseInt(groupIdNumber); + + // update the largest group id number if necessary + if (largestGroupIdNumber == null) { + largestGroupIdNumber = groupIdNumber; + } else if (groupIdNumber > largestGroupIdNumber) { + largestGroupIdNumber = groupIdNumber; + } + } + } + + const nextAvailableGroupId = 'group' + (largestGroupIdNumber + 1); + return nextAvailableGroupId; + } + + /** + * Get all the group ids + * @returns an array with all the group ids + */ + getGroupIds() { + const groupIds = []; + const groupNodes = this.groupNodes; + for (let group of groupNodes) { + if (group != null) { + const groupId = group.id; + if (groupId != null) { + groupIds.push(groupId); + } + } + } + + const inactiveGroupNodes = this.getInactiveGroupNodes(); + for (let inactiveGroup of inactiveGroupNodes) { + if (inactiveGroup != null) { + const inactiveGroupId = inactiveGroup.id; + if (inactiveGroupId != null) { + groupIds.push(inactiveGroupId); + } + } + } + return groupIds; + } + + /** + * Get the next available node id + * @param nodeIdsToSkip (optional) An array of additional node ids to not + * use. This parameter is used in cases where we are creating multiple new + * nodes at once. + * Example + * We ask for two new node ids by calling getNextAvailableNodeId() twice. + * The first time it returns "node10". + * If we ask the second time without actually creating and adding node10, + * it will return "node10" again. If we provide "node10" in the + * nodeIdsToSkip, then getNextAvailableNodeId() will properly return to us + * "node11". + * @returns the next available node id + */ + getNextAvailableNodeId(nodeIdsToSkip = []) { + let largestNodeIdNumber = -1; + for (const nodeId of this.getNodeIds() + .concat(this.getInactiveNodeIds()) + .concat(nodeIdsToSkip)) { + const nodeIdNumber = parseInt(nodeId.replace('node', '')); + if (nodeIdNumber > largestNodeIdNumber) { + largestNodeIdNumber = nodeIdNumber; + } + } + return 'node' + (largestNodeIdNumber + 1); + } + + /** + * Get all the node ids from inactive steps + * @returns an array with all the inactive node ids + */ + getInactiveNodeIds() { + return this.project.inactiveNodes.map(node => { + return node.id; + }); + } + + /** + * Copy the node with the specified nodeId + * @param nodeId the node id to copy + * @return copied node + */ + copyNode(nodeId) { + const node = this.getNodeById(nodeId); + const nodeCopy = this.UtilService.makeCopyOfJSONObject(node); + nodeCopy.id = this.getNextAvailableNodeId(); + nodeCopy.transitionLogic = {}; // clear transition logic + nodeCopy.constraints = []; // clear constraints + + const newComponentIds = []; + for (let component of nodeCopy.components) { + const newComponentId = this.getUnusedComponentId(newComponentIds); + newComponentIds.push(newComponentId); + component.id = newComponentId; + } + return nodeCopy; + } + + /** + * Delete a node from the project and update transitions. + * + * If we are deleting the project start node id, we will need to change it to the + * next logical node id that will be used as the project start. + * + * @param nodeId the node id to delete from the project. It can be a step or an activity. + */ + deleteNode(nodeId) { + const parentGroup = this.getParentGroup(nodeId); + if (parentGroup != null && parentGroup.startId === nodeId) { + this.setGroupStartIdToNextChildId(parentGroup); + } + if (this.isProjectStartNodeIdOrContainsProjectStartNodeId(nodeId)) { + this.updateProjectStartNodeIdToNextLogicalNode(nodeId); + } + if (this.isGroupNode(nodeId)) { + this.removeChildNodes(nodeId); + } + this.removeNodeIdFromTransitions(nodeId); + this.removeNodeIdFromGroups(nodeId); + this.removeNodeIdFromNodes(nodeId); + } + + updateProjectStartNodeIdToNextLogicalNode(nodeId) { + if (this.isGroupNode(nodeId)) { + this.updateProjectStartNodeIdToNextLogicalNodeForRemovingGroup(nodeId); + } else { + this.updateProjectStartNodeIdToNextLogicalNodeForRemovingStep(nodeId); + } + } + + /** + * Set the startNodeId of the specified group to the first node of the next group. + * If the next group doesn't have any nodes, startNodeId should point + * to the next group. + */ + updateProjectStartNodeIdToNextLogicalNodeForRemovingGroup(nodeId) { + const transitions = this.getTransitionsByFromNodeId(nodeId); + if (transitions.length == 0) { + this.setStartNodeId('group0'); + } else { + let nextNodeId = transitions[0].to; + if (this.isGroupNode(nextNodeId)) { + const nextGroupStartId = this.getGroupStartId(nextNodeId); + if (nextGroupStartId == null) { + this.setStartNodeId(nextNodeId); + } else { + this.setStartNodeId(nextGroupStartId); + } + } else { + this.setStartNodeId(nextNodeId); + } + } + } + + /** + * Set the startNodeId to the next node in the transitions. + * If there are no transitions, set it to the parent group of the node. + */ + updateProjectStartNodeIdToNextLogicalNodeForRemovingStep(nodeId) { + const transitions = this.getTransitionsByFromNodeId(nodeId); + const parentGroupId = this.getParentGroupId(nodeId); + if (transitions.length == 0) { + this.setStartNodeId(parentGroupId); + } else { + let nextNodeId = transitions[0].to; + if (this.isNodeInGroup(nextNodeId, parentGroupId)) { + this.setStartNodeId(nextNodeId); + } else { + this.setStartNodeId(this.getParentGroupId(nodeId)); + } + } + } + + setGroupStartIdToNextChildId(group) { + let hasSetNewStartId = false; + const transitions = this.getTransitionsByFromNodeId(group.startId); + if (transitions.length > 0) { + const transition = transitions[0]; + const toNodeId = transition.to; + if (this.isNodeInGroup(toNodeId, group.id)) { + group.startId = toNodeId; + hasSetNewStartId = true; + } + } + + if (!hasSetNewStartId) { + group.startId = ''; + } + } + + removeChildNodes(groupId) { + const group = this.getNodeById(groupId); + for (let i = 0; i < group.ids.length; i++) { + const childId = group.ids[i]; + this.removeNodeIdFromTransitions(childId); + this.removeNodeIdFromGroups(childId); + this.removeNodeIdFromNodes(childId); + i--; // so it won't skip the next element + } + } + + isProjectStartNodeIdOrContainsProjectStartNodeId(nodeId) { + return ( + this.getStartNodeId() === nodeId || + (this.isGroupNode(nodeId) && this.containsStartNodeId(nodeId)) + ); + } + + containsStartNodeId(groupId) { + const group = this.getNodeById(groupId); + const projectStartNodeId = this.getStartNodeId(); + for (let childId of group.ids) { + if (childId === projectStartNodeId) { + return true; + } + } + return false; + } + + /** + * Update the transitions to handle removing a node + * @param nodeId the node id to remove + */ + removeNodeIdFromTransitions(nodeId) { + const nodeToRemove = this.getNodeById(nodeId); + const nodesByToNodeId = this.getNodesByToNodeId(nodeId); + + const nodeToRemoveTransitionLogic = nodeToRemove.transitionLogic; + let nodeToRemoveTransitions = []; + + if (nodeToRemoveTransitionLogic != null && nodeToRemoveTransitionLogic.transitions != null) { + nodeToRemoveTransitions = nodeToRemoveTransitionLogic.transitions; + } + + const parentIdOfNodeToRemove = this.getParentGroupId(nodeId); + const parentGroup = this.getNodeById(parentIdOfNodeToRemove); + + // update the start id if we are removing the start node of a group + if (parentGroup != null) { + const parentGroupStartId = parentGroup.startId; + if (parentGroupStartId != null) { + if (parentGroupStartId === nodeId) { + // the node we are removing is the start node + + if (nodeToRemoveTransitions != null && nodeToRemoveTransitions.length > 0) { + for (let nodeToRemoveTransition of nodeToRemoveTransitions) { + if (nodeToRemoveTransition != null) { + const toNodeId = nodeToRemoveTransition.to; + if (toNodeId != null) { + /* + * we need to check that the to node id is in the + * same group. some transitions point to a node id + * in the next group which we would not want to use + * for the start id. + */ + if (this.getParentGroupId(toNodeId) == parentIdOfNodeToRemove) { + // set the new start id + parentGroup.startId = toNodeId; + } + } + } + } + } else { + // there are no transitions so we will have an empty start id + parentGroup.startId = ''; + } + } + } + } + + for (let n = 0; n < nodesByToNodeId.length; n++) { + const node = nodesByToNodeId[n]; + if (node != null) { + const parentIdOfFromNode = this.getParentGroupId(node.id); + const transitionLogic = node.transitionLogic; + + if (transitionLogic != null) { + const transitions = transitionLogic.transitions; + for (let t = 0; t < transitions.length; t++) { + const transition = transitions[t]; + if (nodeId === transition.to) { + // we have found the transition to the node we are removing + + // copy the transitions from the node we are removing + let transitionsCopy = angular.toJson(nodeToRemoveTransitions); + transitionsCopy = angular.fromJson(transitionsCopy); + + /* + * if the parent from group is different than the parent removing group + * remove transitions that are to a node in a different group than + * the parent removing group + */ + + if (parentIdOfFromNode != parentIdOfNodeToRemove) { + for (let tc = 0; tc < transitionsCopy.length; tc++) { + const tempTransition = transitionsCopy[tc]; + if (tempTransition != null) { + const tempToNodeId = tempTransition.to; + if (tempToNodeId != null) { + const parentIdOfToNode = this.getParentGroupId(tempToNodeId); + if (parentIdOfNodeToRemove != parentIdOfToNode) { + // remove the transition + transitionsCopy.splice(tc, 1); + tc--; + } + } + } + } + } + + if (this.isFirstNodeInBranchPath(nodeId)) { + /* + * Get the node ids that have a branchPathTaken + * constraint from the before node and to the node + * we are removing. If there are any, we need to + * update the branchPathTaken constraint with the + * next nodeId that comes after the node we are + * removing. + */ + const nodeIdsInBranch = this.getNodeIdsInBranch(node.id, nodeId); + + if (nodeIdsInBranch != null) { + for (let nodeIdInBranch of nodeIdsInBranch) { + const nodeInBranch = this.getNodeById(nodeIdInBranch); + for (let transitionCopy of transitionsCopy) { + if (transitionCopy != null) { + const currentFromNodeId = node.id; + const currentToNodeId = nodeId; + const newFromNodeId = node.id; + const newToNodeId = transitionCopy.to; + + /* + * change the branch path taken constraint by changing + * the toNodeId + */ + this.updateBranchPathTakenConstraint( + nodeInBranch, + currentFromNodeId, + currentToNodeId, + newFromNodeId, + newToNodeId + ); + } + } + } + } + } else if (this.isBranchPoint(nodeId)) { + /* + * get all the branches that have the node we + * are removing as the start point + */ + const branches = this.getBranchesByBranchStartPointNodeId(nodeId); + + for (let branch of branches) { + if (branch != null) { + /* + * get the branch paths. these paths do not + * contain the start point or merge point. + */ + const branchPaths = branch.branchPaths; + + if (branchPaths != null) { + for (let branchPath of branchPaths) { + if (branchPath != null) { + const currentFromNodeId = nodeId; + const currentToNodeId = branchPath[0]; + const newFromNodeId = node.id; + const newToNodeId = branchPath[0]; + for (let branchPathNodeId of branchPath) { + const branchPathNode = this.getNodeById(branchPathNodeId); + this.updateBranchPathTakenConstraint( + branchPathNode, + currentFromNodeId, + currentToNodeId, + newFromNodeId, + newToNodeId + ); + } + } + } + } + } + } + } + + // remove the transition to the node we are removing + transitions.splice(t, 1); + + if (transitionsCopy != null) { + let insertIndex = t; + + /* + * loop through all the transitions from the node we are removing + * and insert them into the transitions of the from node + * e.g. + * the node that comes before the node we are removing has these transitions + * "transitions": [ + * { + * "to": "node4" + * }, + * { + * "to": "node6" + * } + * ] + * + * we are removing node4. node4 has a transition to node5. + * + * the node that comes before the node we are removing now has these transitions + * "transitions": [ + * { + * "to": "node5" + * }, + * { + * "to": "node6" + * } + * ] + */ + for (let transitionCopy of transitionsCopy) { + if (!this.isTransitionExist(transitions, transitionCopy)) { + const toNodeId = transitionCopy.to; + if ( + this.isApplicationNode(node.id) && + this.isGroupNode(toNodeId) && + this.hasGroupStartId(toNodeId) + ) { + this.addToTransition(node, this.getGroupStartId(toNodeId)); + } else { + transitions.splice(insertIndex, 0, transitionCopy); + insertIndex++; + } + } + } + } + t--; + + // check if the node we are moving is a group + if (this.isGroupNode(nodeId)) { + /* + * we are moving a group so we need to update transitions that + * go into the group + */ + const groupIdWeAreMoving = nodeId; + const groupThatTransitionsToGroupWeAreMoving = node; + this.updateChildrenTransitionsIntoGroupWeAreMoving( + groupThatTransitionsToGroupWeAreMoving, + groupIdWeAreMoving + ); + } + } + } + + if ( + transitions.length === 0 && + parentIdOfNodeToRemove != 'group0' && + parentIdOfNodeToRemove != this.getParentGroupId(node.id) + ) { + /* + * the from node no longer has any transitions so we will make it transition to the + * parent of the node we are removing + */ + this.addToTransition(node, parentIdOfNodeToRemove); + } + + if (this.isBranchPoint(nodeId)) { + /* + * the node we are deleting is a branch point so we to + * copy the transition logic to the node that comes + * before it + */ + node.transitionLogic = this.UtilService.makeCopyOfJSONObject( + nodeToRemoveTransitionLogic + ); + + /* + * set the transitions for the node that comes before + * the one we are removing + */ + node.transitionLogic.transitions = transitions; + } + } + } + } + + if (nodeToRemoveTransitionLogic != null) { + nodeToRemoveTransitionLogic.transitions = []; + } + + if (this.isGroupNode(nodeId)) { + this.removeTransitionsOutOfGroup(nodeId); + } + } + + isTransitionExist(transitions: any[], transition: any) { + for (const tempTransition of transitions) { + if (tempTransition.from === transition.from && tempTransition.to === transition.to) { + return true; + } + } + return false; + } + + /** + * Remove the node id from all groups + * @param nodeId the node id to remove + */ + removeNodeIdFromGroups(nodeId) { + for (const group of this.getGroupNodes()) { + this.removeNodeIdFromGroup(group, nodeId); + } + for (const inactiveGroup of this.getInactiveGroupNodes()) { + this.removeNodeIdFromGroup(inactiveGroup, nodeId); + } + } + + /** + * Remove a node from a group. + * If the node is a start node of the group, update the group's start node to + * the next node in the group after removing. + * @param group The group to remove from. + * @param nodeId The node id to remove. + */ + removeNodeIdFromGroup(group, nodeId) { + const ids = group.ids; + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + if (id === nodeId) { + ids.splice(i, 1); + if (id === group.startId) { + this.shiftGroupStartNodeByOne(group); + } + } + } + } + + // TODO handle the case when the start node of the group is a branch point + shiftGroupStartNodeByOne(group) { + const transitionsFromStartNode = this.getTransitionsByFromNodeId(group.startId); + if (transitionsFromStartNode.length > 0) { + group.startId = transitionsFromStartNode[0].to; + } else { + group.startId = ''; + } + } + + /** + * Remove the node from the array of nodes + * @param nodeId the node id to remove + */ + removeNodeIdFromNodes(nodeId) { + const nodes = this.project.nodes; + for (let n = 0; n < nodes.length; n++) { + const node = nodes[n]; + if (node != null) { + if (nodeId === node.id) { + nodes.splice(n, 1); + } + } + } + + const inactiveNodes = this.project.inactiveNodes; + if (inactiveNodes != null) { + for (let i = 0; i < inactiveNodes.length; i++) { + const inactiveNode = inactiveNodes[i]; + if (inactiveNode != null) { + if (nodeId === inactiveNode.id) { + inactiveNodes.splice(i, 1); + } + } + } + } + + this.idToNode[nodeId] = null; + } + + /** + * Remove the node from the inactive nodes array + * @param nodeId the node to remove from the inactive nodes array + */ + removeNodeIdFromInactiveNodes(nodeId) { + const inactiveNodes = this.project.inactiveNodes; + if (inactiveNodes != null) { + for (let i = 0; i < inactiveNodes.length; i++) { + const inactiveNode = inactiveNodes[i]; + if (inactiveNode != null) { + const inactiveNodeId = inactiveNode.id; + if (inactiveNodeId === nodeId) { + inactiveNodes.splice(i, 1); + } + } + } + } + } + + /** + * Create a new component + * @param nodeId the node id to create the component in + * @param componentType the component type + * @param insertAfterComponentId Insert the new compnent after the given + * component id. If this argument is null, we will place the new component + * in the first position. + */ + createComponent(nodeId, componentType, insertAfterComponentId = null) { + const node = this.getNodeById(nodeId); + const service = this.upgrade.$injector.get(componentType + 'Service'); + const component = service.createComponent(); + if (service.componentHasWork()) { + if (node.showSaveButton == false) { + if (this.doesAnyComponentInNodeShowSubmitButton(node.id)) { + component.showSaveButton = true; + } else { + node.showSaveButton = true; + } + } + } + this.addComponentToNode(node, component, insertAfterComponentId); + return component; + } + + /** + * Returns true iff any component in the step generates work + * @param nodeId the node id + * @return whether any components in the step generates work + */ + doesAnyComponentHaveWork(nodeId) { + const node = this.getNodeById(nodeId); + for (const component of node.components) { + const service = this.upgrade.$injector.get(component.type + 'Service'); + if (service != null && service.componentHasWork()) { + return true; + } + } + return false; + } + + /** + * Check if any of the components in the node are showing their submit button. + * @param nodeId {string} The node id to check. + * @return {boolean} Whether any of the components in the node show their submit button. + */ + doesAnyComponentInNodeShowSubmitButton(nodeId) { + const node = this.getNodeById(nodeId); + for (const component of node.components) { + if (component.showSubmitButton == true) { + return true; + } + } + return false; + } + + /** + * Add the component to the node + * @param node the node + * @param component the component + * @param insertAfterComponentId Insert the component after this given + * component id. If this argument is null, we will place the new component + * in the first position. + */ + addComponentToNode(node, component, insertAfterComponentId) { + if (insertAfterComponentId == null) { + node.components.splice(0, 0, component); + } else { + // place the new component after the insertAfterComponentId + + // boolean flag for whether we have added the component yet + let added = false; + + const components = node.components; + for (let c = 0; c < components.length; c++) { + const tempComponent = components[c]; + if ( + tempComponent != null && + tempComponent.id != null && + tempComponent.id == insertAfterComponentId + ) { + /* + * we have found the component we want to add the new + * one after + */ + + components.splice(c + 1, 0, component); + added = true; + break; + } + } + + if (!added) { + /* + * the component has not been added yet so we will just add + * it at the end + */ + node.components.push(component); + } + } + } + + /** + * TODO: Deprecated, should be removed; replaced by getMaxScoreForWorkgroupId in + * StudentStatusService + * Get the max score for the project. If the project contains branches, we + * will only calculate the max score for a single path from the first node + * to the last node in the project. + * @returns the max score for the project or null if none of the components in the project + * has max scores. + */ + getMaxScore() { + let maxScore = null; + const startNodeId = this.getStartNodeId(); + + // get all the paths in the project + const allPaths = this.getAllPaths([], startNodeId); + + if (allPaths != null && allPaths.length > -1) { + const firstPath = allPaths[0]; + for (let nodeId of firstPath) { + const nodeMaxScore = this.getMaxScoreForNode(nodeId); + if (nodeMaxScore != null) { + if (maxScore == null) { + maxScore = nodeMaxScore; + } else { + maxScore += nodeMaxScore; + } + } + } + } + return maxScore; + } + + /** + * Set the max score for a component + * @param nodeId set the max score from a component in this node + * @param componentId set the max score from this component + * @param maxScore set it to this maxScore + */ + setMaxScoreForComponent(nodeId, componentId, maxScore) { + if (nodeId != null && componentId != null && maxScore != null && typeof maxScore === 'number') { + let component = this.getComponentByNodeIdAndComponentId(nodeId, componentId); + if (component != null) { + component.maxScore = maxScore; + } + } + } + + hasGroupStartId(nodeId) { + const startId = this.getGroupStartId(nodeId); + return startId != null && startId != ''; + } + + /** + * Update the transitions so that the fromGroup points to the newToGroup + * + * Before + * fromGroup -> oldToGroup -> newToGroup + * + * After + * fromGroup -> newToGroup + * oldToGroup becomes dangling and has no transitions to or from it + */ + updateTransitionsForExtractingGroup(fromGroupId, oldToGroupId, newToGroupId) { + /* + * make the transitions + * fromGroup -> newToGroup + */ + if (fromGroupId != null && oldToGroupId != null) { + const fromGroup = this.getNodeById(fromGroupId); + const oldToGroup = this.getNodeById(oldToGroupId); + let newToGroup = null; + let newToGroupStartId = null; + + if (newToGroupId != null) { + newToGroup = this.getNodeById(newToGroupId); + } + + if (newToGroup != null) { + newToGroupStartId = newToGroup.startId; + } + + if (fromGroup != null && oldToGroup != null) { + const childIds = fromGroup.ids; + + // update the children of the from group to point to the new to group + if (childIds != null) { + for (let childId of childIds) { + const child = this.getNodeById(childId); + const transitions = this.getTransitionsByFromNodeId(childId); + + if (transitions != null) { + for (let t = 0; t < transitions.length; t++) { + const transition = transitions[t]; + if (transition != null) { + const toNodeId = transition.to; + if (toNodeId === oldToGroupId) { + // the transition is to the group + if (newToGroupId == null && newToGroupStartId == null) { + // there is no new to group so we will remove the transition + transitions.splice(t, 1); + t--; + } else { + // make the transition point to the new to group + transition.to = newToGroupId; + } + } else if (this.isNodeInGroup(toNodeId, oldToGroupId)) { + // the transition is to a node in the group + if (newToGroupId == null && newToGroupStartId == null) { + // there is no new to group so we will remove the transition + transitions.splice(t, 1); + t--; + } else if (newToGroupStartId == null || newToGroupStartId == '') { + // make the transition point to the new to group + transition.to = newToGroupId; + } else { + // make the transition point to the new group start id + transition.to = newToGroupStartId; + } + } + } + } + } + } + } + } + } + + /* + * remove the transitions from the oldToGroup + */ + if (oldToGroupId != null && newToGroupId != null) { + const oldToGroup = this.getNodeById(oldToGroupId); + if (oldToGroup != null) { + const childIds = oldToGroup.ids; + + // remove the transitions from the old to group that point to the new to group + if (childIds != null) { + for (let childId of childIds) { + const child = this.getNodeById(childId); + const transitions = this.getTransitionsByFromNodeId(childId); + if (transitions != null) { + for (let t = 0; t < transitions.length; t++) { + const transition = transitions[t]; + if (transition != null) { + const toNodeId = transition.to; + if (toNodeId === newToGroupId) { + // the transition is to the group so we will remove it + transitions.splice(t, 1); + t--; + } else if (this.isNodeInGroup(toNodeId, newToGroupId)) { + // the transition is to a node in the group so we will remove it + transitions.splice(t, 1); + t--; + } + } + } + } + } + } + } + } + } + + /** + * Update the transitions so that the fromGroup points to the newToGroup + * + * Before + * fromGroup -> oldToGroup + * newToGroup is dangling and has no transitions to or from it + * + * After + * fromGroup -> newToGroup -> oldToGroup + */ + updateTransitionsForInsertingGroup(fromGroupId, oldToGroupIds, newToGroupId) { + let fromGroup = null; + let newToGroup = null; + if (fromGroupId != null) { + fromGroup = this.getNodeById(fromGroupId); + } + + if (newToGroupId != null) { + newToGroup = this.getNodeById(newToGroupId); + } + + /* + * make the transitions that point to the old group now point + * to the new group + * fromGroup -> newToGroup + */ + if (fromGroup != null && newToGroup != null) { + const childIds = fromGroup.ids; + const newToGroupStartId = newToGroup.startId; + if (childIds != null) { + for (let childId of childIds) { + const child = this.getNodeById(childId); + + // get the transitions from the child + const transitions = this.getTransitionsByFromNodeId(childId); + + if (transitions == null || transitions.length == 0) { + /* + * the child does not have any transitions so we will make it + * point to the new group + */ + if (newToGroupStartId == null || newToGroupStartId == '') { + this.addToTransition(child, newToGroupId); + } else { + this.addToTransition(child, newToGroupStartId); + } + } else if (transitions != null) { + for (let transition of transitions) { + if (transition != null) { + const toNodeId = transition.to; + if (oldToGroupIds != null) { + for (let oldToGroupId of oldToGroupIds) { + if (toNodeId === oldToGroupId) { + /* + * the transition is to the group so we will update the transition + * to the new group + */ + transition.to = newToGroupId; + } else if (this.isNodeInGroup(toNodeId, oldToGroupId)) { + /* + * the transition is to a node in the old group so we will update + * the transition to point to the new group + */ + if (newToGroupStartId == null || newToGroupStartId == '') { + transition.to = newToGroupId; + } else { + transition.to = newToGroupStartId; + } + } + } + } + } + } + } + } + } + } + + /* + * make the steps that do not have a transition now point to the old + * group + * newToGroup -> oldToGroup + */ + if (newToGroup != null) { + const childIds = newToGroup.ids; + if (childIds != null) { + for (let childId of childIds) { + const child = this.getNodeById(childId); + const transitions = this.getTransitionsByFromNodeId(childId); + + if (transitions == null || transitions.length == 0) { + if (oldToGroupIds != null) { + for (let oldToGroupId of oldToGroupIds) { + const oldToGroup = this.getNodeById(oldToGroupId); + if (oldToGroup != null) { + const oldToGroupStartId = oldToGroup.startId; + const transition = {}; + let toNodeId = ''; + if (oldToGroupStartId == null) { + // there is no start node id so we will just point to the group + toNodeId = oldToGroup; + } else { + // there is a start node id so we will point to it + toNodeId = oldToGroupStartId; + } + + // create the transition from the child to the old group + this.addToTransition(child, toNodeId); + } + } + } + } + } + } + } + } + + /** + * Update the child transitions because we are moving a group. We will + * update the transitions into and out of the group in the location + * we are extracting the group from and also in the location we are + * inserting the group into. + * @param node the group we are moving + * @param nodeId we will put the group after this node id + */ + updateChildrenTransitionsInAndOutOfGroup(node, nodeId = null) { + let transitionsBefore = null; + + // get the group nodes that point to the group we are moving + const previousGroupNodes = this.getGroupNodesByToNodeId(node.id); + + // get all the transitions from the group we are moving + const transitionsAfter = this.getTransitionsByFromNodeId(node.id); + + let extracted = false; + + /* + * extract the group we are moving by updating the transitions of the + * from group and the new to group. also remove the transitions from the + * group we are moving. + */ + + for (let previousGroupNode of previousGroupNodes) { + if (transitionsAfter == null || transitionsAfter.length == 0) { + // the group we are moving does not have any transitions + + /* + * remove the transitions to the group we are moving and make + * new transitions from the from group to the new to group + */ + this.updateTransitionsForExtractingGroup(previousGroupNode.id, node.id, null); + extracted = true; + } else { + // the group we are moving has transitions + + // make the previous group point to the new to group + for (let transitionAfter of transitionsAfter) { + if (transitionAfter != null) { + const toNodeId = transitionAfter.to; + + /* + * remove the transitions to the group we are moving and make + * new transitions from the from group to the new to group + */ + this.updateTransitionsForExtractingGroup(previousGroupNode.id, node.id, toNodeId); + extracted = true; + } + } + } + } + + if (!extracted) { + /* + * we have not removed the transitions yet because the group + * we are moving does not have any groups before it + */ + + if (transitionsAfter != null) { + // remove the transitions from the group we are moving + for (let transitionAfter of transitionsAfter) { + if (transitionAfter != null) { + const toNodeId = transitionAfter.to; + + // remove the transitions to the group we are moving + this.updateTransitionsForExtractingGroup(null, node.id, toNodeId); + extracted = true; + } + } + } + } + + let inserted = false; + + /* + * create the transitions from the from group to the group we are moving + * and the transitions from the group we are moving to the old to group + */ + if (nodeId != null) { + // get the transitions from the previous group to the next group + const transitionsAfter = this.getTransitionsByFromNodeId(nodeId); + + for (let transitionAfter of transitionsAfter) { + if (transitionAfter != null) { + const toNodeId = transitionAfter.to; + + /* + * create the transitions that traverse from the from group + * to the group we are moving. also create the transitions + * that traverse from the group we are moving to the old + * to group. + */ + this.updateTransitionsForInsertingGroup(nodeId, [toNodeId], node.id); + inserted = true; + } + } + } + + if (!inserted) { + /* + * we have not inserted the transitions yet because there were no + * previous group transitions + */ + + if (nodeId == null) { + /* + * the previous node id is null which means there was no previous + * group. this means the group we are inserting will become the + * first group. this happens when the group we are moving + * is moved inside the root (group0). + */ + + const startGroupId = this.getStartGroupId(); + + if (startGroupId != null) { + // get the start group for the whole project (group0) + const startGroup = this.getNodeById(startGroupId); + + if (startGroup != null) { + const firstGroupId = startGroup.startId; + + /* + * create the transitions that traverse from the group + * we are moving to the previous first activity. + */ + this.updateTransitionsForInsertingGroup(nodeId, [firstGroupId], node.id); + } + } + } else { + /* + * we have not inserted the group yet because the from group doesn't + * have a group after it + */ + + /* + * create the transitions that traverse from the from group + * to the group we are moving. + */ + this.updateTransitionsForInsertingGroup(nodeId, null, node.id); + } + } + } + + /** + * Remove the node from the inactive nodes array + * @param nodeId the node to remove + * @returns the node that was removed + */ + removeNodeFromInactiveNodes(nodeId) { + let node = null; + if (nodeId != null) { + let parentGroup = this.getParentGroup(nodeId); + if (parentGroup != null) { + this.removeChildFromParent(nodeId); + } + + let inactiveNodes = this.project.inactiveNodes; + if (inactiveNodes != null) { + for (let i = 0; i < inactiveNodes.length; i++) { + let inactiveNode = inactiveNodes[i]; + if (inactiveNode != null) { + if (nodeId === inactiveNode.id) { + node = inactiveNode; + inactiveNodes.splice(i, 1); + break; + } + } + } + } + this.removeNodeFromInactiveStepNodes(nodeId); + this.removeNodeFromInactiveGroupNodes(nodeId); + } + return node; + } + + /** + * Remove the child node from the parent group. + * @param nodeId The child node to remove from the parent. + */ + removeChildFromParent(nodeId) { + let parentGroup = this.getParentGroup(nodeId); + if (parentGroup != null) { + // Remove the child from the parent + for (let i = 0; i < parentGroup.ids.length; i++) { + let childId = parentGroup.ids[i]; + if (nodeId == childId) { + parentGroup.ids.splice(i, 1); + break; + } + } + if (nodeId == parentGroup.startId) { + /* + * The child we removed was the start id of the group so we + * will update the start id. + */ + let startIdUpdated = false; + let transitions = this.getTransitionsByFromNodeId(nodeId); + if ( + transitions != null && + transitions.length > 0 && + transitions[0] != null && + transitions[0].to != null + ) { + parentGroup.startId = transitions[0].to; + startIdUpdated = true; + } + if (!startIdUpdated && parentGroup.ids.length > 0) { + parentGroup.startId = parentGroup.ids[0]; + startIdUpdated = true; + } + if (!startIdUpdated) { + parentGroup.startId = ''; + } + } + } + } + + /** + * Remove the node from the inactive step nodes array. + * @param nodeId The node id of the node we want to remove from the + * inactive step nodes array. + */ + removeNodeFromInactiveStepNodes(nodeId) { + for (let i = 0; i < this.inactiveStepNodes.length; i++) { + if (nodeId == this.inactiveStepNodes[i].id) { + this.inactiveStepNodes.splice(i, 1); + break; + } + } + } + + /** + * Remove the node from the inactive group nodes array. + * @param nodeId The node id of the group we want to remove from the + * inactive group nodes array. + */ + removeNodeFromInactiveGroupNodes(nodeId) { + for (let i = 0; i < this.inactiveGroupNodes.length; i++) { + if (nodeId == this.inactiveGroupNodes[i].id) { + this.inactiveGroupNodes.splice(i, 1); + break; + } + } + } + + /** + * Move the node to the active nodes array. If the node is a group node, + * also move all of its children to active. + */ + moveToActive(node) { + if (!this.isActive(node.id)) { + this.removeNodeFromInactiveNodes(node.id); + this.addNode(node); + if (this.isGroupNode(node.id)) { + for (const childId of node.ids) { + this.addNode(this.removeNodeFromInactiveNodes(childId)); + } + } + } + } + + /** + * Add a group's cthild nodes to the inactive nodes. + * @param node The group node. + */ + addGroupChildNodesToInactive(node) { + for (const childId of node.ids) { + const childNode = this.getNodeById(childId); + this.project.inactiveNodes.push(childNode); + this.inactiveStepNodes.push(childNode); + } + } + + /** + * Remove transition from nodes in the specified group that go out of the group + * @param nodeId the group id + */ + removeTransitionsOutOfGroup(groupId) { + const group = this.getNodeById(groupId); + for (const childId of group.ids) { + const transitions = this.getTransitionsByFromNodeId(childId); + for (let t = 0; t < transitions.length; t++) { + const transition = transitions[t]; + const parentGroupId = this.getParentGroupId(transition.to); + if (parentGroupId != groupId) { + // this is a transition that goes out of the specified group + transitions.splice(t, 1); + t--; // so it won't skip the next element + } + } + } + } + + /* + * Update the step transitions that point into the group we are moving + * For example + * group1 has children node1 and node2 (node2 transitions to node3) + * group2 has children node3 and node4 (node4 transitions to node5) + * group3 has children node5 and node6 + * if we move group2 after group3 we will need to change the + * transition from node2 to node3 and make node2 transition to node5 + * the result will be + * group1 has children node1 and node2 (node2 transitions to node5) + * group3 has children node5 and node6 + * group2 has children node3 and node4 (node4 transitions to node5) + * note: the (node4 transition to node5) will be removed later + * when is called removeTransitionsOutOfGroup + * note: when group2 is added in a later function call, we will add + * the node6 to node3 transition + * @param groupThatTransitionsToGroupWeAreMoving the group object + * that transitions to the group we are moving. we may need to update + * the transitions of this group's children. + * @param groupIdWeAreMoving the group id of the group we are moving + */ + updateChildrenTransitionsIntoGroupWeAreMoving( + groupThatTransitionsToGroupWeAreMoving, + groupIdWeAreMoving + ) { + if (groupThatTransitionsToGroupWeAreMoving != null && groupIdWeAreMoving != null) { + const group = this.getNodeById(groupIdWeAreMoving); + if (group != null) { + // get all the nodes that have a transition to the node we are removing + const nodesByToNodeId = this.getNodesByToNodeId(groupIdWeAreMoving); + + // get the transitions of the node we are removing + const nodeToRemoveTransitionLogic = group.transitionLogic; + let nodeToRemoveTransitions = []; + + if ( + nodeToRemoveTransitionLogic != null && + nodeToRemoveTransitionLogic.transitions != null + ) { + nodeToRemoveTransitions = nodeToRemoveTransitionLogic.transitions; + } + + if (nodeToRemoveTransitions.length == 0) { + /* + * The group we are moving is the last group in the project + * and does not have any transitions. We will loop through + * all the nodes that transition into this group and remove + * those transitions. + */ + + // get child ids of the group that comes before the group we are moving + const childIds = groupThatTransitionsToGroupWeAreMoving.ids; + + if (childIds != null) { + for (let childId of childIds) { + const transitionsFromChild = this.getTransitionsByFromNodeId(childId); + if (transitionsFromChild != null) { + for (let tfc = 0; tfc < transitionsFromChild.length; tfc++) { + const transitionFromChild = transitionsFromChild[tfc]; + if (transitionFromChild != null) { + const toNodeId = transitionFromChild.to; + const toNodeIdParentGroupId = this.getParentGroupId(toNodeId); + + if (groupIdWeAreMoving === toNodeIdParentGroupId) { + // the transition is to a child in the group we are moving + transitionsFromChild.splice(tfc, 1); + + /* + * move the counter back one because we have just removed an + * element from the array + */ + tfc--; + } + } + } + } + } + } + } else if (nodeToRemoveTransitions.length > 0) { + // get the first group that comes after the group we are removing + const firstNodeToRemoveTransition = nodeToRemoveTransitions[0]; + const firstNodeToRemoveTransitionToNodeId = firstNodeToRemoveTransition.to; + + if (this.isGroupNode(firstNodeToRemoveTransitionToNodeId)) { + // get the group that comes after the group we are moving + const groupNode = this.getNodeById(firstNodeToRemoveTransitionToNodeId); + + // get child ids of the group that comes before the group we are moving + const childIds = groupThatTransitionsToGroupWeAreMoving.ids; + + if (childIds != null) { + for (let childId of childIds) { + const transitionsFromChild = this.getTransitionsByFromNodeId(childId); + if (transitionsFromChild != null) { + for (let transitionFromChild of transitionsFromChild) { + if (transitionFromChild != null) { + const toNodeId = transitionFromChild.to; + + // get the parent group id of the toNodeId + const toNodeIdParentGroupId = this.getParentGroupId(toNodeId); + + if (groupIdWeAreMoving === toNodeIdParentGroupId) { + // the transition is to a child in the group we are moving + + if (groupNode.startId == null) { + // change the transition to point to the after group + transitionFromChild.to = firstNodeToRemoveTransitionToNodeId; + } else { + // change the transition to point to the start id of the after group + transitionFromChild.to = groupNode.startId; + } + } + } + } + } + } + } + } + } + } + } + } + + /** + * Get an unused component id + * @param componentIdsToSkip (optional) An array of additional component ids + * to skip. This is used when we are creating multiple new components. There + * is avery small chance that we create duplicate component ids that aren't + * already in the project. We avoid this problem by using this parameter. + * Example + * We want to create two new components. We first generate a new component + * id for the first new component for example "1234567890". Then we generate + * a new component id for the second new component and pass in + * ["1234567890"] as componentIdsToSkip because the new "1234567890" + * component hasn't actually been added to the project yet. + * @return a component id that isn't already being used in the project + */ + getUnusedComponentId(componentIdsToSkip = []) { + // we want to make an id with 10 characters + const idLength = 10; + + let newComponentId = this.UtilService.generateKey(idLength); + + // check if the component id is already used in the project + if (this.isComponentIdUsed(newComponentId)) { + /* + * the component id is already used in the project so we need to + * try generating another one + */ + let alreadyUsed = true; + + /* + * keep trying to generate a new component id until we have found + * one that isn't already being used + */ + while (!alreadyUsed) { + newComponentId = this.UtilService.generateKey(idLength); + + // check if the id is already being used in the project + alreadyUsed = this.isComponentIdUsed(newComponentId); + + if (componentIdsToSkip != null && componentIdsToSkip.indexOf(newComponentId) != -1) { + /* + * the new component is in the componentIdsToSkip so it has + * already been used + */ + alreadyUsed = true; + } + } + } + return newComponentId; + } + + /** + * Check if the component id is already being used in the project + * @param componentId check if this component id is already being used in + * the project + * @return whether the component id is already being used in the project + */ + isComponentIdUsed(componentId) { + for (const node of this.project.nodes.concat(this.project.inactiveNodes)) { + if (node.components != null) { + for (const component of node.components) { + if (componentId === component.id) { + return true; + } + } + } + } + return false; + } + + /** + * Get the next available constraint id for a node + * @param nodeId get the next available constraint id for this node + * e.g. node8Constraint2 + * @return the next available constraint id for the node + */ + getNextAvailableConstraintIdForNodeId(nodeId) { + let nextAvailableConstraintId = null; + if (nodeId != null) { + const usedConstraintIds = []; + const node = this.getNodeById(nodeId); + if (node != null) { + const constraints = node.constraints; + if (constraints != null) { + for (let constraint of constraints) { + if (constraint != null) { + const constraintId = constraint.id; + usedConstraintIds.push(constraintId); + } + } + } + } + + let foundNextAvailableConstraintId = false; + let counter = 1; + + while (!foundNextAvailableConstraintId) { + const potentialConstraintId = nodeId + 'Constraint' + counter; + if (usedConstraintIds.indexOf(potentialConstraintId) == -1) { + nextAvailableConstraintId = potentialConstraintId; + foundNextAvailableConstraintId = true; + } else { + counter++; + } + } + } + return nextAvailableConstraintId; + } + + /** + * Get the node ids in the branch by looking for nodes that have branch + * path taken constraints with the given fromNodeId and toNodeId + * @param fromNodeId the from node id + * @param toNodeId the to node id + * @return an array of nodes that are in the branch path + */ + getNodeIdsInBranch(fromNodeId, toNodeId) { + const nodeIdsInBranch = []; + for (const node of this.getNodes()) { + if (this.hasBranchPathTakenConstraint(node, fromNodeId, toNodeId)) { + nodeIdsInBranch.push(node.id); + } + } + this.orderNodeIds(nodeIdsInBranch); + return nodeIdsInBranch; + } + + /** + * Order the node ids so that they show up in the same order as in the + * project. + * @param constraints An array of node ids. + * @return An array of ordered node ids. + */ + orderNodeIds(nodeIds) { + let orderedNodeIds = this.getFlattenedProjectAsNodeIds(); + return nodeIds.sort(this.nodeIdsComparatorGenerator(orderedNodeIds)); + } + + /** + * Create the node ids comparator function that is used for sorting an + * array of node ids. + * @param orderedNodeIds An array of node ids in the order in which they + * show up in the project. + * @return A comparator that orders node ids in the order in which they show + * up in the project. + */ + nodeIdsComparatorGenerator(orderedNodeIds) { + return function(nodeIdA, nodeIdB) { + let nodeIdAIndex = orderedNodeIds.indexOf(nodeIdA); + let nodeIdBIndex = orderedNodeIds.indexOf(nodeIdB); + if (nodeIdAIndex < nodeIdBIndex) { + return -1; + } else if (nodeIdAIndex > nodeIdBIndex) { + return 1; + } + return 0; + }; + } + + /** + * Check if a node has a branch path taken constraint + * @param node the node to check + * @param fromNodeId the from node id of the branch path taken + * @param toNodeId the to node id of the branch path taken + * @return whether the node has a branch path taken constraint with the + * given from node id and to node id + */ + hasBranchPathTakenConstraint(node, fromNodeId, toNodeId) { + const constraints = node.constraints; + if (constraints != null) { + for (let constraint of constraints) { + for (let removalCriterion of constraint.removalCriteria) { + if (removalCriterion.name == 'branchPathTaken') { + const params = removalCriterion.params; + if (params.fromNodeId == fromNodeId && params.toNodeId == toNodeId) { + return true; + } + } + } + } + } + return false; + } + + /** + * Remove all branch path taken constraints from a node. + * @param nodeId Remove the constraints from this node. + */ + removeBranchPathTakenNodeConstraintsIfAny(nodeId) { + const node = this.getNodeById(nodeId); + const constraints = node.constraints; + if (constraints != null) { + for (let c = 0; c < constraints.length; c++) { + const constraint = constraints[c]; + const removalCriteria = constraint.removalCriteria; + for (let removalCriterion of removalCriteria) { + if (removalCriterion.name == 'branchPathTaken') { + constraints.splice(c, 1); + c--; // update the counter so we don't skip over the next element + } + } + } + } + } + + /** + * @param nodeId Get the branch path taken constraints from this node. + * @return {Array} An array of branch path taken constraints from the node. + */ + getBranchPathTakenConstraintsByNodeId(nodeId) { + const branchPathTakenConstraints = []; + const node = this.getNodeById(nodeId); + const constraints = node.constraints; + if (constraints != null) { + for (let constraint of constraints) { + for (let removalCriterion of constraint.removalCriteria) { + if (removalCriterion.name == 'branchPathTaken') { + branchPathTakenConstraints.push(constraint); + break; + } + } + } + } + return branchPathTakenConstraints; + } + + /** + * Check if a node is the first node in a branch path + * @param nodeId the node id + * @return whether the node is the first node in a branch path + */ + isFirstNodeInBranchPath(nodeId) { + for (const node of this.getNodes()) { + if (node.transitionLogic != null && node.transitionLogic.transitions != null) { + for (const transition of node.transitionLogic.transitions) { + if (transition.to === nodeId) { + return true; + } + } + } + } + return false; + } + + addSpace(space) { + if (this.project.spaces == null) { + this.project.spaces = []; + } + if (!this.isSpaceExists(space.id)) { + this.project.spaces.push(space); + this.saveProject(); + } + } + + removeSpace(id) { + let spaces = this.getSpaces(); + for (let s = 0; s < spaces.length; s++) { + if (spaces[s].id == id) { + spaces.splice(s, 1); + this.saveProject(); + return; + } + } + } + + getFeaturedProjectIcons() { + return this.http + .get(this.ConfigService.getConfigParam('featuredProjectIconsURL')) + .toPromise() + .then(data => { + return data; + }); + } + + setFeaturedProjectIcon(projectIcon) { + const isCustom = false; + return this.setProjectIcon(projectIcon, isCustom); + } + + setCustomProjectIcon(projectIcon) { + const isCustom = true; + return this.setProjectIcon(projectIcon, isCustom); + } + + setProjectIcon(projectIcon, isCustom) { + return this.http + .post(this.ConfigService.getConfigParam('projectIconURL'), { + projectId: this.ConfigService.getProjectId(), + projectIcon: projectIcon, + isCustom: isCustom + }) + .toPromise() + .then(result => { + return result; + }); + } + + broadcastSavingProject() { + this.savingProjectSource.next(); + } + + broadcastErrorSavingProject() { + this.errorSavingProjectSource.next(); + } + + broadcastNotAllowedToEditThisProject() { + this.notAllowedToEditThisProjectSource.next(); + } + + broadcastNotLoggedInProjectNotSaved() { + this.notLoggedInProjectNotSavedSource.next(); + } + + broadcastProjectSaved() { + this.projectSavedSource.next(); + } } From 39445f9a2482d247734c44a9464a0289e0b77881 Mon Sep 17 00:00:00 2001 From: Geoffrey Kwan Date: Tue, 12 Jan 2021 12:22:50 -0500 Subject: [PATCH 08/26] Cleaned up some Outside URL authoring code. #2763 --- src/main/webapp/site/src/messages.xlf | 119 ++++++++++++++++++ .../outside-url-authoring.component.ts | 56 ++++----- 2 files changed, 146 insertions(+), 29 deletions(-) diff --git a/src/main/webapp/site/src/messages.xlf b/src/main/webapp/site/src/messages.xlf index 806169769b..d18fc71df1 100644 --- a/src/main/webapp/site/src/messages.xlf +++ b/src/main/webapp/site/src/messages.xlf @@ -2963,6 +2963,10 @@ app/forgot/student/forgot-student-username/forgot-student-username.component.html 58 + + ../../wise5/components/outsideURL/outside-url-authoring/outside-url-authoring.component.html + 29 + Forgot Teacher Username @@ -6148,6 +6152,121 @@ 15 + + Show Open Education Resources + + ../../wise5/components/outsideURL/outside-url-authoring/outside-url-authoring.component.html + 5 + + + + Select from our curated library of OERs or enter your own in the 'URL' field below. (Note: Outside resources must start with 'https://' to be displayed.) + + ../../wise5/components/outsideURL/outside-url-authoring/outside-url-authoring.component.html + 9 + + + + Filter by Topic + + ../../wise5/components/outsideURL/outside-url-authoring/outside-url-authoring.component.html + 15 + + + + + + + + ../../wise5/components/outsideURL/outside-url-authoring/outside-url-authoring.component.html + 19 + + + + Clear All + + ../../wise5/components/outsideURL/outside-url-authoring/outside-url-authoring.component.html + 36 + + + ../../wise5/components/outsideURL/outside-url-authoring/outside-url-authoring.component.html + 38 + + + + Item(s) Found + + ../../wise5/components/outsideURL/outside-url-authoring/outside-url-authoring.component.html + 43 + + + + Selected + + ../../wise5/components/outsideURL/outside-url-authoring/outside-url-authoring.component.html + 61 + + + + Info + + ../../wise5/components/outsideURL/outside-url-authoring/outside-url-authoring.component.html + 70 + + + + Select + + ../../wise5/components/outsideURL/outside-url-authoring/outside-url-authoring.component.html + 78 + + + + No results found + + ../../wise5/components/outsideURL/outside-url-authoring/outside-url-authoring.component.html + 85 + + + + URL + + ../../wise5/components/outsideURL/outside-url-authoring/outside-url-authoring.component.html + 91 + + + + Enter URL Here + + ../../wise5/components/outsideURL/outside-url-authoring/outside-url-authoring.component.html + 95 + + + + Width (px) (optional) + + ../../wise5/components/outsideURL/outside-url-authoring/outside-url-authoring.component.html + 101 + + + + Height (px) + + ../../wise5/components/outsideURL/outside-url-authoring/outside-url-authoring.component.html + 108 + + + + Reload resource + + ../../wise5/components/outsideURL/outside-url-authoring/outside-url-authoring.component.html + 117 + + + ../../wise5/components/outsideURL/outside-url-authoring/outside-url-authoring.component.html + 119 + + Edit Unit Rubric diff --git a/src/main/webapp/wise5/components/outsideURL/outside-url-authoring/outside-url-authoring.component.ts b/src/main/webapp/wise5/components/outsideURL/outside-url-authoring/outside-url-authoring.component.ts index 5bd81b665f..971f208891 100644 --- a/src/main/webapp/wise5/components/outsideURL/outside-url-authoring/outside-url-authoring.component.ts +++ b/src/main/webapp/wise5/components/outsideURL/outside-url-authoring/outside-url-authoring.component.ts @@ -18,7 +18,24 @@ export class OutsideUrlAuthoring extends ComponentAuthoring { allOpenEducationalResources: any[]; filteredOpenEducationalResources: any[]; outsideURLIFrameId: string; - subjects: any[]; + subjects: any[] = [ + { + value: 'Earth and Space Sciences', + label: $localize`Earth and Space Sciences` + }, + { + value: 'Life Sciences', + label: $localize`Life Sciences` + }, + { + value: 'Physical Sciences', + label: $localize`Physical Sciences` + }, + { + value: 'Engineering, Technology, and Applications of Science', + label: $localize`Engineering, Technology, and Applications of Science` + } + ]; searchText: string; selectedSubjects: any[]; urlChanged: Subject = new Subject(); @@ -41,29 +58,11 @@ export class OutsideUrlAuthoring extends ComponentAuthoring { super.ngOnInit(); this.outsideURLIFrameId = 'outsideResource_' + this.componentId; this.isShowOERs = this.componentContent.url === ''; - this.subjects = [ - { - value: 'Earth and Space Sciences', - label: $localize`Earth and Space Sciences` - }, - { - value: 'Life Sciences', - label: $localize`Life Sciences` - }, - { - value: 'Physical Sciences', - label: $localize`Physical Sciences` - }, - { - value: 'Engineering, Technology, and Applications of Science', - label: $localize`Engineering, Technology, and Applications of Science` - } - ]; this.searchText = ''; this.selectedSubjects = []; this.OutsideURLService.getOpenEducationalResources().then((openEducationalResources: any) => { this.allOpenEducationalResources = openEducationalResources.sort((a, b) => - a.metadata.title > b.metadata.title ? 1 : -1 + a.metadata.title.localeCompare(b.metadata.title) ); this.filteredOpenEducationalResources = this.allOpenEducationalResources; }); @@ -76,12 +75,12 @@ export class OutsideUrlAuthoring extends ComponentAuthoring { }); this.widthChangedSubscription = this.widthChanged .pipe(debounceTime(1000), distinctUntilChanged()) - .subscribe((width: string) => { + .subscribe(() => { this.componentChanged(); }); this.heightChangedSubscription = this.heightChanged .pipe(debounceTime(1000), distinctUntilChanged()) - .subscribe((height: string) => { + .subscribe(() => { this.componentChanged(); }); } @@ -116,19 +115,18 @@ export class OutsideUrlAuthoring extends ComponentAuthoring { searchFieldChanged(): void { this.filteredOpenEducationalResources = this.allOpenEducationalResources.filter((oer) => { - if (!this.isTextMatch(this.searchText, JSON.stringify(oer))) { - return false; - } if (this.isAnySubjectChosen()) { - return this.isSubjectMatch(this.selectedSubjects, oer); - } else { - return true; + return ( + this.isTextMatch(this.searchText, JSON.stringify(oer)) && + this.isSubjectMatch(this.selectedSubjects, oer) + ); } + return this.isTextMatch(this.searchText, JSON.stringify(oer)); }); } isTextMatch(searchText: string, testText: string): boolean { - return testText.toLowerCase().indexOf(searchText.toLowerCase()) !== -1; + return testText.toLowerCase().includes(searchText.toLowerCase()); } isAnySubjectChosen(): boolean { From 88788d1a09c1ea76c8e8300b2cdbe41cb41fe403 Mon Sep 17 00:00:00 2001 From: Geoffrey Kwan Date: Tue, 12 Jan 2021 15:09:50 -0500 Subject: [PATCH 09/26] Saved TeacherProjectService related files again to have prettier format them again. #2676 --- ...e-new-component-location.component.spec.ts | 5 ++- .../src/app/services/projectService.spec.ts | 10 +++--- .../services/teacherProjectService.spec.ts | 12 +++---- .../wise5/components/componentController.ts | 28 ++++++++-------- .../conceptMap/conceptMapService.ts | 16 +++++----- .../wise5/components/label/labelService.ts | 6 ++-- .../wise5/services/notificationService.ts | 10 +++--- .../webapp/wise5/services/projectService.ts | 10 +++--- .../wise5/services/teacherProjectService.ts | 32 ++++++++----------- 9 files changed, 62 insertions(+), 67 deletions(-) diff --git a/src/main/webapp/site/src/app/authoring-tool/add-component/choose-new-component-location/choose-new-component-location.component.spec.ts b/src/main/webapp/site/src/app/authoring-tool/add-component/choose-new-component-location/choose-new-component-location.component.spec.ts index bf37d2f8b4..de52cd46fb 100644 --- a/src/main/webapp/site/src/app/authoring-tool/add-component/choose-new-component-location/choose-new-component-location.component.spec.ts +++ b/src/main/webapp/site/src/app/authoring-tool/add-component/choose-new-component-location/choose-new-component-location.component.spec.ts @@ -68,7 +68,10 @@ describe('ChooseNewComponentLocation', () => { }); it('insertComponentAfter() should create a new component and save the project', () => { - spyOn(teacherProjectService, 'createComponent').and.returnValue({ id: 'comp3', type: 'Discussion' }); + spyOn(teacherProjectService, 'createComponent').and.returnValue({ + id: 'comp3', + type: 'Discussion' + }); spyOn(teacherProjectService, 'saveProject').and.returnValue(new Promise(() => {})); component.insertComponentAfter('comp2'); expect(teacherProjectService.createComponent).toHaveBeenCalled(); diff --git a/src/main/webapp/site/src/app/services/projectService.spec.ts b/src/main/webapp/site/src/app/services/projectService.spec.ts index bc96161e41..a68122a26b 100644 --- a/src/main/webapp/site/src/app/services/projectService.spec.ts +++ b/src/main/webapp/site/src/app/services/projectService.spec.ts @@ -108,7 +108,7 @@ describe('ProjectService', () => { }); function createNormalSpy() { - spyOn(configService, 'getConfigParam').and.callFake(param => { + spyOn(configService, 'getConfigParam').and.callFake((param) => { if (param === 'projectBaseURL') { return projectBaseURL; } else if (param === 'projectURL') { @@ -162,10 +162,8 @@ function shouldNotReplaceAssetPathsInHtmlComponentContent() { function shouldRetrieveProjectWhenConfigProjectURLIsValid() { it('should retrieve project when Config.projectURL is valid', () => { - spyOn(configService, 'getConfigParam') - .withArgs('projectURL') - .and.returnValue(projectURL); - service.retrieveProject().then(response => { + spyOn(configService, 'getConfigParam').withArgs('projectURL').and.returnValue(projectURL); + service.retrieveProject().then((response) => { expect(response).toEqual(scootersProjectJSON); }); http.expectOne(projectURL); @@ -353,7 +351,7 @@ function shouldIdentifyBranchStartAndMergePoints() { } function expectFunctionCallToReturnValue(func, nodeIdArray, expectedValue) { - nodeIdArray.forEach(nodeId => { + nodeIdArray.forEach((nodeId) => { expect(service[func](nodeId)).toEqual(expectedValue); }); } diff --git a/src/main/webapp/site/src/app/services/teacherProjectService.spec.ts b/src/main/webapp/site/src/app/services/teacherProjectService.spec.ts index 28b22cd33a..22729cdbe2 100644 --- a/src/main/webapp/site/src/app/services/teacherProjectService.spec.ts +++ b/src/main/webapp/site/src/app/services/teacherProjectService.spec.ts @@ -106,7 +106,7 @@ describe('TeacherProjectService', () => { }); function createNormalSpy() { - spyOn(configService, 'getConfigParam').and.callFake(param => { + spyOn(configService, 'getConfigParam').and.callFake((param) => { if (param === 'projectBaseURL') { return projectBaseURL; } else if (param === 'projectURL') { @@ -120,7 +120,7 @@ function createNormalSpy() { } function createConfigServiceGetConfigParamSpy() { - spyOn(configService, 'getConfigParam').and.callFake(param => { + spyOn(configService, 'getConfigParam').and.callFake((param) => { if (param === 'projectBaseURL') { return projectBaseURL; } else if (param === 'projectURL') { @@ -147,7 +147,7 @@ function registerNewProject() { scootersProjectJSONString ); http.expectOne(registerNewProjectURL).flush(newProjectIdExpected); - newProjectIdActual.then(result => { + newProjectIdActual.then((result) => { expect(result).toEqual(newProjectIdExpected); }); }); @@ -249,7 +249,7 @@ function getLibraryProjects() { createConfigServiceGetConfigParamSpy(); const result = service.getLibraryProjects(); http.expectOne(getLibraryProjectsURL).flush(libraryProjects); - result.then(projects => { + result.then((projects) => { expect(projects).toEqual(libraryProjects); }); }); @@ -936,9 +936,7 @@ function shouldHandleSaveProjectResponseHelper( function shouldNotSaveProjectWhenTheUserDoesNotHavePermissionToEditTheProject() { it('should not save project when the user does not have permission to edit the project', () => { service.setProject(scootersProjectJSON); - spyOn(configService, 'getConfigParam') - .withArgs('canEditProject') - .and.returnValue(false); + spyOn(configService, 'getConfigParam').withArgs('canEditProject').and.returnValue(false); expect(service.saveProject()).toEqual(null); }); } diff --git a/src/main/webapp/wise5/components/componentController.ts b/src/main/webapp/wise5/components/componentController.ts index 5dfac72bea..df26de6fc6 100644 --- a/src/main/webapp/wise5/components/componentController.ts +++ b/src/main/webapp/wise5/components/componentController.ts @@ -230,12 +230,12 @@ class ComponentController { } initializeScopeGetComponentState(scope, childControllerName) { - scope.getComponentState = isSubmit => { + scope.getComponentState = (isSubmit) => { const deferred = this.$q.defer(); const childController = scope[childControllerName]; if (this.hasDirtyWorkToSendToParent(childController, isSubmit)) { const action = this.getDirtyWorkToSendToParentAction(childController, isSubmit); - childController.createComponentState(action).then(componentState => { + childController.createComponentState(action).then((componentState) => { deferred.resolve(componentState); }); } else { @@ -460,7 +460,7 @@ class ComponentController { * data has changed. */ createComponentStateAndBroadcast(action) { - this.createComponentState(action).then(componentState => { + this.createComponentState(action).then((componentState) => { this.emitComponentStudentDataChanged(componentState); }); } @@ -670,7 +670,7 @@ class ComponentController { } importWorkByStudentWorkId(studentWorkId) { - this.StudentDataService.getStudentWorkById(studentWorkId).then(componentState => { + this.StudentDataService.getStudentWorkById(studentWorkId).then((componentState) => { if (componentState != null) { this.setStudentWork(componentState); this.setParentStudentWorkIdToCurrentStudentWork(studentWorkId); @@ -751,7 +751,7 @@ class ComponentController { } attachStudentAsset(studentAsset) { - return this.StudentAssetService.copyAssetForReference(studentAsset).then(copiedAsset => { + return this.StudentAssetService.copyAssetForReference(studentAsset).then((copiedAsset) => { const attachment = { studentAssetId: copiedAsset.id, iconURL: copiedAsset.iconURL, @@ -829,7 +829,7 @@ class ComponentController { this.audioRecordedSubscription = this.AudioRecorderService.audioRecorded$.subscribe( ({ requester, audioFile }) => { if (requester === `${this.nodeId}-${this.componentId}`) { - this.StudentAssetService.uploadAsset(audioFile).then(studentAsset => { + this.StudentAssetService.uploadAsset(audioFile).then((studentAsset) => { this.attachStudentAsset(studentAsset).then(() => { this.StudentAssetService.deleteAsset(studentAsset); }); @@ -871,7 +871,7 @@ class ComponentController { $scope.nodeId = nodeId; $scope.componentId = componentId; $scope.componentState = componentState; - $scope.closeDialog = function() { + $scope.closeDialog = function () { $mdDialog.hide(); }; } @@ -882,12 +882,14 @@ class ComponentController { if (componentState.nodeId == nodeId && componentState.componentId == componentId) { setTimeout(() => { const componentService = this.$injector.get(componentState.componentType + 'Service'); - componentService.generateImageFromRenderedComponentState(componentState).then(image => { - clearTimeout(destroyDoneRenderingComponentListenerTimeout); - doneRenderingComponentSubscription.unsubscribe(); - deferred.resolve(image); - this.$mdDialog.hide(); - }); + componentService + .generateImageFromRenderedComponentState(componentState) + .then((image) => { + clearTimeout(destroyDoneRenderingComponentListenerTimeout); + doneRenderingComponentSubscription.unsubscribe(); + deferred.resolve(image); + this.$mdDialog.hide(); + }); }, 1000); } } diff --git a/src/main/webapp/wise5/components/conceptMap/conceptMapService.ts b/src/main/webapp/wise5/components/conceptMap/conceptMapService.ts index 791c1ab87b..c96b76dfbd 100644 --- a/src/main/webapp/wise5/components/conceptMap/conceptMapService.ts +++ b/src/main/webapp/wise5/components/conceptMap/conceptMapService.ts @@ -776,7 +776,7 @@ export class ConceptMapService extends ComponentService { let svgString = svgElement.innerHTML; // find all the images in the svg and replace them with Base64 images - this.getHrefToBase64ImageReplacements(svgString, true).then(images => { + this.getHrefToBase64ImageReplacements(svgString, true).then((images) => { /* * Loop through all the image objects. Each object contains * an image href and a Base64 image. @@ -826,7 +826,7 @@ export class ConceptMapService extends ComponentService { const thisUtilService = this.UtilService; // the function that is called after the image is fully loaded - image.onload = event => { + image.onload = (event) => { // get the image that was loaded const image: any = event.target; @@ -842,13 +842,13 @@ export class ConceptMapService extends ComponentService { const imageObject = thisUtilService.getImageObjectFromBase64String(base64Image); // create a student asset image - this.StudentAssetService.uploadAsset(imageObject).then(unreferencedAsset => { + this.StudentAssetService.uploadAsset(imageObject).then((unreferencedAsset) => { /* * make a copy of the unreferenced asset so that we * get a referenced asset */ this.StudentAssetService.copyAssetForReference(unreferencedAsset).then( - referencedAsset => { + (referencedAsset) => { if (referencedAsset != null) { /* * get the asset url @@ -970,7 +970,7 @@ export class ConceptMapService extends ComponentService { const ctx = myCanvas.getContext('2d'); // the function that is called after the image is fully loaded - image.onload = function(event) { + image.onload = function (event) { // get the image that was loaded const image: any = event.target; @@ -1147,7 +1147,7 @@ export class ConceptMapService extends ComponentService { let svgString = serializer.serializeToString(svgElement); // find all the images in the svg and replace them with Base64 images - this.getHrefToBase64ImageReplacements(svgString).then(images => { + this.getHrefToBase64ImageReplacements(svgString).then((images) => { /* * Loop through all the image objects. Each object contains * an image href and a Base64 image. @@ -1183,7 +1183,7 @@ export class ConceptMapService extends ComponentService { const image = new Image(); // the function that is called after the image is fully loaded - image.onload = event => { + image.onload = (event) => { // get the image that was loaded let image: any = event.target; @@ -1199,7 +1199,7 @@ export class ConceptMapService extends ComponentService { const imageObject = this.UtilService.getImageObjectFromBase64String(base64Image); // add the image to the student assets - this.StudentAssetService.uploadAsset(imageObject).then(asset => { + this.StudentAssetService.uploadAsset(imageObject).then((asset) => { resolve(asset); }); }; diff --git a/src/main/webapp/wise5/components/label/labelService.ts b/src/main/webapp/wise5/components/label/labelService.ts index c0b7e43fc6..e366feaa73 100644 --- a/src/main/webapp/wise5/components/label/labelService.ts +++ b/src/main/webapp/wise5/components/label/labelService.ts @@ -288,7 +288,7 @@ export class LabelService extends ComponentService { const image = new Image(); const thisUtilService = this.UtilService; return new Promise((resolve, reject) => { - image.onload = event => { + image.onload = (event) => { const image: any = event.target; myCanvas.width = image.width; myCanvas.height = image.height; @@ -297,13 +297,13 @@ export class LabelService extends ComponentService { const imageObject = thisUtilService.getImageObjectFromBase64String(base64Image); // create a student asset image - this.StudentAssetService.uploadAsset(imageObject).then(unreferencedAsset => { + this.StudentAssetService.uploadAsset(imageObject).then((unreferencedAsset) => { /* * make a copy of the unreferenced asset so that we * get a referenced asset */ this.StudentAssetService.copyAssetForReference(unreferencedAsset).then( - referencedAsset => { + (referencedAsset) => { if (referencedAsset != null) { /* * get the asset url diff --git a/src/main/webapp/wise5/services/notificationService.ts b/src/main/webapp/wise5/services/notificationService.ts index 9398cb35af..0a16c51279 100644 --- a/src/main/webapp/wise5/services/notificationService.ts +++ b/src/main/webapp/wise5/services/notificationService.ts @@ -18,11 +18,9 @@ export class NotificationService { private setIsJSONValidSource: Subject = new Subject(); public setIsJSONValid$: Observable = this.setIsJSONValidSource.asObservable(); private serverConnectionStatusSource: Subject = new Subject(); - public serverConnectionStatus$: Observable = - this.serverConnectionStatusSource.asObservable(); + public serverConnectionStatus$: Observable = this.serverConnectionStatusSource.asObservable(); private viewCurrentAmbientNotificationSource: Subject = new Subject(); - public viewCurrentAmbientNotification$: Observable = - this.viewCurrentAmbientNotificationSource.asObservable(); + public viewCurrentAmbientNotification$: Observable = this.viewCurrentAmbientNotificationSource.asObservable(); constructor( private upgrade: UpgradeModule, @@ -138,7 +136,7 @@ export class NotificationService { this.ConfigService.getWorkgroupId(), notificationData, notificationGroupId - ).then(notification => { + ).then((notification) => { this.addNotification(notification); }); } @@ -228,7 +226,7 @@ export class NotificationService { let notifications = this.notifications; for (const p in args) { if (args.hasOwnProperty(p) && args[p] !== null) { - notifications = notifications.filter(notification => { + notifications = notifications.filter((notification) => { return notification[p] === args[p]; }); } diff --git a/src/main/webapp/wise5/services/projectService.ts b/src/main/webapp/wise5/services/projectService.ts index 008fc36889..cb88abdaef 100644 --- a/src/main/webapp/wise5/services/projectService.ts +++ b/src/main/webapp/wise5/services/projectService.ts @@ -414,7 +414,7 @@ export class ProjectService { getGroupNodesIdToOrder() { const idToOrder = {}; - const onlyGroupNodes = Object.entries(this.idToOrder).filter(item => { + const onlyGroupNodes = Object.entries(this.idToOrder).filter((item) => { return this.isGroupNode(item[0]); }); for (const [key, value] of onlyGroupNodes) { @@ -520,7 +520,7 @@ export class ProjectService { '(\'|"|\\\\\'|\\\\")', 'gi' ), - matchedString => { + (matchedString) => { /* * once found, we prepend the contentBaseURL + "assets/" to the string within the quotes * and keep everything else the same. @@ -866,7 +866,7 @@ export class ProjectService { * the target ids show up in the project. */ constraintsComparatorGenerator(orderedNodeIds) { - return function(constraintA, constraintB) { + return function (constraintA, constraintB) { let constraintAIndex = orderedNodeIds.indexOf(constraintA.targetId); let constraintBIndex = orderedNodeIds.indexOf(constraintB.targetId); if (constraintAIndex < constraintBIndex) { @@ -1002,7 +1002,7 @@ export class ProjectService { * @returns an array with all the node ids */ getNodeIds() { - return this.applicationNodes.map(node => { + return this.applicationNodes.map((node) => { return node.id; }); } @@ -1102,7 +1102,7 @@ export class ProjectService { return this.http .get(projectURL, { headers: headers }) .toPromise() - .then(projectJSON => { + .then((projectJSON) => { this.setProject(projectJSON); return projectJSON; }); diff --git a/src/main/webapp/wise5/services/teacherProjectService.ts b/src/main/webapp/wise5/services/teacherProjectService.ts index 0e861ce396..5620ee19d5 100644 --- a/src/main/webapp/wise5/services/teacherProjectService.ts +++ b/src/main/webapp/wise5/services/teacherProjectService.ts @@ -23,13 +23,9 @@ export class TeacherProjectService extends ProjectService { private errorSavingProjectSource: Subject = new Subject(); public errorSavingProject$: Observable = this.errorSavingProjectSource.asObservable(); private notAllowedToEditThisProjectSource: Subject = new Subject(); - public notAllowedToEditThisProject$: Observable< - any - > = this.notAllowedToEditThisProjectSource.asObservable(); + public notAllowedToEditThisProject$: Observable = this.notAllowedToEditThisProjectSource.asObservable(); private notLoggedInProjectNotSavedSource: Subject = new Subject(); - public notLoggedInProjectNotSaved$: Observable< - any - > = this.notLoggedInProjectNotSavedSource.asObservable(); + public notLoggedInProjectNotSaved$: Observable = this.notLoggedInProjectNotSavedSource.asObservable(); private projectSavedSource: Subject = new Subject(); public projectSaved$: Observable = this.projectSavedSource.asObservable(); private savingProjectSource: Subject = new Subject(); @@ -215,7 +211,7 @@ export class TeacherProjectService extends ProjectService { return this.http .post(`${this.ConfigService.getConfigParam('copyProjectURL')}/${projectId}`, null) .toPromise() - .then(newProject => { + .then((newProject) => { return newProject; }); } @@ -232,7 +228,7 @@ export class TeacherProjectService extends ProjectService { projectJSONString: projectJSONString }) .toPromise() - .then(newProjectId => { + .then((newProjectId) => { return newProjectId; }); } @@ -1220,14 +1216,14 @@ export class TeacherProjectService extends ProjectService { return this.http .get(this.ConfigService.getConfigParam('getLibraryProjectsURL')) .toPromise() - .then(projects => { + .then((projects) => { return projects; }); } sortAndFilterUniqueLibraryProjects(libraryProjects) { const flatProjectList = libraryProjects - .map(grade => { + .map((grade) => { return grade.children; }) .flat(); @@ -1321,7 +1317,7 @@ export class TeacherProjectService extends ProjectService { } removeTeacherRemovalConstraint(node: any, periodId: number) { - node.constraints = node.constraints.filter(constraint => { + node.constraints = node.constraints.filter((constraint) => { return !( constraint.action === 'makeThisNodeNotVisitable' && constraint.targetId === node.id && @@ -1425,10 +1421,10 @@ export class TeacherProjectService extends ProjectService { * objects. */ cleanupBeforeSave() { - this.getActiveNodes().forEach(activeNode => { + this.getActiveNodes().forEach((activeNode) => { this.cleanupNode(activeNode); }); - this.getInactiveNodes().forEach(inactiveNode => { + this.getInactiveNodes().forEach((inactiveNode) => { this.cleanupNode(inactiveNode); }); } @@ -1455,7 +1451,7 @@ export class TeacherProjectService extends ProjectService { if (node.components != null) { // activity node does not have components but step node does - node.components.forEach(component => { + node.components.forEach((component) => { this.cleanupComponent(component); }); } @@ -1877,7 +1873,7 @@ export class TeacherProjectService extends ProjectService { * @returns an array with all the inactive node ids */ getInactiveNodeIds() { - return this.project.inactiveNodes.map(node => { + return this.project.inactiveNodes.map((node) => { return node.id; }); } @@ -3344,7 +3340,7 @@ export class TeacherProjectService extends ProjectService { * up in the project. */ nodeIdsComparatorGenerator(orderedNodeIds) { - return function(nodeIdA, nodeIdB) { + return function (nodeIdA, nodeIdB) { let nodeIdAIndex = orderedNodeIds.indexOf(nodeIdA); let nodeIdBIndex = orderedNodeIds.indexOf(nodeIdB); if (nodeIdAIndex < nodeIdBIndex) { @@ -3466,7 +3462,7 @@ export class TeacherProjectService extends ProjectService { return this.http .get(this.ConfigService.getConfigParam('featuredProjectIconsURL')) .toPromise() - .then(data => { + .then((data) => { return data; }); } @@ -3489,7 +3485,7 @@ export class TeacherProjectService extends ProjectService { isCustom: isCustom }) .toPromise() - .then(result => { + .then((result) => { return result; }); } From 976daab78ffe66a467a421104258b92da4038796 Mon Sep 17 00:00:00 2001 From: breity Date: Tue, 12 Jan 2021 16:52:39 -0800 Subject: [PATCH 10/26] Moved AlertStatusComponent styles to component. #2876 --- .../alert-status-corner.component.html | 8 +-- .../alert-status-corner.component.ts | 3 +- .../alert-status-corner.scss | 34 +++++++++ .../notebook-parent.component.ts | 54 ++++++++++++++ .../wise5/themes/default/style/author.css | 2 +- .../wise5/themes/default/style/author.css.map | 2 +- .../themes/default/style/modules/_alerts.scss | 72 ------------------- .../default/style/modules/_nav--grading.scss | 18 +++-- .../wise5/themes/default/style/monitor.css | 2 +- .../themes/default/style/monitor.css.map | 2 +- .../webapp/wise5/themes/default/style/vle.css | 2 +- .../wise5/themes/default/style/vle.css.map | 2 +- 12 files changed, 111 insertions(+), 90 deletions(-) create mode 100644 src/main/webapp/site/src/app/classroom-monitor/alert-status-corner/alert-status-corner.scss create mode 100644 src/main/webapp/site/src/app/notebook/notebook-parent/notebook-parent.component.ts diff --git a/src/main/webapp/site/src/app/classroom-monitor/alert-status-corner/alert-status-corner.component.html b/src/main/webapp/site/src/app/classroom-monitor/alert-status-corner/alert-status-corner.component.html index 9f6b5bf5b4..5522004fe4 100644 --- a/src/main/webapp/site/src/app/classroom-monitor/alert-status-corner/alert-status-corner.component.html +++ b/src/main/webapp/site/src/app/classroom-monitor/alert-status-corner/alert-status-corner.component.html @@ -1,6 +1,4 @@ -
-
+
+
diff --git a/src/main/webapp/site/src/app/classroom-monitor/alert-status-corner/alert-status-corner.component.ts b/src/main/webapp/site/src/app/classroom-monitor/alert-status-corner/alert-status-corner.component.ts index ff9ba04e3f..a522ebecfb 100644 --- a/src/main/webapp/site/src/app/classroom-monitor/alert-status-corner/alert-status-corner.component.ts +++ b/src/main/webapp/site/src/app/classroom-monitor/alert-status-corner/alert-status-corner.component.ts @@ -2,7 +2,8 @@ import { Component, Input } from '@angular/core'; @Component({ selector: 'alert-status-corner', - templateUrl: 'alert-status-corner.component.html' + templateUrl: 'alert-status-corner.component.html', + styleUrls: ['alert-status-corner.scss'] }) export class AlertStatusCornerComponent { @Input() diff --git a/src/main/webapp/site/src/app/classroom-monitor/alert-status-corner/alert-status-corner.scss b/src/main/webapp/site/src/app/classroom-monitor/alert-status-corner/alert-status-corner.scss new file mode 100644 index 0000000000..d7366d735a --- /dev/null +++ b/src/main/webapp/site/src/app/classroom-monitor/alert-status-corner/alert-status-corner.scss @@ -0,0 +1,34 @@ +@import '~style/abstracts/variables', '~style/themes/default'; + +$warn: map-get($default-colors, warn); +$secondary-text: map-get($default-colors, secondary-text); + +.corner { + border-top-right-radius: $card-border-radius; + overflow: hidden; +} + +.corner-content { + width: 0; + height: 0; + border-top-color: $secondary-text; + border-bottom-color: $secondary-text; + color: $secondary-text; + + border-top: 36px solid; + border-left: 36px solid transparent; + + &:after { + content: '!'; + color: #ffffff; + top: 2px; + right: 10px; + position: absolute; + font-weight: 700; + } +} + +.corner-warn { + border-top-color: $warn; + border-bottom-color: $warn; +} diff --git a/src/main/webapp/site/src/app/notebook/notebook-parent/notebook-parent.component.ts b/src/main/webapp/site/src/app/notebook/notebook-parent/notebook-parent.component.ts new file mode 100644 index 0000000000..3e1789ff6e --- /dev/null +++ b/src/main/webapp/site/src/app/notebook/notebook-parent/notebook-parent.component.ts @@ -0,0 +1,54 @@ +import { Component, Input } from '@angular/core'; +import { ConfigService } from '../../../../../wise5/services/configService'; +import { NotebookService } from '../../../../../wise5/services/notebookService'; +import { UtilService } from '../../../../../wise5/services/utilService'; + +Component({ + selector: 'notebook-parent' +}); +export class NotebookParentComponent { + config: any; + notesVisible: boolean; + + @Input() + workgroupId: number; + + constructor( + private ConfigService: ConfigService, + public NotebookService: NotebookService, + private UtilService: UtilService + ) {} + + ngOnInit(): void { + if (this.workgroupId === null) { + this.workgroupId = this.ConfigService.getWorkgroupId(); + } + + if (this.isStudentNotebook()) { + this.config = this.UtilService.makeCopyOfJSONObject( + this.NotebookService.getStudentNotebookConfig() + ); + } else { + this.config = this.UtilService.makeCopyOfJSONObject( + this.NotebookService.getTeacherNotebookConfig() + ); + } + + if (!this.config.enabled) { + return; + } + + this.initComplete(); + } + + initComplete(): void {} + + isStudentNotebook(): boolean { + return ( + this.ConfigService.getMode() === 'studentRun' || + this.ConfigService.getMode() === 'preview' || + ((this.ConfigService.isRunOwner() || this.ConfigService.isRunSharedTeacher()) && + this.ConfigService.getWorkgroupId() !== this.workgroupId) + ); + } +} diff --git a/src/main/webapp/wise5/themes/default/style/author.css b/src/main/webapp/wise5/themes/default/style/author.css index 5c2b148299..5f0c2c3518 100644 --- a/src/main/webapp/wise5/themes/default/style/author.css +++ b/src/main/webapp/wise5/themes/default/style/author.css @@ -1,2 +1,2 @@ -body{background:#eee}body.vle{overflow:hidden}a:focus,a:hover{color:#7e57c2}blockquote{background-color:#fff;padding:8px;margin:16px 0;border:solid #7e57c2;border-width:0 0 0 3px}.has-indicator:after{content:"";position:absolute;border-radius:50%;padding:5px;background-color:#f05843}.has-indicator--icon-button:after{top:25px;left:5px}.badge{border-radius:4px;padding:2px 6px;font-size:12px;font-weight:500;font-style:normal;background-color:#aaa}.badge.md-button{min-width:0;min-height:0;line-height:inherit}.badge.md-button:focus,.badge.md-button:hover{background-color:#aaa}.badge.md-button:focus{outline:1px dotted #aaa}.badge--info{background-color:#ef6c00;color:#fff}.badge--warn{background-color:#c62828;color:#fff}.badge--success{background-color:#00c853;color:#fff}.divider--withmargin{margin:16px 0}.divider--dashed{border-top-style:dashed}a{color:#7e57c2;cursor:pointer}.active{background-color:hsla(0,0%,62%,.2);color:rgba(0,0,0,.87)}.avatar{border-radius:50%;box-sizing:content-box}.avatar--square{border-radius:4px}.avatar.md-18{height:30px;width:30px}.avatar.md-24{height:36px;width:36px}.avatar.md-36{height:48px;width:48px}.avatar.md-48{height:60px;width:60px}.avatar--icon{background-color:#ddd;white-space:normal!important}.avatar--icon:not(.md-avatar){padding:6px}.avatar--icon.md-18{height:18px;width:18px}.avatar--icon.md-24{height:24px;width:24px}.avatar--icon.md-36{height:36px;width:36px}.avatar--icon.md-48{height:48px;width:48px}md-toolbar.md-light-theme:not(.md-menu-toolbar) md-icon{color:rgba(0,0,0,.54)}md-toolbar.md-light-theme:not(.md-menu-toolbar) .md-button:disabled md-icon{color:rgba(0,0,0,.26)}.md-button:not([disabled]).primary,md-icon.primary{color:#7e57c2!important}.md-button:not([disabled]).success,md-icon.success{color:#00c853!important}.md-button:not([disabled]).warn,md-icon.warn{color:#c62828!important}.md-button:not([disabled]).info,md-icon.info{color:#ef6c00!important}.md-button:not([disabled]).accent,md-icon.accent{color:#f05843!important}.md-button:not([disabled]).accent-1,md-icon.accent-1{color:#795c3a!important}.md-button:not([disabled]).accent-2,md-icon.accent-2{color:#cad266!important}md-input-container.md-wise-theme label{color:rgba(0,0,0,.87)}md-select-menu.md-default-theme md-option[selected],md-select-menu md-option[selected]{background-color:#fff}.md-autocomplete-suggestions-container.md-default-theme li .highlight,.md-autocomplete-suggestions-container li .highlight{color:#7e57c2;background-color:#fff}.primary{color:#7e57c2}.accent{color:#f05843}.accent-1{color:#795c3a}.accent-2{color:#cad266}.warn{color:#c62828}.info{color:#ef6c00}.success{color:#00c853}.divider{color:rgba(0,0,0,.12)}.gray-lightest{color:#f7f7f7}.gray-lighter{color:#eee}.gray-light{color:#ddd}.gray{color:#ccc}.gray-dark{color:#aaa}.gray-darker{color:#757575}.gray-darkest{color:#333}.text{color:rgba(0,0,0,.87)}.text-secondary{color:rgba(0,0,0,.54)}.text-disabled{color:rgba(0,0,0,.26)}.text-light{color:#fff}.text-light-secondary{color:hsla(0,0%,100%,.7)}.text-light-disabled{color:hsla(0,0%,100%,.5)}.selected-bg{color:#fff}.score{color:#ffc107}.body{color:rgba(0,0,0,.87)}.body-bg{color:#eee}.primary-bg{background-color:#7e57c2}.accent-bg{background-color:#f05843}.accent-1-bg{background-color:#795c3a}.accent-2-bg{background-color:#cad266}.warn-bg{background-color:#c62828}.info-bg{background-color:#ef6c00}.success-bg{background-color:#00c853}.divider-bg{background-color:rgba(0,0,0,.12)}.gray-lightest-bg{background-color:#f7f7f7}.gray-lighter-bg{background-color:#eee}.gray-light-bg{background-color:#ddd}.gray-bg{background-color:#ccc}.gray-dark-bg{background-color:#aaa}.gray-darker-bg{background-color:#757575}.gray-darkest-bg{background-color:#333}.text-bg{background-color:rgba(0,0,0,.87)}.text-secondary-bg{background-color:rgba(0,0,0,.54)}.text-disabled-bg{background-color:rgba(0,0,0,.26)}.text-light-bg{background-color:#fff}.text-light-secondary-bg{background-color:hsla(0,0%,100%,.7)}.text-light-disabled-bg{background-color:hsla(0,0%,100%,.5)}.selected-bg-bg{background-color:#fff}.score-bg{background-color:#ffc107}.body-bg{background-color:rgba(0,0,0,.87)}.body-bg-bg{background-color:#eee}md-progress-circular.primary path{stroke:#7e57c2}md-progress-circular.accent path{stroke:#f05843}md-progress-circular.accent-1 path{stroke:#795c3a}md-progress-circular.accent-2 path{stroke:#cad266}md-progress-circular.warn path{stroke:#c62828}md-progress-circular.info path{stroke:#ef6c00}md-progress-circular.success path{stroke:#00c853}md-progress-circular.divider path{stroke:rgba(0,0,0,.12)}md-progress-circular.gray-lightest path{stroke:#f7f7f7}md-progress-circular.gray-lighter path{stroke:#eee}md-progress-circular.gray-light path{stroke:#ddd}md-progress-circular.gray path{stroke:#ccc}md-progress-circular.gray-dark path{stroke:#aaa}md-progress-circular.gray-darker path{stroke:#757575}md-progress-circular.gray-darkest path{stroke:#333}md-progress-circular.text path{stroke:rgba(0,0,0,.87)}md-progress-circular.text-secondary path{stroke:rgba(0,0,0,.54)}md-progress-circular.text-disabled path{stroke:rgba(0,0,0,.26)}md-progress-circular.text-light path{stroke:#fff}md-progress-circular.text-light-secondary path{stroke:hsla(0,0%,100%,.7)}md-progress-circular.text-light-disabled path{stroke:hsla(0,0%,100%,.5)}md-progress-circular.selected-bg path{stroke:#fff}md-progress-circular.score path{stroke:#ffc107}md-progress-circular.body path{stroke:rgba(0,0,0,.87)}md-progress-circular.body-bg path{stroke:#eee}.l-constrained{margin-left:auto;margin-right:auto;max-width:100%;position:relative}@media (min-width:600px){.l-constrained{width:1280px}}.l-constrained-md{width:960px;max-width:100%}.l-footer{position:fixed;bottom:0;left:0;right:0;z-index:1;background-color:#fff;border-top:1px solid #eee}.button--footer{margin:0;padding-top:0;padding-bottom:0;min-width:0;display:flex}.button--footer__element{padding-left:8px}.l-header{z-index:3}.l-header .logo{margin-left:0!important;height:36px;width:36px;vertical-align:middle}.l-header .logo-link{min-width:auto;display:none;padding:0 4px;margin-right:12px}@media only screen and (min-width:600px){.l-header .logo-link{display:block}}.l-header .logo-link:focus,.l-header .logo-link:hover{border:0}@media only screen and (max-width:599px){.l-header .md-toolbar-tools h1,.l-header .md-toolbar-tools h2,.l-header .md-toolbar-tools h3{font-size:15px}}.l-main{background-color:#eee}.l-main--with-toolbar{margin-top:42px}#content{transition:margin-top .5s}.view-content{margin:0 auto;padding:8px;position:absolute;left:0;right:0;transition:opacity .5s}@media only screen and (min-width:960px){.view-content{padding:16px}}.view-content.ng-enter{opacity:0}.view-content .ng-enter-active{opacity:1;transition-delay:.25s}.view-content.ng-hide,.view-content.ng-hide-add,.view-content.ng-hide-add-active,.view-content.ng-hide-remove,.view-content.ng-hide-remove-active,.view-content.ng-leave-active{opacity:0}.view-content--with-sidemenu{padding:8px}@media only screen and (min-width:600px){.view-content--with-sidemenu{margin-left:54px;padding:16px}}@media only screen and (min-width:600px){[dir=rtl] .view-content--with-sidemenu{margin-left:auto;margin-right:54px}}.content-head{margin:8px 0}.content-head h1,.content-head h2,.content-head h3{font-weight:300;margin-top:0;margin-bottom:0;font-size:36px}@media only screen and (max-width:959px){.content-head h1,.content-head h2,.content-head h3{font-size:32px;text-align:center}}@media only screen and (max-width:959px){.content-head__more{margin-top:8px}}.content-head__item,h2.content-head__item{margin:0 8px}.content-head__item .md-subhead,h2.content-head__item .md-subhead{padding-left:4px}@media only screen and (max-width:959px){.content-head__item .md-subhead,h2.content-head__item .md-subhead{display:block;padding-left:0}}.content-head__item md-icon,h2.content-head__item md-icon{vertical-align:text-bottom}.stepSelectMenuContainer md-select-menu,.stepSelectMenuContainer md-select-menu md-content{max-height:500px}.l-nav{background-color:#eee!important}.l-node{margin-top:42px;padding:0}@media only screen and (min-width:600px){.l-node{padding:0 0 16px;background-color:#eee!important}}@media only screen and (min-width:960px){.l-node{padding:0 0 32px}}.l-notebook{margin-top:42px;background-color:#eee!important}.l-sidebar__header{background-color:#fff!important;color:#795c3a!important}.l-sidebar__header md-select{color:rgba(0,0,0,.87)}.status-icon{margin:0 4px;z-index:1;vertical-align:bottom}.md-button.status-icon{height:auto;width:auto;min-height:0;line-height:inherit;margin:0 4px;padding:0}.status-corner-wrapper{position:absolute;z-index:1;overflow:hidden}.status-corner{width:0;height:0;border-top-color:rgba(0,0,0,.26);border-bottom-color:rgba(0,0,0,.26);color:rgba(0,0,0,.26)}.status-corner:after{content:"!";color:#fff;top:2px;right:10px;position:absolute;font-weight:700}.status-corner--warn{border-top-color:#c62828!important;border-bottom-color:#c62828!important}.status-corner-top-right{top:0;right:0;border-top-right-radius:4px}.status-corner-top-right .status-corner{border-top:36px solid;border-left:36px solid transparent}.status-corner-top-left{top:0;left:0;border-top-left-radius:4px}.status-corner-top-left .status-corner{border-top:36px solid;border-right:36px solid transparent}.status-corner-bottom-right{bottom:0;right:0;border-bottom-right-radius:4px}.status-corner-bottom-right .status-corner{border-bottom:36px solid;border-left:36px solid transparent}.status-corner-bottom-left{bottom:0;left:0;border-bottom-left-radius:4px}.status-corner-bottom-left .status-corner{border-bottom:36px solid;border-right:36px solid transparent}.avatar--icon--alert{background-color:#fff}.avatar--icon--alert__icon{font-size:48px;margin:-4px 0 0 -4px}md-dialog{width:600px}.dialog--wide{width:960px}.dialog--wider{width:1280px}.help-bubble{border-radius:4px;max-width:320px}@media (min-width:600px){.help-bubble{max-width:552px}}@media (min-width:960px){.help-bubble{max-width:912px}}@media (min-width:1280px){.help-bubble{max-width:1232px}}.help-bubble___title__content,.help-bubble__title{border-top-left-radius:4px;border-top-right-radius:4px}.help-bubble___title__content{padding:0 0 0 12px;background-color:#ef6c00}.help-bubble___title__content .md-icon-button{margin-right:0;padding-top:0;padding-bottom:0}.help-bubble__content{overflow:auto;padding:8px 12px;max-height:480px}.help-bubble__actions{border-bottom-left-radius:4px;border-bottom-right-radius:4px}div.hopscotch-bubble{border-radius:4px;border:0;font-family:Roboto,Helvetica Neue,sans-serif;font-size:14px;z-index:6}div.hopscotch-bubble .hopscotch-bubble-arrow-container{position:absolute;width:20px;height:20px}div.hopscotch-bubble .hopscotch-bubble-arrow-container.up{top:0;left:12px}div.hopscotch-bubble .hopscotch-bubble-arrow-container.up .hopscotch-bubble-arrow{border-bottom:10px solid #ef6c00;border-left:10px solid transparent;border-right:10px solid transparent;position:relative;top:-10px}div.hopscotch-bubble .hopscotch-bubble-arrow-container.up .hopscotch-bubble-arrow-border{border:0}div.hopscotch-bubble .hopscotch-bubble-arrow-container.down{bottom:-34px;left:12px}div.hopscotch-bubble .hopscotch-bubble-arrow-container.down .hopscotch-bubble-arrow{border-top:10px solid #ef6c00;border-left:10px solid transparent;border-right:10px solid transparent;position:relative;top:-10px}div.hopscotch-bubble .hopscotch-bubble-arrow-container.down .hopscotch-bubble-arrow-border{border:0}div.hopscotch-bubble .hopscotch-bubble-arrow-container.left{top:12px;left:-10px}div.hopscotch-bubble .hopscotch-bubble-arrow-container.left .hopscotch-bubble-arrow{border-right:10px solid #ef6c00;border-bottom:10px solid transparent;border-top:10px solid transparent;position:relative;left:0;top:-10px}div.hopscotch-bubble .hopscotch-bubble-arrow-container.left .hopscotch-bubble-arrow-border{border:0}div.hopscotch-bubble .hopscotch-bubble-arrow-container.right{top:12px;right:-30px}div.hopscotch-bubble .hopscotch-bubble-arrow-container.right .hopscotch-bubble-arrow{border-left:10px solid #ef6c00;border-bottom:10px solid transparent;border-top:10px solid transparent;position:relative;right:0;top:-10px}div.hopscotch-bubble .hopscotch-bubble-arrow-container.right .hopscotch-bubble-arrow-border{border:0}.input-container{padding-top:12px}.input-container--component{margin-bottom:0}.input-container--open-response.md-has-icon{padding-left:0}.input-container--open-response .md-errors-spacer{display:none}.input-wrapper{position:relative}.input-wrapper--focused .input--textarea__action md-icon{color:#7e57c2}.input--textarea,.input-container textarea.input--textarea{padding:8px;background-color:#f7f7f7;border:1px solid #ccc;margin-bottom:8px}.input--textarea:focus,.input-container textarea.input--textarea:focus{background-color:#fff}.input--textarea[disabled],.input-container textarea.input--textarea[disabled]{color:rgba(0,0,0,.54)}.input-container textarea.input--textarea{width:100%}.input--textarea--disabled{color:rgba(0,0,0,.54)}.input--textarea__action{position:absolute;right:-4px}.input--textarea__action[disabled] md-icon{color:rgba(0,0,0,.26)!important}.input--textarea__action--notebook{top:6px}.input-wrapper--richtext .input--textarea__action--notebook{top:-7px}.input--textarea__action--revision{bottom:6px}.input-wrapper--richtext .input--textarea__action--revision{bottom:-5px}.input-label,md-input-container:not(.md-input-invalid):not(.md-input-focused).md-input-has-value label.input-label{line-height:1.2;color:rgba(0,0,0,.87)}.input-label.input-label--focused,md-input-container:not(.md-input-invalid):not(.md-input-focused).md-input-has-value label.input-label.input-label--focused{color:#7e57c2}.autocomplete input{text-overflow:ellipsis;overflow:hidden;word-wrap:none;font-weight:500;color:rgba(0,0,0,.54)}@media only screen and (min-width:600px){.autocomplete--minwidth{min-width:300px}}@media only screen and (min-width:960px){.autocomplete--minwidth{min-width:300px}}.autocomplete--flat md-autocomplete-wrap{background-color:#fff}.autocomplete--flat md-autocomplete-wrap:not(.md-menu-showing){box-shadow:none;background-color:#eee}.select__header{height:48px}.select__header input{height:100%;width:100%;padding:0 8px;outline:none;border:0;font-size:14px;font-weight:500}.table{max-width:100%;width:auto;min-width:100px;margin:8px 0}.table tbody>tr>td,.table tbody>tr>th,.table tfoot>tr>td,.table tfoot>tr>th,.table thead>tr>td,.table thead>tr>th{border:1px solid #ccc;padding:6px;font-size:15px;min-height:32px;height:32px;min-width:32px;vertical-align:top}.table td.inactive,.table th{background-color:#f7f7f7;opacity:1;visibility:visible}.table md-input-container{margin:0}.table .md-errors-spacer{display:none}.table--student td.inactive{padding:8px 10px}.table--full-width{width:100%}.table--list{border:0;border-collapse:collapse;background-color:#fff;max-width:100%;overflow:auto}.table--list td,.table--list th{padding:0 4px;border:0}.table--list td{min-height:56px;height:56px}.table--list tr.md-button{display:table-row;text-align:left;width:auto;text-transform:none;font-size:inherit;font-weight:400}.table--list__wrap{min-width:600px}@media only screen and (max-width:959px){.table-wrap-sticky{overflow-x:auto}}.table--list__thead{font-size:14px;font-weight:700}.table--list__thead__tr{height:100%;margin:0}.table--list__thead__th{background-color:#757575;color:#fff;min-height:42px;height:42px}.table--list__thead__link{color:#fff;text-transform:none;margin:0;min-width:0;white-space:normal;line-height:1.4;width:100%}.table--list__thead__sort{margin:0}.table--list__thead__sort--reverse{transform:rotate(180deg)}.td--wrap{min-width:180px;white-space:normal;line-height:1.2}@media only screen and (max-width:959px){.td--max-width{max-width:180px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}}.md-toolbar-tools{font-size:18px}.md-toolbar-tools .autocomplete,.md-toolbar-tools .autocomplete input,.md-toolbar-tools .autocomplete md-autocomplete-wrap{height:36px}.md-toolbar--wise{min-height:42px}.md-toolbar--wise .md-toolbar-tools{height:42px;max-height:42px}.md-toolbar--wise .md-button.md-icon-button{height:42px;line-height:42px;width:42px}.md-toolbar--wise--sm .md-toolbar-tools{padding:0 8px;font-size:15px}.md-toolbar--sidenav{background-color:#333!important}.md-toolbar--sidenav .md-toolbar-tools{font-size:16px;font-weight:500}.toolbar{position:fixed;left:0;right:0;top:52px;z-index:3}.toolbar__title{margin-left:8px;font-size:16px;font-weight:500}.toolbar__tools{padding-right:8px}[dir=rtl] .toolbar__tools{padding-right:16px;padding-left:8px}.md-button.toolbar__nav,.toolbar__nav{margin:0}.md-button.toolbar__select,.toolbar__select{margin:0 4px;min-height:32px;background-color:#f7f7f7}.md-button.toolbar__select .md-select-value,.toolbar__select .md-select-value{height:32px;text-align:left}[dir=rtl] .md-button.toolbar__select .md-select-value,[dir=rtl] .toolbar__select .md-select-value{text-align:right}.toolbar__select--fixedwidth{width:168px}@media only screen and (min-width:600px){.toolbar__select--fixedwidth{width:264px}}@media only screen and (min-width:960px){.toolbar__select--fixedwidth{width:432px}}.list-item{background-color:#fff;border-bottom:1px solid #eee}.list-item.md-subheader,.list-item .md-subheader{color:rgba(0,0,0,.87);background-color:#fff}.list-item.md-subheader md-icon,.list-item .md-subheader md-icon{vertical-align:middle}.list-item.md-subheader .md-subheader-inner,.list-item .md-subheader .md-subheader-inner{padding:0}.list-item.md-subheader .md-avatar,.list-item .md-subheader .md-avatar{margin-right:8px}.list-item .autocomplete{margin:8px 0}.list-item--info._md-button-wrap>div.md-button:first-child,.list-item--info .md-subheader-content{border-left:4px solid #ef6c00!important;margin-left:-4px}.list-item--warn._md-button-wrap>div.md-button:first-child,.list-item--warn .md-subheader-content{border-left:4px solid #c62828!important;margin-left:-4px}.list-item--expanded{border-bottom-width:0}.list-item--noclick,.list-item--noclick.md-button{cursor:default;background-color:#f7f7f7}.list-item--actions{padding:0 8px!important}.list-item__subheader-button{text-transform:none;width:100%;padding:8px 16px;margin:0;white-space:normal;text-align:left;line-height:1.4}.user-list{font-size:15px}#nav{position:relative}.nav{margin-bottom:16px}.nav-mask{position:absolute;top:0;bottom:0;left:0;right:0;background-color:rgba(0,0,0,.25);z-index:1}.nav-mask.ng-hide{opacity:0}.nav-head{color:rgba(0,0,0,.54);font-weight:500}.nav-head md-icon{line-height:20px}.nav-contents--root{padding:6px 6px 12px}.nav-contents--group{background-color:#ddd;padding:8px}.nav-contents--root,.nav-contents__list{padding:0}@media (min-width:600px){.nav-contents__list{padding:8px}}.nav-item{transition:opacity .25s ease-in-out}.nav-item.prev md-list-item{background-color:#fff}.nav-item--card__content{border-top-right-radius:4px;border-top-left-radius:4px}.nav-item--card__content:focus{outline:none}.nav-item--card__content:focus .nav-item__title>span{border-bottom:1px dashed #ccc}.nav-item--root{transition:margin .25s,box-shadow .5s}.nav-item--root.expanded{flex-basis:100%;max-width:100%;max-height:none!important;margin:8px auto;padding-left:4px}.nav-item--root.expanded:first-of-type{margin-top:0}.nav-item--list__info-item{padding:0 16px 0 4px;display:inline-block}.nav-item--list__reorder{margin-left:8px;color:rgba(0,0,0,.26)}.nav-item--card--group:not(.expanded){box-shadow:0 3px 1px -2px rgba(0,0,0,.14),0 2px 2px 0 rgba(0,0,0,.098),0 1px 5px 0 rgba(0,0,0,.084),3px 3px 0 1px #d5d5d5,6px 6px 0 1px #aaa}.nav-item__collapse{margin:0}.nav-item__more{border-top:1px solid #ddd;border-bottom-right-radius:4px;border-bottom-left-radius:4px;padding:8px 16px;min-height:40px}.nav-item__users{height:auto;cursor:pointer;color:#fff;margin:0;padding:1px 6px}.nav-item__users>md-icon{padding-right:4px;color:#fff}.nav-item__users:focus.success-bg,.nav-item__users:hover.success-bg{background-color:#00c853}.nav-item__title{padding-left:16px;line-height:1.2;font-weight:400}[dir=rtl] .nav-item__title{padding-left:auto;padding-right:16px}.nav-item__info{padding:0 8px}.nav-item__progress{width:48px}.nav-item__progress>.md-container{top:0}.nav-item__progress-value{margin-left:8px;width:36px}.progress-wrapper{padding:2px 0;cursor:pointer}.notice{text-align:center;padding:8px;background-color:rgba(0,0,0,.04);width:100%}@media (min-width:600px){.notice{max-width:80%;border-radius:3px;margin:24px auto}}.menu-progress{position:absolute;top:10px;right:12px}.menu-progress path{stroke:#cad266!important;stroke-width:2px}[dir=rtl] .menu-progress{right:auto;left:12px}.menu-sidenav__item{font-weight:700;font-size:14px}.menu-sidenav__icon{margin-top:12px!important;margin-right:12px!important;margin-left:12px}.active .menu-sidenav__icon,.active .menu-sidenav__item{color:#7e57c2}.menu-sidebar{position:absolute;top:94px;bottom:0;left:0;background-color:#fff;width:56px;overflow:hidden;padding:8px 0;text-align:center;border-right:1px solid #ccc}@media only screen and (max-width:599px){.menu-sidebar{display:none}}[dir=rtl] .menu-sidebar{right:0;left:auto}.md-button.md-icon-button.menu-sidebar__link{margin-top:6px;margin-bottom:6px}#node{margin:0 auto;position:absolute;left:0;right:0}@media only screen and (min-width:600px){#node{padding:24px 16px;margin-bottom:32px}}@media only screen and (min-width:960px){#node{padding:32px}}#node.ng-enter{transition:opacity .5s;opacity:0}#node.ng-enter-active{opacity:1}@media only screen and (min-width:600px){.node-notice{margin-top:-8px;margin-bottom:16px}}@media only screen and (min-width:960px){.node-notice{margin-top:-16px}}.node-content{padding:0 0 48px;background-color:#fff;border-radius:3px;overflow:visible}@media only screen and (max-width:599px){.node-content{box-shadow:none}}@media only screen and (min-width:600px){.node-content{padding:0;border-top:2px solid;border-bottom:2px solid}}md-content.node-content{background-color:#fff}.node-content__rubric{position:absolute;top:-22px;left:0;right:0;z-index:1}.node-content__rubric .avatar--icon{transform:scale(.94)}@media only screen and (max-width:599px){.node-content__rubric .avatar--icon{transform:scale(.8)}}.node-icon{color:#fff;vertical-align:inherit}.node-select{margin:0 8px;min-width:0;font-weight:500;font-size:15px}.node-select .md-select-value :first-child{transform:translateZ(0);flex:1 0 0}.node-select .md-select-value .node-select__icon,.node-select .md-select-value .node-select__status{display:none}.node-select .md-select-icon{margin-left:0;color:rgba(0,0,0,.87)}.node-select-option--group{background-color:#f7f7f7;border-bottom:1px solid #eee;border-top:1px solid #eee}.node-select-option--node{padding-left:20px}.node-select__icon{margin-right:8px}.node-select__status{margin-left:8px}.node-select__text{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}@media only screen and (min-width:600px){.node-select__text{margin-top:2px}}.node-title{line-height:1.2;text-transform:none;margin-top:3px}@media only screen and (max-width:599px){.node-title{font-size:15px}}.node-content__actions{padding:0 16px 16px}@media only screen and (min-width:960px){.node-content__actions{padding:0 24px 24px}}@media only screen and (min-width:1280px){.node-content__actions{padding:0 32px 32px}}.node-content__actions .md-button:first-child{margin-left:0}.node-content__actions .md-button:last-child{margin-right:0}.node-content__actions__info{font-style:italic;margin-left:8px;color:rgba(0,0,0,.54)}.node-content__actions__more{border-bottom:1px dotted}.md-button.md-icon-button.node-nav:first-of-type{margin-right:0}@media only screen and (min-width:600px){.node-sidebar-active{margin-right:68px}}@media only screen and (max-width:599px){.node-sidebar-visible{margin-bottom:42px}}.node-sidebar{position:absolute;right:0;top:0;width:52px}.node-sidebar__toolbar{position:fixed;width:52px;background-color:#fff;padding:8px 0;border-radius:3px}@media only screen and (max-width:599px){.node-sidebar__toolbar{right:0;bottom:0;left:0;width:100%;border-radius:0;padding:0;min-height:0;height:42px}}.node-info{margin:0}@media only screen and (max-width:599px){.node-info{margin:-16px;border-radius:0}}.node-info .divider{margin-left:-8px;margin-right:-8px}.node-info .component:first-child{margin-top:-16px}.node-info .component,.node-info .component__content,.node-info .component__header{margin-left:-8px;margin-right:-8px}.node-info .component__actions{display:none}.node-rubric{border:2px solid #7e57c2;margin:8px 16px 24px;padding:16px;background-color:#fff}.node__label--vertical-alignment{vertical-align:middle;display:inline-block}@media only screen and (min-width:600px){.notebook-launcher.md-button.md-fab{z-index:61}}.notebook-report{border-radius:4px 4px 0 0;margin:0;height:100%}.notebook-report .note-toolbar{margin:-8px -8px 8px;padding:4px;border-bottom:0;border-top:0;border-color:#ddd currentcolor;border-style:solid none;border-width:1px 0;border-radius:0}.notebook-report .note-toolbar .btn-group{margin-top:0}.notebook-report .note-btn{border:0;border-top-left-radius:0;border-top-right-radius:0;border-bottom-left-radius:0;border-bottom-right-radius:0}.notebook-report-container{height:550px;width:450px;max-height:90%;bottom:0;right:96px;position:absolute;z-index:3}@media only screen and (min-width:960px){.notes-visible .notebook-report-container{right:516px;transition:right .25s}}.notebook-report-container__full{top:16px;bottom:16px;left:16px;right:16px;max-height:none;max-width:none;height:auto;width:auto}.notebook-report-container__full .notebook-report{height:100%;width:100%;margin:0 auto;max-height:none;border-radius:4px}.notebook-report-container__collapsed{width:300px;height:auto}.notebook-report-container__collapsed .notebook-report__actions,.notebook-report-container__collapsed .notebook-report__content,.notebook-report-container__collapsed .notebook-report__content__header{display:none}.notebook-report__toolbar{background-color:#333!important;border-radius:4px 4px 0 0}.notebook-report__toolbar__title{max-width:150px}.notebook-report__content{background-color:#fff}.notebook-report__content h1,.notebook-report__content h2,.notebook-report__content h3,.notebook-report__content h4{font-size:22px}.notebook-report__content .note-editor.note-frame{border:0;border-radius:0;padding:0;box-shadow:none}.notebook-report__content .note-resizebar{display:none}.notebook-report__content__header{padding:8px;background-color:#fff;font-size:16px}@media only screen and (max-width:599px){.notebook-report-container:not(.notebook-report-container__collapsed){top:0;bottom:0;left:0;right:0;max-height:none;max-width:none;height:auto;width:auto}.notebook-report-container:not(.notebook-report-container__collapsed) .notebook-report{border-radius:0}.notebook-tools--full{display:none}}.notebook-report-backdrop{position:fixed;top:0;left:0;width:100%;height:100%;z-index:3;background-color:#212121;opacity:.48}.notebook-menu{transition:opacity .5s}.notebook-menu.ng-enter{opacity:0}.notebook-menu .ng-enter-active{opacity:1;transition-delay:.25s}.notebook-menu.ng-hide,.notebook-menu.ng-hide-add,.notebook-menu.ng-hide-add-active,.notebook-menu.ng-hide-remove,.notebook-menu.ng-hide-remove-active,.notebook-menu.ng-leave-active{opacity:0}.notebook-item{transition:box-shadow .25s;margin:0 16px 16px;display:block}.notebook-item__content{height:250px;min-width:230px;position:relative;padding:0;background-color:#ccc;border-top-left-radius:4px;border-top-right-radius:4px}.notebook-item__content__attachment,.notebook-item__content__text{position:absolute;left:0;right:0}.notebook-item__content__attachment{background-repeat:no-repeat!important;border-top-left-radius:4px;border-top-right-radius:4px;background-position:top!important;background-size:cover!important;top:0;bottom:0}.notebook-item__content__text{bottom:0;padding:8px;font-weight:500;overflow:hidden;max-height:120px;min-height:56px;background-color:hsla(0,0%,100%,.95);border-top:1px solid #eee}.notebook-item__content__text:after{content:"";text-align:right;position:absolute;bottom:0;right:0;width:100%;height:.8em;background:linear-gradient(180deg,hsla(0,0%,100%,0),hsla(0,0%,100%,.95) 100%)}.notebook-item__content--text-only:after{content:"note";font-family:Material Icons;font-weight:400;font-style:normal;display:inline-block;line-height:1;text-transform:none;letter-spacing:normal;word-wrap:normal;white-space:nowrap;direction:ltr;-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;-moz-osx-font-smoothing:grayscale;font-feature-settings:"liga";font-size:80px;color:rgba(0,0,0,.26)}.notebook-item--question__content--text-only{content:"live_help"}.notebook-item__content__location{opacity:.9;padding:8px 0}.notebook-item__content__location md-icon{font-size:22px}.notebook-item__edit{cursor:pointer}.notebook-item__actions{margin:0;padding:0 8px;color:#fff;background-color:#333;border-bottom-left-radius:4px;border-bottom-right-radius:4px}.notebook-item__actions md-icon{color:#fff}.notebook-item__text-input{margin:0}.notebook-item__text-input__textarea{padding-left:0;padding-right:0}.notebook-item__attachment{background-color:#ddd;padding:16px;margin-bottom:16px;text-align:center;position:relative}.notebook-item__attachment__content{max-width:100%;height:auto}.notebook-item__attachment__delete{position:absolute;top:4px;right:-2px;width:34px!important;height:34px!important;min-height:0}.notebook-item__attachment__delete md-icon{margin-left:-2px;font-size:22px}.notebook-item__info{font-style:italic;opacity:.8;color:#8a6942}.notebook-item__info a,.notebook-item__info md-icon{color:#8a6942}.notebook-item__info md-icon{font-size:1.5em;min-width:0;width:auto}.notebook-item__upload{text-align:center;padding:24px;background-color:#eee;margin-bottom:16px;color:rgba(0,0,0,.54);border-radius:4px;cursor:pointer;border:1px dashed transparent;transition:all .25s}.notebook-item__upload md-icon,.notebook-item__upload span{transition:color .25s}.notebook-item__upload.dragover,.notebook-item__upload:focus,.notebook-item__upload:hover{border-color:#f05843;background-color:#fdebe8;color:#f05843}.notebook-item__upload.dragover md-icon,.notebook-item__upload.dragover span,.notebook-item__upload:focus md-icon,.notebook-item__upload:focus span,.notebook-item__upload:hover md-icon,.notebook-item__upload:hover span{color:#f05843}.view-notebook-item{width:600px}.notebook-item--report{background-color:#fff}.notebook-item--report .note-editor{margin-bottom:16px;border-color:#ccc}.notebook-item--report__container.ui-scrollpoint .notebook-item--report__toolbar{position:fixed;top:94px;left:0;right:0;z-index:1}@media only screen and (min-width:600px){.notebook-item--report__container.ui-scrollpoint .notebook-item--report__toolbar{left:54px;padding:0 24px}}@media only screen and (min-width:960px){.notebook-item--report__container.ui-scrollpoint .notebook-item--report__toolbar{padding:0 32px}}.notebook-item--report__container.ui-scrollpoint .notebook-item--report__toolbar .note-toolbar{margin:0 16px}@media only screen and (min-width:600px){.notebook-item--report__container.ui-scrollpoint .notebook-item--report__toolbar .note-toolbar{margin:0 8px}}@media only screen and (min-width:960px){.notebook-item--report__container.ui-scrollpoint .notebook-item--report__toolbar .note-toolbar{margin:0 16px}}.notebook-item--report__container.ui-scrollpoint .note-editor{padding-top:40px}.notebook-item--report__toolbar .note-toolbar{background-color:#ddd;border:1px solid #ccc;margin-bottom:-2px}.notebook-item--report__heading{text-align:center;margin-bottom:32px}.notebook-item--report__add-note{font-weight:700}.notebook-item--report__note-img{max-width:100%;height:auto!important}@media only screen and (min-width:600px){.notebook-sidebar{width:400px;max-width:none}}@media only screen and (min-width:960px){.notebook-sidebar{width:500px;max-width:none}}.notebook-items{width:100%;overflow:auto;margin-top:16px;margin-bottom:76px}.notebook-items .notebook-item{width:100%}.notebook-items .notebook-item__content{height:200px;min-width:0}.notebook-items--grading{margin-bottom:0}@media only screen and (max-width:599px){.notebook-enabled .md-fab-bottom-left,.notebook-enabled .md-fab-bottom-right{bottom:50px!important}}.notebook-grading{background-color:#fff;display:block}.notification-btn{width:60px!important}.notification-btn md-icon{margin-left:20px}.notification-count{border-radius:50%;position:absolute;background-color:#f05843;width:22px;left:4px;height:22px;line-height:18px;font-size:12px;font-weight:700;border:2px solid}.notification-count:before{content:"";position:absolute;right:-7px;top:5px;border-left:6px solid hsla(0,0%,100%,.87);border-top:4px solid transparent;border-bottom:4px solid transparent}.notification-list{padding:8px 0}.notification-dismiss{width:500px}.notification-dismiss__input{margin-bottom:0}md-list md-list-item.md-2-line .md-list-item-text h4.notification-list-item__source,md-list md-list-item.md-3-line .md-list-item-text h4.notification-list-item__source,md-list md-list-item .md-list-item-text h4.notification-list-item__source{color:rgba(0,0,0,.54);font-size:12px}md-list md-list-item.md-2-line .md-list-item-text h4.notification-list-item__source md-icon,md-list md-list-item.md-3-line .md-list-item-text h4.notification-list-item__source md-icon,md-list md-list-item .md-list-item-text h4.notification-list-item__source md-icon{font-size:18px;min-width:0;width:auto;margin-left:-4px;line-height:20px}.account-menu{border-radius:4px;padding:0;font-size:15px;max-width:380px}@media (min-width:1280px){.account-menu{min-width:380px!important}}.account-menu h3{margin:0;font-weight:300}.account-menu--fixed-height{height:304px}.account-menu--fixed-width{width:320px}@media (min-width:960px){.account-menu--fixed-width{width:380px}}.account-menu__icon{background-color:#fff;border-radius:50%}.account-menu__caret{position:absolute;right:28px;top:-8px;outline:none}.account-menu__caret:before{content:"";position:absolute;border-bottom:8px solid #fff;border-left:8px solid transparent;border-right:8px solid transparent}.account-menu__caret--notification,.account-menu__caret--pause{right:80px}.account-menu__caret--notification--with-pause{right:132px}[dir=rtl] .account-menu__caret{right:auto;left:28px}[dir=rtl] .account-menu__caret--notification,[dir=rtl] .account-menu__caret--pause{left:80px;right:auto}[dir=rtl] .account-menu__caret--notification--with-pause{left:132px;right:auto}.account-menu__info{padding:8px 12px}.account-menu__info__title{font-weight:500}.account-menu__info__team{font-weight:400;color:rgba(0,0,0,.54)}.account-menu__users,.account-menu__users md-list-item{padding:0}.account-menu__users md-list-item .md-avatar{margin:0 8px 0 0;height:48px;width:48px}.account-menu__progress md-progress-linear{transform:rotate(270deg);width:26px}.account-menu__grade{position:relative;margin-right:4px}.account-menu__grade md-icon{color:#ffc107}.account-menu__grade__overlay{position:absolute;top:0;width:100%;background-color:hsla(0,0%,100%,.6)}.account-menu__actions{background-color:#f7f7f7}.account-menu__control{padding:16px}.annotations{margin:16px 4px 16px 62px;position:relative;font-size:15px}.annotations hr{margin:10px 0 8px;border-color:rgba(0,0,0,.12)}.annotations:after{content:"";position:absolute;width:0;height:0;left:-16px;right:auto;top:0;bottom:auto;border-top:20px solid transparent;border-bottom:20px solid transparent;border-right:16px solid #757575}.annotations-container--student--report{border-top:1px solid #ddd}.annotations--report{margin-top:0;margin-bottom:0}.annotations__header{position:relative;border-top-right-radius:4px;padding:10px 12px;font-weight:700;transition:all 1s;color:#fff;background-color:#757575}.annotations__avatar{background-color:#f05843;padding:2px;position:absolute;top:0;left:-62px}.annotations__icon{transition:all 1s;color:#fff}.annotations__body{padding:12px;background-color:#fff;border-bottom-left-radius:4px;border-bottom-right-radius:4px;overflow:auto}.annotations__status{background-color:#fff;color:#ef6c00;display:inline-block;margin-left:8px;font-size:12px}.annotations__status.ng-enter,.annotations__status.ng-leave{transition:all 1s}.annotations__status.ng-enter,.annotations__status.ng-leave.ng-leave-active{opacity:0}.annotations__status.ng-enter.ng-enter-active,.annotations__status.ng-leave{opacity:1}.annotations__score{font-weight:700}.annotations__info{font-style:italic;opacity:.8;border-bottom:1px dotted;font-size:13px}.annotations--inside .annotations{margin-left:72px}.annotations--info{margin-bottom:32px;margin-right:8px;margin-left:72px}@media only screen and (min-width:600px){.annotations--info{margin:16px 16px 32px 76px}}.annotations--info:after{border-right:16px solid #ef6c00}.annotations--info .annotations__avatar{background-color:#fff}.annotations--info .annotations__header{background-color:#ef6c00}.component{position:relative}.component__wrapper{padding:0 16px;margin:24px 0}.component__content{overflow-x:auto;font-size:15px;overflow-y:hidden}@media only screen and (min-width:600px){.component__content{padding:0 8px}}.component-header,h3.component__header{padding:8px 12px;margin:0;font-size:14px}.component__rubric{position:absolute;left:-20px;top:12px}.notebook-enabled .component_content img{transition:all .25s;cursor:pointer;cursor:copy}.notebook-enabled .component_content img:focus,.notebook-enabled .component_content img:hover{box-shadow:0 0 5px 1px #f05843}.component__actions .md-button:first-child{margin-left:0}.component__actions .md-button:last-child{margin-right:0}.component__actions__info{font-style:italic;margin-left:8px;color:rgba(0,0,0,.54)}.component__actions__more{border-bottom:1px dotted}.component__prompt{margin-bottom:8px;font-weight:500}.component__prompt__content{display:inline}.component__attachment{position:relative;margin:0 8px;padding-bottom:8px}@media only screen and (min-width:600px){.component__attachment{padding-top:8px}}@media only screen and (max-width:599px){.component__add-attachment{width:100%}}.component__attachment__content{max-height:100px;width:auto}.component__attachment__delete{position:absolute;top:0;right:0;min-width:0;background-color:hsla(0,0%,100%,.75)!important;border-radius:0;padding:4px;margin:0}.component__attachment__delete>md-icon{margin-top:0}.component__revision{margin:8px 0;padding:8px}.component__revision:nth-child(odd){background-color:#f7f7f7}.component__revision__content{padding:4px 0 8px;border-bottom:1px solid #ddd}.component__revision__actions{color:#757575;padding-top:4px}.component__content--Discussion{overflow:hidden}.discussion-content{background-color:#eee;box-shadow:inset 0 0 3px #aaa}.discussion-posts{padding:12px 12px 8px}@media only screen and (min-width:1280px){.discussion-posts{padding:16px 16px 0}}.discussion-post{margin:0 auto 16px;max-width:600px}@media only screen and (min-width:600px){.discussion-post{margin-bottom:24px}}@media only screen and (min-width:1280px){.discussion-post{margin-bottom:32px}}.discussion-post md-divider{position:relative;width:auto}.discussion-post__contents{padding:16px}.discussion-post__avatar,md-list-item>.md-avatar.discussion-post__avatar{margin-right:8px}.discussion-post__avatar--reply,md-list-item>.md-avatar.discussion-post__avatar--reply{margin-top:8px}.discussion-post__user,md-list-item .md-list-item-text h3.discussion-post__user{padding-bottom:4px;font-weight:700;line-height:1.3;overflow:visible;white-space:normal}.discussion-post__date{color:#aaa}.discussion-post__date--reply{margin-left:8px;font-weight:400}.discussion-post__content{margin-top:16px;white-space:pre-wrap}.discussion-post__attachment{max-width:100%;height:auto!important;margin-top:16px}.discussion-new{background-color:#fff;max-width:570px;margin-left:auto;margin-right:auto;padding:8px;transition:all .25s;transform:scale(.95)}.discussion-new--focused{transform:scale(1)}md-input-container.discussion-new__input-container{margin:0;padding:0}md-input-container.discussion-new__input-container>textarea.md-input{min-height:68px}.discussion-new__input--textarea,.input-container textarea.discussion-new__input--textarea{padding:8px;border:0}.discussion-new__actions{padding:0 8px}.discussion-new__actions .md-button:first-of-type{margin-left:0}.discussion-new__actions .md-button:last-of-type{margin-right:0}.discussion-new__attachment{padding:0;margin:0 0 8px}.discussion-new__attachment__content{margin-top:0;margin-bottom:16px}.discussion-comments{padding:0}.discussion-comments__contents{background-color:#f7f7f7}.discussion-comments__header{background-color:transparent;padding:0}.discussion-comments__header .md-subheader-inner{padding-bottom:8px}.discussion-comments__list{padding:0;max-height:9999px;overflow-y:auto}@media only screen and (min-width:600px){.discussion-comments__list{max-height:400px}}.input--textarea.discussion-reply__input,.input-container textarea.input--textarea.discussion-reply__input{background-color:#fff;padding:4px;font-size:14px;border:0;margin-left:-1px;margin-bottom:0;resize:none}.discussion-reply,md-list-item.discussion-reply{margin:0 12px 8px;padding:0;min-height:56px}.discusstion-reply__details,md-list-item .md-list-item-text.discusstion-reply__details{margin:8px 0}.discussion-post__user--reply,md-list-item .md-list-item-text h3.discussion-post__user--reply{font-size:14px;padding:0;margin:0}.discusstion-reply__content{margin-top:2px}.discusstion-reply__content p{font-weight:400!important;color:rgba(0,0,0,.87)!important}.discussion-new-reply{padding:8px}.discussion-new-reply__input-container{padding-top:0;margin:0}.discussion-new-reply__input-container .md-errors-spacer{display:none}.discussion-new-reply__actions{margin-left:8px}.discussion-new-reply__actions .md-button{margin-top:0;margin-bottom:0}.embedded-content__iframe{border:0}.graph-select{min-width:150px;max-width:200px}.graph-controls{margin:8px 0;padding:8px 0;border-color:#eee;border-style:solid;border-width:1px 0}.match-content{background-color:#eee;margin-bottom:16px;padding:8px;box-shadow:inset 0 0 3px #aaa}.match-divider{margin:16px 8px 8px}@media only screen and (min-width:960px){.match-divider--horizontal{display:none}}.match-bucket__header{padding:12px;font-weight:500;color:#7e57c2}.match-bucket__content{padding:0}.match-bucket--choices .match-bucket__header{color:#795c3a}.match-bucket__contents{min-height:120px;padding:0 8px 8px;background-color:#ddd;transition:background-color .25s;border-bottom-left-radius:4px;border-bottom-right-radius:4px;-moz-column-gap:8px;column-gap:8px}@media only screen and (max-width:599px){.match-bucket__contents{-moz-column-count:1!important;column-count:1!important}}.match-bucket__contents img{max-width:100%;height:auto}.match-bucket__contents--over{background-color:#7e57c2}.match-bucket__item{list-style-type:none;cursor:move;padding-top:8px;-moz-column-break-inside:avoid;break-inside:avoid}.match-bucket__item__contents{background-color:#fff;padding:8px!important;border:1px solid #ccc}.match-bucket__item__contents .md-list-item-text{width:100%}.match-bucket__item__contents__text{margin-right:4px}.match-feedback{transition:opacity .25s;margin:8px -8px -8px;color:#fff;padding:4px 8px}.match-feedback.ng-hide{opacity:0;transition:opacity 1ms}.match-feedback md-icon{color:#fff}.outside-content iframe{border:1px solid #eee}.outside-content__source{margin-top:4px;text-align:end}.outside-content__source a{max-width:240px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;display:inline-block}.notebook-toolbar md-divider{margin:8px 0}@media only screen and (max-width:959px){.notebook-toolbar{border-top:1px solid #ddd}}.notebook-toolbar__add-menu{position:absolute;bottom:40px}.notebook-toolbar__add-menu .md-fab-action-item{background-color:#fff}.notebook-toolbar__add-icon{border-radius:50%}#closeNotebookSettingsButton{float:right}[dir=rtl] #closeNotebookSettingsButton{float:left}highchart{display:block} +body{background:#eee}body.vle{overflow:hidden}a:focus,a:hover{color:#7e57c2}blockquote{background-color:#fff;padding:8px;margin:16px 0;border:solid #7e57c2;border-width:0 0 0 3px}.has-indicator:after{content:"";position:absolute;border-radius:50%;padding:5px;background-color:#f05843}.has-indicator--icon-button:after{top:25px;left:5px}.badge{border-radius:4px;padding:2px 6px;font-size:12px;font-weight:500;font-style:normal;background-color:#aaa}.badge.md-button{min-width:0;min-height:0;line-height:inherit}.badge.md-button:focus,.badge.md-button:hover{background-color:#aaa}.badge.md-button:focus{outline:1px dotted #aaa}.badge--info{background-color:#ef6c00;color:#fff}.badge--warn{background-color:#c62828;color:#fff}.badge--success{background-color:#00c853;color:#fff}.divider--withmargin{margin:16px 0}.divider--dashed{border-top-style:dashed}a{color:#7e57c2;cursor:pointer}.active{background-color:hsla(0,0%,62%,.2);color:rgba(0,0,0,.87)}.avatar{border-radius:50%;box-sizing:content-box}.avatar--square{border-radius:4px}.avatar.md-18{height:30px;width:30px}.avatar.md-24{height:36px;width:36px}.avatar.md-36{height:48px;width:48px}.avatar.md-48{height:60px;width:60px}.avatar--icon{background-color:#ddd;white-space:normal!important}.avatar--icon:not(.md-avatar){padding:6px}.avatar--icon.md-18{height:18px;width:18px}.avatar--icon.md-24{height:24px;width:24px}.avatar--icon.md-36{height:36px;width:36px}.avatar--icon.md-48{height:48px;width:48px}md-toolbar.md-light-theme:not(.md-menu-toolbar) md-icon{color:rgba(0,0,0,.54)}md-toolbar.md-light-theme:not(.md-menu-toolbar) .md-button:disabled md-icon{color:rgba(0,0,0,.26)}.md-button:not([disabled]).primary,md-icon.primary{color:#7e57c2!important}.md-button:not([disabled]).success,md-icon.success{color:#00c853!important}.md-button:not([disabled]).warn,md-icon.warn{color:#c62828!important}.md-button:not([disabled]).info,md-icon.info{color:#ef6c00!important}.md-button:not([disabled]).accent,md-icon.accent{color:#f05843!important}.md-button:not([disabled]).accent-1,md-icon.accent-1{color:#795c3a!important}.md-button:not([disabled]).accent-2,md-icon.accent-2{color:#cad266!important}md-input-container.md-wise-theme label{color:rgba(0,0,0,.87)}md-select-menu.md-default-theme md-option[selected],md-select-menu md-option[selected]{background-color:#fff}.md-autocomplete-suggestions-container.md-default-theme li .highlight,.md-autocomplete-suggestions-container li .highlight{color:#7e57c2;background-color:#fff}.primary{color:#7e57c2}.accent{color:#f05843}.accent-1{color:#795c3a}.accent-2{color:#cad266}.warn{color:#c62828}.info{color:#ef6c00}.success{color:#00c853}.divider{color:rgba(0,0,0,.12)}.gray-lightest{color:#f7f7f7}.gray-lighter{color:#eee}.gray-light{color:#ddd}.gray{color:#ccc}.gray-dark{color:#aaa}.gray-darker{color:#757575}.gray-darkest{color:#333}.text{color:rgba(0,0,0,.87)}.text-secondary{color:rgba(0,0,0,.54)}.text-disabled{color:rgba(0,0,0,.26)}.text-light{color:#fff}.text-light-secondary{color:hsla(0,0%,100%,.7)}.text-light-disabled{color:hsla(0,0%,100%,.5)}.selected-bg{color:#fff}.score{color:#ffc107}.body{color:rgba(0,0,0,.87)}.body-bg{color:#eee}.primary-bg{background-color:#7e57c2}.accent-bg{background-color:#f05843}.accent-1-bg{background-color:#795c3a}.accent-2-bg{background-color:#cad266}.warn-bg{background-color:#c62828}.info-bg{background-color:#ef6c00}.success-bg{background-color:#00c853}.divider-bg{background-color:rgba(0,0,0,.12)}.gray-lightest-bg{background-color:#f7f7f7}.gray-lighter-bg{background-color:#eee}.gray-light-bg{background-color:#ddd}.gray-bg{background-color:#ccc}.gray-dark-bg{background-color:#aaa}.gray-darker-bg{background-color:#757575}.gray-darkest-bg{background-color:#333}.text-bg{background-color:rgba(0,0,0,.87)}.text-secondary-bg{background-color:rgba(0,0,0,.54)}.text-disabled-bg{background-color:rgba(0,0,0,.26)}.text-light-bg{background-color:#fff}.text-light-secondary-bg{background-color:hsla(0,0%,100%,.7)}.text-light-disabled-bg{background-color:hsla(0,0%,100%,.5)}.selected-bg-bg{background-color:#fff}.score-bg{background-color:#ffc107}.body-bg{background-color:rgba(0,0,0,.87)}.body-bg-bg{background-color:#eee}md-progress-circular.primary path{stroke:#7e57c2}md-progress-circular.accent path{stroke:#f05843}md-progress-circular.accent-1 path{stroke:#795c3a}md-progress-circular.accent-2 path{stroke:#cad266}md-progress-circular.warn path{stroke:#c62828}md-progress-circular.info path{stroke:#ef6c00}md-progress-circular.success path{stroke:#00c853}md-progress-circular.divider path{stroke:rgba(0,0,0,.12)}md-progress-circular.gray-lightest path{stroke:#f7f7f7}md-progress-circular.gray-lighter path{stroke:#eee}md-progress-circular.gray-light path{stroke:#ddd}md-progress-circular.gray path{stroke:#ccc}md-progress-circular.gray-dark path{stroke:#aaa}md-progress-circular.gray-darker path{stroke:#757575}md-progress-circular.gray-darkest path{stroke:#333}md-progress-circular.text path{stroke:rgba(0,0,0,.87)}md-progress-circular.text-secondary path{stroke:rgba(0,0,0,.54)}md-progress-circular.text-disabled path{stroke:rgba(0,0,0,.26)}md-progress-circular.text-light path{stroke:#fff}md-progress-circular.text-light-secondary path{stroke:hsla(0,0%,100%,.7)}md-progress-circular.text-light-disabled path{stroke:hsla(0,0%,100%,.5)}md-progress-circular.selected-bg path{stroke:#fff}md-progress-circular.score path{stroke:#ffc107}md-progress-circular.body path{stroke:rgba(0,0,0,.87)}md-progress-circular.body-bg path{stroke:#eee}.l-constrained{margin-left:auto;margin-right:auto;max-width:100%;position:relative}@media (min-width:600px){.l-constrained{width:1280px}}.l-constrained-md{width:960px;max-width:100%}.l-footer{position:fixed;bottom:0;left:0;right:0;z-index:1;background-color:#fff;border-top:1px solid #eee}.button--footer{margin:0;padding-top:0;padding-bottom:0;min-width:0;display:flex}.button--footer__element{padding-left:8px}.l-header{z-index:3}.l-header .logo{margin-left:0!important;height:36px;width:36px;vertical-align:middle}.l-header .logo-link{min-width:auto;display:none;padding:0 4px;margin-right:12px}@media only screen and (min-width:600px){.l-header .logo-link{display:block}}.l-header .logo-link:focus,.l-header .logo-link:hover{border:0}@media only screen and (max-width:599px){.l-header .md-toolbar-tools h1,.l-header .md-toolbar-tools h2,.l-header .md-toolbar-tools h3{font-size:15px}}.l-main{background-color:#eee}.l-main--with-toolbar{margin-top:42px}#content{transition:margin-top .5s}.view-content{margin:0 auto;padding:8px;position:absolute;left:0;right:0;transition:opacity .5s}@media only screen and (min-width:960px){.view-content{padding:16px}}.view-content.ng-enter{opacity:0}.view-content .ng-enter-active{opacity:1;transition-delay:.25s}.view-content.ng-hide,.view-content.ng-hide-add,.view-content.ng-hide-add-active,.view-content.ng-hide-remove,.view-content.ng-hide-remove-active,.view-content.ng-leave-active{opacity:0}.view-content--with-sidemenu{padding:8px}@media only screen and (min-width:600px){.view-content--with-sidemenu{margin-left:54px;padding:16px}}@media only screen and (min-width:600px){[dir=rtl] .view-content--with-sidemenu{margin-left:auto;margin-right:54px}}.content-head{margin:8px 0}.content-head h1,.content-head h2,.content-head h3{font-weight:300;margin-top:0;margin-bottom:0;font-size:36px}@media only screen and (max-width:959px){.content-head h1,.content-head h2,.content-head h3{font-size:32px;text-align:center}}@media only screen and (max-width:959px){.content-head__more{margin-top:8px}}.content-head__item,h2.content-head__item{margin:0 8px}.content-head__item .md-subhead,h2.content-head__item .md-subhead{padding-left:4px}@media only screen and (max-width:959px){.content-head__item .md-subhead,h2.content-head__item .md-subhead{display:block;padding-left:0}}.content-head__item md-icon,h2.content-head__item md-icon{vertical-align:text-bottom}.stepSelectMenuContainer md-select-menu,.stepSelectMenuContainer md-select-menu md-content{max-height:500px}.l-nav{background-color:#eee!important}.l-node{margin-top:42px;padding:0}@media only screen and (min-width:600px){.l-node{padding:0 0 16px;background-color:#eee!important}}@media only screen and (min-width:960px){.l-node{padding:0 0 32px}}.l-notebook{margin-top:42px;background-color:#eee!important}.l-sidebar__header{background-color:#fff!important;color:#795c3a!important}.l-sidebar__header md-select{color:rgba(0,0,0,.87)}.status-icon{margin:0 4px;z-index:1;vertical-align:bottom}.md-button.status-icon{height:auto;width:auto;min-height:0;line-height:inherit;margin:0 4px;padding:0}.avatar--icon--alert{background-color:#fff}.avatar--icon--alert__icon{font-size:48px;margin:-4px 0 0 -4px}md-dialog{width:600px}.dialog--wide{width:960px}.dialog--wider{width:1280px}.help-bubble{border-radius:4px;max-width:320px}@media (min-width:600px){.help-bubble{max-width:552px}}@media (min-width:960px){.help-bubble{max-width:912px}}@media (min-width:1280px){.help-bubble{max-width:1232px}}.help-bubble___title__content,.help-bubble__title{border-top-left-radius:4px;border-top-right-radius:4px}.help-bubble___title__content{padding:0 0 0 12px;background-color:#ef6c00}.help-bubble___title__content .md-icon-button{margin-right:0;padding-top:0;padding-bottom:0}.help-bubble__content{overflow:auto;padding:8px 12px;max-height:480px}.help-bubble__actions{border-bottom-left-radius:4px;border-bottom-right-radius:4px}div.hopscotch-bubble{border-radius:4px;border:0;font-family:Roboto,Helvetica Neue,sans-serif;font-size:14px;z-index:6}div.hopscotch-bubble .hopscotch-bubble-arrow-container{position:absolute;width:20px;height:20px}div.hopscotch-bubble .hopscotch-bubble-arrow-container.up{top:0;left:12px}div.hopscotch-bubble .hopscotch-bubble-arrow-container.up .hopscotch-bubble-arrow{border-bottom:10px solid #ef6c00;border-left:10px solid transparent;border-right:10px solid transparent;position:relative;top:-10px}div.hopscotch-bubble .hopscotch-bubble-arrow-container.up .hopscotch-bubble-arrow-border{border:0}div.hopscotch-bubble .hopscotch-bubble-arrow-container.down{bottom:-34px;left:12px}div.hopscotch-bubble .hopscotch-bubble-arrow-container.down .hopscotch-bubble-arrow{border-top:10px solid #ef6c00;border-left:10px solid transparent;border-right:10px solid transparent;position:relative;top:-10px}div.hopscotch-bubble .hopscotch-bubble-arrow-container.down .hopscotch-bubble-arrow-border{border:0}div.hopscotch-bubble .hopscotch-bubble-arrow-container.left{top:12px;left:-10px}div.hopscotch-bubble .hopscotch-bubble-arrow-container.left .hopscotch-bubble-arrow{border-right:10px solid #ef6c00;border-bottom:10px solid transparent;border-top:10px solid transparent;position:relative;left:0;top:-10px}div.hopscotch-bubble .hopscotch-bubble-arrow-container.left .hopscotch-bubble-arrow-border{border:0}div.hopscotch-bubble .hopscotch-bubble-arrow-container.right{top:12px;right:-30px}div.hopscotch-bubble .hopscotch-bubble-arrow-container.right .hopscotch-bubble-arrow{border-left:10px solid #ef6c00;border-bottom:10px solid transparent;border-top:10px solid transparent;position:relative;right:0;top:-10px}div.hopscotch-bubble .hopscotch-bubble-arrow-container.right .hopscotch-bubble-arrow-border{border:0}.input-container{padding-top:12px}.input-container--component{margin-bottom:0}.input-container--open-response.md-has-icon{padding-left:0}.input-container--open-response .md-errors-spacer{display:none}.input-wrapper{position:relative}.input-wrapper--focused .input--textarea__action md-icon{color:#7e57c2}.input--textarea,.input-container textarea.input--textarea{padding:8px;background-color:#f7f7f7;border:1px solid #ccc;margin-bottom:8px}.input--textarea:focus,.input-container textarea.input--textarea:focus{background-color:#fff}.input--textarea[disabled],.input-container textarea.input--textarea[disabled]{color:rgba(0,0,0,.54)}.input-container textarea.input--textarea{width:100%}.input--textarea--disabled{color:rgba(0,0,0,.54)}.input--textarea__action{position:absolute;right:-4px}.input--textarea__action[disabled] md-icon{color:rgba(0,0,0,.26)!important}.input--textarea__action--notebook{top:6px}.input-wrapper--richtext .input--textarea__action--notebook{top:-7px}.input--textarea__action--revision{bottom:6px}.input-wrapper--richtext .input--textarea__action--revision{bottom:-5px}.input-label,md-input-container:not(.md-input-invalid):not(.md-input-focused).md-input-has-value label.input-label{line-height:1.2;color:rgba(0,0,0,.87)}.input-label.input-label--focused,md-input-container:not(.md-input-invalid):not(.md-input-focused).md-input-has-value label.input-label.input-label--focused{color:#7e57c2}.autocomplete input{text-overflow:ellipsis;overflow:hidden;word-wrap:none;font-weight:500;color:rgba(0,0,0,.54)}@media only screen and (min-width:600px){.autocomplete--minwidth{min-width:300px}}@media only screen and (min-width:960px){.autocomplete--minwidth{min-width:300px}}.autocomplete--flat md-autocomplete-wrap{background-color:#fff}.autocomplete--flat md-autocomplete-wrap:not(.md-menu-showing){box-shadow:none;background-color:#eee}.select__header{height:48px}.select__header input{height:100%;width:100%;padding:0 8px;outline:none;border:0;font-size:14px;font-weight:500}.table{max-width:100%;width:auto;min-width:100px;margin:8px 0}.table tbody>tr>td,.table tbody>tr>th,.table tfoot>tr>td,.table tfoot>tr>th,.table thead>tr>td,.table thead>tr>th{border:1px solid #ccc;padding:6px;font-size:15px;min-height:32px;height:32px;min-width:32px;vertical-align:top}.table td.inactive,.table th{background-color:#f7f7f7;opacity:1;visibility:visible}.table md-input-container{margin:0}.table .md-errors-spacer{display:none}.table--student td.inactive{padding:8px 10px}.table--full-width{width:100%}.table--list{border:0;border-collapse:collapse;background-color:#fff;max-width:100%;overflow:auto}.table--list td,.table--list th{padding:0 4px;border:0}.table--list td{min-height:56px;height:56px}.table--list tr.md-button{display:table-row;text-align:left;width:auto;text-transform:none;font-size:inherit;font-weight:400}.table--list__wrap{min-width:600px}@media only screen and (max-width:959px){.table-wrap-sticky{overflow-x:auto}}.table--list__thead{font-size:14px;font-weight:700}.table--list__thead__tr{height:100%;margin:0}.table--list__thead__th{background-color:#757575;color:#fff;min-height:42px;height:42px}.table--list__thead__link{color:#fff;text-transform:none;margin:0;min-width:0;white-space:normal;line-height:1.4;width:100%}.table--list__thead__sort{margin:0}.table--list__thead__sort--reverse{transform:rotate(180deg)}.td--wrap{min-width:180px;white-space:normal;line-height:1.2}@media only screen and (max-width:959px){.td--max-width{max-width:180px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}}.md-toolbar-tools{font-size:18px}.md-toolbar-tools .autocomplete,.md-toolbar-tools .autocomplete input,.md-toolbar-tools .autocomplete md-autocomplete-wrap{height:36px}.md-toolbar--wise{min-height:42px}.md-toolbar--wise .md-toolbar-tools{height:42px;max-height:42px}.md-toolbar--wise .md-button.md-icon-button{height:42px;line-height:42px;width:42px}.md-toolbar--wise--sm .md-toolbar-tools{padding:0 8px;font-size:15px}.md-toolbar--sidenav{background-color:#333!important}.md-toolbar--sidenav .md-toolbar-tools{font-size:16px;font-weight:500}.toolbar{position:fixed;left:0;right:0;top:52px;z-index:3}.toolbar__title{margin-left:8px;font-size:16px;font-weight:500}.toolbar__tools{padding-right:8px}[dir=rtl] .toolbar__tools{padding-right:16px;padding-left:8px}.md-button.toolbar__nav,.toolbar__nav{margin:0}.md-button.toolbar__select,.toolbar__select{margin:0 4px;min-height:32px;background-color:#f7f7f7}.md-button.toolbar__select .md-select-value,.toolbar__select .md-select-value{height:32px;text-align:left}[dir=rtl] .md-button.toolbar__select .md-select-value,[dir=rtl] .toolbar__select .md-select-value{text-align:right}.toolbar__select--fixedwidth{width:168px}@media only screen and (min-width:600px){.toolbar__select--fixedwidth{width:264px}}@media only screen and (min-width:960px){.toolbar__select--fixedwidth{width:432px}}.list-item{background-color:#fff;border-bottom:1px solid #eee}.list-item.md-subheader,.list-item .md-subheader{color:rgba(0,0,0,.87);background-color:#fff}.list-item.md-subheader md-icon,.list-item .md-subheader md-icon{vertical-align:middle}.list-item.md-subheader .md-subheader-inner,.list-item .md-subheader .md-subheader-inner{padding:0}.list-item.md-subheader .md-avatar,.list-item .md-subheader .md-avatar{margin-right:8px}.list-item .autocomplete{margin:8px 0}.list-item--info._md-button-wrap>div.md-button:first-child,.list-item--info .md-subheader-content{border-left:4px solid #ef6c00!important;margin-left:-4px}.list-item--warn._md-button-wrap>div.md-button:first-child,.list-item--warn .md-subheader-content{border-left:4px solid #c62828!important;margin-left:-4px}.list-item--expanded{border-bottom-width:0}.list-item--noclick,.list-item--noclick.md-button{cursor:default;background-color:#f7f7f7}.list-item--actions{padding:0 8px!important}.list-item__subheader-button{text-transform:none;width:100%;padding:8px 16px;margin:0;white-space:normal;text-align:left;line-height:1.4}.user-list{font-size:15px}#nav{position:relative}.nav{margin-bottom:16px}.nav-mask{position:absolute;top:0;bottom:0;left:0;right:0;background-color:rgba(0,0,0,.25);z-index:1}.nav-mask.ng-hide{opacity:0}.nav-head{color:rgba(0,0,0,.54);font-weight:500}.nav-head md-icon{line-height:20px}.nav-contents--root{padding:6px 6px 12px}.nav-contents--group{background-color:#ddd;padding:8px}.nav-contents--root,.nav-contents__list{padding:0}@media (min-width:600px){.nav-contents__list{padding:8px}}.nav-item{transition:opacity .25s ease-in-out}.nav-item.prev md-list-item{background-color:#fff}.nav-item--card__content{border-top-right-radius:4px;border-top-left-radius:4px}.nav-item--card__content:focus{outline:none}.nav-item--card__content:focus .nav-item__title>span{border-bottom:1px dashed #ccc}.nav-item--root{transition:margin .25s,box-shadow .5s}.nav-item--root.expanded{flex-basis:100%;max-width:100%;max-height:none!important;margin:8px auto;padding-left:4px}.nav-item--root.expanded:first-of-type{margin-top:0}.nav-item--list__info-item{padding:0 16px 0 4px;display:inline-block}.nav-item--list__reorder{margin-left:8px;color:rgba(0,0,0,.26)}.nav-item--card--group:not(.expanded){box-shadow:0 3px 1px -2px rgba(0,0,0,.14),0 2px 2px 0 rgba(0,0,0,.098),0 1px 5px 0 rgba(0,0,0,.084),3px 3px 0 1px #d5d5d5,6px 6px 0 1px #aaa}.nav-item__collapse{margin:0}.nav-item__more{border-top:1px solid #ddd;border-bottom-right-radius:4px;border-bottom-left-radius:4px;padding:8px 16px;min-height:40px}.nav-item__users{height:auto;cursor:pointer;color:#fff;margin:0;padding:1px 6px}.nav-item__users>md-icon{padding-right:4px;color:#fff}.nav-item__users:focus.success-bg,.nav-item__users:hover.success-bg{background-color:#00c853}.nav-item__title{padding-left:16px;line-height:1.2;font-weight:400}[dir=rtl] .nav-item__title{padding-left:auto;padding-right:16px}.nav-item__info{padding:0 8px}.nav-item__progress{width:48px}.nav-item__progress>.md-container{top:0}.nav-item__progress-value{margin-left:8px;width:36px}.progress-wrapper{padding:2px 0;cursor:pointer}.notice{text-align:center;padding:8px;background-color:rgba(0,0,0,.04);width:100%}@media (min-width:600px){.notice{max-width:80%;border-radius:3px;margin:24px auto}}.menu-progress{position:absolute;top:10px;right:12px}.menu-progress path{stroke:#cad266!important;stroke-width:2px}[dir=rtl] .menu-progress{right:auto;left:12px}.menu-sidenav__item{font-weight:700;font-size:14px}.menu-sidenav__icon{margin-top:12px!important;margin-right:12px!important;margin-left:12px}.active .menu-sidenav__icon,.active .menu-sidenav__item{color:#7e57c2}.menu-sidebar{position:absolute;top:94px;bottom:0;left:0;background-color:#fff;width:56px;overflow:hidden;padding:8px 0;text-align:center;border-right:1px solid #ccc}@media only screen and (max-width:599px){.menu-sidebar{display:none}}[dir=rtl] .menu-sidebar{right:0;left:auto}.md-button.md-icon-button.menu-sidebar__link{margin-top:6px;margin-bottom:6px}#node{margin:0 auto;position:absolute;left:0;right:0}@media only screen and (min-width:600px){#node{padding:24px 16px;margin-bottom:32px}}@media only screen and (min-width:960px){#node{padding:32px}}#node.ng-enter{transition:opacity .5s;opacity:0}#node.ng-enter-active{opacity:1}@media only screen and (min-width:600px){.node-notice{margin-top:-8px;margin-bottom:16px}}@media only screen and (min-width:960px){.node-notice{margin-top:-16px}}.node-content{padding:0 0 48px;background-color:#fff;border-radius:3px;overflow:visible}@media only screen and (max-width:599px){.node-content{box-shadow:none}}@media only screen and (min-width:600px){.node-content{padding:0;border-top:2px solid;border-bottom:2px solid}}md-content.node-content{background-color:#fff}.node-content__rubric{position:absolute;top:-22px;left:0;right:0;z-index:1}.node-content__rubric .avatar--icon{transform:scale(.94)}@media only screen and (max-width:599px){.node-content__rubric .avatar--icon{transform:scale(.8)}}.node-icon{color:#fff;vertical-align:inherit}.node-select{margin:0 8px;min-width:0;font-weight:500;font-size:15px}.node-select .md-select-value :first-child{transform:translateZ(0);flex:1 0 0}.node-select .md-select-value .node-select__icon,.node-select .md-select-value .node-select__status{display:none}.node-select .md-select-icon{margin-left:0;color:rgba(0,0,0,.87)}.node-select-option--group{background-color:#f7f7f7;border-bottom:1px solid #eee;border-top:1px solid #eee}.node-select-option--node{padding-left:20px}.node-select__icon{margin-right:8px}.node-select__status{margin-left:8px}.node-select__text{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}@media only screen and (min-width:600px){.node-select__text{margin-top:2px}}.node-title{line-height:1.2;text-transform:none;margin-top:3px}@media only screen and (max-width:599px){.node-title{font-size:15px}}.node-content__actions{padding:0 16px 16px}@media only screen and (min-width:960px){.node-content__actions{padding:0 24px 24px}}@media only screen and (min-width:1280px){.node-content__actions{padding:0 32px 32px}}.node-content__actions .md-button:first-child{margin-left:0}.node-content__actions .md-button:last-child{margin-right:0}.node-content__actions__info{font-style:italic;margin-left:8px;color:rgba(0,0,0,.54)}.node-content__actions__more{border-bottom:1px dotted}.md-button.md-icon-button.node-nav:first-of-type{margin-right:0}@media only screen and (min-width:600px){.node-sidebar-active{margin-right:68px}}@media only screen and (max-width:599px){.node-sidebar-visible{margin-bottom:42px}}.node-sidebar{position:absolute;right:0;top:0;width:52px}.node-sidebar__toolbar{position:fixed;width:52px;background-color:#fff;padding:8px 0;border-radius:3px}@media only screen and (max-width:599px){.node-sidebar__toolbar{right:0;bottom:0;left:0;width:100%;border-radius:0;padding:0;min-height:0;height:42px}}.node-info{margin:0}@media only screen and (max-width:599px){.node-info{margin:-16px;border-radius:0}}.node-info .divider{margin-left:-8px;margin-right:-8px}.node-info .component:first-child{margin-top:-16px}.node-info .component,.node-info .component__content,.node-info .component__header{margin-left:-8px;margin-right:-8px}.node-info .component__actions{display:none}.node-rubric{border:2px solid #7e57c2;margin:8px 16px 24px;padding:16px;background-color:#fff}.node__label--vertical-alignment{vertical-align:middle;display:inline-block}@media only screen and (min-width:600px){.notebook-launcher.md-button.md-fab{z-index:61}}.notebook-report{border-radius:4px 4px 0 0;margin:0;height:100%}.notebook-report .note-toolbar{margin:-8px -8px 8px;padding:4px;border-bottom:0;border-top:0;border-color:#ddd currentcolor;border-style:solid none;border-width:1px 0;border-radius:0}.notebook-report .note-toolbar .btn-group{margin-top:0}.notebook-report .note-btn{border:0;border-top-left-radius:0;border-top-right-radius:0;border-bottom-left-radius:0;border-bottom-right-radius:0}.notebook-report-container{height:550px;width:450px;max-height:90%;bottom:0;right:96px;position:absolute;z-index:3}@media only screen and (min-width:960px){.notes-visible .notebook-report-container{right:516px;transition:right .25s}}.notebook-report-container__full{top:16px;bottom:16px;left:16px;right:16px;max-height:none;max-width:none;height:auto;width:auto}.notebook-report-container__full .notebook-report{height:100%;width:100%;margin:0 auto;max-height:none;border-radius:4px}.notebook-report-container__collapsed{width:300px;height:auto}.notebook-report-container__collapsed .notebook-report__actions,.notebook-report-container__collapsed .notebook-report__content,.notebook-report-container__collapsed .notebook-report__content__header{display:none}.notebook-report__toolbar{background-color:#333!important;border-radius:4px 4px 0 0}.notebook-report__toolbar__title{max-width:150px}.notebook-report__content{background-color:#fff}.notebook-report__content h1,.notebook-report__content h2,.notebook-report__content h3,.notebook-report__content h4{font-size:22px}.notebook-report__content .note-editor.note-frame{border:0;border-radius:0;padding:0;box-shadow:none}.notebook-report__content .note-resizebar{display:none}.notebook-report__content__header{padding:8px;background-color:#fff;font-size:16px}@media only screen and (max-width:599px){.notebook-report-container:not(.notebook-report-container__collapsed){top:0;bottom:0;left:0;right:0;max-height:none;max-width:none;height:auto;width:auto}.notebook-report-container:not(.notebook-report-container__collapsed) .notebook-report{border-radius:0}.notebook-tools--full{display:none}}.notebook-report-backdrop{position:fixed;top:0;left:0;width:100%;height:100%;z-index:3;background-color:#212121;opacity:.48}.notebook-menu{transition:opacity .5s}.notebook-menu.ng-enter{opacity:0}.notebook-menu .ng-enter-active{opacity:1;transition-delay:.25s}.notebook-menu.ng-hide,.notebook-menu.ng-hide-add,.notebook-menu.ng-hide-add-active,.notebook-menu.ng-hide-remove,.notebook-menu.ng-hide-remove-active,.notebook-menu.ng-leave-active{opacity:0}.notebook-item{transition:box-shadow .25s;margin:0 16px 16px;display:block}.notebook-item__content{height:250px;min-width:230px;position:relative;padding:0;background-color:#ccc;border-top-left-radius:4px;border-top-right-radius:4px}.notebook-item__content__attachment,.notebook-item__content__text{position:absolute;left:0;right:0}.notebook-item__content__attachment{background-repeat:no-repeat!important;border-top-left-radius:4px;border-top-right-radius:4px;background-position:top!important;background-size:cover!important;top:0;bottom:0}.notebook-item__content__text{bottom:0;padding:8px;font-weight:500;overflow:hidden;max-height:120px;min-height:56px;background-color:hsla(0,0%,100%,.95);border-top:1px solid #eee}.notebook-item__content__text:after{content:"";text-align:right;position:absolute;bottom:0;right:0;width:100%;height:.8em;background:linear-gradient(180deg,hsla(0,0%,100%,0),hsla(0,0%,100%,.95) 100%)}.notebook-item__content--text-only:after{content:"note";font-family:Material Icons;font-weight:400;font-style:normal;display:inline-block;line-height:1;text-transform:none;letter-spacing:normal;word-wrap:normal;white-space:nowrap;direction:ltr;-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;-moz-osx-font-smoothing:grayscale;font-feature-settings:"liga";font-size:80px;color:rgba(0,0,0,.26)}.notebook-item--question__content--text-only{content:"live_help"}.notebook-item__content__location{opacity:.9;padding:8px 0}.notebook-item__content__location md-icon{font-size:22px}.notebook-item__edit{cursor:pointer}.notebook-item__actions{margin:0;padding:0 8px;color:#fff;background-color:#333;border-bottom-left-radius:4px;border-bottom-right-radius:4px}.notebook-item__actions md-icon{color:#fff}.notebook-item__text-input{margin:0}.notebook-item__text-input__textarea{padding-left:0;padding-right:0}.notebook-item__attachment{background-color:#ddd;padding:16px;margin-bottom:16px;text-align:center;position:relative}.notebook-item__attachment__content{max-width:100%;height:auto}.notebook-item__attachment__delete{position:absolute;top:4px;right:-2px;width:34px!important;height:34px!important;min-height:0}.notebook-item__attachment__delete md-icon{margin-left:-2px;font-size:22px}.notebook-item__info{font-style:italic;opacity:.8;color:#8a6942}.notebook-item__info a,.notebook-item__info md-icon{color:#8a6942}.notebook-item__info md-icon{font-size:1.5em;min-width:0;width:auto}.notebook-item__upload{text-align:center;padding:24px;background-color:#eee;margin-bottom:16px;color:rgba(0,0,0,.54);border-radius:4px;cursor:pointer;border:1px dashed transparent;transition:all .25s}.notebook-item__upload md-icon,.notebook-item__upload span{transition:color .25s}.notebook-item__upload.dragover,.notebook-item__upload:focus,.notebook-item__upload:hover{border-color:#f05843;background-color:#fdebe8;color:#f05843}.notebook-item__upload.dragover md-icon,.notebook-item__upload.dragover span,.notebook-item__upload:focus md-icon,.notebook-item__upload:focus span,.notebook-item__upload:hover md-icon,.notebook-item__upload:hover span{color:#f05843}.view-notebook-item{width:600px}.notebook-item--report{background-color:#fff}.notebook-item--report .note-editor{margin-bottom:16px;border-color:#ccc}.notebook-item--report__container.ui-scrollpoint .notebook-item--report__toolbar{position:fixed;top:94px;left:0;right:0;z-index:1}@media only screen and (min-width:600px){.notebook-item--report__container.ui-scrollpoint .notebook-item--report__toolbar{left:54px;padding:0 24px}}@media only screen and (min-width:960px){.notebook-item--report__container.ui-scrollpoint .notebook-item--report__toolbar{padding:0 32px}}.notebook-item--report__container.ui-scrollpoint .notebook-item--report__toolbar .note-toolbar{margin:0 16px}@media only screen and (min-width:600px){.notebook-item--report__container.ui-scrollpoint .notebook-item--report__toolbar .note-toolbar{margin:0 8px}}@media only screen and (min-width:960px){.notebook-item--report__container.ui-scrollpoint .notebook-item--report__toolbar .note-toolbar{margin:0 16px}}.notebook-item--report__container.ui-scrollpoint .note-editor{padding-top:40px}.notebook-item--report__toolbar .note-toolbar{background-color:#ddd;border:1px solid #ccc;margin-bottom:-2px}.notebook-item--report__heading{text-align:center;margin-bottom:32px}.notebook-item--report__add-note{font-weight:700}.notebook-item--report__note-img{max-width:100%;height:auto!important}@media only screen and (min-width:600px){.notebook-sidebar{width:400px;max-width:none}}@media only screen and (min-width:960px){.notebook-sidebar{width:500px;max-width:none}}.notebook-items{width:100%;overflow:auto;margin-top:16px;margin-bottom:76px}.notebook-items .notebook-item{width:100%}.notebook-items .notebook-item__content{height:200px;min-width:0}.notebook-items--grading{margin-bottom:0}@media only screen and (max-width:599px){.notebook-enabled .md-fab-bottom-left,.notebook-enabled .md-fab-bottom-right{bottom:50px!important}}.notebook-grading{background-color:#fff;display:block}.notification-btn{width:60px!important}.notification-btn md-icon{margin-left:20px}.notification-count{border-radius:50%;position:absolute;background-color:#f05843;width:22px;left:4px;height:22px;line-height:18px;font-size:12px;font-weight:700;border:2px solid}.notification-count:before{content:"";position:absolute;right:-7px;top:5px;border-left:6px solid hsla(0,0%,100%,.87);border-top:4px solid transparent;border-bottom:4px solid transparent}.notification-list{padding:8px 0}.notification-dismiss{width:500px}.notification-dismiss__input{margin-bottom:0}md-list md-list-item.md-2-line .md-list-item-text h4.notification-list-item__source,md-list md-list-item.md-3-line .md-list-item-text h4.notification-list-item__source,md-list md-list-item .md-list-item-text h4.notification-list-item__source{color:rgba(0,0,0,.54);font-size:12px}md-list md-list-item.md-2-line .md-list-item-text h4.notification-list-item__source md-icon,md-list md-list-item.md-3-line .md-list-item-text h4.notification-list-item__source md-icon,md-list md-list-item .md-list-item-text h4.notification-list-item__source md-icon{font-size:18px;min-width:0;width:auto;margin-left:-4px;line-height:20px}.account-menu{border-radius:4px;padding:0;font-size:15px;max-width:380px}@media (min-width:1280px){.account-menu{min-width:380px!important}}.account-menu h3{margin:0;font-weight:300}.account-menu--fixed-height{height:304px}.account-menu--fixed-width{width:320px}@media (min-width:960px){.account-menu--fixed-width{width:380px}}.account-menu__icon{background-color:#fff;border-radius:50%}.account-menu__caret{position:absolute;right:28px;top:-8px;outline:none}.account-menu__caret:before{content:"";position:absolute;border-bottom:8px solid #fff;border-left:8px solid transparent;border-right:8px solid transparent}.account-menu__caret--notification,.account-menu__caret--pause{right:80px}.account-menu__caret--notification--with-pause{right:132px}[dir=rtl] .account-menu__caret{right:auto;left:28px}[dir=rtl] .account-menu__caret--notification,[dir=rtl] .account-menu__caret--pause{left:80px;right:auto}[dir=rtl] .account-menu__caret--notification--with-pause{left:132px;right:auto}.account-menu__info{padding:8px 12px}.account-menu__info__title{font-weight:500}.account-menu__info__team{font-weight:400;color:rgba(0,0,0,.54)}.account-menu__users,.account-menu__users md-list-item{padding:0}.account-menu__users md-list-item .md-avatar{margin:0 8px 0 0;height:48px;width:48px}.account-menu__progress md-progress-linear{transform:rotate(270deg);width:26px}.account-menu__grade{position:relative;margin-right:4px}.account-menu__grade md-icon{color:#ffc107}.account-menu__grade__overlay{position:absolute;top:0;width:100%;background-color:hsla(0,0%,100%,.6)}.account-menu__actions{background-color:#f7f7f7}.account-menu__control{padding:16px}.annotations{margin:16px 4px 16px 62px;position:relative;font-size:15px}.annotations hr{margin:10px 0 8px;border-color:rgba(0,0,0,.12)}.annotations:after{content:"";position:absolute;width:0;height:0;left:-16px;right:auto;top:0;bottom:auto;border-top:20px solid transparent;border-bottom:20px solid transparent;border-right:16px solid #757575}.annotations-container--student--report{border-top:1px solid #ddd}.annotations--report{margin-top:0;margin-bottom:0}.annotations__header{position:relative;border-top-right-radius:4px;padding:10px 12px;font-weight:700;transition:all 1s;color:#fff;background-color:#757575}.annotations__avatar{background-color:#f05843;padding:2px;position:absolute;top:0;left:-62px}.annotations__icon{transition:all 1s;color:#fff}.annotations__body{padding:12px;background-color:#fff;border-bottom-left-radius:4px;border-bottom-right-radius:4px;overflow:auto}.annotations__status{background-color:#fff;color:#ef6c00;display:inline-block;margin-left:8px;font-size:12px}.annotations__status.ng-enter,.annotations__status.ng-leave{transition:all 1s}.annotations__status.ng-enter,.annotations__status.ng-leave.ng-leave-active{opacity:0}.annotations__status.ng-enter.ng-enter-active,.annotations__status.ng-leave{opacity:1}.annotations__score{font-weight:700}.annotations__info{font-style:italic;opacity:.8;border-bottom:1px dotted;font-size:13px}.annotations--inside .annotations{margin-left:72px}.annotations--info{margin-bottom:32px;margin-right:8px;margin-left:72px}@media only screen and (min-width:600px){.annotations--info{margin:16px 16px 32px 76px}}.annotations--info:after{border-right:16px solid #ef6c00}.annotations--info .annotations__avatar{background-color:#fff}.annotations--info .annotations__header{background-color:#ef6c00}.component{position:relative}.component__wrapper{padding:0 16px;margin:24px 0}.component__content{overflow-x:auto;font-size:15px;overflow-y:hidden}@media only screen and (min-width:600px){.component__content{padding:0 8px}}.component-header,h3.component__header{padding:8px 12px;margin:0;font-size:14px}.component__rubric{position:absolute;left:-20px;top:12px}.notebook-enabled .component_content img{transition:all .25s;cursor:pointer;cursor:copy}.notebook-enabled .component_content img:focus,.notebook-enabled .component_content img:hover{box-shadow:0 0 5px 1px #f05843}.component__actions .md-button:first-child{margin-left:0}.component__actions .md-button:last-child{margin-right:0}.component__actions__info{font-style:italic;margin-left:8px;color:rgba(0,0,0,.54)}.component__actions__more{border-bottom:1px dotted}.component__prompt{margin-bottom:8px;font-weight:500}.component__prompt__content{display:inline}.component__attachment{position:relative;margin:0 8px;padding-bottom:8px}@media only screen and (min-width:600px){.component__attachment{padding-top:8px}}@media only screen and (max-width:599px){.component__add-attachment{width:100%}}.component__attachment__content{max-height:100px;width:auto}.component__attachment__delete{position:absolute;top:0;right:0;min-width:0;background-color:hsla(0,0%,100%,.75)!important;border-radius:0;padding:4px;margin:0}.component__attachment__delete>md-icon{margin-top:0}.component__revision{margin:8px 0;padding:8px}.component__revision:nth-child(odd){background-color:#f7f7f7}.component__revision__content{padding:4px 0 8px;border-bottom:1px solid #ddd}.component__revision__actions{color:#757575;padding-top:4px}.component__content--Discussion{overflow:hidden}.discussion-content{background-color:#eee;box-shadow:inset 0 0 3px #aaa}.discussion-posts{padding:12px 12px 8px}@media only screen and (min-width:1280px){.discussion-posts{padding:16px 16px 0}}.discussion-post{margin:0 auto 16px;max-width:600px}@media only screen and (min-width:600px){.discussion-post{margin-bottom:24px}}@media only screen and (min-width:1280px){.discussion-post{margin-bottom:32px}}.discussion-post md-divider{position:relative;width:auto}.discussion-post__contents{padding:16px}.discussion-post__avatar,md-list-item>.md-avatar.discussion-post__avatar{margin-right:8px}.discussion-post__avatar--reply,md-list-item>.md-avatar.discussion-post__avatar--reply{margin-top:8px}.discussion-post__user,md-list-item .md-list-item-text h3.discussion-post__user{padding-bottom:4px;font-weight:700;line-height:1.3;overflow:visible;white-space:normal}.discussion-post__date{color:#aaa}.discussion-post__date--reply{margin-left:8px;font-weight:400}.discussion-post__content{margin-top:16px;white-space:pre-wrap}.discussion-post__attachment{max-width:100%;height:auto!important;margin-top:16px}.discussion-new{background-color:#fff;max-width:570px;margin-left:auto;margin-right:auto;padding:8px;transition:all .25s;transform:scale(.95)}.discussion-new--focused{transform:scale(1)}md-input-container.discussion-new__input-container{margin:0;padding:0}md-input-container.discussion-new__input-container>textarea.md-input{min-height:68px}.discussion-new__input--textarea,.input-container textarea.discussion-new__input--textarea{padding:8px;border:0}.discussion-new__actions{padding:0 8px}.discussion-new__actions .md-button:first-of-type{margin-left:0}.discussion-new__actions .md-button:last-of-type{margin-right:0}.discussion-new__attachment{padding:0;margin:0 0 8px}.discussion-new__attachment__content{margin-top:0;margin-bottom:16px}.discussion-comments{padding:0}.discussion-comments__contents{background-color:#f7f7f7}.discussion-comments__header{background-color:transparent;padding:0}.discussion-comments__header .md-subheader-inner{padding-bottom:8px}.discussion-comments__list{padding:0;max-height:9999px;overflow-y:auto}@media only screen and (min-width:600px){.discussion-comments__list{max-height:400px}}.input--textarea.discussion-reply__input,.input-container textarea.input--textarea.discussion-reply__input{background-color:#fff;padding:4px;font-size:14px;border:0;margin-left:-1px;margin-bottom:0;resize:none}.discussion-reply,md-list-item.discussion-reply{margin:0 12px 8px;padding:0;min-height:56px}.discusstion-reply__details,md-list-item .md-list-item-text.discusstion-reply__details{margin:8px 0}.discussion-post__user--reply,md-list-item .md-list-item-text h3.discussion-post__user--reply{font-size:14px;padding:0;margin:0}.discusstion-reply__content{margin-top:2px}.discusstion-reply__content p{font-weight:400!important;color:rgba(0,0,0,.87)!important}.discussion-new-reply{padding:8px}.discussion-new-reply__input-container{padding-top:0;margin:0}.discussion-new-reply__input-container .md-errors-spacer{display:none}.discussion-new-reply__actions{margin-left:8px}.discussion-new-reply__actions .md-button{margin-top:0;margin-bottom:0}.embedded-content__iframe{border:0}.graph-select{min-width:150px;max-width:200px}.graph-controls{margin:8px 0;padding:8px 0;border-color:#eee;border-style:solid;border-width:1px 0}.match-content{background-color:#eee;margin-bottom:16px;padding:8px;box-shadow:inset 0 0 3px #aaa}.match-divider{margin:16px 8px 8px}@media only screen and (min-width:960px){.match-divider--horizontal{display:none}}.match-bucket__header{padding:12px;font-weight:500;color:#7e57c2}.match-bucket__content{padding:0}.match-bucket--choices .match-bucket__header{color:#795c3a}.match-bucket__contents{min-height:120px;padding:0 8px 8px;background-color:#ddd;transition:background-color .25s;border-bottom-left-radius:4px;border-bottom-right-radius:4px;-moz-column-gap:8px;column-gap:8px}@media only screen and (max-width:599px){.match-bucket__contents{-moz-column-count:1!important;column-count:1!important}}.match-bucket__contents img{max-width:100%;height:auto}.match-bucket__contents--over{background-color:#7e57c2}.match-bucket__item{list-style-type:none;cursor:move;padding-top:8px;-moz-column-break-inside:avoid;break-inside:avoid}.match-bucket__item__contents{background-color:#fff;padding:8px!important;border:1px solid #ccc}.match-bucket__item__contents .md-list-item-text{width:100%}.match-bucket__item__contents__text{margin-right:4px}.match-feedback{transition:opacity .25s;margin:8px -8px -8px;color:#fff;padding:4px 8px}.match-feedback.ng-hide{opacity:0;transition:opacity 1ms}.match-feedback md-icon{color:#fff}.outside-content iframe{border:1px solid #eee}.outside-content__source{margin-top:4px;text-align:end}.outside-content__source a{max-width:240px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;display:inline-block}.notebook-toolbar md-divider{margin:8px 0}@media only screen and (max-width:959px){.notebook-toolbar{border-top:1px solid #ddd}}.notebook-toolbar__add-menu{position:absolute;bottom:40px}.notebook-toolbar__add-menu .md-fab-action-item{background-color:#fff}.notebook-toolbar__add-icon{border-radius:50%}#closeNotebookSettingsButton{float:right}[dir=rtl] #closeNotebookSettingsButton{float:left}highchart{display:block} /*# sourceMappingURL=author.css.map */ diff --git a/src/main/webapp/wise5/themes/default/style/author.css.map b/src/main/webapp/wise5/themes/default/style/author.css.map index 10e047f626..80d6d468ec 100644 --- a/src/main/webapp/wise5/themes/default/style/author.css.map +++ b/src/main/webapp/wise5/themes/default/style/author.css.map @@ -1 +1 @@ -{"version":3,"sources":["src/main/webapp/wise5/themes/default/style/base/_presets.scss","src/main/webapp/wise5/themes/default/style/base/_config--author.scss","src/main/webapp/wise5/themes/default/style/base/_config.scss","src/main/webapp/wise5/themes/default/style/base/_helpers.scss","src/main/webapp/wise5/themes/default/style/layouts/_l-default.scss","src/main/webapp/wise5/themes/default/style/material/_config.scss","src/main/webapp/wise5/themes/default/style/layouts/_l-footer.scss","src/main/webapp/wise5/themes/default/style/layouts/_l-header.scss","src/main/webapp/wise5/themes/default/style/layouts/_l-main.scss","src/main/webapp/wise5/themes/default/style/author.css","src/main/webapp/wise5/themes/default/style/layouts/_l-nav.scss","src/main/webapp/wise5/themes/default/style/layouts/_l-node.scss","src/main/webapp/wise5/themes/default/style/layouts/_l-notebook.scss","src/main/webapp/wise5/themes/default/style/layouts/_l-sidebar.scss","src/main/webapp/wise5/themes/default/style/modules/_alerts.scss","src/main/webapp/wise5/themes/default/style/modules/_dialog.scss","src/main/webapp/wise5/themes/default/style/modules/_help.scss","src/main/webapp/wise5/themes/default/style/modules/_inputs.scss","src/main/webapp/wise5/themes/default/style/modules/_table.scss","src/main/webapp/wise5/themes/default/style/modules/_toolbar.scss","src/main/webapp/wise5/themes/default/style/modules/_list.scss","src/main/webapp/wise5/themes/default/style/modules/_nav.scss","src/main/webapp/wise5/themes/default/style/modules/_notice.scss","src/main/webapp/wise5/themes/default/style/modules/_menu.scss","src/main/webapp/wise5/themes/default/style/modules/_node.scss","src/main/webapp/wise5/themes/default/style/modules/_notebook.scss","src/main/webapp/wise5/themes/default/style/modules/_notifications.scss","src/main/webapp/wise5/themes/default/style/modules/_account-menu.scss","src/main/webapp/wise5/themes/default/style/modules/_annotations.scss","src/main/webapp/wise5/themes/default/style/modules/_component.scss","src/main/webapp/wise5/themes/default/style/modules/_component--discussion.scss","src/main/webapp/wise5/themes/default/style/modules/_component--embedded.scss","src/main/webapp/wise5/themes/default/style/modules/_component--graph.scss","src/main/webapp/wise5/themes/default/style/modules/_component--match.scss","src/main/webapp/wise5/themes/default/style/modules/_component--outside.scss","src/main/webapp/wise5/themes/default/style/modules/_notebook-toolbar.scss","src/main/webapp/wise5/themes/default/style/modules/_highcharts.scss"],"names":[],"mappings":"AAIA,KACE,eCSuB,CDVzB,SAIM,eAAgB,CAItB,gBAEQ,aCbgB,CDiBxB,WACE,qBAAgD,CAChD,WAAY,CACZ,aAAc,CAGd,oBAAuB,CAAvB,sBAAuB,CAGzB,qBAEQ,UAAW,CACX,iBAAkB,CAClB,iBAAkB,CAClB,WAAY,CACZ,wBC3BW,CD+BnB,kCAEQ,QAAS,CACT,QAAS,CAKjB,OACI,iBEQoB,CFPpB,eAAgB,CAChB,cGlC8B,CHmC9B,eAAgB,CAChB,iBAAkB,CAClB,qBClCkB,CD4BtB,iBASQ,WAAY,CACZ,YAAa,CACb,mBAAoB,CAX5B,8CAcY,qBC1CU,CD4BtB,uBAkBY,uBC9CU,CDmDtB,aACI,wBC3Da,CD4Db,UAAc,CAGlB,aACI,wBCjEa,CDkEb,UAAc,CAGlB,gBACI,wBCpEgB,CDqEhB,UAAc,CAIlB,qBACI,aAAc,CAGlB,iBACI,uBAAwB,CAI5B,EACE,aC7FsB,CD8FtB,cAAe,CAGjB,QACI,kCAAuC,CACvC,qBChFyB,CDoF7B,QACE,iBAAkB,CAClB,sBAAuB,CAGzB,gBACE,iBExDsB,CF4DxB,cAEI,WAAqC,CACrC,UAAoC,CAHxC,cAMI,WAAqC,CACrC,UAAoC,CAPxC,cAUI,WAAqC,CACrC,UAAoC,CAXxC,cAcI,WAAqC,CACrC,UAAoC,CAKxC,cACE,qBCxHqB,CDyHrB,4BAA8B,CAFhC,8BAKM,WA1ImB,CAqIzB,oBASI,WAAY,CACZ,UAAW,CAVf,oBAaI,WAAY,CACZ,UAAW,CAdf,oBAiBI,WAAY,CACZ,UAAW,CAlBf,oBAqBI,WAAY,CACZ,UAAW,CAIf,wDAEI,qBC7ImC,CD2IvC,4EAOM,qBCjJgC,CDuJtC,mDAEI,uBAAkC,CAFtC,mDAKI,uBAAkC,CALtC,6CAQI,uBAA+B,CARnC,6CAWI,uBAA+B,CAXnC,iDAcI,uBAAiC,CAdrC,qDAiBI,uBAAmC,CAjBvC,qDAoBI,uBAAmC,CAKvC,uCACE,qBCnL2B,CDsL7B,uFACE,qBCzM0C,CD4M5C,2HAEE,aC/MsB,CDgNtB,qBC/M0C,CDqNxC,SACE,aCvNkB,CDsNpB,QACE,aClNa,CDiNf,UACE,aCjNe,CDgNjB,UACE,aChNe,CD+MjB,MACE,aC/MW,CD8Mb,MACE,aC9MW,CD6Mb,SACE,aC7Mc,CD4MhB,SACE,qBC5M0B,CD2M5B,eACE,aC3MoB,CD0MtB,cACE,UC1MmB,CDyMrB,YACE,UCzMiB,CDwMnB,MACE,UCxMW,CDuMb,WACE,UCvMgB,CDsMlB,aACE,aCtMkB,CDqMpB,cACE,UCrMmB,CDoMrB,MACE,qBCpMuB,CDmMzB,gBACE,qBCnMiC,CDkMnC,eACE,qBClMgC,CDiMlC,YACE,UCjMgC,CDgMlC,sBACE,wBChM6C,CD+L/C,qBACE,wBC/L4C,CD8L9C,aACE,UCtNsC,CDqNxC,OACE,aC7LY,CD4Ld,MACE,qBCpMuB,CDmMzB,SACE,UC1MmB,CDgNrB,YACE,wBC9NkB,CD6NpB,WACE,wBCzNa,CDwNf,aACE,wBCxNe,CDuNjB,aACE,wBCvNe,CDsNjB,SACE,wBCtNW,CDqNb,SACE,wBCrNW,CDoNb,YACE,wBCpNc,CDmNhB,YACE,gCCnN0B,CDkN5B,kBACE,wBClNoB,CDiNtB,iBACE,qBCjNmB,CDgNrB,eACE,qBChNiB,CD+MnB,SACE,qBC/MW,CD8Mb,cACE,qBC9MgB,CD6MlB,gBACE,wBC7MkB,CD4MpB,iBACE,qBC5MmB,CD2MrB,SACE,gCC3MuB,CD0MzB,mBACE,gCC1MiC,CDyMnC,kBACE,gCCzMgC,CDwMlC,eACE,qBCxMgC,CDuMlC,yBACE,mCCvM6C,CDsM/C,wBACE,mCCtM4C,CDqM9C,gBACE,qBC7NsC,CD4NxC,UACE,wBCpMY,CDmMd,SACE,gCC3MuB,CD0MzB,YACE,qBCjNmB,CDuNrB,kCAEQ,cCtOY,CDoOpB,iCAEQ,cCjOO,CD+Nf,mCAEQ,cChOS,CD8NjB,mCAEQ,cC/NS,CD6NjB,+BAEQ,cC9NK,CD4Nb,+BAEQ,cC7NK,CD2Nb,kCAEQ,cC5NQ,CD0NhB,kCAEQ,sBC3NoB,CDyN5B,wCAEQ,cC1Nc,CDwNtB,uCAEQ,WCzNa,CDuNrB,qCAEQ,WCxNW,CDsNnB,+BAEQ,WCvNK,CDqNb,oCAEQ,WCtNU,CDoNlB,sCAEQ,cCrNY,CDmNpB,uCAEQ,WCpNa,CDkNrB,+BAEQ,sBCnNiB,CDiNzB,yCAEQ,sBClN2B,CDgNnC,wCAEQ,sBCjN0B,CD+MlC,qCAEQ,WChN0B,CD8MlC,+CAEQ,yBC/MuC,CD6M/C,8CAEQ,yBC9MsC,CD4M9C,sCAEQ,WCrOgC,CDmOxC,gCAEQ,cC5MM,CD0Md,+BAEQ,sBCnNiB,CDiNzB,kCAEQ,WCzNa,CGXzB,eACE,gBAAiB,CACjB,iBAAkB,CAClB,cAAe,CACf,iBAAkB,CAElB,yBANF,eAOM,YCW2B,CDThC,CAED,kBACE,WCK8B,CDJ9B,cAAe,CEbjB,UACE,cAAe,CACf,QAAS,CACT,MAAO,CACP,OAAQ,CACR,SAAU,CACV,qBAAyB,CACzB,yBLIuB,CKAzB,gBACE,QAAS,CACT,aAAc,CACd,gBAAiB,CACjB,WAAY,CACZ,YAAa,CAGf,yBACE,gBAAiB,CCpBnB,UACI,SAAU,CADd,gBAIQ,uBAAyB,CACzB,WAAY,CACZ,UAAW,CACX,qBAAsB,CAP9B,qBAWQ,cAAe,CACf,YAAa,CACb,aAAc,CACd,iBAAkB,CAElB,yCAhBR,qBAiBY,aAAc,CAMrB,CAvBL,sDAqBY,QAAc,CAKtB,yCA1BJ,6FA6BgB,cJlBkB,CImBrB,CC9Bb,QACE,qBPUuB,COPzB,sBACE,eNmCwB,CMhC1B,SACE,yBAA2B,CAG7B,cACE,aAAc,CACd,WAAY,CACZ,iBAAkB,CAClB,MAAO,CACP,OAAQ,CACR,sBAAyB,CAEzB,yCARF,cASI,YAAa,CAuBhB,CAhCD,uBAaI,SAAU,CAbd,+BAiBI,SAAU,CACV,qBAAuB,CAlB3B,gLA8BI,SAAU,CAId,6BACE,WAAY,CAEZ,yCAHF,6BAII,gBAAiB,CACjB,YAAa,CAEhB,CAEC,yCCwZA,uCDvZE,gBAAiB,CACjB,iBAAkB,CAErB,CAED,cACE,YAAa,CADf,mDAMI,eAAgB,CAChB,YAAa,CACb,eAAgB,CAChB,cL3D8B,CK6D9B,yCAXJ,mDAYM,cL9D4B,CK+D5B,iBAAkB,CAErB,CAID,yCADF,oBAEI,cAAe,CAElB,CAED,0CAEE,YAAa,CAFf,kEAKI,gBAAiB,CAEjB,yCAPJ,kEAQM,aAAc,CACd,cAAe,CAElB,CAXH,0DAcI,0BAA2B,CAI/B,2FAEE,gBAAiB,CEzGnB,OACI,+BAA6C,CCDjD,QACE,eTuCwB,CStCxB,SAAU,CAEV,yCAJF,QAKI,gBAAiB,CACjB,+BAA6C,CAMhD,CAHC,yCATF,QAUI,gBAAiB,CAEpB,CCZD,YACI,eVuCsB,CUtCtB,+BAA6C,CCEjD,mBACE,+BAAoC,CACpC,uBAAmC,CAFrC,6BAKI,qBZQyB,CapB7B,aACI,YAAa,CACb,SAAU,CACV,qBAAsB,CAG1B,uBACI,WAAY,CACZ,UAAW,CACX,YAAa,CACb,mBAAoB,CACpB,YAAa,CACb,SAAU,CAGd,uBACI,iBAAkB,CAClB,SAAU,CACV,eAAgB,CAGpB,eACI,OAAQ,CACR,QAAS,CACT,gCbFkC,CaGlC,mCbHkC,CaIlC,qBbJkC,CaDtC,qBAQQ,WAAY,CACZ,UAAc,CACd,OAAQ,CACR,UAAW,CACX,iBAAkB,CAClB,eAAgB,CAIxB,qBACI,kCAA0C,CAC1C,qCAA6C,CAGjD,yBACI,KAAM,CACN,OAAQ,CACR,2BZQoB,CYXxB,wCAMQ,qBAAsB,CACtB,kCAAmC,CAI3C,wBACI,KAAM,CACN,MAAO,CACP,0BZHoB,CYAxB,uCAMQ,qBAAsB,CACtB,mCAAoC,CAI5C,4BACI,QAAS,CACT,OAAQ,CACR,8BZdoB,CYWxB,2CAMQ,wBAAyB,CACzB,kCAAmC,CAI3C,2BACI,QAAS,CACT,MAAO,CACP,6BZzBoB,CYsBxB,0CAMQ,wBAAyB,CACzB,mCAAoC,CAI5C,qBACI,qBAAyB,CAG7B,2BACI,cAAe,CACf,oBAAqB,CC7FzB,UACI,WVkB4B,CUfhC,cACI,WVe4B,CUZhC,eACI,YVY6B,CWrBjC,aACI,iBdqDoB,CcpDpB,eAAgB,CAEhB,yBAJJ,aAKQ,eAAuC,CAU9C,CAPG,yBARJ,aASQ,eAAuC,CAM9C,CAHG,0BAZJ,aAaQ,gBAAuC,CAE9C,CAOD,kDAJI,0BdoCoB,CcnCpB,2BfTa,CeYjB,8BAGI,kBAAqB,CACrB,wBfhBa,CeYjB,8CAOQ,cAAe,CACf,aAAc,CACd,gBAAiB,CAIzB,sBACI,aAAc,CACd,gBAAiB,CACjB,gBAAiB,CAGrB,sBACI,6BdYoB,CcXpB,8BdWoB,CcRxB,qBACI,iBdOoB,CcLpB,QAAc,CACd,4CAAiD,CACjD,cbrC8B,CasC9B,SAAU,CANd,uDASQ,iBAAkB,CAClB,UAAW,CACX,WAAY,CAXpB,0DAgBY,KAAM,CACN,SAAU,CAjBtB,kFAoBgB,gCfxDC,CeyDD,kCAAmC,CACnC,mCAAoC,CACpC,iBAAkB,CAClB,SAAU,CAxB1B,yFA4BgB,QAGuC,CA/BvD,4DAoCY,YAAa,CACb,SAAU,CArCtB,oFAwCgB,6Bf5EC,Ce6ED,kCAAmC,CACnC,mCAAoC,CACpC,iBAAkB,CAClB,SAAU,CA5C1B,2FAgDgB,QAGuC,CAnDvD,4DAwDY,QAAS,CACT,UAAW,CAzDvB,oFA4DgB,+BfhGC,CeiGD,oCAAqC,CACrC,iCAAkC,CAClC,iBAAkB,CAClB,MAAO,CACP,SAAU,CAjE1B,2FAqEgB,QAGqC,CAxErD,6DA6EY,QAAS,CACT,WAAY,CA9ExB,qFAiFgB,8BfrHC,CesHD,oCAAqC,CACrC,iCAAkC,CAClC,iBAAkB,CAClB,OAAQ,CACR,SAAU,CAtF1B,4FA0FgB,QAGqC,CCrIrD,iBACI,gBAAiB,CAGrB,4BACI,eAAgB,CAGpB,4CAEQ,cAAe,CAFvB,kDAMQ,YAAa,CAIrB,eACI,iBAAkB,CAGtB,yDAEQ,ahB7BgB,CgBiCxB,2DACI,WAAY,CACZ,wBhBvBsB,CgBwBtB,qBhBrBa,CgBsBb,iBAAkB,CAJtB,uEAOQ,qBAAyB,CAPjC,+EAWQ,qBhBxB+B,CgB4BvC,0CACI,UAAW,CAGf,2BACI,qBhBjCmC,CgBoCvC,yBACI,iBAAkB,CAClB,UAAW,CAFf,2CAKQ,+BAAwC,CAIhD,mCACI,OAjE8B,CAmE9B,4DACI,QAnEoC,CAuE5C,mCACI,UAzE8B,CA2E9B,4DACI,WAAsD,CAK9D,mHACI,eAAgB,CAChB,qBhBjEyB,CgB+D7B,6JAKQ,ahBvFgB,CgB2FxB,oBAEQ,sBAAuB,CACvB,eAAgB,CAChB,cAAe,CACf,eAAgB,CAChB,qBhB7E+B,CgBkFnC,yCADJ,wBAEQ,eAAgB,CAMvB,CAHG,yCALJ,wBAMQ,eAAgB,CAEvB,CAED,yCAEQ,qBAAyB,CAFjC,+DAKY,eAAgB,CAChB,qBhBxGa,CgB6GzB,gBACI,Wf5EiC,Ce2ErC,sBAIQ,WAAY,CACZ,UAAW,CACX,aAAc,CACd,YAAa,CACb,QAAc,CACd,cdtH0B,CcuH1B,eAAgB,CCrIxB,OACE,cAAe,CACf,UAAW,CACX,eAAgB,CAChB,YAAa,CAJf,kHAYM,qBjBIW,CiBHX,WAAY,CACZ,cfA4B,CeC5B,eAAgB,CAChB,WAAY,CACZ,cAAe,CACf,kBAAmB,CAlBzB,6BAwBI,wBjBXsB,CiBYtB,SAAU,CACV,kBAAmB,CA1BvB,0BA8BI,QAAS,CA9Bb,yBAkCI,YAAa,CAIjB,4BAGM,gBAAiB,CAKvB,mBACE,UAAW,CAGb,aACE,QAAc,CACd,wBAAyB,CACzB,qBAAyB,CACzB,cAAe,CACf,aAAc,CALhB,gCASI,aAAc,CACd,QAAc,CAVlB,gBAcI,eAAgB,CAChB,WAAY,CAfhB,0BAoBM,iBAAkB,CAClB,eAAgB,CAChB,UAAW,CACX,mBAAoB,CACpB,iBAAkB,CAClB,eAAmB,CAKzB,mBACE,eb9D8B,CakE9B,yCADF,mBAEI,eAAgB,CAEnB,CAED,oBACE,cf7EgC,Ce8EhC,eAAgB,CAGlB,wBACE,WAAY,CACZ,QAAS,CAGX,wBACE,wBjBnFsB,CiBoFtB,UjB/EoC,CiBgFpC,ehB5DwB,CgB6DxB,WhB7DwB,CgBgE1B,0BACE,UAAc,CACd,mBAAoB,CACpB,QAAS,CACT,WAAY,CACZ,kBAAmB,CACnB,eAAgB,CAChB,UAAW,CAGb,0BACE,QAAS,CAGX,mCACE,wBAAyB,CAG3B,UACE,eAAgB,CAChB,kBAAmB,CACnB,eAAgB,CAIhB,yCADF,eAEI,eAAgB,CAChB,eAAgB,CAChB,sBAAuB,CACvB,kBAAmB,CAEtB,CC1ID,kBACI,cAAe,CADnB,2HAWY,WAAY,CAKxB,kBACI,ejB0BsB,CiB3B1B,oCAIQ,WjBuBkB,CiBtBlB,ejBsBkB,CiB3B1B,4CASQ,WjBkBkB,CiBjBlB,gBjBiBkB,CiBhBlB,UjBgBkB,CiBZ1B,wCAEQ,aAAc,CACd,chBpB0B,CgBwBlC,qBACI,+BAAkD,CADtD,uCAIQ,chB5B0B,CgB6B1B,eAAgB,CAIxB,SACI,cAAe,CACf,MAAO,CACP,OAAQ,CACR,Qd5CoB,Cc6CpB,SAAU,CAGd,gBACI,eAAgB,CAChB,chB3C8B,CgB4C9B,eAAgB,CAGpB,gBACI,iBAAkB,CV+3BtB,0BU53BI,kBAAmB,CACnB,gBAAiB,CAGrB,sCACI,QAAS,CAGb,4CACI,YAAa,CACb,eAAgB,CAChB,wBlB/DsB,CkB4D1B,8EAMQ,WAAY,CACZ,eAAgB,CV43BxB,kGUt3BU,gBAAiB,CAK3B,6BACI,WAAY,CAEZ,yCAHJ,6BAIQ,WAAY,CAMnB,CAHG,yCAPJ,6BAQQ,WAAY,CAEnB,CCrGD,WACI,qBAAyB,CACzB,4BnBYqB,CmBdzB,iDAKQ,qBnBeqB,CmBdrB,qBAAyB,CANjC,iEASY,qBAAsB,CATlC,yFAaY,SAAU,CAbtB,uEAiBY,gBAAiB,CAjB7B,yBAsBQ,YAAa,CAIrB,kGAEQ,uCAA+C,CAC/C,gBAAiB,CAIzB,kGAIQ,uCAA+C,CAC/C,gBAAiB,CAIzB,qBACI,qBAAsB,CAG1B,kDACI,cAAe,CACf,wBnBnCsB,CmBsC1B,oBACI,uBAAyB,CAG7B,6BACI,mBAAoB,CACpB,UAAW,CACX,gBAAiB,CACjB,QAAS,CACT,kBAAmB,CACnB,eAAgB,CAChB,eAAgB,CAGpB,WACI,cjBpD8B,CkBdlC,KACI,iBAAkB,CAGtB,KACI,kBAAmB,CAGvB,UACI,iBAAkB,CAClB,KAAM,CACN,QAAS,CACT,MAAO,CACP,OAAQ,CACR,gCAAkC,CAClC,SAAU,CAPd,kBAUQ,SAAU,CAIlB,UACI,qBpBFmC,CoBGnC,eAAgB,CAFpB,kBAKQ,gBAAiB,CAIzB,oBACI,oBAAqB,CAGzB,qBACI,qBpBrBmB,CoBsBnB,WAAY,CAOhB,wCACI,SAAU,CAEV,yBAHJ,oBAIQ,WAAY,CAEnB,CAED,UACI,mCAAqC,CADzC,4BAKY,qBAKG,CAKf,yBACI,2BnBdoB,CmBepB,0BnBfoB,CmBaxB,+BAKQ,YAAa,CALrB,qDAQY,6BpB3DK,CoBgEjB,gBACI,qCAA0C,CAD9C,yBAIQ,eAAgB,CAEhB,cAAe,CACf,yBAA2B,CAC3B,eAAgB,CAChB,gBAAiB,CATzB,uCAYY,YAAa,CAYzB,2BACI,oBAAqB,CACrB,oBAAqB,CAGzB,yBACI,eAAgB,CAChB,qBpBzFkC,CoBgGtC,sCAEQ,4IACsF,CAI9F,oBACI,QAAS,CAGb,gBACI,yBpBnHmB,CoBoHnB,8BnB7EoB,CmB8EpB,6BnB9EoB,CmB+EpB,gBAAiB,CACjB,eAAgB,CAGpB,iBACI,WAAY,CACZ,cAAe,CACf,UAAc,CACd,QAAS,CACT,eAAgB,CALpB,yBAQQ,iBAAkB,CAClB,UAAc,CATtB,oEAcY,wBpB5IQ,CoBiJpB,iBACI,iBAAkB,CAClB,eAAgB,CAChB,eAAgB,CZ+8BpB,2BY58BI,iBAAkB,CAClB,kBAAmB,CAGvB,gBACI,aAAc,CAGlB,oBACI,UAAW,CADf,kCAIQ,KAAM,CAId,0BACI,eAAgB,CAChB,UAAW,CAGf,kBACI,aAAc,CACd,cAAe,CCzLnB,QACE,iBAAkB,CAClB,WAAY,CACZ,gCAAkC,CAClC,UAAW,CAEX,yBANF,QAOI,aAAc,CACd,iBpBiDsB,CoBhDtB,gBAAiB,CAEpB,CCPD,eACI,iBAAkB,CAClB,QAAS,CACT,UAAW,CAHf,oBAMQ,wBAAoC,CACpC,gBAAiB,Cd0oCzB,yBctoCE,UAAU,CACV,SAAS,CAOX,oBACI,eAAgB,CAEhB,cpBZ8B,CoBelC,oBACI,yBAA2B,CAC3B,2BAA6B,CAC7B,gBAAiB,CAGrB,wDAEQ,atBpCgB,CsBwCxB,cACI,iBAAkB,CAClB,QAA8C,CAC9C,QAAS,CACT,MAAO,CACP,qBAAsB,CACtB,UA9CqB,CA+CrB,eAAgB,CAChB,aAAc,CACd,iBAAkB,CAClB,2BtBnCa,CsBqCb,yCAZJ,cAaQ,YAAa,CAEpB,Cd4nCD,wBc1nCE,OAAO,CACP,SAAS,CAGX,6CACI,cAAe,CACf,iBAAkB,CC1DtB,MACI,aAAc,CACd,iBAAkB,CAClB,MAAO,CACP,OAAQ,CAER,yCANJ,MAQQ,iBAAkB,CAClB,kBAAmB,CAe1B,CAZG,yCAZJ,MAaQ,YAAa,CAWpB,CAxBD,eAiBQ,sBAAuB,CACvB,SAAU,CAlBlB,sBAsBQ,SAAU,CAOd,yCADJ,aAEQ,eAAgB,CAChB,kBAAmB,CAM1B,CAHG,yCANJ,aAOQ,gBAAiB,CAExB,CAED,cACI,gBAAiB,CACjB,qBAAyB,CACzB,iBtBSsB,CsBRtB,gBAAiB,CAEjB,yCANJ,cAOQ,eAAgB,CAQvB,CALG,yCAVJ,cAWQ,SAAU,CACV,oBAAqB,CACrB,uBAAwB,CAE/B,CAED,wBAEQ,qBAAyB,CAIjC,sBACI,iBAAkB,CAClB,SAAU,CACV,MAAO,CACP,OAAQ,CACR,SAAU,CALd,oCAQQ,oBAAsB,CAEtB,yCAVR,oCAWY,mBAAqB,CAE5B,CAGL,WACI,UAAc,CACd,sBAAuB,CAG3B,aACI,YAAa,CACb,WAAY,CACZ,eAAgB,CAChB,crB/E8B,CqB2ElC,2CAQY,uBAA6B,CAC7B,UAAW,CATvB,oGAiBY,YAAa,CAjBzB,6BAsBQ,aAAc,CACd,qBvB5FqB,CuBgG7B,2BAEI,wBvBzGsB,CuB0GtB,4BvBzGqB,CuB0GrB,yBvB1GqB,CuB6GzB,0BACI,iBAAkB,CAGtB,mBACI,gBAAiB,CAGrB,qBACI,eAAgB,CAGpB,mBACI,sBAAuB,CACvB,kBAAmB,CACnB,eAAgB,CAEhB,yCALJ,mBAMQ,cAAe,CAEtB,CAED,YACI,eAAgB,CAChB,mBAAoB,CACpB,cAAe,CAEf,yCALJ,YAMQ,crBzI0B,CqB2IjC,CAED,uBACI,mBAAoB,CAEpB,yCAHJ,uBAIQ,mBAAoB,CAc3B,CAXG,0CAPJ,uBAQQ,mBAAoB,CAU3B,CAlBD,8CAYQ,aAAc,CAZtB,6CAgBQ,cAAe,CAIvB,6BACI,iBAAkB,CAClB,eAAgB,CAChB,qBvB7JmC,CuBgKvC,6BACI,wBAAyB,CAG7B,iDAKQ,cAAe,CAKnB,yCADJ,qBAEQ,iBAAuC,CAE9C,CAGG,yCADJ,sBAEQ,kBtB/JkB,CsBiKzB,CAED,cACI,iBAAkB,CAClB,OAAQ,CACR,KAAM,CACN,UnB3MoB,CmB8MxB,uBACI,cAAe,CACf,UnBhNoB,CmBiNpB,qBAAyB,CACzB,aAAc,CACd,iBtBjKsB,CsBmKtB,yCAPJ,uBAQQ,OAAQ,CACR,QAAS,CACT,MAAO,CACP,UAAW,CACX,eAAgB,CAChB,SAAU,CACV,YAAa,CACb,WtBzLkB,CsB2LzB,CAID,WACI,QAAS,CAET,yCAHJ,WAIQ,YAAa,CACb,eAAgB,CAsBvB,CA3BD,oBASQ,gBAAiB,CACjB,iBAAkB,CAV1B,kCAeY,gBAAiB,CAf7B,mFAoBQ,gBAAiB,CACjB,iBAAkB,CArB1B,+BAyBQ,YAAa,CAIrB,aACI,wBvBvQoB,CuBwQpB,oBAAqB,CACrB,YAAa,CACb,qBAAyB,CAO7B,iCACI,qBAAsB,CACtB,oBAAqB,CC/QrB,yCADJ,oCAGY,UAAW,CACd,CAIT,iBACI,yBAA0B,CAC1B,QAAS,CACT,WAAY,CAHhB,+BAMQ,oBAAqB,CACrB,WAAY,CACZ,eAAc,CAAd,YAAc,CAEd,8BxBPe,CwBOf,uBxBPe,CwBOf,kBxBPe,CwBQf,eAAgB,CAXxB,0CAcY,YAAa,CAdzB,2BAmBQ,QAAc,CACd,wBAAyB,CACzB,yBAA0B,CAC1B,2BAA4B,CAC5B,4BAA6B,CAIrC,2BACI,YAAa,CACb,WAAY,CACZ,cAAe,CACf,QAAS,CACT,UAAW,CACX,iBAAkB,CAClB,SAAU,CAIV,yCADJ,0CAGY,WAAY,CACZ,qBAAuB,CAC1B,CAIT,iCACI,QAAS,CACT,WAAY,CACZ,SAAU,CACV,UAAW,CACX,eAAgB,CAChB,cAAe,CACf,WAAY,CACZ,UAAW,CARf,kDAWQ,WAAY,CACZ,UAAW,CACX,aAAc,CACd,eAAgB,CAChB,iBvBnBgB,CuBuBxB,sCACI,WAAY,CACZ,WAAY,CAFhB,wMAKQ,YAAa,CAIrB,0BACI,+BAAkD,CAClD,yBAA0D,CAG9D,iCACI,eAAgB,CAGpB,0BAKI,qBAAyB,CAL7B,oHAEQ,ctBnF0B,CsBiFlC,kDAQQ,QAAc,CACd,eAAgB,CAChB,SAAU,CACV,eAAgB,CAXxB,0CAeQ,YAAa,CAIrB,kCACI,WAAY,CACZ,qBAAyB,CACzB,ctBvG8B,CsB0GlC,yCACI,sEAEQ,KAAM,CACN,QAAS,CACT,MAAO,CACP,OAAQ,CACR,eAAgB,CAChB,cAAe,CACf,WAAY,CACZ,UAAW,CATnB,uFAYY,eAAgB,CAK5B,sBACI,YAAa,CAChB,CAGL,0BACI,cAAe,CACf,KAAM,CACN,MAAO,CACP,UAAW,CACX,WAAY,CACZ,SAAU,CACV,wBAAoC,CACpC,WAAY,CAGhB,eACI,sBAAyB,CAD7B,wBAIQ,SAAU,CAJlB,gCAQQ,SAAU,CACV,qBAAuB,CAT/B,sLAkBQ,SAAU,CAIlB,eACI,0BAA4B,CAC5B,kBAAmB,CACnB,aAAc,CAGlB,wBACI,YAAa,CACb,eAAgB,CAChB,iBAAkB,CAClB,SAAU,CACV,qBxB3Ka,CwB4Kb,0BvBtIoB,CuBuIpB,2BvBvIoB,CuB0IxB,kEACI,iBAAkB,CAClB,MAAO,CACP,OAAQ,CAGZ,oCACI,qCAAuC,CACvC,0BvBlJoB,CuBmJpB,2BvBnJoB,CuBoJpB,iCAA0C,CAC1C,+BAAiC,CACjC,KAAM,CACN,QAAS,CAGb,8BACI,QAAS,CACT,WAAY,CACZ,eAAgB,CAChB,eAAgB,CAChB,gBAAiB,CACjB,eAAgB,CAChB,oCAAwC,CACxC,yBxB1MqB,CwBkMzB,oCAWQ,UAAW,CACX,gBAAiB,CACjB,iBAAkB,CAClB,QAAS,CACT,OAAQ,CACR,UAAW,CACX,WAAa,CACb,6EAAiF,CAIzF,yCAEQ,cAAe,CACf,0BAA6B,CAC7B,eAAmB,CACnB,iBAAkB,CAClB,oBAAqB,CACrB,aAAc,CACd,mBAAoB,CACpB,qBAAsB,CACtB,gBAAiB,CACjB,kBAAmB,CACnB,aAAc,CAGd,kCAAmC,CAEnC,iCAAkC,CAGlC,iCAAkC,CAGlC,4BAA6B,CAE7B,cAAe,CACf,qBxB1O8B,CwB8OtC,6CACI,mBAAoB,CAGxB,kCACI,UAAY,CACZ,aAAc,CAFlB,0CAKQ,cAAe,CAIvB,qBACI,cAAe,CAGnB,wBACI,QAAS,CACT,aAAc,CACd,UAAc,CACd,qBxBtQqB,CwBuQrB,6BvBpOoB,CuBqOpB,8BvBrOoB,CuB+NxB,gCASQ,UAAc,CAItB,2BACI,QAAS,CAGb,qCACI,cAAe,CACf,eAAgB,CAGpB,2BACI,qBxB7RmB,CwB8RnB,YAAa,CACb,kBAAmB,CACnB,iBAAkB,CAClB,iBAAkB,CAGtB,oCACI,cAAe,CACf,WAAY,CAGhB,mCACI,iBAAkB,CAClB,OAAQ,CACR,UAAW,CAEX,oBAAsB,CACtB,qBAAuB,CACvB,YAAa,CAPjB,2CAUQ,gBAAiB,CACjB,cAAe,CAIvB,qBACI,iBAAkB,CAClB,UAAW,CACX,aAAqC,CAHzC,oDAMQ,aAAqC,CAN7C,6BAUQ,eAAgB,CAChB,WAAY,CACZ,UAAW,CAInB,uBACI,iBAAkB,CAClB,YAAa,CACb,qBxB5UqB,CwB6UrB,kBAAmB,CACnB,qBxBvUmC,CwBwUnC,iBAAkB,CAClB,cAAe,CACf,6BAA8B,CAC9B,mBAAqB,CATzB,2DAYQ,qBAAuB,CAZ/B,0FAgBQ,oBxBjWW,CwBkWX,wBAA+C,CAC/C,axBnWW,CwBiVnB,2NAqBY,axBtWO,CwB2WnB,oBACI,WpB/V4B,CoBqWhC,uBACI,qBAAyB,CAD7B,oCAIQ,kBAAmB,CACnB,iBxB7WS,CwBiXjB,iFAGY,cAAe,CACf,QAAgD,CAChD,MAAO,CACP,OAAQ,CACR,SAAU,CAEV,yCATZ,iFAUgB,SAAmC,CAInC,cAJmC,CAsB1C,CAfG,yCAjBZ,iFAkBgB,cAAe,CActB,CAhCT,+FAsBgB,aAAc,CAEd,yCAxBhB,+FAyBoB,YAAa,CAMpB,CAHG,yCA5BhB,+FA6BoB,aAAc,CAErB,CA/Bb,8DAmCY,gBAAiB,CAK7B,8CAGQ,qBxB7Ze,CwB8Zf,qBxB7ZS,CwB8ZT,kBAAmB,CAI3B,gCACI,iBAAkB,CAClB,kBAAmB,CAMvB,iCACI,eAAgB,CAGpB,iCACI,cAAe,CACf,qBAAuB,CAIvB,yCADJ,kBAEQ,WAAY,CACZ,cAAe,CAOtB,CAJG,yCANJ,kBAOQ,WAAY,CACZ,cAAe,CAEtB,CAED,gBACI,UAAW,CACX,aAAc,CACd,eAAgB,CAChB,kBAAmB,CAJvB,+BAOQ,UAAW,CAPnB,wCAWQ,YAAa,CACb,WAAY,CAIpB,yBACI,eAAgB,CAGpB,yCACI,6EAEQ,qBAA6C,CAChD,CAIT,kBACI,qBAAyB,CACzB,aAAc,CC7elB,kBACI,oBAAsB,CAD1B,0BAIQ,gBAAiB,CAIzB,oBACI,iBAAkB,CAClB,iBAAkB,CAClB,wBzBLe,CyBMf,UAAW,CACX,QAAS,CACT,WAAY,CACZ,gBAAiB,CACjB,cAAe,CACf,eAAgB,CAChB,gBAAiB,CAVrB,2BAaQ,UAAW,CACX,iBAAkB,CAClB,UAAW,CACX,OAAQ,CACR,yCAA6C,CAC7C,gCAAiC,CACjC,mCAAoC,CAI5C,mBACI,aAAc,CAGlB,sBACI,WAAY,CAGhB,6BACI,eAAgB,CAGpB,kPAIQ,qBzB1B+B,CyB2B/B,cvBlC0B,CuB6BlC,0QAQY,cvBrCsB,CuBsCtB,WAAY,CACZ,UAAW,CACX,gBAAiB,CACjB,gBvBzCsB,CwBdlC,cACI,iBzBqDoB,CyBpDpB,SAAU,CACV,cxBW8B,CwBV9B,eAAgB,CAEhB,0BANJ,cAOQ,yBAA2B,CAOlC,CAdD,iBAWQ,QAAS,CACT,eAAgB,CAIxB,4BACI,YzBiCyE,CyB9B7E,2BACI,WAAY,CAEZ,yBAHJ,2BAIQ,WAAY,CAEnB,CAED,oBACI,qB1BNkC,C0BOlC,iBAAkB,CAGtB,qBACI,iBAAkB,CAClB,UAAW,CACX,QAAS,CACT,YAAa,CAJjB,4BAOQ,UAAW,CACX,iBAAkB,CAClB,4BAA6B,CAC7B,iCAAkC,CAClC,kCAAmC,CAI3C,+DACI,UAAW,CAGf,+CACI,WAAY,ClBmwDhB,+BkB9vDI,UAAW,CACX,SAAU,ClBiwDd,mFkB9vDI,SAAS,CACT,UAAU,ClBiwDd,yDkB9vDM,UAAW,CACX,UAAU,CAIhB,oBACI,gBAAiB,CAGrB,2BACI,eAAgB,CAGpB,0BACI,eAAgB,CAChB,qB1B5DmC,C0B+DvC,uDAIQ,SAAU,CAJlB,6CAOY,gBAAiB,CACjB,WAAY,CACZ,UAAW,CAKvB,2CAEQ,wBAAyB,CACzB,UAAW,CAInB,qBACI,iBAAkB,CAClB,gBAAiB,CAFrB,6BAKQ,a1BnFU,C0BuFlB,8BACI,iBAAkB,CAClB,KAAM,CACN,UAAW,CACX,mCAA0C,CAG9C,uBACI,wB1B7GsB,C0BgH1B,uBACI,YAAa,CC9HjB,aACI,yBAA0B,CAC1B,iBAAkB,CAClB,czBW8B,CyBdlC,gBAMQ,iBAAkB,CAClB,4BAA6B,CAPrC,mBAWQ,UAAW,CACX,iBAAkB,CAClB,OAAQ,CACR,QAAS,CACT,UAAW,CACX,UAAW,CACX,KAAQ,CACR,WAAY,CACZ,iCAAkC,CAClC,oCAAqC,CACrC,+B3BHgB,C2BOxB,wCACI,yB3BXmB,C2BcvB,qBACI,YAAa,CACb,eAAgB,CAGpB,qBACI,iBAAkB,CAElB,2BAA4B,CAC5B,iBAAkB,CAClB,eAAgB,CAChB,iBAAkB,CAClB,UAAc,CACd,wB3BxBoB,C2B2BxB,qBACI,wB3BxCe,C2ByCf,WAAY,CACZ,iBAAkB,CAClB,KAAM,CACN,UAAW,CAGf,mBACI,iBAAkB,CAClB,UAAc,CAGlB,mBACI,YAAa,CACb,qBAAyB,CACzB,6B1BPoB,C0BQpB,8B1BRoB,C0BSpB,aAAc,CAGlB,qBACI,qBAAyB,CACzB,a3B1Da,C2B2Db,oBAAqB,CACrB,eAAgB,CAChB,czBzD8B,CyBoDlC,4DAQQ,iBAAkB,CAR1B,4EAYQ,SAAS,CAZjB,4EAgBQ,SAAS,CAIjB,oBACI,eAAgB,CAGpB,mBACI,iBAAkB,CAClB,UAAY,CACZ,wBAAyB,CACzB,czBhF8B,CyBmFlC,kCAEQ,gBAAiB,CAKzB,mBACI,kBAAmB,CACnB,gBAAiB,CACjB,gBAAiB,CAEjB,yCALJ,mBAMQ,0BAA2B,CAclC,CApBD,yBAUQ,+B3BxGS,C2B8FjB,wCAcQ,qBAAyB,CAdjC,wCAkBQ,wB3BhHS,C4BVjB,WACI,iBAAkB,CAGtB,oBACI,cAAe,CACf,aAAc,CAGlB,oBACI,eAAgB,CAChB,c1BG8B,C0BF9B,iBAAkB,CAElB,yCALJ,oBAMQ,aAAc,CAErB,CAED,uCACI,gBAAiB,CACjB,QAAS,CACT,c1BR8B,C0BWlC,mBACI,iBAAkB,CAClB,UAAW,CACX,QAAS,CAGb,yCAGY,mBAAqB,CACrB,cAAe,CACf,WAAY,CALxB,8FAUgB,8B5BnCG,C4B0CnB,2CAEQ,aAAc,CAFtB,0CAMQ,cAAe,CAIvB,0BACI,iBAAkB,CAClB,eAAgB,CAEhB,qB5BzCmC,C4B4CvC,0BACI,wBAAyB,CAG7B,mBACI,iBAAkB,CAClB,eAAgB,CAGpB,4BACI,cAAe,CAGnB,uBACI,iBAAkB,CAClB,YAAa,CACb,kBAAmB,CAEnB,yCALJ,uBAMQ,eAAgB,CAEvB,CAGG,yCADJ,2BAEQ,UAAW,CAElB,CAED,gCACI,gBAAiB,CACjB,UAAW,CAGf,+BACI,iBAAkB,CAClB,KAAM,CACN,OAAQ,CACR,WAAY,CACZ,8CAAsD,CACtD,eAAgB,CAChB,WAAY,CACZ,QAAS,CARb,uCAeQ,YAAa,CAIrB,qBACI,YAAa,CACb,WAAY,CAFhB,oCAKQ,wB5B9GkB,C4BkH1B,8BACI,iBAAoB,CACpB,4B5BlHmB,C4BqHvB,8BACI,a5BnHoB,C4BoHpB,eAAgB,CCnIpB,gCACI,eAAgB,CAGpB,oBACI,qB7BMqB,C6BJrB,6B7BOkB,C6BJtB,kBACI,qBAAsB,CAEtB,0CAHJ,kBAIQ,mBAAoB,CAE3B,CAED,iBACI,kBAAmB,CACnB,ezBJ4B,CyBM5B,yCAJJ,iBAKQ,kBAAmB,CAY1B,CATG,0CARJ,iBASQ,kBAAmB,CAQ1B,CAjBD,4BAcQ,iBAAkB,CAClB,UAAW,CAInB,2BACI,YAAa,CAGjB,yEACI,gBAAiB,CAGrB,uFACI,cAAe,CAInB,gFACI,kBAAmB,CACnB,eAAgB,CAChB,eAAgB,CAChB,gBAAiB,CACjB,kBAAmB,CAGvB,uBACI,U7B7CkB,C6BgDtB,8BACI,eAAgB,CAChB,eAAgB,CAGpB,0BACI,eAAgB,CAChB,oBAAqB,CAGzB,6BACI,cAAe,CACf,qBAAuB,CACvB,eAAgB,CAGpB,gBACI,qBAAyB,CACzB,eAAgB,CAChB,gBAAiB,CACjB,iBAAkB,CAClB,WAAY,CACZ,mBAAqB,CACrB,oBAAsB,CAG1B,yBACI,kBAAmB,CAGvB,mDACI,QAAS,CACT,SAAU,CAFd,qEAKQ,eAAgB,CAIxB,2FACI,WAAY,CACZ,QAAc,CAGlB,yBACI,aAAc,CADlB,kDAKY,aAAc,CAL1B,iDASY,cAAe,CAK3B,4BACI,SAAU,CACV,cAAe,CAGnB,qCACI,YAAa,CACb,kBAAmB,CAGvB,qBACI,SAAU,CAGd,+BACI,wB7B7HsB,C6BgI1B,6BACI,4BAA6B,CAC7B,SAAU,CAFd,iDAKQ,kBAAmB,CAI3B,2BACI,SAAU,CACV,iBAAkB,CAClB,eAAgB,CAEhB,yCALJ,2BAMQ,gBAAiB,CAExB,CAED,2GACI,qBAAyB,CACzB,WAAY,CACZ,cAAqC,CACrC,QAAc,CACd,gBAAiB,CACjB,eAAgB,CAChB,WAAY,CAGhB,gDACI,iBAAkB,CAClB,SAAU,CACV,eAAgB,CAGpB,uFACI,YAAa,CAGjB,8FACI,cAAqC,CACrC,SAAU,CACV,QAAS,CAGb,4BACI,cAAe,CADnB,8BAIQ,yBAA2B,CAC3B,+BAA+B,CAIvC,sBACI,WAAY,CAGhB,uCACI,aAAc,CACd,QAAS,CAFb,yDAKQ,YAAa,CAIrB,+BACI,eAAgB,CADpB,0CAIQ,YAAa,CACb,eAAgB,CCjNxB,0BACI,QAAc,CCLlB,cACI,eAAgB,CAChB,eAAgB,CAGpB,gBACI,YAAa,CACb,aAAc,CAGd,iBAAqB,CAArB,kBAAqB,CAArB,kBAAqB,CCPzB,eACI,qBhCUqB,CgCTrB,kBAAmB,CACnB,WAAY,CACZ,6BhCUkB,CgCPtB,eACI,mBAAoB,CAIpB,yCADJ,2BAEQ,YAAa,CAEpB,CAED,sBACI,YAAa,CACb,eAAgB,CAChB,ahCtBoB,CgCyBxB,uBACI,SAAU,CAGd,6CAEQ,ahCzBa,CgC6BrB,wBACI,gBAAiB,CAEjB,iBAAkB,CAClB,qBhCzBmB,CgC0BnB,gCAAkC,CAClC,6B/BYoB,C+BXpB,8B/BWoB,C+BTpB,mBAAe,CAAf,cAAe,CAEf,yCAXJ,wBAYQ,6BAA0B,CAA1B,wBAA0B,CAOjC,CAnBD,4BAgBQ,cAAe,CACf,WAAY,CAIpB,8BACI,wBhCzDoB,CgC4DxB,oBACI,oBAAqB,CACrB,WAAY,CACZ,eAAgB,CAChB,8BAAmB,CAAnB,kBAAmB,CAOvB,8BACI,qBAAyB,CACzB,qBAAuB,CACvB,qBhC3Da,CgCwDjB,iDAMQ,UAAW,CAInB,oCACI,gBAAiB,CAGrB,gBACI,uBAAyB,CAGzB,oBAAqB,CACrB,UAAc,CACd,eAAgB,CANpB,wBASQ,SAAU,CACV,sBAAuB,CAV/B,wBAcQ,UAAc,CCpGtB,wBAEI,qBjCYqB,CiCRzB,yBACE,cAAe,CACf,cAAe,CAFjB,2BAKI,eAAgB,CAChB,kBAAmB,CACnB,eAAgB,CAChB,sBAAuB,CACvB,oBAAqB,CCfzB,6BAEQ,YAAa,CAGjB,yCALJ,kBAMQ,yBlCSe,CkCPtB,CAED,4BACI,iBAAkB,CAClB,WAAY,CAFhB,gDAKQ,qBAAyB,CAIjC,4BACI,iBAAkB,CAGtB,6BACE,WAAW,C1BoyEb,uC0BhyEE,UAAU,CC5BZ,UACE,aAAc","file":"author.css","sourcesContent":["// Config\n$avatar-icon-padding: 6px !default;\n$avatar-icon-padding-lg: 8px !default;\n\nbody {\n background: color('body-bg');\n\n &.vle {\n overflow: hidden;\n }\n}\n\na {\n &:hover, &:focus { // TODO: remove when bootstrap css dependency is removed\n color: color('primary');\n }\n}\n\nblockquote {\n background-color: lighten(color('primary'), 56%);\n padding: 8px;\n margin: 16px 0;\n border-style: solid;\n border-color: color('primary');\n border-width: 0 0 0 3px;\n}\n\n.has-indicator {\n &:after {\n content: '';\n position: absolute;\n border-radius: 50%;\n padding: 5px;\n background-color: color('accent');\n }\n}\n\n.has-indicator--icon-button {\n &:after {\n top: 25px;\n left: 5px;\n }\n}\n\n// Badges\n.badge {\n border-radius: $card-border-radius;\n padding: 2px 6px;\n font-size: rem(1.2);\n font-weight: 500;\n font-style: normal;\n background-color: color('gray-dark');\n\n &.md-button {\n min-width: 0;\n min-height: 0;\n line-height: inherit;\n\n &:hover, &:focus {\n background-color: color('gray-dark');\n }\n\n &:focus {\n outline: 1px dotted color('gray-dark');\n }\n }\n}\n\n.badge--info {\n background-color: color('info');\n color: #ffffff;\n}\n\n.badge--warn {\n background-color: color('warn');\n color: #ffffff;\n}\n\n.badge--success {\n background-color: color('success');\n color: #ffffff;\n}\n\n// Dividers\n.divider--withmargin {\n margin: 16px 0;\n}\n\n.divider--dashed {\n border-top-style: dashed;\n}\n\n// Links\na {\n color: color('primary');\n cursor: pointer;\n}\n\n.active {\n background-color: rgba(158,158,158,0.2);\n color: color('text');\n}\n\n// Images & Icons\n.avatar {\n border-radius: 50%;\n box-sizing: content-box;\n}\n\n.avatar--square {\n border-radius: $card-border-radius;\n}\n\n// Rules for sizing avatars (matches material icons plus avatar-icon padding)\n.avatar {\n &.md-18 {\n height: 18px + $avatar-icon-padding*2;\n width: 18px + $avatar-icon-padding*2;\n }\n &.md-24 {\n height: 24px + $avatar-icon-padding*2;\n width: 24px + $avatar-icon-padding*2;\n }\n &.md-36 {\n height: 36px + $avatar-icon-padding*2;\n width: 36px + $avatar-icon-padding*2;\n }\n &.md-48 {\n height: 48px + $avatar-icon-padding*2;\n width: 48px + $avatar-icon-padding*2;\n }\n}\n\n// Rules for sizing avatar backgrounds (when using a child md-icon)\n.avatar--icon {\n background-color: color('gray-light');\n white-space: normal !important;\n\n &:not(.md-avatar) {\n padding: $avatar-icon-padding;\n }\n\n &.md-18 {\n height: 18px;\n width: 18px;\n }\n &.md-24 {\n height: 24px;\n width: 24px;\n }\n &.md-36 {\n height: 36px;\n width: 36px;\n }\n &.md-48 {\n height: 48px;\n width: 48px;\n }\n}\n\nmd-toolbar.md-light-theme:not(.md-menu-toolbar) {\n md-icon {\n color: color('text-secondary');\n }\n\n .md-button:disabled {\n md-icon {\n color: color('text-disabled');\n }\n }\n}\n\n// hacks for now\nmd-icon, .md-button:not([disabled]) {\n &.primary {\n color: color('primary') !important;\n }\n &.success {\n color: color('success') !important;\n }\n &.warn {\n color: color('warn') !important;\n }\n &.info {\n color: color('info') !important;\n }\n &.accent {\n color: color('accent') !important;\n }\n &.accent-1 {\n color: color('accent-1') !important;\n }\n &.accent-2 {\n color: color('accent-2') !important;\n }\n}\n\n// Theme overrides\nmd-input-container.md-wise-theme label {\n color: color('text');\n}\n\nmd-select-menu.md-default-theme md-option[selected], md-select-menu md-option[selected] {\n background-color: color('selected-bg');\n}\n\n.md-autocomplete-suggestions-container.md-default-theme li .highlight,\n.md-autocomplete-suggestions-container li .highlight {\n color: color('primary');\n background-color: color('selected-bg');\n}\n\n// Color\n// Set classes for each named color in the $colors map\n@each $key, $value in $colors {\n .#{$key} {\n color: $value;\n }\n}\n\n// Set classes for each named color in the $colors map\n@each $key, $value in $colors {\n .#{$key}-bg {\n background-color: $value;\n }\n}\n\n// Set theme colors for angular-ui elements\n@each $key, $value in $colors {\n md-progress-circular.#{$key} {\n path {\n stroke: $value;\n }\n }\n}\n","// Authoring Tool Colors\n$_primary-color: #7e57c2; // should match the default color of your ng-material default theme's 'primary' palette\n$_selected-bg: lighten($_primary-color, 56%);\n\n$color: (\n 'primary': $_primary-color,\n 'accent': #F05843, // should match the default color of your ng-material default theme's 'accent' palette\n 'accent-1': #795C3A,\n 'accent-2': #CAD266,\n 'warn': #c62828, // should match the default color of your ng-material default theme's 'warn' palette\n 'info': #ef6c00,\n 'success': #00C853,\n 'divider': rgba(0, 0, 0, 0.12),\n 'gray-lightest': #f7f7f7,\n 'gray-lighter': #eeeeee,\n 'gray-light': #dddddd,\n 'gray': #cccccc,\n 'gray-dark': #aaaaaa,\n 'gray-darker': #757575,\n 'gray-darkest': #333333,\n 'text': rgba(0, 0, 0, 0.87),\n 'text-secondary': rgba(0, 0, 0, 0.54),\n 'text-disabled': rgba(0, 0, 0, 0.26),\n 'text-light': rgba(255, 255, 255, 1),\n 'text-light-secondary': rgba(255, 255, 255, 0.70),\n 'text-light-disabled': rgba(255, 255, 255, 0.50),\n 'selected-bg': $_selected-bg,\n 'score': #FFC107\n);\n\n$body-color: (\n 'body': map-get($color, 'text'),\n 'body-bg': map-get($color, 'gray-lighter')\n);\n\n$colors: map-merge($color, $body-color);\n","// Colors\n$_primary-color: #1C8CA8; // should match the default color of your ng-material default theme's 'primary' palette\n$_selected-bg: lighten($_primary-color, 59%);\n\n$color: (\n 'primary': $_primary-color,\n 'accent': #F05843, // should match the default color of your ng-material default theme's 'accent' palette\n 'accent-1': #795C3A,\n 'accent-2': #CAD266,\n 'warn': #c62828, // should match the default color of your ng-material default theme's 'warn' palette\n 'info': #ef6c00,\n 'success': #00C853,\n 'divider': rgba(0, 0, 0, 0.12),\n 'gray-lightest': #f7f7f7,\n 'gray-lighter': #eeeeee,\n 'gray-light': #dddddd,\n 'gray': #cccccc,\n 'gray-dark': #aaaaaa,\n 'gray-darker': #757575,\n 'gray-darkest': #333333,\n 'text': rgba(0, 0, 0, 0.87),\n 'text-secondary': rgba(0, 0, 0, 0.54),\n 'text-disabled': rgba(0, 0, 0, 0.26),\n 'text-light': rgba(255, 255, 255, 1),\n 'text-light-secondary': rgba(255, 255, 255, 0.70),\n 'text-light-disabled': rgba(255, 255, 255, 0.50),\n 'selected-bg': $_selected-bg,\n 'score': #FFC107\n);\n\n$body-color: (\n 'body': map-get($color, 'text'),\n 'body-bg': map-get($color, 'gray-lighter')\n);\n\n$colors: (map-merge($color, $body-color));\n\n// Typography\n$baseline-grid: 8px;\n$body-font-size-base: rem(1.500);\n$caption-font-size-base: rem(1.300);\n\n// Layout\n$wise-toolbar-height: 42px;\n\n// Menus\n$menu-border-radius: 2px;\n$max-visible-items: 6;\n$menu-item-height: 6 * $baseline-grid !default;\n$dense-menu-item-height: 4 * $baseline-grid !default;\n$max-menu-height: 2 * $baseline-grid + $max-visible-items * $menu-item-height !default;\n$max-dense-menu-height: 2 * $baseline-grid + $max-visible-items * $dense-menu-item-height !default;\n\n// Cards\n$card-border-radius: 4px;\n\n// Buttons\n$button-border-radius: 3px;\n","// Helper functions and mixins\n\n// Get colors from $colors map\n@function color($key) {\n @if map-has-key($colors, $key) {\n @return map-get($colors, $key);\n }\n @warn \"Unknown `#{$key}` in $colors.\";\n @return null;\n}\n\n// set size in pixels given rem\n@function rem($multiplier) {\n $font-size: 10px;\n @return $multiplier * $font-size;\n}\n","// 1. Config\n\n// 2. Base\n.l-constrained {\n margin-left: auto;\n margin-right: auto;\n max-width: 100%;\n position: relative;\n\n @media (min-width: $layout-breakpoint-xs) {\n width: $layout-breakpoint-md;\n }\n}\n\n.l-constrained-md {\n width: $layout-breakpoint-sm;\n max-width: 100%;\n}\n","// Helpers\n@function rem($multiplier) {\n $font-size: 10px;\n @return $multiplier * $font-size;\n}\n\n// Angular Material variables for use and !default overrides\n$md-toolbar-height: 52px;\n$md-toolbar-medium-tall-height: 74px;\n$md-toolbar-tall-height: 104px;\n$md-toolbar-height-mobile-portrait: 52px;\n$md-toolbar-height-mobile-landscape: 52px;\n\n$caption-font-size-base: rem(1.300);\n\n//$list-item-height: 56px;\n\n$card-border-radius: 4px;\n\n$layout-breakpoint-xs: 600px;\n$layout-breakpoint-sm: 960px;\n$layout-breakpoint-md: 1280px;\n$layout-breakpoint-lg: 1920px;\n\n$button-border-radius: 3px;\n\n$tooltip-fontsize-lg: rem(1.1);\n$tooltip-fontsize-sm: rem(1.1);\n//$tooltip-height-lg: rem(2.2);\n$tooltip-height-sm: rem(2.2);\n//$tooltip-top-margin-lg: rem(1.4);\n$tooltip-top-margin-sm: rem(1.4);\n//$tooltip-lr-padding-lg: rem(0.8);\n$tooltip-lr-padding-sm: rem(0.8);\n//$tooltip-max-width: rem(3.20);\n\n$progress-linear-bar-height: 7px;\n","// 1. Config\n\n// 2. Base\n.l-footer {\n position: fixed;\n bottom: 0;\n left: 0;\n right: 0;\n z-index: 1;\n background-color: #ffffff;\n border-top: 1px solid color('gray-lighter');\n}\n\n// Buttons\n.button--footer {\n margin: 0;\n padding-top: 0;\n padding-bottom: 0;\n min-width: 0;\n display: flex;\n}\n\n.button--footer__element {\n padding-left: 8px;\n}\n","// 1. Config\n\n// 2. Base\n.l-header {\n z-index: 3;\n\n .logo {\n margin-left: 0 !important;\n height: 36px;\n width: 36px;\n vertical-align: middle;\n }\n\n .logo-link {\n min-width: auto;\n display: none;\n padding: 0 4px;\n margin-right: 12px;\n\n @media only screen and (min-width: ($layout-breakpoint-xs)) {\n display: block;\n }\n\n &:hover, &:focus {\n border: 0 none;\n }\n }\n\n // Handle mobile portrait\n @media only screen and (max-width: ($layout-breakpoint-xs - 1)) {\n .md-toolbar-tools {\n h1, h2, h3 {\n font-size: $body-font-size-base;\n }\n }\n }\n}\n","// 1. Config\n\n// 2. Base\n.l-main {\n background-color: color('body-bg');\n}\n\n.l-main--with-toolbar {\n margin-top: $wise-toolbar-height;\n}\n\n#content {\n transition: margin-top 0.5s;\n}\n\n.view-content {\n margin: 0 auto;\n padding: 8px;\n position: absolute;\n left: 0;\n right: 0;\n transition: opacity 500ms;\n\n @media only screen and (min-width: $layout-breakpoint-sm) {\n padding: 16px;\n }\n\n &.ng-enter {\n opacity: 0;\n }\n\n .ng-enter-active {\n opacity: 1;\n transition-delay: 250ms;\n }\n\n &.ng-leave-active,\n &.ng-hide {\n opacity: 0;\n }\n\n &.ng-hide-add,\n &.ng-hide-add-active,\n &.ng-hide-remove,\n &.ng-hide-remove-active {\n opacity: 0;\n }\n}\n\n.view-content--with-sidemenu {\n padding: 8px;\n\n @media only screen and (min-width: $layout-breakpoint-xs) {\n margin-left: 54px;\n padding: 16px;\n }\n}\n[dir='rtl'] .view-content--with-sidemenu {\n @media only screen and (min-width: $layout-breakpoint-xs) {\n margin-left: auto;\n margin-right: 54px;\n }\n}\n\n.content-head {\n margin: 8px 0;\n\n h1,\n h2,\n h3 {\n font-weight: 300;\n margin-top: 0;\n margin-bottom: 0;\n font-size: rem(3.6);\n\n @media only screen and (max-width: $layout-breakpoint-sm - 1) {\n font-size: rem(3.2);\n text-align: center;\n }\n }\n}\n\n.content-head__more {\n @media only screen and (max-width: $layout-breakpoint-sm - 1) {\n margin-top: 8px;\n }\n}\n\n.content-head__item,\nh2.content-head__item {\n margin: 0 8px;\n\n .md-subhead {\n padding-left: 4px;\n\n @media only screen and (max-width: $layout-breakpoint-sm - 1) {\n display: block;\n padding-left: 0;\n }\n }\n\n md-icon {\n vertical-align: text-bottom;\n }\n}\n\n.stepSelectMenuContainer md-select-menu,\n.stepSelectMenuContainer md-select-menu md-content {\n max-height: 500px;\n}\n","/*\n WISE Project Styles\n */\nbody {\n background: #eeeeee; }\n body.vle {\n overflow: hidden; }\n\na:hover, a:focus {\n color: #7e57c2; }\n\nblockquote {\n background-color: white;\n padding: 8px;\n margin: 16px 0;\n border-style: solid;\n border-color: #7e57c2;\n border-width: 0 0 0 3px; }\n\n.has-indicator:after {\n content: '';\n position: absolute;\n border-radius: 50%;\n padding: 5px;\n background-color: #F05843; }\n\n.has-indicator--icon-button:after {\n top: 25px;\n left: 5px; }\n\n.badge {\n border-radius: 4px;\n padding: 2px 6px;\n font-size: 12px;\n font-weight: 500;\n font-style: normal;\n background-color: #aaaaaa; }\n .badge.md-button {\n min-width: 0;\n min-height: 0;\n line-height: inherit; }\n .badge.md-button:hover, .badge.md-button:focus {\n background-color: #aaaaaa; }\n .badge.md-button:focus {\n outline: 1px dotted #aaaaaa; }\n\n.badge--info {\n background-color: #ef6c00;\n color: #ffffff; }\n\n.badge--warn {\n background-color: #c62828;\n color: #ffffff; }\n\n.badge--success {\n background-color: #00C853;\n color: #ffffff; }\n\n.divider--withmargin {\n margin: 16px 0; }\n\n.divider--dashed {\n border-top-style: dashed; }\n\na {\n color: #7e57c2;\n cursor: pointer; }\n\n.active {\n background-color: rgba(158, 158, 158, 0.2);\n color: rgba(0, 0, 0, 0.87); }\n\n.avatar {\n border-radius: 50%;\n box-sizing: content-box; }\n\n.avatar--square {\n border-radius: 4px; }\n\n.avatar.md-18 {\n height: 30px;\n width: 30px; }\n\n.avatar.md-24 {\n height: 36px;\n width: 36px; }\n\n.avatar.md-36 {\n height: 48px;\n width: 48px; }\n\n.avatar.md-48 {\n height: 60px;\n width: 60px; }\n\n.avatar--icon {\n background-color: #dddddd;\n white-space: normal !important; }\n .avatar--icon:not(.md-avatar) {\n padding: 6px; }\n .avatar--icon.md-18 {\n height: 18px;\n width: 18px; }\n .avatar--icon.md-24 {\n height: 24px;\n width: 24px; }\n .avatar--icon.md-36 {\n height: 36px;\n width: 36px; }\n .avatar--icon.md-48 {\n height: 48px;\n width: 48px; }\n\nmd-toolbar.md-light-theme:not(.md-menu-toolbar) md-icon {\n color: rgba(0, 0, 0, 0.54); }\n\nmd-toolbar.md-light-theme:not(.md-menu-toolbar) .md-button:disabled md-icon {\n color: rgba(0, 0, 0, 0.26); }\n\nmd-icon.primary, .md-button:not([disabled]).primary {\n color: #7e57c2 !important; }\n\nmd-icon.success, .md-button:not([disabled]).success {\n color: #00C853 !important; }\n\nmd-icon.warn, .md-button:not([disabled]).warn {\n color: #c62828 !important; }\n\nmd-icon.info, .md-button:not([disabled]).info {\n color: #ef6c00 !important; }\n\nmd-icon.accent, .md-button:not([disabled]).accent {\n color: #F05843 !important; }\n\nmd-icon.accent-1, .md-button:not([disabled]).accent-1 {\n color: #795C3A !important; }\n\nmd-icon.accent-2, .md-button:not([disabled]).accent-2 {\n color: #CAD266 !important; }\n\nmd-input-container.md-wise-theme label {\n color: rgba(0, 0, 0, 0.87); }\n\nmd-select-menu.md-default-theme md-option[selected], md-select-menu md-option[selected] {\n background-color: white; }\n\n.md-autocomplete-suggestions-container.md-default-theme li .highlight,\n.md-autocomplete-suggestions-container li .highlight {\n color: #7e57c2;\n background-color: white; }\n\n.primary {\n color: #7e57c2; }\n\n.accent {\n color: #F05843; }\n\n.accent-1 {\n color: #795C3A; }\n\n.accent-2 {\n color: #CAD266; }\n\n.warn {\n color: #c62828; }\n\n.info {\n color: #ef6c00; }\n\n.success {\n color: #00C853; }\n\n.divider {\n color: rgba(0, 0, 0, 0.12); }\n\n.gray-lightest {\n color: #f7f7f7; }\n\n.gray-lighter {\n color: #eeeeee; }\n\n.gray-light {\n color: #dddddd; }\n\n.gray {\n color: #cccccc; }\n\n.gray-dark {\n color: #aaaaaa; }\n\n.gray-darker {\n color: #757575; }\n\n.gray-darkest {\n color: #333333; }\n\n.text {\n color: rgba(0, 0, 0, 0.87); }\n\n.text-secondary {\n color: rgba(0, 0, 0, 0.54); }\n\n.text-disabled {\n color: rgba(0, 0, 0, 0.26); }\n\n.text-light {\n color: white; }\n\n.text-light-secondary {\n color: rgba(255, 255, 255, 0.7); }\n\n.text-light-disabled {\n color: rgba(255, 255, 255, 0.5); }\n\n.selected-bg {\n color: white; }\n\n.score {\n color: #FFC107; }\n\n.body {\n color: rgba(0, 0, 0, 0.87); }\n\n.body-bg {\n color: #eeeeee; }\n\n.primary-bg {\n background-color: #7e57c2; }\n\n.accent-bg {\n background-color: #F05843; }\n\n.accent-1-bg {\n background-color: #795C3A; }\n\n.accent-2-bg {\n background-color: #CAD266; }\n\n.warn-bg {\n background-color: #c62828; }\n\n.info-bg {\n background-color: #ef6c00; }\n\n.success-bg {\n background-color: #00C853; }\n\n.divider-bg {\n background-color: rgba(0, 0, 0, 0.12); }\n\n.gray-lightest-bg {\n background-color: #f7f7f7; }\n\n.gray-lighter-bg {\n background-color: #eeeeee; }\n\n.gray-light-bg {\n background-color: #dddddd; }\n\n.gray-bg {\n background-color: #cccccc; }\n\n.gray-dark-bg {\n background-color: #aaaaaa; }\n\n.gray-darker-bg {\n background-color: #757575; }\n\n.gray-darkest-bg {\n background-color: #333333; }\n\n.text-bg {\n background-color: rgba(0, 0, 0, 0.87); }\n\n.text-secondary-bg {\n background-color: rgba(0, 0, 0, 0.54); }\n\n.text-disabled-bg {\n background-color: rgba(0, 0, 0, 0.26); }\n\n.text-light-bg {\n background-color: white; }\n\n.text-light-secondary-bg {\n background-color: rgba(255, 255, 255, 0.7); }\n\n.text-light-disabled-bg {\n background-color: rgba(255, 255, 255, 0.5); }\n\n.selected-bg-bg {\n background-color: white; }\n\n.score-bg {\n background-color: #FFC107; }\n\n.body-bg {\n background-color: rgba(0, 0, 0, 0.87); }\n\n.body-bg-bg {\n background-color: #eeeeee; }\n\nmd-progress-circular.primary path {\n stroke: #7e57c2; }\n\nmd-progress-circular.accent path {\n stroke: #F05843; }\n\nmd-progress-circular.accent-1 path {\n stroke: #795C3A; }\n\nmd-progress-circular.accent-2 path {\n stroke: #CAD266; }\n\nmd-progress-circular.warn path {\n stroke: #c62828; }\n\nmd-progress-circular.info path {\n stroke: #ef6c00; }\n\nmd-progress-circular.success path {\n stroke: #00C853; }\n\nmd-progress-circular.divider path {\n stroke: rgba(0, 0, 0, 0.12); }\n\nmd-progress-circular.gray-lightest path {\n stroke: #f7f7f7; }\n\nmd-progress-circular.gray-lighter path {\n stroke: #eeeeee; }\n\nmd-progress-circular.gray-light path {\n stroke: #dddddd; }\n\nmd-progress-circular.gray path {\n stroke: #cccccc; }\n\nmd-progress-circular.gray-dark path {\n stroke: #aaaaaa; }\n\nmd-progress-circular.gray-darker path {\n stroke: #757575; }\n\nmd-progress-circular.gray-darkest path {\n stroke: #333333; }\n\nmd-progress-circular.text path {\n stroke: rgba(0, 0, 0, 0.87); }\n\nmd-progress-circular.text-secondary path {\n stroke: rgba(0, 0, 0, 0.54); }\n\nmd-progress-circular.text-disabled path {\n stroke: rgba(0, 0, 0, 0.26); }\n\nmd-progress-circular.text-light path {\n stroke: white; }\n\nmd-progress-circular.text-light-secondary path {\n stroke: rgba(255, 255, 255, 0.7); }\n\nmd-progress-circular.text-light-disabled path {\n stroke: rgba(255, 255, 255, 0.5); }\n\nmd-progress-circular.selected-bg path {\n stroke: white; }\n\nmd-progress-circular.score path {\n stroke: #FFC107; }\n\nmd-progress-circular.body path {\n stroke: rgba(0, 0, 0, 0.87); }\n\nmd-progress-circular.body-bg path {\n stroke: #eeeeee; }\n\n.l-constrained {\n margin-left: auto;\n margin-right: auto;\n max-width: 100%;\n position: relative; }\n @media (min-width: 600px) {\n .l-constrained {\n width: 1280px; } }\n\n.l-constrained-md {\n width: 960px;\n max-width: 100%; }\n\n.l-footer {\n position: fixed;\n bottom: 0;\n left: 0;\n right: 0;\n z-index: 1;\n background-color: #ffffff;\n border-top: 1px solid #eeeeee; }\n\n.button--footer {\n margin: 0;\n padding-top: 0;\n padding-bottom: 0;\n min-width: 0;\n display: flex; }\n\n.button--footer__element {\n padding-left: 8px; }\n\n.l-header {\n z-index: 3; }\n .l-header .logo {\n margin-left: 0 !important;\n height: 36px;\n width: 36px;\n vertical-align: middle; }\n .l-header .logo-link {\n min-width: auto;\n display: none;\n padding: 0 4px;\n margin-right: 12px; }\n @media only screen and (min-width: 600px) {\n .l-header .logo-link {\n display: block; } }\n .l-header .logo-link:hover, .l-header .logo-link:focus {\n border: 0 none; }\n @media only screen and (max-width: 599px) {\n .l-header .md-toolbar-tools h1, .l-header .md-toolbar-tools h2, .l-header .md-toolbar-tools h3 {\n font-size: 15px; } }\n\n.l-main {\n background-color: #eeeeee; }\n\n.l-main--with-toolbar {\n margin-top: 42px; }\n\n#content {\n transition: margin-top 0.5s; }\n\n.view-content {\n margin: 0 auto;\n padding: 8px;\n position: absolute;\n left: 0;\n right: 0;\n transition: opacity 500ms; }\n @media only screen and (min-width: 960px) {\n .view-content {\n padding: 16px; } }\n .view-content.ng-enter {\n opacity: 0; }\n .view-content .ng-enter-active {\n opacity: 1;\n transition-delay: 250ms; }\n .view-content.ng-leave-active, .view-content.ng-hide {\n opacity: 0; }\n .view-content.ng-hide-add, .view-content.ng-hide-add-active, .view-content.ng-hide-remove, .view-content.ng-hide-remove-active {\n opacity: 0; }\n\n.view-content--with-sidemenu {\n padding: 8px; }\n @media only screen and (min-width: 600px) {\n .view-content--with-sidemenu {\n margin-left: 54px;\n padding: 16px; } }\n\n@media only screen and (min-width: 600px) {\n [dir='rtl'] .view-content--with-sidemenu {\n margin-left: auto;\n margin-right: 54px; } }\n\n.content-head {\n margin: 8px 0; }\n .content-head h1,\n .content-head h2,\n .content-head h3 {\n font-weight: 300;\n margin-top: 0;\n margin-bottom: 0;\n font-size: 36px; }\n @media only screen and (max-width: 959px) {\n .content-head h1,\n .content-head h2,\n .content-head h3 {\n font-size: 32px;\n text-align: center; } }\n\n@media only screen and (max-width: 959px) {\n .content-head__more {\n margin-top: 8px; } }\n\n.content-head__item,\nh2.content-head__item {\n margin: 0 8px; }\n .content-head__item .md-subhead,\n h2.content-head__item .md-subhead {\n padding-left: 4px; }\n @media only screen and (max-width: 959px) {\n .content-head__item .md-subhead,\n h2.content-head__item .md-subhead {\n display: block;\n padding-left: 0; } }\n .content-head__item md-icon,\n h2.content-head__item md-icon {\n vertical-align: text-bottom; }\n\n.stepSelectMenuContainer md-select-menu,\n.stepSelectMenuContainer md-select-menu md-content {\n max-height: 500px; }\n\n.l-nav {\n background-color: #eeeeee !important; }\n\n.l-node {\n margin-top: 42px;\n padding: 0; }\n @media only screen and (min-width: 600px) {\n .l-node {\n padding: 0 0 16px;\n background-color: #eeeeee !important; } }\n @media only screen and (min-width: 960px) {\n .l-node {\n padding: 0 0 32px; } }\n\n.l-notebook {\n margin-top: 42px;\n background-color: #eeeeee !important; }\n\n.l-sidebar__header {\n background-color: #ffffff !important;\n color: #795C3A !important; }\n .l-sidebar__header md-select {\n color: rgba(0, 0, 0, 0.87); }\n\n.status-icon {\n margin: 0 4px;\n z-index: 1;\n vertical-align: bottom; }\n\n.md-button.status-icon {\n height: auto;\n width: auto;\n min-height: 0;\n line-height: inherit;\n margin: 0 4px;\n padding: 0; }\n\n.status-corner-wrapper {\n position: absolute;\n z-index: 1;\n overflow: hidden; }\n\n.status-corner {\n width: 0;\n height: 0;\n border-top-color: rgba(0, 0, 0, 0.26);\n border-bottom-color: rgba(0, 0, 0, 0.26);\n color: rgba(0, 0, 0, 0.26); }\n .status-corner:after {\n content: '!';\n color: #ffffff;\n top: 2px;\n right: 10px;\n position: absolute;\n font-weight: 700; }\n\n.status-corner--warn {\n border-top-color: #c62828 !important;\n border-bottom-color: #c62828 !important; }\n\n.status-corner-top-right {\n top: 0;\n right: 0;\n border-top-right-radius: 4px; }\n .status-corner-top-right .status-corner {\n border-top: 36px solid;\n border-left: 36px solid transparent; }\n\n.status-corner-top-left {\n top: 0;\n left: 0;\n border-top-left-radius: 4px; }\n .status-corner-top-left .status-corner {\n border-top: 36px solid;\n border-right: 36px solid transparent; }\n\n.status-corner-bottom-right {\n bottom: 0;\n right: 0;\n border-bottom-right-radius: 4px; }\n .status-corner-bottom-right .status-corner {\n border-bottom: 36px solid;\n border-left: 36px solid transparent; }\n\n.status-corner-bottom-left {\n bottom: 0;\n left: 0;\n border-bottom-left-radius: 4px; }\n .status-corner-bottom-left .status-corner {\n border-bottom: 36px solid;\n border-right: 36px solid transparent; }\n\n.avatar--icon--alert {\n background-color: #ffffff; }\n\n.avatar--icon--alert__icon {\n font-size: 48px;\n margin: -4px 0 0 -4px; }\n\nmd-dialog {\n width: 600px; }\n\n.dialog--wide {\n width: 960px; }\n\n.dialog--wider {\n width: 1280px; }\n\n.help-bubble {\n border-radius: 4px;\n max-width: 320px; }\n @media (min-width: 600px) {\n .help-bubble {\n max-width: 552px; } }\n @media (min-width: 960px) {\n .help-bubble {\n max-width: 912px; } }\n @media (min-width: 1280px) {\n .help-bubble {\n max-width: 1232px; } }\n\n.help-bubble__title {\n border-top-left-radius: 4px;\n border-top-right-radius: 4px; }\n\n.help-bubble___title__content {\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n padding: 0px 0 0 12px;\n background-color: #ef6c00; }\n .help-bubble___title__content .md-icon-button {\n margin-right: 0;\n padding-top: 0;\n padding-bottom: 0; }\n\n.help-bubble__content {\n overflow: auto;\n padding: 8px 12px;\n max-height: 480px; }\n\n.help-bubble__actions {\n border-bottom-left-radius: 4px;\n border-bottom-right-radius: 4px; }\n\ndiv.hopscotch-bubble {\n border-radius: 4px;\n border: 0 none;\n font-family: Roboto, \"Helvetica Neue\", sans-serif;\n font-size: 14px;\n z-index: 6; }\n div.hopscotch-bubble .hopscotch-bubble-arrow-container {\n position: absolute;\n width: 20px;\n height: 20px; }\n div.hopscotch-bubble .hopscotch-bubble-arrow-container.up {\n top: 0;\n left: 12px; }\n div.hopscotch-bubble .hopscotch-bubble-arrow-container.up .hopscotch-bubble-arrow {\n border-bottom: 10px solid #ef6c00;\n border-left: 10px solid transparent;\n border-right: 10px solid transparent;\n position: relative;\n top: -10px; }\n div.hopscotch-bubble .hopscotch-bubble-arrow-container.up .hopscotch-bubble-arrow-border {\n border: 0 none;\n /*border-bottom: 12px solid color('gray-darker');\n border-left: 12px solid transparent;\n border-right: 12px solid transparent;*/ }\n div.hopscotch-bubble .hopscotch-bubble-arrow-container.down {\n bottom: -34px;\n left: 12px; }\n div.hopscotch-bubble .hopscotch-bubble-arrow-container.down .hopscotch-bubble-arrow {\n border-top: 10px solid #ef6c00;\n border-left: 10px solid transparent;\n border-right: 10px solid transparent;\n position: relative;\n top: -10px; }\n div.hopscotch-bubble .hopscotch-bubble-arrow-container.down .hopscotch-bubble-arrow-border {\n border: 0 none;\n /*border-top: 12px solid color('gray-darker');\n border-left: 12px solid transparent;\n border-right: 12px solid transparent;*/ }\n div.hopscotch-bubble .hopscotch-bubble-arrow-container.left {\n top: 12px;\n left: -10px; }\n div.hopscotch-bubble .hopscotch-bubble-arrow-container.left .hopscotch-bubble-arrow {\n border-right: 10px solid #ef6c00;\n border-bottom: 10px solid transparent;\n border-top: 10px solid transparent;\n position: relative;\n left: 0;\n top: -10px; }\n div.hopscotch-bubble .hopscotch-bubble-arrow-container.left .hopscotch-bubble-arrow-border {\n border: 0 none;\n /*border-right: 12px solid color('gray-darker');\n border-bottom: 12px solid transparent;\n border-top: 12px solid transparent;*/ }\n div.hopscotch-bubble .hopscotch-bubble-arrow-container.right {\n top: 12px;\n right: -30px; }\n div.hopscotch-bubble .hopscotch-bubble-arrow-container.right .hopscotch-bubble-arrow {\n border-left: 10px solid #ef6c00;\n border-bottom: 10px solid transparent;\n border-top: 10px solid transparent;\n position: relative;\n right: 0;\n top: -10px; }\n div.hopscotch-bubble .hopscotch-bubble-arrow-container.right .hopscotch-bubble-arrow-border {\n border: 0 none;\n /*border-right: 12px solid color('gray-darker');\n border-bottom: 12px solid transparent;\n border-top: 12px solid transparent;*/ }\n\n.input-container {\n padding-top: 12px; }\n\n.input-container--component {\n margin-bottom: 0; }\n\n.input-container--open-response.md-has-icon {\n padding-left: 0; }\n\n.input-container--open-response .md-errors-spacer {\n display: none; }\n\n.input-wrapper {\n position: relative; }\n\n.input-wrapper--focused .input--textarea__action md-icon {\n color: #7e57c2; }\n\n.input--textarea, .input-container textarea.input--textarea {\n padding: 8px;\n background-color: #f7f7f7;\n border: 1px solid #cccccc;\n margin-bottom: 8px; }\n .input--textarea:focus, .input-container textarea.input--textarea:focus {\n background-color: #ffffff; }\n .input--textarea[disabled], .input-container textarea.input--textarea[disabled] {\n color: rgba(0, 0, 0, 0.54); }\n\n.input-container textarea.input--textarea {\n width: 100%; }\n\n.input--textarea--disabled {\n color: rgba(0, 0, 0, 0.54); }\n\n.input--textarea__action {\n position: absolute;\n right: -4px; }\n .input--textarea__action[disabled] md-icon {\n color: rgba(0, 0, 0, 0.26) !important; }\n\n.input--textarea__action--notebook {\n top: 6px; }\n .input-wrapper--richtext .input--textarea__action--notebook {\n top: -7px; }\n\n.input--textarea__action--revision {\n bottom: 6px; }\n .input-wrapper--richtext .input--textarea__action--revision {\n bottom: -5px; }\n\n.input-label, md-input-container:not(.md-input-invalid):not(.md-input-focused).md-input-has-value label.input-label {\n line-height: 1.2;\n color: rgba(0, 0, 0, 0.87); }\n .input-label.input-label--focused, md-input-container:not(.md-input-invalid):not(.md-input-focused).md-input-has-value label.input-label.input-label--focused {\n color: #7e57c2; }\n\n.autocomplete input {\n text-overflow: ellipsis;\n overflow: hidden;\n word-wrap: none;\n font-weight: 500;\n color: rgba(0, 0, 0, 0.54); }\n\n@media only screen and (min-width: 600px) {\n .autocomplete--minwidth {\n min-width: 300px; } }\n\n@media only screen and (min-width: 960px) {\n .autocomplete--minwidth {\n min-width: 300px; } }\n\n.autocomplete--flat md-autocomplete-wrap {\n background-color: #ffffff; }\n .autocomplete--flat md-autocomplete-wrap:not(.md-menu-showing) {\n box-shadow: none;\n background-color: #eeeeee; }\n\n.select__header {\n height: 48px; }\n .select__header input {\n height: 100%;\n width: 100%;\n padding: 0 8px;\n outline: none;\n border: 0 none;\n font-size: 14px;\n font-weight: 500; }\n\n.table {\n max-width: 100%;\n width: auto;\n min-width: 100px;\n margin: 8px 0; }\n .table thead > tr > th,\n .table thead > tr > td,\n .table tbody > tr > th,\n .table tbody > tr > td,\n .table tfoot > tr > th,\n .table tfoot > tr > td {\n border: 1px solid #cccccc;\n padding: 6px;\n font-size: 15px;\n min-height: 32px;\n height: 32px;\n min-width: 32px;\n vertical-align: top; }\n .table td.inactive,\n .table th {\n background-color: #f7f7f7;\n opacity: 1;\n visibility: visible; }\n .table md-input-container {\n margin: 0; }\n .table .md-errors-spacer {\n display: none; }\n\n.table--student td.inactive {\n padding: 8px 10px; }\n\n.table--full-width {\n width: 100%; }\n\n.table--list {\n border: 0 none;\n border-collapse: collapse;\n background-color: #ffffff;\n max-width: 100%;\n overflow: auto; }\n .table--list th,\n .table--list td {\n padding: 0 4px;\n border: 0 none; }\n .table--list td {\n min-height: 56px;\n height: 56px; }\n .table--list tr.md-button {\n display: table-row;\n text-align: left;\n width: auto;\n text-transform: none;\n font-size: inherit;\n font-weight: normal; }\n\n.table--list__wrap {\n min-width: 600px; }\n\n@media only screen and (max-width: 959px) {\n .table-wrap-sticky {\n overflow-x: auto; } }\n\n.table--list__thead {\n font-size: 14px;\n font-weight: 700; }\n\n.table--list__thead__tr {\n height: 100%;\n margin: 0; }\n\n.table--list__thead__th {\n background-color: #757575;\n color: white;\n min-height: 42px;\n height: 42px; }\n\n.table--list__thead__link {\n color: #ffffff;\n text-transform: none;\n margin: 0;\n min-width: 0;\n white-space: normal;\n line-height: 1.4;\n width: 100%; }\n\n.table--list__thead__sort {\n margin: 0; }\n\n.table--list__thead__sort--reverse {\n transform: rotate(180deg); }\n\n.td--wrap {\n min-width: 180px;\n white-space: normal;\n line-height: 1.2; }\n\n@media only screen and (max-width: 959px) {\n .td--max-width {\n max-width: 180px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap; } }\n\n.md-toolbar-tools {\n font-size: 18px; }\n .md-toolbar-tools .autocomplete {\n height: 36px; }\n .md-toolbar-tools .autocomplete md-autocomplete-wrap {\n height: 36px; }\n .md-toolbar-tools .autocomplete input {\n height: 36px; }\n\n.md-toolbar--wise {\n min-height: 42px; }\n .md-toolbar--wise .md-toolbar-tools {\n height: 42px;\n max-height: 42px; }\n .md-toolbar--wise .md-button.md-icon-button {\n height: 42px;\n line-height: 42px;\n width: 42px; }\n\n.md-toolbar--wise--sm .md-toolbar-tools {\n padding: 0 8px;\n font-size: 15px; }\n\n.md-toolbar--sidenav {\n background-color: #333333 !important; }\n .md-toolbar--sidenav .md-toolbar-tools {\n font-size: 16px;\n font-weight: 500; }\n\n.toolbar {\n position: fixed;\n left: 0;\n right: 0;\n top: 52px;\n z-index: 3; }\n\n.toolbar__title {\n margin-left: 8px;\n font-size: 16px;\n font-weight: 500; }\n\n.toolbar__tools {\n padding-right: 8px; }\n\n[dir=rtl] .toolbar__tools {\n padding-right: 16px;\n padding-left: 8px; }\n\n.toolbar__nav, .md-button.toolbar__nav {\n margin: 0; }\n\n.toolbar__select, .md-button.toolbar__select {\n margin: 0 4px;\n min-height: 32px;\n background-color: #f7f7f7; }\n .toolbar__select .md-select-value, .md-button.toolbar__select .md-select-value {\n height: 32px;\n text-align: left; }\n\n[dir=rtl] .toolbar__select .md-select-value, [dir=rtl] .md-button.toolbar__select .md-select-value {\n text-align: right; }\n\n.toolbar__select--fixedwidth {\n width: 168px; }\n @media only screen and (min-width: 600px) {\n .toolbar__select--fixedwidth {\n width: 264px; } }\n @media only screen and (min-width: 960px) {\n .toolbar__select--fixedwidth {\n width: 432px; } }\n\n.list-item {\n background-color: #ffffff;\n border-bottom: 1px solid #eeeeee; }\n .list-item .md-subheader, .list-item.md-subheader {\n color: rgba(0, 0, 0, 0.87);\n background-color: #ffffff; }\n .list-item .md-subheader md-icon, .list-item.md-subheader md-icon {\n vertical-align: middle; }\n .list-item .md-subheader .md-subheader-inner, .list-item.md-subheader .md-subheader-inner {\n padding: 0; }\n .list-item .md-subheader .md-avatar, .list-item.md-subheader .md-avatar {\n margin-right: 8px; }\n .list-item .autocomplete {\n margin: 8px 0; }\n\n.list-item--info._md-button-wrap > div.md-button:first-child, .list-item--info .md-subheader-content {\n border-left: 4px solid #ef6c00 !important;\n margin-left: -4px; }\n\n.list-item--warn._md-button-wrap > div.md-button:first-child, .list-item--warn .md-subheader-content {\n border-left: 4px solid #c62828 !important;\n margin-left: -4px; }\n\n.list-item--expanded {\n border-bottom-width: 0; }\n\n.list-item--noclick, .list-item--noclick.md-button {\n cursor: default;\n background-color: #f7f7f7; }\n\n.list-item--actions {\n padding: 0 8px !important; }\n\n.list-item__subheader-button {\n text-transform: none;\n width: 100%;\n padding: 8px 16px;\n margin: 0;\n white-space: normal;\n text-align: left;\n line-height: 1.4; }\n\n.user-list {\n font-size: 15px; }\n\n#nav {\n position: relative; }\n\n.nav {\n margin-bottom: 16px; }\n\n.nav-mask {\n position: absolute;\n top: 0;\n bottom: 0;\n left: 0;\n right: 0;\n background-color: rgba(0, 0, 0, 0.25);\n z-index: 1; }\n .nav-mask.ng-hide {\n opacity: 0; }\n\n.nav-head {\n color: rgba(0, 0, 0, 0.54);\n font-weight: 500; }\n .nav-head md-icon {\n line-height: 20px; }\n\n.nav-contents--root {\n padding: 6px 6px 12px; }\n\n.nav-contents--group {\n background-color: #dddddd;\n padding: 8px; }\n\n.nav-contents--root {\n padding: 0; }\n\n.nav-contents__list {\n padding: 0; }\n @media (min-width: 600px) {\n .nav-contents__list {\n padding: 8px; } }\n\n.nav-item {\n transition: opacity 250ms ease-in-out; }\n .nav-item.prev md-list-item {\n background-color: white;\n /*&._md-button-wrap>div.md-button:first-child {\n border-left: 4px solid color('primary');\n margin-left: -4px;\n }*/ }\n\n.nav-item--card__content {\n border-top-right-radius: 4px;\n border-top-left-radius: 4px; }\n .nav-item--card__content:focus {\n outline: none; }\n .nav-item--card__content:focus .nav-item__title > span {\n border-bottom: 1px dashed #cccccc; }\n\n.nav-item--root {\n transition: margin 250ms, box-shadow 500ms; }\n .nav-item--root.expanded {\n flex-basis: 100%;\n max-width: 100%;\n max-height: none !important;\n margin: 8px auto;\n padding-left: 4px; }\n .nav-item--root.expanded:first-of-type {\n margin-top: 0; }\n\n.nav-item--list__info-item {\n padding: 0 16px 0 4px;\n display: inline-block; }\n\n.nav-item--list__reorder {\n margin-left: 8px;\n color: rgba(0, 0, 0, 0.26); }\n\n.nav-item--card--group:not(.expanded) {\n box-shadow: 0px 3px 1px -2px rgba(0, 0, 0, 0.14), 0px 2px 2px 0px rgba(0, 0, 0, 0.098), 0px 1px 5px 0px rgba(0, 0, 0, 0.084), 3px 3px 0px 1px #d5d5d5, 6px 6px 0px 1px #aaaaaa; }\n\n.nav-item__collapse {\n margin: 0; }\n\n.nav-item__more {\n border-top: 1px solid #dddddd;\n border-bottom-right-radius: 4px;\n border-bottom-left-radius: 4px;\n padding: 8px 16px;\n min-height: 40px; }\n\n.nav-item__users {\n height: auto;\n cursor: pointer;\n color: #ffffff;\n margin: 0;\n padding: 1px 6px; }\n .nav-item__users > md-icon {\n padding-right: 4px;\n color: #ffffff; }\n .nav-item__users:hover.success-bg, .nav-item__users:focus.success-bg {\n background-color: #00C853; }\n\n.nav-item__title {\n padding-left: 16px;\n line-height: 1.2;\n font-weight: 400; }\n\n[dir=rtl] .nav-item__title {\n padding-left: auto;\n padding-right: 16px; }\n\n.nav-item__info {\n padding: 0 8px; }\n\n.nav-item__progress {\n width: 48px; }\n .nav-item__progress > .md-container {\n top: 0; }\n\n.nav-item__progress-value {\n margin-left: 8px;\n width: 36px; }\n\n.progress-wrapper {\n padding: 2px 0;\n cursor: pointer; }\n\n.notice {\n text-align: center;\n padding: 8px;\n background-color: rgba(0, 0, 0, 0.04);\n width: 100%; }\n @media (min-width: 600px) {\n .notice {\n max-width: 80%;\n border-radius: 3px;\n margin: 24px auto; } }\n\n.menu-progress {\n position: absolute;\n top: 10px;\n right: 12px; }\n .menu-progress path {\n stroke: #CAD266 !important;\n stroke-width: 2px; }\n\n[dir=rtl] .menu-progress {\n right: auto;\n left: 12px; }\n\n.menu-sidenav__item {\n font-weight: 700;\n font-size: 14px; }\n\n.menu-sidenav__icon {\n margin-top: 12px !important;\n margin-right: 12px !important;\n margin-left: 12px; }\n\n.active .menu-sidenav__icon, .active .menu-sidenav__item {\n color: #7e57c2; }\n\n.menu-sidebar {\n position: absolute;\n top: 94px;\n bottom: 0;\n left: 0;\n background-color: #fff;\n width: 56px;\n overflow: hidden;\n padding: 8px 0;\n text-align: center;\n border-right: 1px solid #cccccc; }\n @media only screen and (max-width: 599px) {\n .menu-sidebar {\n display: none; } }\n\n[dir=rtl] .menu-sidebar {\n right: 0;\n left: auto; }\n\n.md-button.md-icon-button.menu-sidebar__link {\n margin-top: 6px;\n margin-bottom: 6px; }\n\n#node {\n margin: 0 auto;\n position: absolute;\n left: 0;\n right: 0; }\n @media only screen and (min-width: 600px) {\n #node {\n padding: 8px;\n padding: 24px 16px;\n margin-bottom: 32px; } }\n @media only screen and (min-width: 960px) {\n #node {\n padding: 32px; } }\n #node.ng-enter {\n transition: opacity .5s;\n opacity: 0; }\n #node.ng-enter-active {\n opacity: 1; }\n\n@media only screen and (min-width: 600px) {\n .node-notice {\n margin-top: -8px;\n margin-bottom: 16px; } }\n\n@media only screen and (min-width: 960px) {\n .node-notice {\n margin-top: -16px; } }\n\n.node-content {\n padding: 0 0 48px;\n background-color: #ffffff;\n border-radius: 3px;\n overflow: visible; }\n @media only screen and (max-width: 599px) {\n .node-content {\n box-shadow: none; } }\n @media only screen and (min-width: 600px) {\n .node-content {\n padding: 0;\n border-top: 2px solid;\n border-bottom: 2px solid; } }\n\nmd-content.node-content {\n background-color: #ffffff; }\n\n.node-content__rubric {\n position: absolute;\n top: -22px;\n left: 0;\n right: 0;\n z-index: 1; }\n .node-content__rubric .avatar--icon {\n transform: scale(0.94); }\n @media only screen and (max-width: 599px) {\n .node-content__rubric .avatar--icon {\n transform: scale(0.8); } }\n\n.node-icon {\n color: #ffffff;\n vertical-align: inherit; }\n\n.node-select {\n margin: 0 8px;\n min-width: 0;\n font-weight: 500;\n font-size: 15px; }\n .node-select .md-select-value *:first-child {\n transform: translate3d(0, 0, 0);\n flex: 1 0 0; }\n .node-select .md-select-value .node-select__icon {\n display: none; }\n .node-select .md-select-value .node-select__status {\n display: none; }\n .node-select .md-select-icon {\n margin-left: 0;\n color: rgba(0, 0, 0, 0.87); }\n\n.node-select-option--group {\n background-color: #f7f7f7;\n border-bottom: 1px solid #eeeeee;\n border-top: 1px solid #eeeeee; }\n\n.node-select-option--node {\n padding-left: 20px; }\n\n.node-select__icon {\n margin-right: 8px; }\n\n.node-select__status {\n margin-left: 8px; }\n\n.node-select__text {\n text-overflow: ellipsis;\n white-space: nowrap;\n overflow: hidden; }\n @media only screen and (min-width: 600px) {\n .node-select__text {\n margin-top: 2px; } }\n\n.node-title {\n line-height: 1.2;\n text-transform: none;\n margin-top: 3px; }\n @media only screen and (max-width: 599px) {\n .node-title {\n font-size: 15px; } }\n\n.node-content__actions {\n padding: 0 16px 16px; }\n @media only screen and (min-width: 960px) {\n .node-content__actions {\n padding: 0 24px 24px; } }\n @media only screen and (min-width: 1280px) {\n .node-content__actions {\n padding: 0 32px 32px; } }\n .node-content__actions .md-button:first-child {\n margin-left: 0; }\n .node-content__actions .md-button:last-child {\n margin-right: 0; }\n\n.node-content__actions__info {\n font-style: italic;\n margin-left: 8px;\n color: rgba(0, 0, 0, 0.54); }\n\n.node-content__actions__more {\n border-bottom: 1px dotted; }\n\n.md-button.md-icon-button.node-nav:first-of-type {\n margin-right: 0; }\n\n@media only screen and (min-width: 600px) {\n .node-sidebar-active {\n margin-right: 68px; } }\n\n@media only screen and (max-width: 599px) {\n .node-sidebar-visible {\n margin-bottom: 42px; } }\n\n.node-sidebar {\n position: absolute;\n right: 0;\n top: 0;\n width: 52px; }\n\n.node-sidebar__toolbar {\n position: fixed;\n width: 52px;\n background-color: #ffffff;\n padding: 8px 0;\n border-radius: 3px; }\n @media only screen and (max-width: 599px) {\n .node-sidebar__toolbar {\n right: 0;\n bottom: 0;\n left: 0;\n width: 100%;\n border-radius: 0;\n padding: 0;\n min-height: 0;\n height: 42px; } }\n\n.node-info {\n margin: 0; }\n @media only screen and (max-width: 599px) {\n .node-info {\n margin: -16px;\n border-radius: 0; } }\n .node-info .divider {\n margin-left: -8px;\n margin-right: -8px; }\n .node-info .component:first-child {\n margin-top: -16px; }\n .node-info .component, .node-info .component__content, .node-info .component__header {\n margin-left: -8px;\n margin-right: -8px; }\n .node-info .component__actions {\n display: none; }\n\n.node-rubric {\n border: 2px solid #7e57c2;\n margin: 8px 16px 24px;\n padding: 16px;\n background-color: #ffffff; }\n\n.node__label--vertical-alignment {\n vertical-align: middle;\n display: inline-block; }\n\n@media only screen and (min-width: 600px) {\n .notebook-launcher.md-button.md-fab {\n z-index: 61; } }\n\n.notebook-report {\n border-radius: 4px 4px 0 0;\n margin: 0;\n height: 100%; }\n .notebook-report .note-toolbar {\n margin: -8px -8px 8px;\n padding: 4px;\n border: 0 none;\n border-top: 1px solid #dddddd;\n border-bottom: 1px solid #dddddd;\n border-radius: 0; }\n .notebook-report .note-toolbar .btn-group {\n margin-top: 0; }\n .notebook-report .note-btn {\n border: 0 none;\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n border-bottom-left-radius: 0;\n border-bottom-right-radius: 0; }\n\n.notebook-report-container {\n height: 550px;\n width: 450px;\n max-height: 90%;\n bottom: 0;\n right: 96px;\n position: absolute;\n z-index: 3; }\n\n@media only screen and (min-width: 960px) {\n .notes-visible .notebook-report-container {\n right: 516px;\n transition: right 250ms; } }\n\n.notebook-report-container__full {\n top: 16px;\n bottom: 16px;\n left: 16px;\n right: 16px;\n max-height: none;\n max-width: none;\n height: auto;\n width: auto; }\n .notebook-report-container__full .notebook-report {\n height: 100%;\n width: 100%;\n margin: 0 auto;\n max-height: none;\n border-radius: 4px; }\n\n.notebook-report-container__collapsed {\n width: 300px;\n height: auto; }\n .notebook-report-container__collapsed .notebook-report__content, .notebook-report-container__collapsed .notebook-report__actions, .notebook-report-container__collapsed .notebook-report__content__header {\n display: none; }\n\n.notebook-report__toolbar {\n background-color: #333333 !important;\n border-radius: 4px 4px 0 0; }\n\n.notebook-report__toolbar__title {\n max-width: 150px; }\n\n.notebook-report__content {\n background-color: #ffffff; }\n .notebook-report__content h1, .notebook-report__content h2, .notebook-report__content h3, .notebook-report__content h4 {\n font-size: 22px; }\n .notebook-report__content .note-editor.note-frame {\n border: 0 none;\n border-radius: 0;\n padding: 0;\n box-shadow: none; }\n .notebook-report__content .note-resizebar {\n display: none; }\n\n.notebook-report__content__header {\n padding: 8px;\n background-color: #ffffff;\n font-size: 16px; }\n\n@media only screen and (max-width: 599px) {\n .notebook-report-container:not(.notebook-report-container__collapsed) {\n top: 0;\n bottom: 0;\n left: 0;\n right: 0;\n max-height: none;\n max-width: none;\n height: auto;\n width: auto; }\n .notebook-report-container:not(.notebook-report-container__collapsed) .notebook-report {\n border-radius: 0; }\n .notebook-tools--full {\n display: none; } }\n\n.notebook-report-backdrop {\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n z-index: 3;\n background-color: #212121;\n opacity: .48; }\n\n.notebook-menu {\n transition: opacity 500ms; }\n .notebook-menu.ng-enter {\n opacity: 0; }\n .notebook-menu .ng-enter-active {\n opacity: 1;\n transition-delay: 250ms; }\n .notebook-menu.ng-leave-active, .notebook-menu.ng-hide {\n opacity: 0; }\n .notebook-menu.ng-hide-add, .notebook-menu.ng-hide-add-active, .notebook-menu.ng-hide-remove, .notebook-menu.ng-hide-remove-active {\n opacity: 0; }\n\n.notebook-item {\n transition: box-shadow 250ms;\n margin: 0 16px 16px;\n display: block; }\n\n.notebook-item__content {\n height: 250px;\n min-width: 230px;\n position: relative;\n padding: 0;\n background-color: #cccccc;\n border-top-left-radius: 4px;\n border-top-right-radius: 4px; }\n\n.notebook-item__content__attachment, .notebook-item__content__text {\n position: absolute;\n left: 0;\n right: 0; }\n\n.notebook-item__content__attachment {\n background-repeat: no-repeat !important;\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n background-position: center top !important;\n background-size: cover !important;\n top: 0;\n bottom: 0; }\n\n.notebook-item__content__text {\n bottom: 0;\n padding: 8px;\n font-weight: 500;\n overflow: hidden;\n max-height: 120px;\n min-height: 56px;\n background-color: rgba(255, 255, 255, 0.95);\n border-top: 1px solid #eeeeee; }\n .notebook-item__content__text:after {\n content: \"\";\n text-align: right;\n position: absolute;\n bottom: 0;\n right: 0;\n width: 100%;\n height: 0.8em;\n background: linear-gradient(180deg, rgba(255, 255, 255, 0), rgba(255, 255, 255, 0.95) 100%); }\n\n.notebook-item__content--text-only:after {\n content: \"note\";\n font-family: 'Material Icons';\n font-weight: normal;\n font-style: normal;\n display: inline-block;\n line-height: 1;\n text-transform: none;\n letter-spacing: normal;\n word-wrap: normal;\n white-space: nowrap;\n direction: ltr;\n /* Support for all WebKit browsers. */\n -webkit-font-smoothing: antialiased;\n /* Support for Safari and Chrome. */\n text-rendering: optimizeLegibility;\n /* Support for Firefox. */\n -moz-osx-font-smoothing: grayscale;\n /* Support for IE. */\n font-feature-settings: 'liga';\n font-size: 80px;\n color: rgba(0, 0, 0, 0.26); }\n\n.notebook-item--question__content--text-only {\n content: \"live_help\"; }\n\n.notebook-item__content__location {\n opacity: 0.9;\n padding: 8px 0; }\n .notebook-item__content__location md-icon {\n font-size: 22px; }\n\n.notebook-item__edit {\n cursor: pointer; }\n\n.notebook-item__actions {\n margin: 0;\n padding: 0 8px;\n color: #ffffff;\n background-color: #333333;\n border-bottom-left-radius: 4px;\n border-bottom-right-radius: 4px; }\n .notebook-item__actions md-icon {\n color: #ffffff; }\n\n.notebook-item__text-input {\n margin: 0; }\n\n.notebook-item__text-input__textarea {\n padding-left: 0;\n padding-right: 0; }\n\n.notebook-item__attachment {\n background-color: #dddddd;\n padding: 16px;\n margin-bottom: 16px;\n text-align: center;\n position: relative; }\n\n.notebook-item__attachment__content {\n max-width: 100%;\n height: auto; }\n\n.notebook-item__attachment__delete {\n position: absolute;\n top: 4px;\n right: -2px;\n width: 34px !important;\n height: 34px !important;\n min-height: 0; }\n .notebook-item__attachment__delete md-icon {\n margin-left: -2px;\n font-size: 22px; }\n\n.notebook-item__info {\n font-style: italic;\n opacity: .8;\n color: #8a6942; }\n .notebook-item__info a, .notebook-item__info md-icon {\n color: #8a6942; }\n .notebook-item__info md-icon {\n font-size: 1.5em;\n min-width: 0;\n width: auto; }\n\n.notebook-item__upload {\n text-align: center;\n padding: 24px;\n background-color: #eeeeee;\n margin-bottom: 16px;\n color: rgba(0, 0, 0, 0.54);\n border-radius: 4px;\n cursor: pointer;\n border: 1px dashed transparent;\n transition: all 250ms; }\n .notebook-item__upload md-icon, .notebook-item__upload span {\n transition: color 250ms; }\n .notebook-item__upload:hover, .notebook-item__upload:focus, .notebook-item__upload.dragover {\n border-color: #F05843;\n background-color: #fdebe8;\n color: #F05843; }\n .notebook-item__upload:hover md-icon, .notebook-item__upload:hover span, .notebook-item__upload:focus md-icon, .notebook-item__upload:focus span, .notebook-item__upload.dragover md-icon, .notebook-item__upload.dragover span {\n color: #F05843; }\n\n.view-notebook-item {\n width: 600px; }\n\n.notebook-item--report {\n background-color: #ffffff; }\n .notebook-item--report .note-editor {\n margin-bottom: 16px;\n border-color: #cccccc; }\n\n.notebook-item--report__container.ui-scrollpoint .notebook-item--report__toolbar {\n position: fixed;\n top: 94px;\n left: 0;\n right: 0;\n z-index: 1; }\n @media only screen and (min-width: 600px) {\n .notebook-item--report__container.ui-scrollpoint .notebook-item--report__toolbar {\n left: 54px; } }\n @media only screen and (min-width: 600px) {\n .notebook-item--report__container.ui-scrollpoint .notebook-item--report__toolbar {\n padding: 0 24px; } }\n @media only screen and (min-width: 960px) {\n .notebook-item--report__container.ui-scrollpoint .notebook-item--report__toolbar {\n padding: 0 32px; } }\n .notebook-item--report__container.ui-scrollpoint .notebook-item--report__toolbar .note-toolbar {\n margin: 0 16px; }\n @media only screen and (min-width: 600px) {\n .notebook-item--report__container.ui-scrollpoint .notebook-item--report__toolbar .note-toolbar {\n margin: 0 8px; } }\n @media only screen and (min-width: 960px) {\n .notebook-item--report__container.ui-scrollpoint .notebook-item--report__toolbar .note-toolbar {\n margin: 0 16px; } }\n\n.notebook-item--report__container.ui-scrollpoint .note-editor {\n padding-top: 40px; }\n\n.notebook-item--report__toolbar .note-toolbar {\n background-color: #dddddd;\n border: 1px solid #cccccc;\n margin-bottom: -2px; }\n\n.notebook-item--report__heading {\n text-align: center;\n margin-bottom: 32px; }\n\n.notebook-item--report__add-note {\n font-weight: 700; }\n\n.notebook-item--report__note-img {\n max-width: 100%;\n height: auto !important; }\n\n@media only screen and (min-width: 600px) {\n .notebook-sidebar {\n width: 400px;\n max-width: none; } }\n\n@media only screen and (min-width: 960px) {\n .notebook-sidebar {\n width: 500px;\n max-width: none; } }\n\n.notebook-items {\n width: 100%;\n overflow: auto;\n margin-top: 16px;\n margin-bottom: 76px; }\n .notebook-items .notebook-item {\n width: 100%; }\n .notebook-items .notebook-item__content {\n height: 200px;\n min-width: 0; }\n\n.notebook-items--grading {\n margin-bottom: 0; }\n\n@media only screen and (max-width: 599px) {\n .notebook-enabled .md-fab-bottom-right, .notebook-enabled .md-fab-bottom-left {\n bottom: 50px !important; } }\n\n.notebook-grading {\n background-color: #ffffff;\n display: block; }\n\n.notification-btn {\n width: 60px !important; }\n .notification-btn md-icon {\n margin-left: 20px; }\n\n.notification-count {\n border-radius: 50%;\n position: absolute;\n background-color: #F05843;\n width: 22px;\n left: 4px;\n height: 22px;\n line-height: 18px;\n font-size: 12px;\n font-weight: 700;\n border: 2px solid; }\n .notification-count:before {\n content: \"\";\n position: absolute;\n right: -7px;\n top: 5px;\n border-left: 6px solid rgba(255, 255, 255, 0.87);\n border-top: 4px solid transparent;\n border-bottom: 4px solid transparent; }\n\n.notification-list {\n padding: 8px 0; }\n\n.notification-dismiss {\n width: 500px; }\n\n.notification-dismiss__input {\n margin-bottom: 0; }\n\nmd-list md-list-item .md-list-item-text h4.notification-list-item__source,\nmd-list md-list-item.md-2-line .md-list-item-text h4.notification-list-item__source,\nmd-list md-list-item.md-3-line .md-list-item-text h4.notification-list-item__source {\n color: rgba(0, 0, 0, 0.54);\n font-size: 12px; }\n md-list md-list-item .md-list-item-text h4.notification-list-item__source md-icon,\n md-list md-list-item.md-2-line .md-list-item-text h4.notification-list-item__source md-icon,\n md-list md-list-item.md-3-line .md-list-item-text h4.notification-list-item__source md-icon {\n font-size: 18px;\n min-width: 0;\n width: auto;\n margin-left: -4px;\n line-height: 20px; }\n\n.account-menu {\n border-radius: 4px;\n padding: 0;\n font-size: 15px;\n max-width: 380px; }\n @media (min-width: 1280px) {\n .account-menu {\n min-width: 380px !important; } }\n .account-menu h3 {\n margin: 0;\n font-weight: 300; }\n\n.account-menu--fixed-height {\n height: 304px; }\n\n.account-menu--fixed-width {\n width: 320px; }\n @media (min-width: 960px) {\n .account-menu--fixed-width {\n width: 380px; } }\n\n.account-menu__icon {\n background-color: white;\n border-radius: 50%; }\n\n.account-menu__caret {\n position: absolute;\n right: 28px;\n top: -8px;\n outline: none; }\n .account-menu__caret:before {\n content: '';\n position: absolute;\n border-bottom: 8px solid #fff;\n border-left: 8px solid transparent;\n border-right: 8px solid transparent; }\n\n.account-menu__caret--pause, .account-menu__caret--notification {\n right: 80px; }\n\n.account-menu__caret--notification--with-pause {\n right: 132px; }\n\n[dir=rtl] .account-menu__caret {\n right: auto;\n left: 28px; }\n\n[dir=rtl] .account-menu__caret--pause, [dir=rtl] .account-menu__caret--notification {\n left: 80px;\n right: auto; }\n\n[dir=rtl] .account-menu__caret--notification--with-pause {\n left: 132px;\n right: auto; }\n\n.account-menu__info {\n padding: 8px 12px; }\n\n.account-menu__info__title {\n font-weight: 500; }\n\n.account-menu__info__team {\n font-weight: 400;\n color: rgba(0, 0, 0, 0.54); }\n\n.account-menu__users {\n padding: 0; }\n .account-menu__users md-list-item {\n padding: 0; }\n .account-menu__users md-list-item .md-avatar {\n margin: 0 8px 0 0;\n height: 48px;\n width: 48px; }\n\n.account-menu__progress md-progress-linear {\n transform: rotate(270deg);\n width: 26px; }\n\n.account-menu__grade {\n position: relative;\n margin-right: 4px; }\n .account-menu__grade md-icon {\n color: #FFC107; }\n\n.account-menu__grade__overlay {\n position: absolute;\n top: 0;\n width: 100%;\n background-color: rgba(255, 255, 255, 0.6); }\n\n.account-menu__actions {\n background-color: #f7f7f7; }\n\n.account-menu__control {\n padding: 16px; }\n\n.annotations {\n margin: 16px 4px 16px 62px;\n position: relative;\n font-size: 15px; }\n .annotations hr {\n margin: 10px 0 8px;\n border-color: rgba(0, 0, 0, 0.12); }\n .annotations:after {\n content: \"\";\n position: absolute;\n width: 0;\n height: 0;\n left: -16px;\n right: auto;\n top: 0px;\n bottom: auto;\n border-top: 20px solid transparent;\n border-bottom: 20px solid transparent;\n border-right: 16px solid #757575; }\n\n.annotations-container--student--report {\n border-top: 1px solid #dddddd; }\n\n.annotations--report {\n margin-top: 0;\n margin-bottom: 0; }\n\n.annotations__header {\n position: relative;\n border-top-right-radius: 4px;\n padding: 10px 12px;\n font-weight: 700;\n transition: all 1s;\n color: #ffffff;\n background-color: #757575; }\n\n.annotations__avatar {\n background-color: #F05843;\n padding: 2px;\n position: absolute;\n top: 0;\n left: -62px; }\n\n.annotations__icon {\n transition: all 1s;\n color: #ffffff; }\n\n.annotations__body {\n padding: 12px;\n background-color: #ffffff;\n border-bottom-left-radius: 4px;\n border-bottom-right-radius: 4px;\n overflow: auto; }\n\n.annotations__status {\n background-color: #ffffff;\n color: #ef6c00;\n display: inline-block;\n margin-left: 8px;\n font-size: 12px; }\n .annotations__status.ng-enter, .annotations__status.ng-leave {\n transition: all 1s; }\n .annotations__status.ng-enter, .annotations__status.ng-leave.ng-leave-active {\n opacity: 0; }\n .annotations__status.ng-leave, .annotations__status.ng-enter.ng-enter-active {\n opacity: 1; }\n\n.annotations__score {\n font-weight: 700; }\n\n.annotations__info {\n font-style: italic;\n opacity: 0.8;\n border-bottom: 1px dotted;\n font-size: 13px; }\n\n.annotations--inside .annotations {\n margin-left: 72px; }\n\n.annotations--info {\n margin-bottom: 32px;\n margin-right: 8px;\n margin-left: 72px; }\n @media only screen and (min-width: 600px) {\n .annotations--info {\n margin: 16px 16px 32px 76px; } }\n .annotations--info:after {\n border-right: 16px solid #ef6c00; }\n .annotations--info .annotations__avatar {\n background-color: #ffffff; }\n .annotations--info .annotations__header {\n background-color: #ef6c00; }\n\n.component {\n position: relative; }\n\n.component__wrapper {\n padding: 0 16px;\n margin: 24px 0; }\n\n.component__content {\n overflow-x: auto;\n font-size: 15px;\n overflow-y: hidden; }\n @media only screen and (min-width: 600px) {\n .component__content {\n padding: 0 8px; } }\n\nh3.component__header, .component-header {\n padding: 8px 12px;\n margin: 0;\n font-size: 14px; }\n\n.component__rubric {\n position: absolute;\n left: -20px;\n top: 12px; }\n\n.notebook-enabled .component_content img {\n transition: all 250ms;\n cursor: pointer;\n cursor: copy; }\n .notebook-enabled .component_content img:hover, .notebook-enabled .component_content img:focus {\n box-shadow: 0 0 5px 1px #F05843; }\n\n.component__actions .md-button:first-child {\n margin-left: 0; }\n\n.component__actions .md-button:last-child {\n margin-right: 0; }\n\n.component__actions__info {\n font-style: italic;\n margin-left: 8px;\n color: rgba(0, 0, 0, 0.54); }\n\n.component__actions__more {\n border-bottom: 1px dotted; }\n\n.component__prompt {\n margin-bottom: 8px;\n font-weight: 500; }\n\n.component__prompt__content {\n display: inline; }\n\n.component__attachment {\n position: relative;\n margin: 0 8px;\n padding-bottom: 8px; }\n @media only screen and (min-width: 600px) {\n .component__attachment {\n padding-top: 8px; } }\n\n@media only screen and (max-width: 599px) {\n .component__add-attachment {\n width: 100%; } }\n\n.component__attachment__content {\n max-height: 100px;\n width: auto; }\n\n.component__attachment__delete {\n position: absolute;\n top: 0;\n right: 0;\n min-width: 0;\n background-color: rgba(255, 255, 255, 0.75) !important;\n border-radius: 0;\n padding: 4px;\n margin: 0; }\n .component__attachment__delete > md-icon {\n margin-top: 0; }\n\n.component__revision {\n margin: 8px 0;\n padding: 8px; }\n .component__revision:nth-child(odd) {\n background-color: #f7f7f7; }\n\n.component__revision__content {\n padding: 4px 0 8px 0;\n border-bottom: 1px solid #dddddd; }\n\n.component__revision__actions {\n color: #757575;\n padding-top: 4px; }\n\n.component__content--Discussion {\n overflow: hidden; }\n\n.discussion-content {\n background-color: #eeeeee;\n box-shadow: inset 0 0 3px #aaaaaa; }\n\n.discussion-posts {\n padding: 12px 12px 8px; }\n @media only screen and (min-width: 1280px) {\n .discussion-posts {\n padding: 16px 16px 0; } }\n\n.discussion-post {\n margin: 0 auto 16px;\n max-width: 600px; }\n @media only screen and (min-width: 600px) {\n .discussion-post {\n margin-bottom: 24px; } }\n @media only screen and (min-width: 1280px) {\n .discussion-post {\n margin-bottom: 32px; } }\n .discussion-post md-divider {\n position: relative;\n width: auto; }\n\n.discussion-post__contents {\n padding: 16px; }\n\n.discussion-post__avatar, md-list-item > .md-avatar.discussion-post__avatar {\n margin-right: 8px; }\n\n.discussion-post__avatar--reply, md-list-item > .md-avatar.discussion-post__avatar--reply {\n margin-top: 8px; }\n\n.discussion-post__user, md-list-item .md-list-item-text h3.discussion-post__user {\n padding-bottom: 4px;\n font-weight: 700;\n line-height: 1.3;\n overflow: visible;\n white-space: normal; }\n\n.discussion-post__date {\n color: #aaaaaa; }\n\n.discussion-post__date--reply {\n margin-left: 8px;\n font-weight: 400; }\n\n.discussion-post__content {\n margin-top: 16px;\n white-space: pre-wrap; }\n\n.discussion-post__attachment {\n max-width: 100%;\n height: auto !important;\n margin-top: 16px; }\n\n.discussion-new {\n background-color: #ffffff;\n max-width: 570px;\n margin-left: auto;\n margin-right: auto;\n padding: 8px;\n transition: all 250ms;\n transform: scale(0.95); }\n\n.discussion-new--focused {\n transform: scale(1); }\n\nmd-input-container.discussion-new__input-container {\n margin: 0;\n padding: 0; }\n md-input-container.discussion-new__input-container > textarea.md-input {\n min-height: 68px; }\n\n.discussion-new__input--textarea, .input-container textarea.discussion-new__input--textarea {\n padding: 8px;\n border: 0 none; }\n\n.discussion-new__actions {\n padding: 0 8px; }\n .discussion-new__actions .md-button:first-of-type {\n margin-left: 0; }\n .discussion-new__actions .md-button:last-of-type {\n margin-right: 0; }\n\n.discussion-new__attachment {\n padding: 0;\n margin: 0 0 8px; }\n\n.discussion-new__attachment__content {\n margin-top: 0;\n margin-bottom: 16px; }\n\n.discussion-comments {\n padding: 0; }\n\n.discussion-comments__contents {\n background-color: #f7f7f7; }\n\n.discussion-comments__header {\n background-color: transparent;\n padding: 0; }\n .discussion-comments__header .md-subheader-inner {\n padding-bottom: 8px; }\n\n.discussion-comments__list {\n padding: 0;\n max-height: 9999px;\n overflow-y: auto; }\n @media only screen and (min-width: 600px) {\n .discussion-comments__list {\n max-height: 400px; } }\n\n.input--textarea.discussion-reply__input, .input-container textarea.input--textarea.discussion-reply__input {\n background-color: #ffffff;\n padding: 4px;\n font-size: 14px;\n border: 0 none;\n margin-left: -1px;\n margin-bottom: 0;\n resize: none; }\n\n.discussion-reply, md-list-item.discussion-reply {\n margin: 0 12px 8px;\n padding: 0;\n min-height: 56px; }\n\n.discusstion-reply__details, md-list-item .md-list-item-text.discusstion-reply__details {\n margin: 8px 0; }\n\n.discussion-post__user--reply, md-list-item .md-list-item-text h3.discussion-post__user--reply {\n font-size: 14px;\n padding: 0;\n margin: 0; }\n\n.discusstion-reply__content {\n margin-top: 2px; }\n .discusstion-reply__content p {\n font-weight: 400 !important;\n color: rgba(0, 0, 0, 0.87) !important; }\n\n.discussion-new-reply {\n padding: 8px; }\n\n.discussion-new-reply__input-container {\n padding-top: 0;\n margin: 0; }\n .discussion-new-reply__input-container .md-errors-spacer {\n display: none; }\n\n.discussion-new-reply__actions {\n margin-left: 8px; }\n .discussion-new-reply__actions .md-button {\n margin-top: 0;\n margin-bottom: 0; }\n\n.embedded-content__iframe {\n border: 0 none; }\n\n.graph-select {\n min-width: 150px;\n max-width: 200px; }\n\n.graph-controls {\n margin: 8px 0;\n padding: 8px 0;\n border: 1px solid #eeeeee;\n border-left-width: 0;\n border-right-width: 0; }\n\n.match-content {\n background-color: #eeeeee;\n margin-bottom: 16px;\n padding: 8px;\n box-shadow: inset 0 0 3px #aaaaaa; }\n\n.match-divider {\n margin: 16px 8px 8px; }\n\n@media only screen and (min-width: 960px) {\n .match-divider--horizontal {\n display: none; } }\n\n.match-bucket__header {\n padding: 12px;\n font-weight: 500;\n color: #7e57c2; }\n\n.match-bucket__content {\n padding: 0; }\n\n.match-bucket--choices .match-bucket__header {\n color: #795C3A; }\n\n.match-bucket__contents {\n min-height: 120px;\n padding: 0 8px 8px;\n background-color: #dddddd;\n transition: background-color 250ms;\n border-bottom-left-radius: 4px;\n border-bottom-right-radius: 4px;\n column-gap: 8px; }\n @media only screen and (max-width: 599px) {\n .match-bucket__contents {\n column-count: 1 !important; } }\n .match-bucket__contents img {\n max-width: 100%;\n height: auto; }\n\n.match-bucket__contents--over {\n background-color: #7e57c2; }\n\n.match-bucket__item {\n list-style-type: none;\n cursor: move;\n padding-top: 8px;\n break-inside: avoid; }\n\n.match-bucket__item__contents {\n background-color: #ffffff;\n padding: 8px !important;\n border: 1px solid #cccccc; }\n .match-bucket__item__contents .md-list-item-text {\n width: 100%; }\n\n.match-bucket__item__contents__text {\n margin-right: 4px; }\n\n.match-feedback {\n transition: opacity 250ms;\n /*padding-left: 8px;\n border-left: 4px solid;*/\n margin: 8px -8px -8px;\n color: #ffffff;\n padding: 4px 8px; }\n .match-feedback.ng-hide {\n opacity: 0;\n transition: opacity 1ms; }\n .match-feedback md-icon {\n color: #ffffff; }\n\n.outside-content iframe {\n border: 1px solid #eeeeee; }\n\n.outside-content__source {\n margin-top: 4px;\n text-align: end; }\n .outside-content__source a {\n max-width: 240px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n display: inline-block; }\n\n.notebook-toolbar md-divider {\n margin: 8px 0; }\n\n@media only screen and (max-width: 959px) {\n .notebook-toolbar {\n border-top: 1px solid #dddddd; } }\n\n.notebook-toolbar__add-menu {\n position: absolute;\n bottom: 40px; }\n .notebook-toolbar__add-menu .md-fab-action-item {\n background-color: #ffffff; }\n\n.notebook-toolbar__add-icon {\n border-radius: 50%; }\n\n#closeNotebookSettingsButton {\n float: right; }\n\n[dir=rtl] #closeNotebookSettingsButton {\n float: left; }\n\nhighchart {\n display: block; }\n","// 1. Config\n\n// 2. Base\n.l-nav {\n background-color: color('body-bg') !important;\n}\n","// 1. Config\n\n// 2. Base\n.l-node {\n margin-top: $wise-toolbar-height;\n padding: 0;\n\n @media only screen and (min-width: $layout-breakpoint-xs) {\n padding: 0 0 16px;\n background-color: color('body-bg') !important;\n }\n\n @media only screen and (min-width: $layout-breakpoint-sm) {\n padding: 0 0 32px;\n }\n}\n","// 1. Config\n\n// 2. Base\n.l-notebook {\n margin-top: $wise-toolbar-height;\n background-color: color('body-bg') !important;\n}\n","// 1. Config\n\n// 2. Base\n.l-sidebar {\n\n}\n\n.l-sidebar__header {\n background-color: #ffffff !important;\n color: color('accent-1') !important;\n\n md-select {\n color: color('body');\n }\n}",".status-icon {\n margin: 0 4px;\n z-index: 1;\n vertical-align: bottom;\n}\n\n.md-button.status-icon {\n height: auto;\n width: auto;\n min-height: 0;\n line-height: inherit;\n margin: 0 4px;\n padding: 0;\n}\n\n.status-corner-wrapper {\n position: absolute;\n z-index: 1;\n overflow: hidden;\n}\n\n.status-corner {\n width: 0;\n height: 0;\n border-top-color: color('text-disabled');\n border-bottom-color: color('text-disabled');\n color: color('text-disabled');\n\n &:after {\n content: '!';\n color: #ffffff;\n top: 2px;\n right: 10px;\n position: absolute;\n font-weight: 700;\n }\n}\n\n.status-corner--warn {\n border-top-color: color('warn') !important;\n border-bottom-color: color('warn') !important;\n}\n\n.status-corner-top-right {\n top: 0;\n right: 0;\n border-top-right-radius: $card-border-radius;\n\n .status-corner {\n border-top: 36px solid;\n border-left: 36px solid transparent;\n }\n}\n\n.status-corner-top-left {\n top: 0;\n left: 0;\n border-top-left-radius: $card-border-radius;\n\n .status-corner {\n border-top: 36px solid;\n border-right: 36px solid transparent;\n }\n}\n\n.status-corner-bottom-right {\n bottom: 0;\n right: 0;\n border-bottom-right-radius: $card-border-radius;\n\n .status-corner {\n border-bottom: 36px solid;\n border-left: 36px solid transparent;\n }\n}\n\n.status-corner-bottom-left {\n bottom: 0;\n left: 0;\n border-bottom-left-radius: $card-border-radius;\n\n .status-corner {\n border-bottom: 36px solid;\n border-right: 36px solid transparent;\n }\n}\n\n.avatar--icon--alert {\n background-color: #ffffff;\n}\n\n.avatar--icon--alert__icon {\n font-size: 48px;\n margin: -4px 0 0 -4px;\n}\n","md-dialog {\n width: $layout-breakpoint-xs;\n}\n\n.dialog--wide {\n width: $layout-breakpoint-sm;\n}\n\n.dialog--wider {\n width: $layout-breakpoint-md;\n}\n",".help-bubble {\n border-radius: $card-border-radius;\n max-width: 320px;\n\n @media (min-width: $layout-breakpoint-xs) {\n max-width: ($layout-breakpoint-xs - 48);\n }\n\n @media (min-width: $layout-breakpoint-sm) {\n max-width: ($layout-breakpoint-sm - 48);\n }\n\n @media (min-width: $layout-breakpoint-md) {\n max-width: ($layout-breakpoint-md - 48);\n }\n}\n\n.help-bubble__title {\n border-top-left-radius: $card-border-radius;\n border-top-right-radius: $card-border-radius;\n}\n\n.help-bubble___title__content {\n border-top-left-radius: $card-border-radius;\n border-top-right-radius: $card-border-radius;\n padding: 0px 0 0 12px;\n background-color: color('info');\n\n .md-icon-button {\n margin-right: 0;\n padding-top: 0;\n padding-bottom: 0;\n }\n}\n\n.help-bubble__content {\n overflow: auto;\n padding: 8px 12px;\n max-height: 480px;\n}\n\n.help-bubble__actions {\n border-bottom-left-radius: $card-border-radius;\n border-bottom-right-radius: $card-border-radius;\n}\n\ndiv.hopscotch-bubble {\n border-radius: $card-border-radius;\n //border: 2px solid color('gray-darker');\n border: 0 none;\n font-family: Roboto, \"Helvetica Neue\", sans-serif;\n font-size: rem(1.4);\n z-index: 6;\n\n .hopscotch-bubble-arrow-container {\n position: absolute;\n width: 20px;\n height: 20px;\n }\n\n .hopscotch-bubble-arrow-container {\n &.up {\n top: 0;\n left: 12px;\n\n .hopscotch-bubble-arrow {\n border-bottom: 10px solid color('info');\n border-left: 10px solid transparent;\n border-right: 10px solid transparent;\n position: relative;\n top: -10px;\n }\n\n .hopscotch-bubble-arrow-border {\n border: 0 none;\n /*border-bottom: 12px solid color('gray-darker');\n border-left: 12px solid transparent;\n border-right: 12px solid transparent;*/\n }\n }\n\n &.down {\n bottom: -34px;\n left: 12px;\n\n .hopscotch-bubble-arrow {\n border-top: 10px solid color('info');\n border-left: 10px solid transparent;\n border-right: 10px solid transparent;\n position: relative;\n top: -10px;\n }\n\n .hopscotch-bubble-arrow-border {\n border: 0 none;\n /*border-top: 12px solid color('gray-darker');\n border-left: 12px solid transparent;\n border-right: 12px solid transparent;*/\n }\n }\n\n &.left {\n top: 12px;\n left: -10px;\n\n .hopscotch-bubble-arrow {\n border-right: 10px solid color('info');\n border-bottom: 10px solid transparent;\n border-top: 10px solid transparent;\n position: relative;\n left: 0;\n top: -10px;\n }\n\n .hopscotch-bubble-arrow-border {\n border: 0 none;\n /*border-right: 12px solid color('gray-darker');\n border-bottom: 12px solid transparent;\n border-top: 12px solid transparent;*/\n }\n }\n\n &.right {\n top: 12px;\n right: -30px;\n\n .hopscotch-bubble-arrow {\n border-left: 10px solid color('info');\n border-bottom: 10px solid transparent;\n border-top: 10px solid transparent;\n position: relative;\n right: 0;\n top: -10px;\n }\n\n .hopscotch-bubble-arrow-border {\n border: 0 none;\n /*border-right: 12px solid color('gray-darker');\n border-bottom: 12px solid transparent;\n border-top: 12px solid transparent;*/\n }\n }\n }\n}\n","// Variables\n$input-action-width: 44px;\n$input-action-vertical-offset: 6px;\n$input-action-vertical-offset-richtext: -7px;\n\n// Base\n.input-container {\n padding-top: 12px;\n}\n\n.input-container--component {\n margin-bottom: 0;\n}\n\n.input-container--open-response {\n &.md-has-icon {\n padding-left: 0;\n }\n\n .md-errors-spacer {\n display: none;\n }\n}\n\n.input-wrapper {\n position: relative;\n}\n\n.input-wrapper--focused {\n .input--textarea__action md-icon {\n color: color('primary');\n }\n}\n\n.input--textarea, .input-container textarea.input--textarea {\n padding: 8px;\n background-color: color('gray-lightest');\n border: 1px solid color('gray');\n margin-bottom: 8px;\n\n &:focus {\n background-color: #ffffff;\n }\n\n &[disabled] {\n color: color('text-secondary');\n }\n}\n\n.input-container textarea.input--textarea {\n width: 100%;\n}\n\n.input--textarea--disabled {\n color: color('text-secondary');\n}\n\n.input--textarea__action {\n position: absolute;\n right: -4px;\n\n &[disabled] md-icon {\n color: color('text-disabled') !important;\n }\n}\n\n.input--textarea__action--notebook {\n top: $input-action-vertical-offset;\n\n .input-wrapper--richtext & {\n top: $input-action-vertical-offset-richtext\n }\n}\n\n.input--textarea__action--revision {\n bottom: $input-action-vertical-offset;\n\n .input-wrapper--richtext & {\n bottom: ($input-action-vertical-offset-richtext + 2px);\n }\n\n}\n\n.input-label, md-input-container:not(.md-input-invalid):not(.md-input-focused).md-input-has-value label.input-label {\n line-height: 1.2;\n color: color('text');\n\n &.input-label--focused {\n color: color('primary');\n }\n}\n\n.autocomplete {\n input {\n text-overflow: ellipsis;\n overflow: hidden;\n word-wrap: none;\n font-weight: 500;\n color: color('text-secondary');\n }\n}\n\n.autocomplete--minwidth {\n @media only screen and (min-width: $layout-breakpoint-xs) {\n min-width: 300px;\n }\n\n @media only screen and (min-width: $layout-breakpoint-sm) {\n min-width: 300px;\n }\n}\n\n.autocomplete--flat {\n md-autocomplete-wrap {\n background-color: #ffffff;\n\n &:not(.md-menu-showing) {\n box-shadow: none;\n background-color: color('gray-lighter');\n }\n }\n}\n\n.select__header {\n height: $menu-item-height;\n\n input {\n height: 100%;\n width: 100%;\n padding: 0 8px;\n outline: none;\n border: 0 none;\n font-size: rem(1.4);\n font-weight: 500;\n }\n}\n",".table {\n max-width: 100%;\n width: auto;\n min-width: 100px;\n margin: 8px 0;\n\n thead,\n tbody,\n tfoot {\n // TODO: remove chaining when bootstrap dependency is removed\n > tr > th,\n > tr > td {\n border: 1px solid color('gray');\n padding: 6px;\n font-size: rem(1.5);\n min-height: 32px;\n height: 32px;\n min-width: 32px;\n vertical-align: top;\n }\n }\n\n td.inactive,\n th {\n background-color: color('gray-lightest');\n opacity: 1;\n visibility: visible;\n }\n\n md-input-container {\n margin: 0;\n }\n\n .md-errors-spacer {\n display: none;\n }\n}\n\n.table--student {\n td {\n &.inactive {\n padding: 8px 10px;\n }\n }\n}\n\n.table--full-width {\n width: 100%;\n}\n\n.table--list {\n border: 0 none;\n border-collapse: collapse;\n background-color: #ffffff;\n max-width: 100%;\n overflow: auto;\n\n th,\n td {\n padding: 0 4px;\n border: 0 none;\n }\n\n td {\n min-height: 56px;\n height: 56px;\n }\n\n tr {\n &.md-button {\n display: table-row;\n text-align: left;\n width: auto;\n text-transform: none;\n font-size: inherit;\n font-weight: normal;\n }\n }\n}\n\n.table--list__wrap {\n min-width: $layout-breakpoint-xs;\n}\n\n.table-wrap-sticky {\n @media only screen and (max-width: $layout-breakpoint-sm - 1) {\n overflow-x: auto;\n }\n}\n\n.table--list__thead {\n font-size: rem(1.4);\n font-weight: 700;\n}\n\n.table--list__thead__tr {\n height: 100%;\n margin: 0;\n}\n\n.table--list__thead__th {\n background-color: color('gray-darker');\n color: color('text-light');\n min-height: $wise-toolbar-height;\n height: $wise-toolbar-height;\n}\n\n.table--list__thead__link {\n color: #ffffff;\n text-transform: none;\n margin: 0;\n min-width: 0;\n white-space: normal;\n line-height: 1.4;\n width: 100%;\n}\n\n.table--list__thead__sort {\n margin: 0;\n}\n\n.table--list__thead__sort--reverse {\n transform: rotate(180deg);\n}\n\n.td--wrap {\n min-width: 180px;\n white-space: normal;\n line-height: 1.2;\n}\n\n.td--max-width {\n @media only screen and (max-width: $layout-breakpoint-sm - 1) {\n max-width: 180px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n}\n",".md-toolbar-tools {\n font-size: 18px;\n\n .autocomplete {\n height: 36px;\n\n md-autocomplete-wrap {\n height: 36px;\n }\n\n input {\n height: 36px;\n }\n }\n}\n\n.md-toolbar--wise {\n min-height: $wise-toolbar-height;\n\n .md-toolbar-tools {\n height: $wise-toolbar-height;\n max-height: $wise-toolbar-height;\n }\n\n .md-button.md-icon-button {\n height: $wise-toolbar-height;\n line-height: $wise-toolbar-height;\n width: $wise-toolbar-height;\n }\n}\n\n.md-toolbar--wise--sm {\n .md-toolbar-tools {\n padding: 0 8px;\n font-size: $body-font-size-base;\n }\n}\n\n.md-toolbar--sidenav {\n background-color: color('gray-darkest') !important;\n\n .md-toolbar-tools {\n font-size: rem(1.6);\n font-weight: 500;\n }\n}\n\n.toolbar {\n position: fixed;\n left: 0;\n right: 0;\n top: $md-toolbar-height;\n z-index: 3;\n}\n\n.toolbar__title {\n margin-left: 8px;\n font-size: rem(1.6);\n font-weight: 500;\n}\n\n.toolbar__tools {\n padding-right: 8px;\n}\n[dir=rtl] .toolbar__tools {\n padding-right: 16px;\n padding-left: 8px;\n}\n\n.toolbar__nav, .md-button.toolbar__nav {\n margin: 0;\n}\n\n.toolbar__select, .md-button.toolbar__select {\n margin: 0 4px;\n min-height: 32px;\n background-color: color('gray-lightest');\n\n .md-select-value {\n height: 32px;\n text-align: left;\n }\n}\n[dir=rtl] {\n .toolbar__select, .md-button.toolbar__select {\n .md-select-value {\n text-align: right;\n }\n }\n}\n\n.toolbar__select--fixedwidth {\n width: 168px;\n\n @media only screen and (min-width: $layout-breakpoint-xs) {\n width: 264px;\n }\n\n @media only screen and (min-width: $layout-breakpoint-sm) {\n width: 432px;\n }\n}\n",".list-item {\n background-color: #ffffff;\n border-bottom: 1px solid color('gray-lighter');\n\n .md-subheader, &.md-subheader {\n color: color('text');\n background-color: #ffffff;\n\n md-icon {\n vertical-align: middle;\n }\n\n .md-subheader-inner {\n padding: 0;\n }\n\n .md-avatar {\n margin-right: 8px;\n }\n }\n\n .autocomplete {\n margin: 8px 0;\n }\n}\n\n.list-item--info {\n &._md-button-wrap>div.md-button:first-child, .md-subheader-content {\n border-left: 4px solid color('info') !important;\n margin-left: -4px;\n }\n}\n\n.list-item--warn {\n //background-color: lighten(color('warn'), 56%);\n\n &._md-button-wrap>div.md-button:first-child, .md-subheader-content {\n border-left: 4px solid color('warn') !important;\n margin-left: -4px;\n }\n}\n\n.list-item--expanded {\n border-bottom-width: 0;\n}\n\n.list-item--noclick, .list-item--noclick.md-button {\n cursor: default;\n background-color: color('gray-lightest');\n}\n\n.list-item--actions {\n padding: 0 8px !important;\n}\n\n.list-item__subheader-button {\n text-transform: none;\n width: 100%;\n padding: 8px 16px;\n margin: 0;\n white-space: normal;\n text-align: left;\n line-height: 1.4;\n}\n\n.user-list {\n font-size: rem(1.5);\n}\n","#nav {\n position: relative;\n}\n\n.nav {\n margin-bottom: 16px;\n}\n\n.nav-mask {\n position: absolute;\n top: 0;\n bottom: 0;\n left: 0;\n right: 0;\n background-color: rgba(0,0,0,0.25);\n z-index: 1;\n\n &.ng-hide {\n opacity: 0;\n }\n}\n\n.nav-head {\n color: color('text-secondary');\n font-weight: 500;\n\n md-icon {\n line-height: 20px;\n }\n}\n\n.nav-contents--root {\n padding: 6px 6px 12px;\n}\n\n.nav-contents--group {\n background-color: color('gray-light');\n padding: 8px;\n}\n\n.nav-contents--root {\n padding: 0;\n}\n\n.nav-contents__list {\n padding: 0;\n\n @media (min-width: $layout-breakpoint-xs) {\n padding: 8px;\n }\n}\n\n.nav-item {\n transition: opacity 250ms ease-in-out;\n\n &.prev {\n md-list-item {\n background-color: color('selected-bg');\n\n /*&._md-button-wrap>div.md-button:first-child {\n border-left: 4px solid color('primary');\n margin-left: -4px;\n }*/\n }\n }\n}\n\n.nav-item--card__content {\n border-top-right-radius: $card-border-radius;\n border-top-left-radius: $card-border-radius;\n\n &:focus {\n outline: none;\n\n .nav-item__title > span {\n border-bottom: 1px dashed color('gray');\n }\n }\n}\n\n.nav-item--root {\n transition: margin 250ms, box-shadow 500ms;\n\n &.expanded {\n flex-basis: 100%;\n //max-width: ($layout-breakpoint-md - 100) !important;\n max-width: 100%;\n max-height: none !important;\n margin: 8px auto;\n padding-left: 4px;\n\n &:first-of-type {\n margin-top: 0;\n }\n\n //@media only screen and (min-width: $layout-breakpoint-sm) {\n //margin: 8px auto;\n //}\n }\n}\n\n//.nav-item--list {\n//}\n\n.nav-item--list__info-item {\n padding: 0 16px 0 4px;\n display: inline-block;\n}\n\n.nav-item--list__reorder {\n margin-left: 8px;\n color: color('text-disabled');\n}\n\n.nav-item--card {\n\n}\n\n.nav-item--card--group {\n &:not(.expanded) {\n box-shadow: 0px 3px 1px -2px rgba(0, 0, 0, 0.14), 0px 2px 2px 0px rgba(0, 0, 0, 0.098),\n 0px 1px 5px 0px rgba(0, 0, 0, 0.084), 3px 3px 0px 1px #d5d5d5, 6px 6px 0px 1px #aaaaaa;\n }\n}\n\n.nav-item__collapse {\n margin: 0;\n}\n\n.nav-item__more {\n border-top: 1px solid color('gray-light');\n border-bottom-right-radius: $card-border-radius;\n border-bottom-left-radius: $card-border-radius;\n padding: 8px 16px;\n min-height: 40px;\n}\n\n.nav-item__users {\n height: auto;\n cursor: pointer;\n color: #ffffff;\n margin: 0;\n padding: 1px 6px;\n\n > md-icon {\n padding-right: 4px;\n color: #ffffff;\n }\n\n &:hover, &:focus {\n &.success-bg {\n background-color: color('success');\n }\n }\n}\n\n.nav-item__title {\n padding-left: 16px;\n line-height: 1.2;\n font-weight: 400;\n}\n[dir=rtl] .nav-item__title {\n padding-left: auto;\n padding-right: 16px;\n}\n\n.nav-item__info {\n padding: 0 8px;\n}\n\n.nav-item__progress {\n width: 48px;\n\n > .md-container {\n top: 0;\n }\n}\n\n.nav-item__progress-value {\n margin-left: 8px;\n width: 36px;\n}\n\n.progress-wrapper {\n padding: 2px 0;\n cursor: pointer;\n}\n",".notice {\n text-align: center;\n padding: 8px;\n background-color: rgba(0,0,0,0.04);\n width: 100%;\n\n @media (min-width: $layout-breakpoint-xs) {\n max-width: 80%;\n border-radius: $button-border-radius;\n margin: 24px auto;\n }\n}","// 1. Variables\n$menu-sidebar-width: 56px;\n\n// 2. Base\n.menu-progress {\n position: absolute;\n top: 10px;\n right: 12px;\n\n path {\n stroke: color('accent-2') !important;\n stroke-width: 2px;\n }\n}\n[dir=rtl] .menu-progress {\n right:auto;\n left:12px;\n}\n\n.menu-sidenav {\n\n}\n\n.menu-sidenav__item {\n font-weight: 700;\n //color: color('text-secondary');\n font-size: rem(1.4);\n}\n\n.menu-sidenav__icon {\n margin-top: 12px !important;\n margin-right: 12px !important;\n margin-left: 12px;\n}\n\n.active {\n .menu-sidenav__icon, .menu-sidenav__item {\n color: color('primary');\n }\n}\n\n.menu-sidebar {\n position: absolute;\n top: $wise-toolbar-height + $md-toolbar-height;\n bottom: 0;\n left: 0;\n background-color: #fff;\n width: $menu-sidebar-width;\n overflow: hidden;\n padding: 8px 0;\n text-align: center;\n border-right: 1px solid color('gray');\n\n @media only screen and (max-width: ($layout-breakpoint-xs - 1)) {\n display: none;\n }\n}\n[dir=rtl] .menu-sidebar {\n right:0;\n left:auto;\n}\n\n.md-button.md-icon-button.menu-sidebar__link {\n margin-top: 6px;\n margin-bottom: 6px;\n}\n","// Variables\n$input-action-width: 48px;\n$input-action-vertical-offset: 0;\n$input-action-vertical-offset-richtext: -7px;\n\n// Base\n#node {\n margin: 0 auto;\n position: absolute;\n left: 0;\n right: 0;\n\n @media only screen and (min-width: $layout-breakpoint-xs) {\n padding: 8px;\n padding: 24px 16px;\n margin-bottom: 32px;\n }\n\n @media only screen and (min-width: $layout-breakpoint-sm) {\n padding: 32px;\n }\n\n &.ng-enter {\n transition: opacity .5s;\n opacity: 0;\n }\n\n &.ng-enter-active {\n opacity: 1;\n }\n}\n\n// TODO: use BEM conventions\n\n.node-notice {\n @media only screen and (min-width: $layout-breakpoint-xs) {\n margin-top: -8px;\n margin-bottom: 16px;\n }\n\n @media only screen and (min-width: $layout-breakpoint-sm) {\n margin-top: -16px;\n }\n}\n\n.node-content {\n padding: 0 0 48px;\n background-color: #ffffff;\n border-radius: $button-border-radius;\n overflow: visible;\n\n @media only screen and (max-width: $layout-breakpoint-xs - 1) {\n box-shadow: none;\n }\n\n @media only screen and (min-width: $layout-breakpoint-xs) {\n padding: 0;\n border-top: 2px solid;\n border-bottom: 2px solid;\n }\n}\n\nmd-content {\n &.node-content {\n background-color: #ffffff;\n }\n}\n\n.node-content__rubric {\n position: absolute;\n top: -22px;\n left: 0;\n right: 0;\n z-index: 1;\n\n .avatar--icon {\n transform: scale(0.94);\n\n @media only screen and (max-width: $layout-breakpoint-xs - 1) {\n transform: scale(0.8);\n }\n }\n}\n\n.node-icon {\n color: #ffffff;\n vertical-align: inherit;\n}\n\n.node-select {\n margin: 0 8px;\n min-width: 0;\n font-weight: 500;\n font-size: $body-font-size-base;\n\n .md-select-value {\n *:first-child {\n transform: translate3d(0,0,0);\n flex: 1 0 0;\n }\n\n .node-select__icon {\n display: none;\n }\n\n .node-select__status {\n display: none;\n }\n }\n\n .md-select-icon {\n margin-left: 0;\n color: color('text');\n }\n}\n\n.node-select-option--group {\n //color: rgba(0,0,0,0.54);\n background-color: color('gray-lightest');\n border-bottom: 1px solid color('gray-lighter');\n border-top: 1px solid color('gray-lighter');\n}\n\n.node-select-option--node {\n padding-left: 20px;\n}\n\n.node-select__icon {\n margin-right: 8px;\n}\n\n.node-select__status {\n margin-left: 8px;\n}\n\n.node-select__text {\n text-overflow: ellipsis;\n white-space: nowrap;\n overflow: hidden;\n\n @media only screen and (min-width: $layout-breakpoint-xs) {\n margin-top: 2px;\n }\n}\n\n.node-title {\n line-height: 1.2;\n text-transform: none;\n margin-top: 3px;\n\n @media only screen and (max-width: $layout-breakpoint-xs - 1) {\n font-size: $body-font-size-base;\n }\n}\n\n.node-content__actions {\n padding: 0 16px 16px;\n\n @media only screen and (min-width: $layout-breakpoint-sm) {\n padding: 0 24px 24px;\n }\n\n @media only screen and (min-width: $layout-breakpoint-md) {\n padding: 0 32px 32px;\n }\n\n .md-button:first-child {\n margin-left: 0;\n }\n\n .md-button:last-child {\n margin-right: 0;\n }\n}\n\n.node-content__actions__info {\n font-style: italic;\n margin-left: 8px;\n color: color('text-secondary');\n}\n\n.node-content__actions__more {\n border-bottom: 1px dotted;\n}\n\n.md-button.md-icon-button.node-nav {\n &:not(:first-of-type) {\n }\n\n &:first-of-type {\n margin-right: 0;\n }\n}\n\n.node-sidebar-active {\n @media only screen and (min-width: $layout-breakpoint-xs) {\n margin-right: ($md-toolbar-height + 16);\n }\n}\n\n.node-sidebar-visible {\n @media only screen and (max-width: $layout-breakpoint-xs - 1) {\n margin-bottom: $wise-toolbar-height;\n }\n}\n\n.node-sidebar {\n position: absolute;\n right: 0;\n top: 0;\n width: $md-toolbar-height;\n}\n\n.node-sidebar__toolbar {\n position: fixed;\n width: $md-toolbar-height;\n background-color: #ffffff;\n padding: 8px 0;\n border-radius: $button-border-radius;\n\n @media only screen and (max-width: $layout-breakpoint-xs - 1) {\n right: 0;\n bottom: 0;\n left: 0;\n width: 100%;\n border-radius: 0;\n padding: 0;\n min-height: 0;\n height: $wise-toolbar-height;\n }\n}\n\n// TODO: move to own sass module file (only gets used by teacher)\n// TODO: use BEM (.node--info)\n.node-info {\n margin: 0;\n\n @media only screen and (max-width: $layout-breakpoint-xs - 1) {\n margin: -16px;\n border-radius: 0;\n }\n\n .divider {\n margin-left: -8px;\n margin-right: -8px;\n }\n\n .component {\n &:first-child {\n margin-top: -16px;\n }\n }\n\n .component, .component__content, .component__header {\n margin-left: -8px;\n margin-right: -8px;\n }\n\n .component__actions {\n display: none;\n }\n}\n\n.node-rubric {\n border: 2px solid color('primary');\n margin: 8px 16px 24px;\n padding: 16px;\n background-color: #ffffff;\n}\n\n.node-rubric--component {\n\n}\n\n.node__label--vertical-alignment {\n vertical-align: middle;\n display: inline-block;\n}\n","// Variables\n$notebook-sidebar-width: 56px;\n\n// Base\n.notebook-launcher {\n @media only screen and (min-width: $layout-breakpoint-xs) {\n &.md-button.md-fab {\n z-index: 61;\n }\n }\n}\n\n.notebook-report {\n border-radius: 4px 4px 0 0;\n margin: 0;\n height: 100%;\n\n .note-toolbar {\n margin: -8px -8px 8px;\n padding: 4px;\n border: 0 none;\n border-top: 1px solid color('gray-light');\n border-bottom: 1px solid color('gray-light');\n border-radius: 0;\n\n .btn-group {\n margin-top: 0;\n }\n }\n\n .note-btn {\n border: 0 none;\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n border-bottom-left-radius: 0;\n border-bottom-right-radius: 0;\n }\n}\n\n.notebook-report-container {\n height: 550px;\n width: 450px;\n max-height: 90%;\n bottom: 0;\n right: 96px;\n position: absolute;\n z-index: 3;\n}\n\n.notes-visible {\n @media only screen and (min-width: $layout-breakpoint-sm) {\n .notebook-report-container {\n right: 516px;\n transition: right 250ms;\n }\n }\n}\n\n.notebook-report-container__full {\n top: 16px;\n bottom: 16px;\n left: 16px;\n right: 16px;\n max-height: none;\n max-width: none;\n height: auto;\n width: auto;\n\n .notebook-report {\n height: 100%;\n width: 100%;\n margin: 0 auto;\n max-height: none;\n border-radius: $card-border-radius;\n }\n}\n\n.notebook-report-container__collapsed {\n width: 300px;\n height: auto;\n\n .notebook-report__content, .notebook-report__actions, .notebook-report__content__header {\n display: none;\n }\n}\n\n.notebook-report__toolbar {\n background-color: color('gray-darkest') !important;\n border-radius: $card-border-radius $card-border-radius 0 0;\n}\n\n.notebook-report__toolbar__title {\n max-width: 150px;\n}\n\n.notebook-report__content {\n h1, h2, h3, h4 {\n font-size: rem(2.2);\n }\n\n background-color: #ffffff;\n\n .note-editor.note-frame {\n border: 0 none;\n border-radius: 0;\n padding: 0;\n box-shadow: none;\n }\n\n .note-resizebar {\n display: none;\n }\n}\n\n.notebook-report__content__header {\n padding: 8px;\n background-color: #ffffff;\n font-size: rem(1.6);\n}\n\n@media only screen and (max-width: $layout-breakpoint-xs - 1) {\n .notebook-report-container {\n &:not(.notebook-report-container__collapsed) {\n top: 0;\n bottom: 0;\n left: 0;\n right: 0;\n max-height: none;\n max-width: none;\n height: auto;\n width: auto;\n\n .notebook-report {\n border-radius: 0;\n }\n }\n }\n\n .notebook-tools--full {\n display: none;\n }\n}\n\n.notebook-report-backdrop {\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n z-index: 3;\n background-color: rgba(33,33,33,1.0);\n opacity: .48;\n}\n\n.notebook-menu {\n transition: opacity 500ms;\n\n &.ng-enter {\n opacity: 0;\n }\n\n .ng-enter-active {\n opacity: 1;\n transition-delay: 250ms;\n }\n\n &.ng-leave-active, &.ng-hide {\n opacity: 0;\n }\n\n &.ng-hide-add, &.ng-hide-add-active,\n &.ng-hide-remove, &.ng-hide-remove-active {\n opacity: 0;\n }\n}\n\n.notebook-item {\n transition: box-shadow 250ms;\n margin: 0 16px 16px;\n display: block;\n}\n\n.notebook-item__content {\n height: 250px;\n min-width: 230px;\n position: relative;\n padding: 0;\n background-color: color('gray');\n border-top-left-radius: $card-border-radius;\n border-top-right-radius: $card-border-radius;\n}\n\n.notebook-item__content__attachment, .notebook-item__content__text {\n position: absolute;\n left: 0;\n right: 0;\n}\n\n.notebook-item__content__attachment {\n background-repeat: no-repeat !important;\n border-top-left-radius: $card-border-radius;\n border-top-right-radius: $card-border-radius;\n background-position: center top !important;\n background-size: cover !important;\n top: 0;\n bottom: 0;\n}\n\n.notebook-item__content__text {\n bottom: 0;\n padding: 8px;\n font-weight: 500;\n overflow: hidden;\n max-height: 120px;\n min-height: 56px;\n background-color: rgba(255,255,255,0.95);\n border-top: 1px solid color('gray-lighter');\n\n &:after {\n content: \"\";\n text-align: right;\n position: absolute;\n bottom: 0;\n right: 0;\n width: 100%;\n height: 0.8em;\n background: linear-gradient(180deg,hsla(0,0%,100%,0),rgba(255,255,255,0.95) 100%);\n }\n}\n\n.notebook-item__content--text-only {\n &:after {\n content: \"note\";\n font-family: 'Material Icons';\n font-weight: normal;\n font-style: normal;\n display: inline-block;\n line-height: 1;\n text-transform: none;\n letter-spacing: normal;\n word-wrap: normal;\n white-space: nowrap;\n direction: ltr;\n\n /* Support for all WebKit browsers. */\n -webkit-font-smoothing: antialiased;\n /* Support for Safari and Chrome. */\n text-rendering: optimizeLegibility;\n\n /* Support for Firefox. */\n -moz-osx-font-smoothing: grayscale;\n\n /* Support for IE. */\n font-feature-settings: 'liga';\n //position: absolute;\n font-size: 80px;\n color: color('text-disabled');\n }\n}\n\n.notebook-item--question__content--text-only {\n content: \"live_help\";\n}\n\n.notebook-item__content__location {\n opacity: 0.9;\n padding: 8px 0;\n\n md-icon {\n font-size: 22px;\n }\n}\n\n.notebook-item__edit {\n cursor: pointer;\n}\n\n.notebook-item__actions {\n margin: 0;\n padding: 0 8px;\n color: #ffffff;\n background-color: color('gray-darkest');\n border-bottom-left-radius: $card-border-radius;\n border-bottom-right-radius: $card-border-radius;\n\n md-icon {\n color: #ffffff;\n }\n}\n\n.notebook-item__text-input {\n margin: 0;\n}\n\n.notebook-item__text-input__textarea {\n padding-left: 0;\n padding-right: 0;\n}\n\n.notebook-item__attachment {\n background-color: color('gray-light');\n padding: 16px;\n margin-bottom: 16px;\n text-align: center;\n position: relative;\n}\n\n.notebook-item__attachment__content {\n max-width: 100%;\n height: auto;\n}\n\n.notebook-item__attachment__delete {\n position: absolute;\n top: 4px;\n right: -2px;\n // TODO: generalize for on item buttons like this (delete attachment, etc)\n width: 34px !important;\n height: 34px !important;\n min-height: 0;\n\n md-icon {\n margin-left: -2px;\n font-size: 22px;\n }\n}\n\n.notebook-item__info {\n font-style: italic;\n opacity: .8;\n color: lighten(color('accent-1'), 5%);\n\n a, md-icon {\n color: lighten(color('accent-1'), 5%);\n }\n\n md-icon {\n font-size: 1.5em;\n min-width: 0;\n width: auto;\n }\n}\n\n.notebook-item__upload {\n text-align: center;\n padding: 24px;\n background-color: color('gray-lighter');\n margin-bottom: 16px;\n color: color('text-secondary');\n border-radius: 4px;\n cursor: pointer;\n border: 1px dashed transparent;\n transition: all 250ms;\n\n md-icon, span {\n transition: color 250ms;\n }\n\n &:hover, &:focus, &.dragover {\n border-color: color('accent');\n background-color: lighten(color('accent'), 35%);\n color: color('accent');\n\n md-icon, span {\n color: color('accent');\n }\n }\n}\n\n.view-notebook-item {\n width: $layout-breakpoint-xs;\n}\n\n.view-notebook-item__content {\n}\n\n.notebook-item--report {\n background-color: #ffffff;\n\n .note-editor {\n margin-bottom: 16px;\n border-color: color('gray');\n }\n}\n\n.notebook-item--report__container {\n &.ui-scrollpoint {\n .notebook-item--report__toolbar {\n position: fixed;\n top: ($wise-toolbar-height + $md-toolbar-height);\n left: 0;\n right: 0;\n z-index: 1;\n\n @media only screen and (min-width: $layout-breakpoint-xs) {\n left: ($notebook-sidebar-width - 2);\n }\n\n @media only screen and (min-width: $layout-breakpoint-xs) {\n padding: 0 24px;\n }\n\n @media only screen and (min-width: $layout-breakpoint-sm) {\n padding: 0 32px;\n }\n\n .note-toolbar {\n margin: 0 16px;\n\n @media only screen and (min-width: $layout-breakpoint-xs) {\n margin: 0 8px;\n }\n\n @media only screen and (min-width: $layout-breakpoint-sm) {\n margin: 0 16px;\n }\n }\n }\n\n .note-editor {\n padding-top: 40px;\n }\n }\n}\n\n.notebook-item--report__toolbar {\n\n .note-toolbar {\n background-color: color('gray-light');\n border: 1px solid color('gray');\n margin-bottom: -2px;\n }\n}\n\n.notebook-item--report__heading {\n text-align: center;\n margin-bottom: 32px;\n}\n\n.notebook-item--report__content {\n}\n\n.notebook-item--report__add-note {\n font-weight: 700;\n}\n\n.notebook-item--report__note-img {\n max-width: 100%;\n height: auto !important;\n}\n\n.notebook-sidebar {\n @media only screen and (min-width: $layout-breakpoint-xs) {\n width: 400px;\n max-width: none;\n }\n\n @media only screen and (min-width: $layout-breakpoint-sm) {\n width: 500px;\n max-width: none;\n }\n}\n\n.notebook-items {\n width: 100%;\n overflow: auto;\n margin-top: 16px;\n margin-bottom: 76px;\n\n .notebook-item {\n width: 100%;\n }\n\n .notebook-item__content {\n height: 200px;\n min-width: 0;\n }\n}\n\n.notebook-items--grading {\n margin-bottom: 0;\n}\n\n@media only screen and (max-width: ($layout-breakpoint-xs - 1)) {\n .notebook-enabled {\n .md-fab-bottom-right, .md-fab-bottom-left {\n bottom: ($wise-toolbar-height + 8) !important;\n }\n }\n}\n\n.notebook-grading {\n background-color: #ffffff;\n display: block;\n}\n",".notification-btn {\n width: 60px !important;\n\n md-icon {\n margin-left: 20px;\n }\n}\n\n.notification-count {\n border-radius: 50%;\n position: absolute;\n background-color: color('accent');\n width: 22px;\n left: 4px;\n height: 22px;\n line-height: 18px;\n font-size: 12px;\n font-weight: 700;\n border: 2px solid;\n\n &:before {\n content: \"\";\n position: absolute;\n right: -7px;\n top: 5px;\n border-left: 6px solid rgba(255,255,255,0.87);\n border-top: 4px solid transparent;\n border-bottom: 4px solid transparent;\n }\n}\n\n.notification-list {\n padding: 8px 0;\n}\n\n.notification-dismiss {\n width: 500px;\n}\n\n.notification-dismiss__input {\n margin-bottom: 0;\n}\n\nmd-list md-list-item .md-list-item-text h4,\nmd-list md-list-item.md-2-line .md-list-item-text h4,\nmd-list md-list-item.md-3-line .md-list-item-text h4 {\n &.notification-list-item__source {\n color: color('text-secondary');\n font-size: rem(1.2);\n\n md-icon {\n font-size: rem(1.8);\n min-width: 0;\n width: auto;\n margin-left: -4px;\n line-height: rem(2);\n }\n }\n}\n",".account-menu {\n border-radius: $card-border-radius;\n padding: 0;\n font-size: $body-font-size-base;\n max-width: 380px;\n\n @media (min-width: $layout-breakpoint-md) {\n min-width: 380px !important;\n }\n\n h3 {\n margin: 0;\n font-weight: 300;\n }\n}\n\n.account-menu--fixed-height {\n height: $max-menu-height;\n}\n\n.account-menu--fixed-width {\n width: 320px;\n\n @media (min-width: $layout-breakpoint-sm) {\n width: 380px;\n }\n}\n\n.account-menu__icon {\n background-color: color('text-light');\n border-radius: 50%;\n}\n\n.account-menu__caret {\n position: absolute;\n right: 28px;\n top: -8px;\n outline: none;\n\n &:before {\n content: '';\n position: absolute;\n border-bottom: 8px solid #fff;\n border-left: 8px solid transparent;\n border-right: 8px solid transparent;\n }\n}\n\n.account-menu__caret--pause, .account-menu__caret--notification {\n right: 80px;\n}\n\n.account-menu__caret--notification--with-pause {\n right: 132px;\n}\n\n[dir=rtl] {\n .account-menu__caret {\n right: auto;\n left: 28px;\n }\n .account-menu__caret--pause, .account-menu__caret--notification {\n left:80px;\n right:auto;\n }\n .account-menu__caret--notification--with-pause {\n left: 132px;\n right:auto;\n }\n}\n\n.account-menu__info {\n padding: 8px 12px;\n}\n\n.account-menu__info__title {\n font-weight: 500;\n}\n\n.account-menu__info__team {\n font-weight: 400;\n color: color('text-secondary');\n}\n\n.account-menu__users {\n padding: 0;\n\n md-list-item {\n padding: 0;\n\n .md-avatar {\n margin: 0 8px 0 0;\n height: 48px;\n width: 48px;\n }\n }\n}\n\n.account-menu__progress {\n md-progress-linear {\n transform: rotate(270deg);\n width: 26px;\n }\n}\n\n.account-menu__grade {\n position: relative;\n margin-right: 4px;\n\n md-icon {\n color: color('score');\n }\n}\n\n.account-menu__grade__overlay {\n position: absolute;\n top: 0;\n width: 100%;\n background-color: rgba(255, 255, 255, 0.6);\n}\n\n.account-menu__actions {\n background-color: color('gray-lightest');\n}\n\n.account-menu__control {\n padding: 16px;\n}\n",".annotations {\n margin: 16px 4px 16px 62px;\n position: relative;\n font-size: rem(1.5);\n\n hr {\n margin: 10px 0 8px;\n border-color: rgba(0,0,0,.12);\n }\n\n &:after {\n content: \"\";\n position: absolute;\n width: 0;\n height: 0;\n left: -16px;\n right: auto;\n top: 0px;\n bottom: auto;\n border-top: 20px solid transparent;\n border-bottom: 20px solid transparent;\n border-right: 16px solid color('gray-darker');\n }\n}\n\n.annotations-container--student--report {\n border-top: 1px solid color('gray-light');\n}\n\n.annotations--report {\n margin-top: 0;\n margin-bottom: 0;\n}\n\n.annotations__header {\n position: relative;\n //border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n padding: 10px 12px;\n font-weight: 700;\n transition: all 1s;\n color: #ffffff;\n background-color: color('gray-darker');\n}\n\n.annotations__avatar {\n background-color: color('accent');\n padding: 2px;\n position: absolute;\n top: 0;\n left: -62px;\n}\n\n.annotations__icon {\n transition: all 1s;\n color: #ffffff;\n}\n\n.annotations__body {\n padding: 12px;\n background-color: #ffffff;\n border-bottom-left-radius: $card-border-radius;\n border-bottom-right-radius: $card-border-radius;\n overflow: auto;\n}\n\n.annotations__status {\n background-color: #ffffff;\n color: color('info');\n display: inline-block;\n margin-left: 8px;\n font-size: rem(1.2);\n\n &.ng-enter, &.ng-leave {\n transition: all 1s;\n }\n\n &.ng-enter, &.ng-leave.ng-leave-active {\n opacity:0;\n }\n\n &.ng-leave, &.ng-enter.ng-enter-active {\n opacity:1;\n }\n}\n\n.annotations__score {\n font-weight: 700;\n}\n\n.annotations__info {\n font-style: italic;\n opacity: 0.8;\n border-bottom: 1px dotted;\n font-size: rem(1.3);\n}\n\n.annotations--inside {\n .annotations {\n margin-left: 72px;\n }\n}\n\n// TODO: move to own file\n.annotations--info {\n margin-bottom: 32px;\n margin-right: 8px;\n margin-left: 72px;\n\n @media only screen and (min-width: ($layout-breakpoint-xs)) {\n margin: 16px 16px 32px 76px;\n }\n\n &:after {\n border-right: 16px solid color('info');\n }\n\n .annotations__avatar {\n background-color: #ffffff;\n }\n\n .annotations__header {\n background-color: color('info');\n }\n}\n",".component {\n position: relative;\n}\n\n.component__wrapper {\n padding: 0 16px;\n margin: 24px 0;\n}\n\n.component__content {\n overflow-x: auto;\n font-size: rem(1.5);\n overflow-y: hidden; // TODO: figure out why this is needed after update to ng-material 1.1.1\n\n @media only screen and (min-width: $layout-breakpoint-xs) {\n padding: 0 8px;\n }\n}\n\nh3.component__header, .component-header {\n padding: 8px 12px;\n margin: 0;\n font-size: rem(1.4);\n}\n\n.component__rubric {\n position: absolute;\n left: -20px;\n top: 12px;\n}\n\n.notebook-enabled {\n .component_content {\n img {\n transition: all 250ms;\n cursor: pointer;\n cursor: copy;\n //position: relative;\n //border: 2px solid transparent;\n\n &:hover, &:focus {\n box-shadow: 0 0 5px 1px color('accent');\n //border: 2px solid #ffffff;\n }\n }\n }\n}\n\n.component__actions {\n .md-button:first-child {\n margin-left: 0;\n }\n\n .md-button:last-child {\n margin-right: 0;\n }\n}\n\n.component__actions__info {\n font-style: italic;\n margin-left: 8px;\n //color: color('accent-1');\n color: color('text-secondary');\n}\n\n.component__actions__more {\n border-bottom: 1px dotted;\n}\n\n.component__prompt {\n margin-bottom: 8px;\n font-weight: 500;\n}\n\n.component__prompt__content {\n display: inline;\n}\n\n.component__attachment {\n position: relative;\n margin: 0 8px;\n padding-bottom: 8px;\n\n @media only screen and (min-width: $layout-breakpoint-xs) {\n padding-top: 8px;\n }\n}\n\n.component__add-attachment {\n @media only screen and (max-width: ($layout-breakpoint-xs - 1)) {\n width: 100%;\n }\n}\n\n.component__attachment__content {\n max-height: 100px;\n width: auto;\n}\n\n.component__attachment__delete {\n position: absolute;\n top: 0;\n right: 0;\n min-width: 0;\n background-color: rgba(255, 255, 255, 0.75) !important;\n border-radius: 0;\n padding: 4px;\n margin: 0;\n\n //@media only screen and (min-width: $layout-breakpoint-sm) {\n //margin-top: 8px;\n //}\n\n > md-icon {\n margin-top: 0;\n }\n}\n\n.component__revision {\n margin: 8px 0;\n padding: 8px;\n\n &:nth-child(odd) {\n background-color: color('gray-lightest');\n }\n}\n\n.component__revision__content {\n padding: 4px 0 8px 0;\n border-bottom: 1px solid color('gray-light');\n}\n\n.component__revision__actions {\n color: color('gray-darker');\n padding-top: 4px;\n}\n","// Variables\n\n// Base\n.component__content--Discussion {\n overflow: hidden;\n}\n\n.discussion-content {\n background-color: color('gray-lighter');\n //margin: 0 0 -16px;\n box-shadow: inset 0 0 3px color('gray-dark');\n}\n\n.discussion-posts {\n padding: 12px 12px 8px;\n\n @media only screen and (min-width: $layout-breakpoint-md) {\n padding: 16px 16px 0;\n }\n}\n\n.discussion-post {\n margin: 0 auto 16px;\n max-width: $layout-breakpoint-xs;\n\n @media only screen and (min-width: $layout-breakpoint-xs) {\n margin-bottom: 24px;\n }\n\n @media only screen and (min-width: $layout-breakpoint-md) {\n margin-bottom: 32px;\n }\n\n // angular-material fix for when discussion posts are shown inside an md-list-item (e.g. in the grading tool)\n md-divider {\n position: relative;\n width: auto;\n }\n}\n\n.discussion-post__contents {\n padding: 16px;\n}\n\n.discussion-post__avatar, md-list-item > .md-avatar.discussion-post__avatar {\n margin-right: 8px;\n}\n\n.discussion-post__avatar--reply, md-list-item > .md-avatar.discussion-post__avatar--reply {\n margin-top: 8px;\n}\n\n\n.discussion-post__user, md-list-item .md-list-item-text h3.discussion-post__user {\n padding-bottom: 4px;\n font-weight: 700;\n line-height: 1.3;\n overflow: visible;\n white-space: normal;\n}\n\n.discussion-post__date {\n color: color('gray-dark');\n}\n\n.discussion-post__date--reply {\n margin-left: 8px;\n font-weight: 400;\n}\n\n.discussion-post__content {\n margin-top: 16px;\n white-space: pre-wrap;\n}\n\n.discussion-post__attachment {\n max-width: 100%;\n height: auto !important;\n margin-top: 16px;\n}\n\n.discussion-new {\n background-color: #ffffff;\n max-width: 570px;\n margin-left: auto;\n margin-right: auto;\n padding: 8px;\n transition: all 250ms;\n transform: scale(0.95);\n}\n\n.discussion-new--focused {\n transform: scale(1);\n}\n\nmd-input-container.discussion-new__input-container {\n margin: 0;\n padding: 0;\n\n > textarea.md-input {\n min-height: 68px;\n }\n}\n\n.discussion-new__input--textarea, .input-container textarea.discussion-new__input--textarea {\n padding: 8px;\n border: 0 none;\n}\n\n.discussion-new__actions {\n padding: 0 8px;\n\n .md-button {\n &:first-of-type {\n margin-left: 0;\n }\n\n &:last-of-type {\n margin-right: 0;\n }\n }\n}\n\n.discussion-new__attachment {\n padding: 0;\n margin: 0 0 8px;\n}\n\n.discussion-new__attachment__content {\n margin-top: 0;\n margin-bottom: 16px;\n}\n\n.discussion-comments {\n padding: 0;\n}\n\n.discussion-comments__contents {\n background-color: color('gray-lightest');\n}\n\n.discussion-comments__header {\n background-color: transparent;\n padding: 0;\n\n .md-subheader-inner {\n padding-bottom: 8px;\n }\n}\n\n.discussion-comments__list {\n padding: 0;\n max-height: 9999px;\n overflow-y: auto;\n\n @media only screen and (min-width: $layout-breakpoint-xs) {\n max-height: 400px;\n }\n}\n\n.input--textarea.discussion-reply__input, .input-container textarea.input--textarea.discussion-reply__input {\n background-color: #ffffff;\n padding: 4px;\n font-size: ($body-font-size-base) - 1;\n border: 0 none;\n margin-left: -1px;\n margin-bottom: 0;\n resize: none;\n}\n\n.discussion-reply, md-list-item.discussion-reply {\n margin: 0 12px 8px;\n padding: 0;\n min-height: 56px;\n}\n\n.discusstion-reply__details, md-list-item .md-list-item-text.discusstion-reply__details {\n margin: 8px 0;\n}\n\n.discussion-post__user--reply, md-list-item .md-list-item-text h3.discussion-post__user--reply {\n font-size: ($body-font-size-base) - 1;\n padding: 0;\n margin: 0;\n}\n\n.discusstion-reply__content {\n margin-top: 2px;\n\n p {\n font-weight: 400 !important;\n color: color('body') !important;\n }\n}\n\n.discussion-new-reply {\n padding: 8px;\n}\n\n.discussion-new-reply__input-container {\n padding-top: 0;\n margin: 0;\n\n .md-errors-spacer {\n display: none;\n }\n}\n\n.discussion-new-reply__actions {\n margin-left: 8px;\n\n .md-button {\n margin-top: 0;\n margin-bottom: 0;\n }\n}\n",".embedded-content {\n\n}\n\n.embedded-content__iframe {\n border: 0 none;\n}\n",".graph-select {\n min-width: 150px;\n max-width: 200px;\n}\n\n.graph-controls {\n margin: 8px 0;\n padding: 8px 0;\n border: 1px solid color('gray-lighter');\n border-left-width: 0;\n border-right-width: 0;\n}\n","// Variables\n\n// Base\n.match-content {\n background-color: color('gray-lighter');\n margin-bottom: 16px;\n padding: 8px;\n box-shadow: inset 0 0 3px color('gray-dark');\n}\n\n.match-divider {\n margin: 16px 8px 8px;\n}\n\n.match-divider--horizontal {\n @media only screen and (min-width: $layout-breakpoint-sm) {\n display: none;\n }\n}\n\n.match-bucket__header {\n padding: 12px;\n font-weight: 500;\n color: color('primary');\n}\n\n.match-bucket__content {\n padding: 0;\n}\n\n.match-bucket--choices {\n .match-bucket__header {\n color: color('accent-1');\n }\n}\n\n.match-bucket__contents {\n min-height: 120px;\n //margin: 0;\n padding: 0 8px 8px;\n background-color: color('gray-light');\n transition: background-color 250ms;\n border-bottom-left-radius: $card-border-radius;\n border-bottom-right-radius: $card-border-radius;\n\n column-gap: 8px;\n\n @media only screen and (max-width: ($layout-breakpoint-xs - 1)) {\n column-count: 1 !important;\n }\n\n img {\n max-width: 100%;\n height: auto;\n }\n}\n\n.match-bucket__contents--over {\n background-color: color('primary');\n}\n\n.match-bucket__item {\n list-style-type: none;\n cursor: move;\n padding-top: 8px;\n break-inside: avoid;\n\n &:hover, &:focus {\n //background-color: rgba(0,0,0,0.2);\n }\n}\n\n.match-bucket__item__contents {\n background-color: #ffffff;\n padding: 8px !important;\n border: 1px solid color('gray');\n\n .md-list-item-text {\n width: 100%;\n }\n}\n\n.match-bucket__item__contents__text {\n margin-right: 4px;\n}\n\n.match-feedback {\n transition: opacity 250ms;\n /*padding-left: 8px;\n border-left: 4px solid;*/\n margin: 8px -8px -8px;\n color: #ffffff;\n padding: 4px 8px;\n\n &.ng-hide {\n opacity: 0;\n transition: opacity 1ms;\n }\n\n md-icon {\n color: #ffffff;\n }\n}\n",".outside-content {\n iframe {\n border: 1px solid color('gray-lighter');\n }\n}\n\n.outside-content__source {\n margin-top: 4px;\n text-align: end;\n\n a {\n max-width: 240px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n display: inline-block;\n }\n}",".notebook-toolbar {\n md-divider {\n margin: 8px 0;\n }\n\n @media only screen and (max-width: ($layout-breakpoint-sm - 1)) {\n border-top: 1px solid color('gray-light');\n }\n}\n\n.notebook-toolbar__add-menu {\n position: absolute;\n bottom: 40px;\n\n .md-fab-action-item {\n background-color: #ffffff;\n }\n}\n\n.notebook-toolbar__add-icon {\n border-radius: 50%;\n}\n\n#closeNotebookSettingsButton {\n float:right;\n}\n\n[dir=rtl] #closeNotebookSettingsButton {\n float:left;\n}","highchart {\n display: block;\n}\n"]} \ No newline at end of file +{"version":3,"sources":["src/main/webapp/wise5/themes/default/style/base/_presets.scss","src/main/webapp/wise5/themes/default/style/base/_config--author.scss","src/main/webapp/wise5/themes/default/style/base/_config.scss","src/main/webapp/wise5/themes/default/style/base/_helpers.scss","src/main/webapp/wise5/themes/default/style/layouts/_l-default.scss","src/main/webapp/wise5/themes/default/style/material/_config.scss","src/main/webapp/wise5/themes/default/style/layouts/_l-footer.scss","src/main/webapp/wise5/themes/default/style/layouts/_l-header.scss","src/main/webapp/wise5/themes/default/style/layouts/_l-main.scss","src/main/webapp/wise5/themes/default/style/author.css","src/main/webapp/wise5/themes/default/style/layouts/_l-nav.scss","src/main/webapp/wise5/themes/default/style/layouts/_l-node.scss","src/main/webapp/wise5/themes/default/style/layouts/_l-notebook.scss","src/main/webapp/wise5/themes/default/style/layouts/_l-sidebar.scss","src/main/webapp/wise5/themes/default/style/modules/_alerts.scss","src/main/webapp/wise5/themes/default/style/modules/_dialog.scss","src/main/webapp/wise5/themes/default/style/modules/_help.scss","src/main/webapp/wise5/themes/default/style/modules/_inputs.scss","src/main/webapp/wise5/themes/default/style/modules/_table.scss","src/main/webapp/wise5/themes/default/style/modules/_toolbar.scss","src/main/webapp/wise5/themes/default/style/modules/_list.scss","src/main/webapp/wise5/themes/default/style/modules/_nav.scss","src/main/webapp/wise5/themes/default/style/modules/_notice.scss","src/main/webapp/wise5/themes/default/style/modules/_menu.scss","src/main/webapp/wise5/themes/default/style/modules/_node.scss","src/main/webapp/wise5/themes/default/style/modules/_notebook.scss","src/main/webapp/wise5/themes/default/style/modules/_notifications.scss","src/main/webapp/wise5/themes/default/style/modules/_account-menu.scss","src/main/webapp/wise5/themes/default/style/modules/_annotations.scss","src/main/webapp/wise5/themes/default/style/modules/_component.scss","src/main/webapp/wise5/themes/default/style/modules/_component--discussion.scss","src/main/webapp/wise5/themes/default/style/modules/_component--embedded.scss","src/main/webapp/wise5/themes/default/style/modules/_component--graph.scss","src/main/webapp/wise5/themes/default/style/modules/_component--match.scss","src/main/webapp/wise5/themes/default/style/modules/_component--outside.scss","src/main/webapp/wise5/themes/default/style/modules/_notebook-toolbar.scss","src/main/webapp/wise5/themes/default/style/modules/_highcharts.scss"],"names":[],"mappings":"AAIA,KACE,eCSuB,CDVzB,SAIM,eAAgB,CAItB,gBAEQ,aCbgB,CDiBxB,WACE,qBAAgD,CAChD,WAAY,CACZ,aAAc,CAGd,oBAAuB,CAAvB,sBAAuB,CAGzB,qBAEQ,UAAW,CACX,iBAAkB,CAClB,iBAAkB,CAClB,WAAY,CACZ,wBC3BW,CD+BnB,kCAEQ,QAAS,CACT,QAAS,CAKjB,OACI,iBEQoB,CFPpB,eAAgB,CAChB,cGlC8B,CHmC9B,eAAgB,CAChB,iBAAkB,CAClB,qBClCkB,CD4BtB,iBASQ,WAAY,CACZ,YAAa,CACb,mBAAoB,CAX5B,8CAcY,qBC1CU,CD4BtB,uBAkBY,uBC9CU,CDmDtB,aACI,wBC3Da,CD4Db,UAAc,CAGlB,aACI,wBCjEa,CDkEb,UAAc,CAGlB,gBACI,wBCpEgB,CDqEhB,UAAc,CAIlB,qBACI,aAAc,CAGlB,iBACI,uBAAwB,CAI5B,EACE,aC7FsB,CD8FtB,cAAe,CAGjB,QACI,kCAAuC,CACvC,qBChFyB,CDoF7B,QACE,iBAAkB,CAClB,sBAAuB,CAGzB,gBACE,iBExDsB,CF4DxB,cAEI,WAAqC,CACrC,UAAoC,CAHxC,cAMI,WAAqC,CACrC,UAAoC,CAPxC,cAUI,WAAqC,CACrC,UAAoC,CAXxC,cAcI,WAAqC,CACrC,UAAoC,CAKxC,cACE,qBCxHqB,CDyHrB,4BAA8B,CAFhC,8BAKM,WA1ImB,CAqIzB,oBASI,WAAY,CACZ,UAAW,CAVf,oBAaI,WAAY,CACZ,UAAW,CAdf,oBAiBI,WAAY,CACZ,UAAW,CAlBf,oBAqBI,WAAY,CACZ,UAAW,CAIf,wDAEI,qBC7ImC,CD2IvC,4EAOM,qBCjJgC,CDuJtC,mDAEI,uBAAkC,CAFtC,mDAKI,uBAAkC,CALtC,6CAQI,uBAA+B,CARnC,6CAWI,uBAA+B,CAXnC,iDAcI,uBAAiC,CAdrC,qDAiBI,uBAAmC,CAjBvC,qDAoBI,uBAAmC,CAKvC,uCACE,qBCnL2B,CDsL7B,uFACE,qBCzM0C,CD4M5C,2HAEE,aC/MsB,CDgNtB,qBC/M0C,CDqNxC,SACE,aCvNkB,CDsNpB,QACE,aClNa,CDiNf,UACE,aCjNe,CDgNjB,UACE,aChNe,CD+MjB,MACE,aC/MW,CD8Mb,MACE,aC9MW,CD6Mb,SACE,aC7Mc,CD4MhB,SACE,qBC5M0B,CD2M5B,eACE,aC3MoB,CD0MtB,cACE,UC1MmB,CDyMrB,YACE,UCzMiB,CDwMnB,MACE,UCxMW,CDuMb,WACE,UCvMgB,CDsMlB,aACE,aCtMkB,CDqMpB,cACE,UCrMmB,CDoMrB,MACE,qBCpMuB,CDmMzB,gBACE,qBCnMiC,CDkMnC,eACE,qBClMgC,CDiMlC,YACE,UCjMgC,CDgMlC,sBACE,wBChM6C,CD+L/C,qBACE,wBC/L4C,CD8L9C,aACE,UCtNsC,CDqNxC,OACE,aC7LY,CD4Ld,MACE,qBCpMuB,CDmMzB,SACE,UC1MmB,CDgNrB,YACE,wBC9NkB,CD6NpB,WACE,wBCzNa,CDwNf,aACE,wBCxNe,CDuNjB,aACE,wBCvNe,CDsNjB,SACE,wBCtNW,CDqNb,SACE,wBCrNW,CDoNb,YACE,wBCpNc,CDmNhB,YACE,gCCnN0B,CDkN5B,kBACE,wBClNoB,CDiNtB,iBACE,qBCjNmB,CDgNrB,eACE,qBChNiB,CD+MnB,SACE,qBC/MW,CD8Mb,cACE,qBC9MgB,CD6MlB,gBACE,wBC7MkB,CD4MpB,iBACE,qBC5MmB,CD2MrB,SACE,gCC3MuB,CD0MzB,mBACE,gCC1MiC,CDyMnC,kBACE,gCCzMgC,CDwMlC,eACE,qBCxMgC,CDuMlC,yBACE,mCCvM6C,CDsM/C,wBACE,mCCtM4C,CDqM9C,gBACE,qBC7NsC,CD4NxC,UACE,wBCpMY,CDmMd,SACE,gCC3MuB,CD0MzB,YACE,qBCjNmB,CDuNrB,kCAEQ,cCtOY,CDoOpB,iCAEQ,cCjOO,CD+Nf,mCAEQ,cChOS,CD8NjB,mCAEQ,cC/NS,CD6NjB,+BAEQ,cC9NK,CD4Nb,+BAEQ,cC7NK,CD2Nb,kCAEQ,cC5NQ,CD0NhB,kCAEQ,sBC3NoB,CDyN5B,wCAEQ,cC1Nc,CDwNtB,uCAEQ,WCzNa,CDuNrB,qCAEQ,WCxNW,CDsNnB,+BAEQ,WCvNK,CDqNb,oCAEQ,WCtNU,CDoNlB,sCAEQ,cCrNY,CDmNpB,uCAEQ,WCpNa,CDkNrB,+BAEQ,sBCnNiB,CDiNzB,yCAEQ,sBClN2B,CDgNnC,wCAEQ,sBCjN0B,CD+MlC,qCAEQ,WChN0B,CD8MlC,+CAEQ,yBC/MuC,CD6M/C,8CAEQ,yBC9MsC,CD4M9C,sCAEQ,WCrOgC,CDmOxC,gCAEQ,cC5MM,CD0Md,+BAEQ,sBCnNiB,CDiNzB,kCAEQ,WCzNa,CGXzB,eACE,gBAAiB,CACjB,iBAAkB,CAClB,cAAe,CACf,iBAAkB,CAElB,yBANF,eAOM,YCW2B,CDThC,CAED,kBACE,WCK8B,CDJ9B,cAAe,CEbjB,UACE,cAAe,CACf,QAAS,CACT,MAAO,CACP,OAAQ,CACR,SAAU,CACV,qBAAyB,CACzB,yBLIuB,CKAzB,gBACE,QAAS,CACT,aAAc,CACd,gBAAiB,CACjB,WAAY,CACZ,YAAa,CAGf,yBACE,gBAAiB,CCpBnB,UACI,SAAU,CADd,gBAIQ,uBAAyB,CACzB,WAAY,CACZ,UAAW,CACX,qBAAsB,CAP9B,qBAWQ,cAAe,CACf,YAAa,CACb,aAAc,CACd,iBAAkB,CAElB,yCAhBR,qBAiBY,aAAc,CAMrB,CAvBL,sDAqBY,QAAc,CAKtB,yCA1BJ,6FA6BgB,cJlBkB,CImBrB,CC9Bb,QACE,qBPUuB,COPzB,sBACE,eNmCwB,CMhC1B,SACE,yBAA2B,CAG7B,cACE,aAAc,CACd,WAAY,CACZ,iBAAkB,CAClB,MAAO,CACP,OAAQ,CACR,sBAAyB,CAEzB,yCARF,cASI,YAAa,CAuBhB,CAhCD,uBAaI,SAAU,CAbd,+BAiBI,SAAU,CACV,qBAAuB,CAlB3B,gLA8BI,SAAU,CAId,6BACE,WAAY,CAEZ,yCAHF,6BAII,gBAAiB,CACjB,YAAa,CAEhB,CAEC,yCCwZA,uCDvZE,gBAAiB,CACjB,iBAAkB,CAErB,CAED,cACE,YAAa,CADf,mDAMI,eAAgB,CAChB,YAAa,CACb,eAAgB,CAChB,cL3D8B,CK6D9B,yCAXJ,mDAYM,cL9D4B,CK+D5B,iBAAkB,CAErB,CAID,yCADF,oBAEI,cAAe,CAElB,CAED,0CAEE,YAAa,CAFf,kEAKI,gBAAiB,CAEjB,yCAPJ,kEAQM,aAAc,CACd,cAAe,CAElB,CAXH,0DAcI,0BAA2B,CAI/B,2FAEE,gBAAiB,CEzGnB,OACI,+BAA6C,CCDjD,QACE,eTuCwB,CStCxB,SAAU,CAEV,yCAJF,QAKI,gBAAiB,CACjB,+BAA6C,CAMhD,CAHC,yCATF,QAUI,gBAAiB,CAEpB,CCZD,YACI,eVuCsB,CUtCtB,+BAA6C,CCEjD,mBACE,+BAAoC,CACpC,uBAAmC,CAFrC,6BAKI,qBZQyB,CapB7B,aACI,YAAa,CACb,SAAU,CACV,qBAAsB,CAG1B,uBACI,WAAY,CACZ,UAAW,CACX,YAAa,CACb,mBAAoB,CACpB,YAAa,CACb,SAAU,CAGd,qBACI,qBAAyB,CAG7B,2BACI,cAAe,CACf,oBAAqB,CCrBzB,UACI,WVkB4B,CUfhC,cACI,WVe4B,CUZhC,eACI,YVY6B,CWrBjC,aACI,iBdqDoB,CcpDpB,eAAgB,CAEhB,yBAJJ,aAKQ,eAAuC,CAU9C,CAPG,yBARJ,aASQ,eAAuC,CAM9C,CAHG,0BAZJ,aAaQ,gBAAuC,CAE9C,CAOD,kDAJI,0BdoCoB,CcnCpB,2BfTa,CeYjB,8BAGI,kBAAqB,CACrB,wBfhBa,CeYjB,8CAOQ,cAAe,CACf,aAAc,CACd,gBAAiB,CAIzB,sBACI,aAAc,CACd,gBAAiB,CACjB,gBAAiB,CAGrB,sBACI,6BdYoB,CcXpB,8BdWoB,CcRxB,qBACI,iBdOoB,CcLpB,QAAc,CACd,4CAAiD,CACjD,cbrC8B,CasC9B,SAAU,CANd,uDASQ,iBAAkB,CAClB,UAAW,CACX,WAAY,CAXpB,0DAgBY,KAAM,CACN,SAAU,CAjBtB,kFAoBgB,gCfxDC,CeyDD,kCAAmC,CACnC,mCAAoC,CACpC,iBAAkB,CAClB,SAAU,CAxB1B,yFA4BgB,QAGuC,CA/BvD,4DAoCY,YAAa,CACb,SAAU,CArCtB,oFAwCgB,6Bf5EC,Ce6ED,kCAAmC,CACnC,mCAAoC,CACpC,iBAAkB,CAClB,SAAU,CA5C1B,2FAgDgB,QAGuC,CAnDvD,4DAwDY,QAAS,CACT,UAAW,CAzDvB,oFA4DgB,+BfhGC,CeiGD,oCAAqC,CACrC,iCAAkC,CAClC,iBAAkB,CAClB,MAAO,CACP,SAAU,CAjE1B,2FAqEgB,QAGqC,CAxErD,6DA6EY,QAAS,CACT,WAAY,CA9ExB,qFAiFgB,8BfrHC,CesHD,oCAAqC,CACrC,iCAAkC,CAClC,iBAAkB,CAClB,OAAQ,CACR,SAAU,CAtF1B,4FA0FgB,QAGqC,CCrIrD,iBACI,gBAAiB,CAGrB,4BACI,eAAgB,CAGpB,4CAEQ,cAAe,CAFvB,kDAMQ,YAAa,CAIrB,eACI,iBAAkB,CAGtB,yDAEQ,ahB7BgB,CgBiCxB,2DACI,WAAY,CACZ,wBhBvBsB,CgBwBtB,qBhBrBa,CgBsBb,iBAAkB,CAJtB,uEAOQ,qBAAyB,CAPjC,+EAWQ,qBhBxB+B,CgB4BvC,0CACI,UAAW,CAGf,2BACI,qBhBjCmC,CgBoCvC,yBACI,iBAAkB,CAClB,UAAW,CAFf,2CAKQ,+BAAwC,CAIhD,mCACI,OAjE8B,CAmE9B,4DACI,QAnEoC,CAuE5C,mCACI,UAzE8B,CA2E9B,4DACI,WAAsD,CAK9D,mHACI,eAAgB,CAChB,qBhBjEyB,CgB+D7B,6JAKQ,ahBvFgB,CgB2FxB,oBAEQ,sBAAuB,CACvB,eAAgB,CAChB,cAAe,CACf,eAAgB,CAChB,qBhB7E+B,CgBkFnC,yCADJ,wBAEQ,eAAgB,CAMvB,CAHG,yCALJ,wBAMQ,eAAgB,CAEvB,CAED,yCAEQ,qBAAyB,CAFjC,+DAKY,eAAgB,CAChB,qBhBxGa,CgB6GzB,gBACI,Wf5EiC,Ce2ErC,sBAIQ,WAAY,CACZ,UAAW,CACX,aAAc,CACd,YAAa,CACb,QAAc,CACd,cdtH0B,CcuH1B,eAAgB,CCrIxB,OACE,cAAe,CACf,UAAW,CACX,eAAgB,CAChB,YAAa,CAJf,kHAYM,qBjBIW,CiBHX,WAAY,CACZ,cfA4B,CeC5B,eAAgB,CAChB,WAAY,CACZ,cAAe,CACf,kBAAmB,CAlBzB,6BAwBI,wBjBXsB,CiBYtB,SAAU,CACV,kBAAmB,CA1BvB,0BA8BI,QAAS,CA9Bb,yBAkCI,YAAa,CAIjB,4BAGM,gBAAiB,CAKvB,mBACE,UAAW,CAGb,aACE,QAAc,CACd,wBAAyB,CACzB,qBAAyB,CACzB,cAAe,CACf,aAAc,CALhB,gCASI,aAAc,CACd,QAAc,CAVlB,gBAcI,eAAgB,CAChB,WAAY,CAfhB,0BAoBM,iBAAkB,CAClB,eAAgB,CAChB,UAAW,CACX,mBAAoB,CACpB,iBAAkB,CAClB,eAAmB,CAKzB,mBACE,eb9D8B,CakE9B,yCADF,mBAEI,eAAgB,CAEnB,CAED,oBACE,cf7EgC,Ce8EhC,eAAgB,CAGlB,wBACE,WAAY,CACZ,QAAS,CAGX,wBACE,wBjBnFsB,CiBoFtB,UjB/EoC,CiBgFpC,ehB5DwB,CgB6DxB,WhB7DwB,CgBgE1B,0BACE,UAAc,CACd,mBAAoB,CACpB,QAAS,CACT,WAAY,CACZ,kBAAmB,CACnB,eAAgB,CAChB,UAAW,CAGb,0BACE,QAAS,CAGX,mCACE,wBAAyB,CAG3B,UACE,eAAgB,CAChB,kBAAmB,CACnB,eAAgB,CAIhB,yCADF,eAEI,eAAgB,CAChB,eAAgB,CAChB,sBAAuB,CACvB,kBAAmB,CAEtB,CC1ID,kBACI,cAAe,CADnB,2HAWY,WAAY,CAKxB,kBACI,ejB0BsB,CiB3B1B,oCAIQ,WjBuBkB,CiBtBlB,ejBsBkB,CiB3B1B,4CASQ,WjBkBkB,CiBjBlB,gBjBiBkB,CiBhBlB,UjBgBkB,CiBZ1B,wCAEQ,aAAc,CACd,chBpB0B,CgBwBlC,qBACI,+BAAkD,CADtD,uCAIQ,chB5B0B,CgB6B1B,eAAgB,CAIxB,SACI,cAAe,CACf,MAAO,CACP,OAAQ,CACR,Qd5CoB,Cc6CpB,SAAU,CAGd,gBACI,eAAgB,CAChB,chB3C8B,CgB4C9B,eAAgB,CAGpB,gBACI,iBAAkB,CVw0BtB,0BUr0BI,kBAAmB,CACnB,gBAAiB,CAGrB,sCACI,QAAS,CAGb,4CACI,YAAa,CACb,eAAgB,CAChB,wBlB/DsB,CkB4D1B,8EAMQ,WAAY,CACZ,eAAgB,CVq0BxB,kGU/zBU,gBAAiB,CAK3B,6BACI,WAAY,CAEZ,yCAHJ,6BAIQ,WAAY,CAMnB,CAHG,yCAPJ,6BAQQ,WAAY,CAEnB,CCrGD,WACI,qBAAyB,CACzB,4BnBYqB,CmBdzB,iDAKQ,qBnBeqB,CmBdrB,qBAAyB,CANjC,iEASY,qBAAsB,CATlC,yFAaY,SAAU,CAbtB,uEAiBY,gBAAiB,CAjB7B,yBAsBQ,YAAa,CAIrB,kGAEQ,uCAA+C,CAC/C,gBAAiB,CAIzB,kGAIQ,uCAA+C,CAC/C,gBAAiB,CAIzB,qBACI,qBAAsB,CAG1B,kDACI,cAAe,CACf,wBnBnCsB,CmBsC1B,oBACI,uBAAyB,CAG7B,6BACI,mBAAoB,CACpB,UAAW,CACX,gBAAiB,CACjB,QAAS,CACT,kBAAmB,CACnB,eAAgB,CAChB,eAAgB,CAGpB,WACI,cjBpD8B,CkBdlC,KACI,iBAAkB,CAGtB,KACI,kBAAmB,CAGvB,UACI,iBAAkB,CAClB,KAAM,CACN,QAAS,CACT,MAAO,CACP,OAAQ,CACR,gCAAkC,CAClC,SAAU,CAPd,kBAUQ,SAAU,CAIlB,UACI,qBpBFmC,CoBGnC,eAAgB,CAFpB,kBAKQ,gBAAiB,CAIzB,oBACI,oBAAqB,CAGzB,qBACI,qBpBrBmB,CoBsBnB,WAAY,CAOhB,wCACI,SAAU,CAEV,yBAHJ,oBAIQ,WAAY,CAEnB,CAED,UACI,mCAAqC,CADzC,4BAKY,qBAKG,CAKf,yBACI,2BnBdoB,CmBepB,0BnBfoB,CmBaxB,+BAKQ,YAAa,CALrB,qDAQY,6BpB3DK,CoBgEjB,gBACI,qCAA0C,CAD9C,yBAIQ,eAAgB,CAEhB,cAAe,CACf,yBAA2B,CAC3B,eAAgB,CAChB,gBAAiB,CATzB,uCAYY,YAAa,CAYzB,2BACI,oBAAqB,CACrB,oBAAqB,CAGzB,yBACI,eAAgB,CAChB,qBpBzFkC,CoBgGtC,sCAEQ,4IACsF,CAI9F,oBACI,QAAS,CAGb,gBACI,yBpBnHmB,CoBoHnB,8BnB7EoB,CmB8EpB,6BnB9EoB,CmB+EpB,gBAAiB,CACjB,eAAgB,CAGpB,iBACI,WAAY,CACZ,cAAe,CACf,UAAc,CACd,QAAS,CACT,eAAgB,CALpB,yBAQQ,iBAAkB,CAClB,UAAc,CATtB,oEAcY,wBpB5IQ,CoBiJpB,iBACI,iBAAkB,CAClB,eAAgB,CAChB,eAAgB,CZw5BpB,2BYr5BI,iBAAkB,CAClB,kBAAmB,CAGvB,gBACI,aAAc,CAGlB,oBACI,UAAW,CADf,kCAIQ,KAAM,CAId,0BACI,eAAgB,CAChB,UAAW,CAGf,kBACI,aAAc,CACd,cAAe,CCzLnB,QACE,iBAAkB,CAClB,WAAY,CACZ,gCAAkC,CAClC,UAAW,CAEX,yBANF,QAOI,aAAc,CACd,iBpBiDsB,CoBhDtB,gBAAiB,CAEpB,CCPD,eACI,iBAAkB,CAClB,QAAS,CACT,UAAW,CAHf,oBAMQ,wBAAoC,CACpC,gBAAiB,CdmlCzB,yBc/kCE,UAAU,CACV,SAAS,CAOX,oBACI,eAAgB,CAEhB,cpBZ8B,CoBelC,oBACI,yBAA2B,CAC3B,2BAA6B,CAC7B,gBAAiB,CAGrB,wDAEQ,atBpCgB,CsBwCxB,cACI,iBAAkB,CAClB,QAA8C,CAC9C,QAAS,CACT,MAAO,CACP,qBAAsB,CACtB,UA9CqB,CA+CrB,eAAgB,CAChB,aAAc,CACd,iBAAkB,CAClB,2BtBnCa,CsBqCb,yCAZJ,cAaQ,YAAa,CAEpB,CdqkCD,wBcnkCE,OAAO,CACP,SAAS,CAGX,6CACI,cAAe,CACf,iBAAkB,CC1DtB,MACI,aAAc,CACd,iBAAkB,CAClB,MAAO,CACP,OAAQ,CAER,yCANJ,MAQQ,iBAAkB,CAClB,kBAAmB,CAe1B,CAZG,yCAZJ,MAaQ,YAAa,CAWpB,CAxBD,eAiBQ,sBAAuB,CACvB,SAAU,CAlBlB,sBAsBQ,SAAU,CAOd,yCADJ,aAEQ,eAAgB,CAChB,kBAAmB,CAM1B,CAHG,yCANJ,aAOQ,gBAAiB,CAExB,CAED,cACI,gBAAiB,CACjB,qBAAyB,CACzB,iBtBSsB,CsBRtB,gBAAiB,CAEjB,yCANJ,cAOQ,eAAgB,CAQvB,CALG,yCAVJ,cAWQ,SAAU,CACV,oBAAqB,CACrB,uBAAwB,CAE/B,CAED,wBAEQ,qBAAyB,CAIjC,sBACI,iBAAkB,CAClB,SAAU,CACV,MAAO,CACP,OAAQ,CACR,SAAU,CALd,oCAQQ,oBAAsB,CAEtB,yCAVR,oCAWY,mBAAqB,CAE5B,CAGL,WACI,UAAc,CACd,sBAAuB,CAG3B,aACI,YAAa,CACb,WAAY,CACZ,eAAgB,CAChB,crB/E8B,CqB2ElC,2CAQY,uBAA6B,CAC7B,UAAW,CATvB,oGAiBY,YAAa,CAjBzB,6BAsBQ,aAAc,CACd,qBvB5FqB,CuBgG7B,2BAEI,wBvBzGsB,CuB0GtB,4BvBzGqB,CuB0GrB,yBvB1GqB,CuB6GzB,0BACI,iBAAkB,CAGtB,mBACI,gBAAiB,CAGrB,qBACI,eAAgB,CAGpB,mBACI,sBAAuB,CACvB,kBAAmB,CACnB,eAAgB,CAEhB,yCALJ,mBAMQ,cAAe,CAEtB,CAED,YACI,eAAgB,CAChB,mBAAoB,CACpB,cAAe,CAEf,yCALJ,YAMQ,crBzI0B,CqB2IjC,CAED,uBACI,mBAAoB,CAEpB,yCAHJ,uBAIQ,mBAAoB,CAc3B,CAXG,0CAPJ,uBAQQ,mBAAoB,CAU3B,CAlBD,8CAYQ,aAAc,CAZtB,6CAgBQ,cAAe,CAIvB,6BACI,iBAAkB,CAClB,eAAgB,CAChB,qBvB7JmC,CuBgKvC,6BACI,wBAAyB,CAG7B,iDAKQ,cAAe,CAKnB,yCADJ,qBAEQ,iBAAuC,CAE9C,CAGG,yCADJ,sBAEQ,kBtB/JkB,CsBiKzB,CAED,cACI,iBAAkB,CAClB,OAAQ,CACR,KAAM,CACN,UnB3MoB,CmB8MxB,uBACI,cAAe,CACf,UnBhNoB,CmBiNpB,qBAAyB,CACzB,aAAc,CACd,iBtBjKsB,CsBmKtB,yCAPJ,uBAQQ,OAAQ,CACR,QAAS,CACT,MAAO,CACP,UAAW,CACX,eAAgB,CAChB,SAAU,CACV,YAAa,CACb,WtBzLkB,CsB2LzB,CAID,WACI,QAAS,CAET,yCAHJ,WAIQ,YAAa,CACb,eAAgB,CAsBvB,CA3BD,oBASQ,gBAAiB,CACjB,iBAAkB,CAV1B,kCAeY,gBAAiB,CAf7B,mFAoBQ,gBAAiB,CACjB,iBAAkB,CArB1B,+BAyBQ,YAAa,CAIrB,aACI,wBvBvQoB,CuBwQpB,oBAAqB,CACrB,YAAa,CACb,qBAAyB,CAO7B,iCACI,qBAAsB,CACtB,oBAAqB,CC/QrB,yCADJ,oCAGY,UAAW,CACd,CAIT,iBACI,yBAA0B,CAC1B,QAAS,CACT,WAAY,CAHhB,+BAMQ,oBAAqB,CACrB,WAAY,CACZ,eAAc,CAAd,YAAc,CAEd,8BxBPe,CwBOf,uBxBPe,CwBOf,kBxBPe,CwBQf,eAAgB,CAXxB,0CAcY,YAAa,CAdzB,2BAmBQ,QAAc,CACd,wBAAyB,CACzB,yBAA0B,CAC1B,2BAA4B,CAC5B,4BAA6B,CAIrC,2BACI,YAAa,CACb,WAAY,CACZ,cAAe,CACf,QAAS,CACT,UAAW,CACX,iBAAkB,CAClB,SAAU,CAIV,yCADJ,0CAGY,WAAY,CACZ,qBAAuB,CAC1B,CAIT,iCACI,QAAS,CACT,WAAY,CACZ,SAAU,CACV,UAAW,CACX,eAAgB,CAChB,cAAe,CACf,WAAY,CACZ,UAAW,CARf,kDAWQ,WAAY,CACZ,UAAW,CACX,aAAc,CACd,eAAgB,CAChB,iBvBnBgB,CuBuBxB,sCACI,WAAY,CACZ,WAAY,CAFhB,wMAKQ,YAAa,CAIrB,0BACI,+BAAkD,CAClD,yBAA0D,CAG9D,iCACI,eAAgB,CAGpB,0BAKI,qBAAyB,CAL7B,oHAEQ,ctBnF0B,CsBiFlC,kDAQQ,QAAc,CACd,eAAgB,CAChB,SAAU,CACV,eAAgB,CAXxB,0CAeQ,YAAa,CAIrB,kCACI,WAAY,CACZ,qBAAyB,CACzB,ctBvG8B,CsB0GlC,yCACI,sEAEQ,KAAM,CACN,QAAS,CACT,MAAO,CACP,OAAQ,CACR,eAAgB,CAChB,cAAe,CACf,WAAY,CACZ,UAAW,CATnB,uFAYY,eAAgB,CAK5B,sBACI,YAAa,CAChB,CAGL,0BACI,cAAe,CACf,KAAM,CACN,MAAO,CACP,UAAW,CACX,WAAY,CACZ,SAAU,CACV,wBAAoC,CACpC,WAAY,CAGhB,eACI,sBAAyB,CAD7B,wBAIQ,SAAU,CAJlB,gCAQQ,SAAU,CACV,qBAAuB,CAT/B,sLAkBQ,SAAU,CAIlB,eACI,0BAA4B,CAC5B,kBAAmB,CACnB,aAAc,CAGlB,wBACI,YAAa,CACb,eAAgB,CAChB,iBAAkB,CAClB,SAAU,CACV,qBxB3Ka,CwB4Kb,0BvBtIoB,CuBuIpB,2BvBvIoB,CuB0IxB,kEACI,iBAAkB,CAClB,MAAO,CACP,OAAQ,CAGZ,oCACI,qCAAuC,CACvC,0BvBlJoB,CuBmJpB,2BvBnJoB,CuBoJpB,iCAA0C,CAC1C,+BAAiC,CACjC,KAAM,CACN,QAAS,CAGb,8BACI,QAAS,CACT,WAAY,CACZ,eAAgB,CAChB,eAAgB,CAChB,gBAAiB,CACjB,eAAgB,CAChB,oCAAwC,CACxC,yBxB1MqB,CwBkMzB,oCAWQ,UAAW,CACX,gBAAiB,CACjB,iBAAkB,CAClB,QAAS,CACT,OAAQ,CACR,UAAW,CACX,WAAa,CACb,6EAAiF,CAIzF,yCAEQ,cAAe,CACf,0BAA6B,CAC7B,eAAmB,CACnB,iBAAkB,CAClB,oBAAqB,CACrB,aAAc,CACd,mBAAoB,CACpB,qBAAsB,CACtB,gBAAiB,CACjB,kBAAmB,CACnB,aAAc,CAGd,kCAAmC,CAEnC,iCAAkC,CAGlC,iCAAkC,CAGlC,4BAA6B,CAE7B,cAAe,CACf,qBxB1O8B,CwB8OtC,6CACI,mBAAoB,CAGxB,kCACI,UAAY,CACZ,aAAc,CAFlB,0CAKQ,cAAe,CAIvB,qBACI,cAAe,CAGnB,wBACI,QAAS,CACT,aAAc,CACd,UAAc,CACd,qBxBtQqB,CwBuQrB,6BvBpOoB,CuBqOpB,8BvBrOoB,CuB+NxB,gCASQ,UAAc,CAItB,2BACI,QAAS,CAGb,qCACI,cAAe,CACf,eAAgB,CAGpB,2BACI,qBxB7RmB,CwB8RnB,YAAa,CACb,kBAAmB,CACnB,iBAAkB,CAClB,iBAAkB,CAGtB,oCACI,cAAe,CACf,WAAY,CAGhB,mCACI,iBAAkB,CAClB,OAAQ,CACR,UAAW,CAEX,oBAAsB,CACtB,qBAAuB,CACvB,YAAa,CAPjB,2CAUQ,gBAAiB,CACjB,cAAe,CAIvB,qBACI,iBAAkB,CAClB,UAAW,CACX,aAAqC,CAHzC,oDAMQ,aAAqC,CAN7C,6BAUQ,eAAgB,CAChB,WAAY,CACZ,UAAW,CAInB,uBACI,iBAAkB,CAClB,YAAa,CACb,qBxB5UqB,CwB6UrB,kBAAmB,CACnB,qBxBvUmC,CwBwUnC,iBAAkB,CAClB,cAAe,CACf,6BAA8B,CAC9B,mBAAqB,CATzB,2DAYQ,qBAAuB,CAZ/B,0FAgBQ,oBxBjWW,CwBkWX,wBAA+C,CAC/C,axBnWW,CwBiVnB,2NAqBY,axBtWO,CwB2WnB,oBACI,WpB/V4B,CoBqWhC,uBACI,qBAAyB,CAD7B,oCAIQ,kBAAmB,CACnB,iBxB7WS,CwBiXjB,iFAGY,cAAe,CACf,QAAgD,CAChD,MAAO,CACP,OAAQ,CACR,SAAU,CAEV,yCATZ,iFAUgB,SAAmC,CAInC,cAJmC,CAsB1C,CAfG,yCAjBZ,iFAkBgB,cAAe,CActB,CAhCT,+FAsBgB,aAAc,CAEd,yCAxBhB,+FAyBoB,YAAa,CAMpB,CAHG,yCA5BhB,+FA6BoB,aAAc,CAErB,CA/Bb,8DAmCY,gBAAiB,CAK7B,8CAGQ,qBxB7Ze,CwB8Zf,qBxB7ZS,CwB8ZT,kBAAmB,CAI3B,gCACI,iBAAkB,CAClB,kBAAmB,CAMvB,iCACI,eAAgB,CAGpB,iCACI,cAAe,CACf,qBAAuB,CAIvB,yCADJ,kBAEQ,WAAY,CACZ,cAAe,CAOtB,CAJG,yCANJ,kBAOQ,WAAY,CACZ,cAAe,CAEtB,CAED,gBACI,UAAW,CACX,aAAc,CACd,eAAgB,CAChB,kBAAmB,CAJvB,+BAOQ,UAAW,CAPnB,wCAWQ,YAAa,CACb,WAAY,CAIpB,yBACI,eAAgB,CAGpB,yCACI,6EAEQ,qBAA6C,CAChD,CAIT,kBACI,qBAAyB,CACzB,aAAc,CC7elB,kBACI,oBAAsB,CAD1B,0BAIQ,gBAAiB,CAIzB,oBACI,iBAAkB,CAClB,iBAAkB,CAClB,wBzBLe,CyBMf,UAAW,CACX,QAAS,CACT,WAAY,CACZ,gBAAiB,CACjB,cAAe,CACf,eAAgB,CAChB,gBAAiB,CAVrB,2BAaQ,UAAW,CACX,iBAAkB,CAClB,UAAW,CACX,OAAQ,CACR,yCAA6C,CAC7C,gCAAiC,CACjC,mCAAoC,CAI5C,mBACI,aAAc,CAGlB,sBACI,WAAY,CAGhB,6BACI,eAAgB,CAGpB,kPAIQ,qBzB1B+B,CyB2B/B,cvBlC0B,CuB6BlC,0QAQY,cvBrCsB,CuBsCtB,WAAY,CACZ,UAAW,CACX,gBAAiB,CACjB,gBvBzCsB,CwBdlC,cACI,iBzBqDoB,CyBpDpB,SAAU,CACV,cxBW8B,CwBV9B,eAAgB,CAEhB,0BANJ,cAOQ,yBAA2B,CAOlC,CAdD,iBAWQ,QAAS,CACT,eAAgB,CAIxB,4BACI,YzBiCyE,CyB9B7E,2BACI,WAAY,CAEZ,yBAHJ,2BAIQ,WAAY,CAEnB,CAED,oBACI,qB1BNkC,C0BOlC,iBAAkB,CAGtB,qBACI,iBAAkB,CAClB,UAAW,CACX,QAAS,CACT,YAAa,CAJjB,4BAOQ,UAAW,CACX,iBAAkB,CAClB,4BAA6B,CAC7B,iCAAkC,CAClC,kCAAmC,CAI3C,+DACI,UAAW,CAGf,+CACI,WAAY,ClB4sDhB,+BkBvsDI,UAAW,CACX,SAAU,ClB0sDd,mFkBvsDI,SAAS,CACT,UAAU,ClB0sDd,yDkBvsDM,UAAW,CACX,UAAU,CAIhB,oBACI,gBAAiB,CAGrB,2BACI,eAAgB,CAGpB,0BACI,eAAgB,CAChB,qB1B5DmC,C0B+DvC,uDAIQ,SAAU,CAJlB,6CAOY,gBAAiB,CACjB,WAAY,CACZ,UAAW,CAKvB,2CAEQ,wBAAyB,CACzB,UAAW,CAInB,qBACI,iBAAkB,CAClB,gBAAiB,CAFrB,6BAKQ,a1BnFU,C0BuFlB,8BACI,iBAAkB,CAClB,KAAM,CACN,UAAW,CACX,mCAA0C,CAG9C,uBACI,wB1B7GsB,C0BgH1B,uBACI,YAAa,CC9HjB,aACI,yBAA0B,CAC1B,iBAAkB,CAClB,czBW8B,CyBdlC,gBAMQ,iBAAkB,CAClB,4BAA6B,CAPrC,mBAWQ,UAAW,CACX,iBAAkB,CAClB,OAAQ,CACR,QAAS,CACT,UAAW,CACX,UAAW,CACX,KAAQ,CACR,WAAY,CACZ,iCAAkC,CAClC,oCAAqC,CACrC,+B3BHgB,C2BOxB,wCACI,yB3BXmB,C2BcvB,qBACI,YAAa,CACb,eAAgB,CAGpB,qBACI,iBAAkB,CAElB,2BAA4B,CAC5B,iBAAkB,CAClB,eAAgB,CAChB,iBAAkB,CAClB,UAAc,CACd,wB3BxBoB,C2B2BxB,qBACI,wB3BxCe,C2ByCf,WAAY,CACZ,iBAAkB,CAClB,KAAM,CACN,UAAW,CAGf,mBACI,iBAAkB,CAClB,UAAc,CAGlB,mBACI,YAAa,CACb,qBAAyB,CACzB,6B1BPoB,C0BQpB,8B1BRoB,C0BSpB,aAAc,CAGlB,qBACI,qBAAyB,CACzB,a3B1Da,C2B2Db,oBAAqB,CACrB,eAAgB,CAChB,czBzD8B,CyBoDlC,4DAQQ,iBAAkB,CAR1B,4EAYQ,SAAS,CAZjB,4EAgBQ,SAAS,CAIjB,oBACI,eAAgB,CAGpB,mBACI,iBAAkB,CAClB,UAAY,CACZ,wBAAyB,CACzB,czBhF8B,CyBmFlC,kCAEQ,gBAAiB,CAKzB,mBACI,kBAAmB,CACnB,gBAAiB,CACjB,gBAAiB,CAEjB,yCALJ,mBAMQ,0BAA2B,CAclC,CApBD,yBAUQ,+B3BxGS,C2B8FjB,wCAcQ,qBAAyB,CAdjC,wCAkBQ,wB3BhHS,C4BVjB,WACI,iBAAkB,CAGtB,oBACI,cAAe,CACf,aAAc,CAGlB,oBACI,eAAgB,CAChB,c1BG8B,C0BF9B,iBAAkB,CAElB,yCALJ,oBAMQ,aAAc,CAErB,CAED,uCACI,gBAAiB,CACjB,QAAS,CACT,c1BR8B,C0BWlC,mBACI,iBAAkB,CAClB,UAAW,CACX,QAAS,CAGb,yCAGY,mBAAqB,CACrB,cAAe,CACf,WAAY,CALxB,8FAUgB,8B5BnCG,C4B0CnB,2CAEQ,aAAc,CAFtB,0CAMQ,cAAe,CAIvB,0BACI,iBAAkB,CAClB,eAAgB,CAEhB,qB5BzCmC,C4B4CvC,0BACI,wBAAyB,CAG7B,mBACI,iBAAkB,CAClB,eAAgB,CAGpB,4BACI,cAAe,CAGnB,uBACI,iBAAkB,CAClB,YAAa,CACb,kBAAmB,CAEnB,yCALJ,uBAMQ,eAAgB,CAEvB,CAGG,yCADJ,2BAEQ,UAAW,CAElB,CAED,gCACI,gBAAiB,CACjB,UAAW,CAGf,+BACI,iBAAkB,CAClB,KAAM,CACN,OAAQ,CACR,WAAY,CACZ,8CAAsD,CACtD,eAAgB,CAChB,WAAY,CACZ,QAAS,CARb,uCAeQ,YAAa,CAIrB,qBACI,YAAa,CACb,WAAY,CAFhB,oCAKQ,wB5B9GkB,C4BkH1B,8BACI,iBAAoB,CACpB,4B5BlHmB,C4BqHvB,8BACI,a5BnHoB,C4BoHpB,eAAgB,CCnIpB,gCACI,eAAgB,CAGpB,oBACI,qB7BMqB,C6BJrB,6B7BOkB,C6BJtB,kBACI,qBAAsB,CAEtB,0CAHJ,kBAIQ,mBAAoB,CAE3B,CAED,iBACI,kBAAmB,CACnB,ezBJ4B,CyBM5B,yCAJJ,iBAKQ,kBAAmB,CAY1B,CATG,0CARJ,iBASQ,kBAAmB,CAQ1B,CAjBD,4BAcQ,iBAAkB,CAClB,UAAW,CAInB,2BACI,YAAa,CAGjB,yEACI,gBAAiB,CAGrB,uFACI,cAAe,CAInB,gFACI,kBAAmB,CACnB,eAAgB,CAChB,eAAgB,CAChB,gBAAiB,CACjB,kBAAmB,CAGvB,uBACI,U7B7CkB,C6BgDtB,8BACI,eAAgB,CAChB,eAAgB,CAGpB,0BACI,eAAgB,CAChB,oBAAqB,CAGzB,6BACI,cAAe,CACf,qBAAuB,CACvB,eAAgB,CAGpB,gBACI,qBAAyB,CACzB,eAAgB,CAChB,gBAAiB,CACjB,iBAAkB,CAClB,WAAY,CACZ,mBAAqB,CACrB,oBAAsB,CAG1B,yBACI,kBAAmB,CAGvB,mDACI,QAAS,CACT,SAAU,CAFd,qEAKQ,eAAgB,CAIxB,2FACI,WAAY,CACZ,QAAc,CAGlB,yBACI,aAAc,CADlB,kDAKY,aAAc,CAL1B,iDASY,cAAe,CAK3B,4BACI,SAAU,CACV,cAAe,CAGnB,qCACI,YAAa,CACb,kBAAmB,CAGvB,qBACI,SAAU,CAGd,+BACI,wB7B7HsB,C6BgI1B,6BACI,4BAA6B,CAC7B,SAAU,CAFd,iDAKQ,kBAAmB,CAI3B,2BACI,SAAU,CACV,iBAAkB,CAClB,eAAgB,CAEhB,yCALJ,2BAMQ,gBAAiB,CAExB,CAED,2GACI,qBAAyB,CACzB,WAAY,CACZ,cAAqC,CACrC,QAAc,CACd,gBAAiB,CACjB,eAAgB,CAChB,WAAY,CAGhB,gDACI,iBAAkB,CAClB,SAAU,CACV,eAAgB,CAGpB,uFACI,YAAa,CAGjB,8FACI,cAAqC,CACrC,SAAU,CACV,QAAS,CAGb,4BACI,cAAe,CADnB,8BAIQ,yBAA2B,CAC3B,+BAA+B,CAIvC,sBACI,WAAY,CAGhB,uCACI,aAAc,CACd,QAAS,CAFb,yDAKQ,YAAa,CAIrB,+BACI,eAAgB,CADpB,0CAIQ,YAAa,CACb,eAAgB,CCjNxB,0BACI,QAAc,CCLlB,cACI,eAAgB,CAChB,eAAgB,CAGpB,gBACI,YAAa,CACb,aAAc,CAGd,iBAAqB,CAArB,kBAAqB,CAArB,kBAAqB,CCPzB,eACI,qBhCUqB,CgCTrB,kBAAmB,CACnB,WAAY,CACZ,6BhCUkB,CgCPtB,eACI,mBAAoB,CAIpB,yCADJ,2BAEQ,YAAa,CAEpB,CAED,sBACI,YAAa,CACb,eAAgB,CAChB,ahCtBoB,CgCyBxB,uBACI,SAAU,CAGd,6CAEQ,ahCzBa,CgC6BrB,wBACI,gBAAiB,CAEjB,iBAAkB,CAClB,qBhCzBmB,CgC0BnB,gCAAkC,CAClC,6B/BYoB,C+BXpB,8B/BWoB,C+BTpB,mBAAe,CAAf,cAAe,CAEf,yCAXJ,wBAYQ,6BAA0B,CAA1B,wBAA0B,CAOjC,CAnBD,4BAgBQ,cAAe,CACf,WAAY,CAIpB,8BACI,wBhCzDoB,CgC4DxB,oBACI,oBAAqB,CACrB,WAAY,CACZ,eAAgB,CAChB,8BAAmB,CAAnB,kBAAmB,CAOvB,8BACI,qBAAyB,CACzB,qBAAuB,CACvB,qBhC3Da,CgCwDjB,iDAMQ,UAAW,CAInB,oCACI,gBAAiB,CAGrB,gBACI,uBAAyB,CAGzB,oBAAqB,CACrB,UAAc,CACd,eAAgB,CANpB,wBASQ,SAAU,CACV,sBAAuB,CAV/B,wBAcQ,UAAc,CCpGtB,wBAEI,qBjCYqB,CiCRzB,yBACE,cAAe,CACf,cAAe,CAFjB,2BAKI,eAAgB,CAChB,kBAAmB,CACnB,eAAgB,CAChB,sBAAuB,CACvB,oBAAqB,CCfzB,6BAEQ,YAAa,CAGjB,yCALJ,kBAMQ,yBlCSe,CkCPtB,CAED,4BACI,iBAAkB,CAClB,WAAY,CAFhB,gDAKQ,qBAAyB,CAIjC,4BACI,iBAAkB,CAGtB,6BACE,WAAW,C1B6uEb,uC0BzuEE,UAAU,CC5BZ,UACE,aAAc","file":"author.css","sourcesContent":["// Config\n$avatar-icon-padding: 6px !default;\n$avatar-icon-padding-lg: 8px !default;\n\nbody {\n background: color('body-bg');\n\n &.vle {\n overflow: hidden;\n }\n}\n\na {\n &:hover, &:focus { // TODO: remove when bootstrap css dependency is removed\n color: color('primary');\n }\n}\n\nblockquote {\n background-color: lighten(color('primary'), 56%);\n padding: 8px;\n margin: 16px 0;\n border-style: solid;\n border-color: color('primary');\n border-width: 0 0 0 3px;\n}\n\n.has-indicator {\n &:after {\n content: '';\n position: absolute;\n border-radius: 50%;\n padding: 5px;\n background-color: color('accent');\n }\n}\n\n.has-indicator--icon-button {\n &:after {\n top: 25px;\n left: 5px;\n }\n}\n\n// Badges\n.badge {\n border-radius: $card-border-radius;\n padding: 2px 6px;\n font-size: rem(1.2);\n font-weight: 500;\n font-style: normal;\n background-color: color('gray-dark');\n\n &.md-button {\n min-width: 0;\n min-height: 0;\n line-height: inherit;\n\n &:hover, &:focus {\n background-color: color('gray-dark');\n }\n\n &:focus {\n outline: 1px dotted color('gray-dark');\n }\n }\n}\n\n.badge--info {\n background-color: color('info');\n color: #ffffff;\n}\n\n.badge--warn {\n background-color: color('warn');\n color: #ffffff;\n}\n\n.badge--success {\n background-color: color('success');\n color: #ffffff;\n}\n\n// Dividers\n.divider--withmargin {\n margin: 16px 0;\n}\n\n.divider--dashed {\n border-top-style: dashed;\n}\n\n// Links\na {\n color: color('primary');\n cursor: pointer;\n}\n\n.active {\n background-color: rgba(158,158,158,0.2);\n color: color('text');\n}\n\n// Images & Icons\n.avatar {\n border-radius: 50%;\n box-sizing: content-box;\n}\n\n.avatar--square {\n border-radius: $card-border-radius;\n}\n\n// Rules for sizing avatars (matches material icons plus avatar-icon padding)\n.avatar {\n &.md-18 {\n height: 18px + $avatar-icon-padding*2;\n width: 18px + $avatar-icon-padding*2;\n }\n &.md-24 {\n height: 24px + $avatar-icon-padding*2;\n width: 24px + $avatar-icon-padding*2;\n }\n &.md-36 {\n height: 36px + $avatar-icon-padding*2;\n width: 36px + $avatar-icon-padding*2;\n }\n &.md-48 {\n height: 48px + $avatar-icon-padding*2;\n width: 48px + $avatar-icon-padding*2;\n }\n}\n\n// Rules for sizing avatar backgrounds (when using a child md-icon)\n.avatar--icon {\n background-color: color('gray-light');\n white-space: normal !important;\n\n &:not(.md-avatar) {\n padding: $avatar-icon-padding;\n }\n\n &.md-18 {\n height: 18px;\n width: 18px;\n }\n &.md-24 {\n height: 24px;\n width: 24px;\n }\n &.md-36 {\n height: 36px;\n width: 36px;\n }\n &.md-48 {\n height: 48px;\n width: 48px;\n }\n}\n\nmd-toolbar.md-light-theme:not(.md-menu-toolbar) {\n md-icon {\n color: color('text-secondary');\n }\n\n .md-button:disabled {\n md-icon {\n color: color('text-disabled');\n }\n }\n}\n\n// hacks for now\nmd-icon, .md-button:not([disabled]) {\n &.primary {\n color: color('primary') !important;\n }\n &.success {\n color: color('success') !important;\n }\n &.warn {\n color: color('warn') !important;\n }\n &.info {\n color: color('info') !important;\n }\n &.accent {\n color: color('accent') !important;\n }\n &.accent-1 {\n color: color('accent-1') !important;\n }\n &.accent-2 {\n color: color('accent-2') !important;\n }\n}\n\n// Theme overrides\nmd-input-container.md-wise-theme label {\n color: color('text');\n}\n\nmd-select-menu.md-default-theme md-option[selected], md-select-menu md-option[selected] {\n background-color: color('selected-bg');\n}\n\n.md-autocomplete-suggestions-container.md-default-theme li .highlight,\n.md-autocomplete-suggestions-container li .highlight {\n color: color('primary');\n background-color: color('selected-bg');\n}\n\n// Color\n// Set classes for each named color in the $colors map\n@each $key, $value in $colors {\n .#{$key} {\n color: $value;\n }\n}\n\n// Set classes for each named color in the $colors map\n@each $key, $value in $colors {\n .#{$key}-bg {\n background-color: $value;\n }\n}\n\n// Set theme colors for angular-ui elements\n@each $key, $value in $colors {\n md-progress-circular.#{$key} {\n path {\n stroke: $value;\n }\n }\n}\n","// Authoring Tool Colors\n$_primary-color: #7e57c2; // should match the default color of your ng-material default theme's 'primary' palette\n$_selected-bg: lighten($_primary-color, 56%);\n\n$color: (\n 'primary': $_primary-color,\n 'accent': #F05843, // should match the default color of your ng-material default theme's 'accent' palette\n 'accent-1': #795C3A,\n 'accent-2': #CAD266,\n 'warn': #c62828, // should match the default color of your ng-material default theme's 'warn' palette\n 'info': #ef6c00,\n 'success': #00C853,\n 'divider': rgba(0, 0, 0, 0.12),\n 'gray-lightest': #f7f7f7,\n 'gray-lighter': #eeeeee,\n 'gray-light': #dddddd,\n 'gray': #cccccc,\n 'gray-dark': #aaaaaa,\n 'gray-darker': #757575,\n 'gray-darkest': #333333,\n 'text': rgba(0, 0, 0, 0.87),\n 'text-secondary': rgba(0, 0, 0, 0.54),\n 'text-disabled': rgba(0, 0, 0, 0.26),\n 'text-light': rgba(255, 255, 255, 1),\n 'text-light-secondary': rgba(255, 255, 255, 0.70),\n 'text-light-disabled': rgba(255, 255, 255, 0.50),\n 'selected-bg': $_selected-bg,\n 'score': #FFC107\n);\n\n$body-color: (\n 'body': map-get($color, 'text'),\n 'body-bg': map-get($color, 'gray-lighter')\n);\n\n$colors: map-merge($color, $body-color);\n","// Colors\n$_primary-color: #1C8CA8; // should match the default color of your ng-material default theme's 'primary' palette\n$_selected-bg: lighten($_primary-color, 59%);\n\n$color: (\n 'primary': $_primary-color,\n 'accent': #F05843, // should match the default color of your ng-material default theme's 'accent' palette\n 'accent-1': #795C3A,\n 'accent-2': #CAD266,\n 'warn': #c62828, // should match the default color of your ng-material default theme's 'warn' palette\n 'info': #ef6c00,\n 'success': #00C853,\n 'divider': rgba(0, 0, 0, 0.12),\n 'gray-lightest': #f7f7f7,\n 'gray-lighter': #eeeeee,\n 'gray-light': #dddddd,\n 'gray': #cccccc,\n 'gray-dark': #aaaaaa,\n 'gray-darker': #757575,\n 'gray-darkest': #333333,\n 'text': rgba(0, 0, 0, 0.87),\n 'text-secondary': rgba(0, 0, 0, 0.54),\n 'text-disabled': rgba(0, 0, 0, 0.26),\n 'text-light': rgba(255, 255, 255, 1),\n 'text-light-secondary': rgba(255, 255, 255, 0.70),\n 'text-light-disabled': rgba(255, 255, 255, 0.50),\n 'selected-bg': $_selected-bg,\n 'score': #FFC107\n);\n\n$body-color: (\n 'body': map-get($color, 'text'),\n 'body-bg': map-get($color, 'gray-lighter')\n);\n\n$colors: (map-merge($color, $body-color));\n\n// Typography\n$baseline-grid: 8px;\n$body-font-size-base: rem(1.500);\n$caption-font-size-base: rem(1.300);\n\n// Layout\n$wise-toolbar-height: 42px;\n\n// Menus\n$menu-border-radius: 2px;\n$max-visible-items: 6;\n$menu-item-height: 6 * $baseline-grid !default;\n$dense-menu-item-height: 4 * $baseline-grid !default;\n$max-menu-height: 2 * $baseline-grid + $max-visible-items * $menu-item-height !default;\n$max-dense-menu-height: 2 * $baseline-grid + $max-visible-items * $dense-menu-item-height !default;\n\n// Cards\n$card-border-radius: 4px;\n\n// Buttons\n$button-border-radius: 3px;\n","// Helper functions and mixins\n\n// Get colors from $colors map\n@function color($key) {\n @if map-has-key($colors, $key) {\n @return map-get($colors, $key);\n }\n @warn \"Unknown `#{$key}` in $colors.\";\n @return null;\n}\n\n// set size in pixels given rem\n@function rem($multiplier) {\n $font-size: 10px;\n @return $multiplier * $font-size;\n}\n","// 1. Config\n\n// 2. Base\n.l-constrained {\n margin-left: auto;\n margin-right: auto;\n max-width: 100%;\n position: relative;\n\n @media (min-width: $layout-breakpoint-xs) {\n width: $layout-breakpoint-md;\n }\n}\n\n.l-constrained-md {\n width: $layout-breakpoint-sm;\n max-width: 100%;\n}\n","// Helpers\n@function rem($multiplier) {\n $font-size: 10px;\n @return $multiplier * $font-size;\n}\n\n// Angular Material variables for use and !default overrides\n$md-toolbar-height: 52px;\n$md-toolbar-medium-tall-height: 74px;\n$md-toolbar-tall-height: 104px;\n$md-toolbar-height-mobile-portrait: 52px;\n$md-toolbar-height-mobile-landscape: 52px;\n\n$caption-font-size-base: rem(1.300);\n\n//$list-item-height: 56px;\n\n$card-border-radius: 4px;\n\n$layout-breakpoint-xs: 600px;\n$layout-breakpoint-sm: 960px;\n$layout-breakpoint-md: 1280px;\n$layout-breakpoint-lg: 1920px;\n\n$button-border-radius: 3px;\n\n$tooltip-fontsize-lg: rem(1.1);\n$tooltip-fontsize-sm: rem(1.1);\n//$tooltip-height-lg: rem(2.2);\n$tooltip-height-sm: rem(2.2);\n//$tooltip-top-margin-lg: rem(1.4);\n$tooltip-top-margin-sm: rem(1.4);\n//$tooltip-lr-padding-lg: rem(0.8);\n$tooltip-lr-padding-sm: rem(0.8);\n//$tooltip-max-width: rem(3.20);\n\n$progress-linear-bar-height: 7px;\n","// 1. Config\n\n// 2. Base\n.l-footer {\n position: fixed;\n bottom: 0;\n left: 0;\n right: 0;\n z-index: 1;\n background-color: #ffffff;\n border-top: 1px solid color('gray-lighter');\n}\n\n// Buttons\n.button--footer {\n margin: 0;\n padding-top: 0;\n padding-bottom: 0;\n min-width: 0;\n display: flex;\n}\n\n.button--footer__element {\n padding-left: 8px;\n}\n","// 1. Config\n\n// 2. Base\n.l-header {\n z-index: 3;\n\n .logo {\n margin-left: 0 !important;\n height: 36px;\n width: 36px;\n vertical-align: middle;\n }\n\n .logo-link {\n min-width: auto;\n display: none;\n padding: 0 4px;\n margin-right: 12px;\n\n @media only screen and (min-width: ($layout-breakpoint-xs)) {\n display: block;\n }\n\n &:hover, &:focus {\n border: 0 none;\n }\n }\n\n // Handle mobile portrait\n @media only screen and (max-width: ($layout-breakpoint-xs - 1)) {\n .md-toolbar-tools {\n h1, h2, h3 {\n font-size: $body-font-size-base;\n }\n }\n }\n}\n","// 1. Config\n\n// 2. Base\n.l-main {\n background-color: color('body-bg');\n}\n\n.l-main--with-toolbar {\n margin-top: $wise-toolbar-height;\n}\n\n#content {\n transition: margin-top 0.5s;\n}\n\n.view-content {\n margin: 0 auto;\n padding: 8px;\n position: absolute;\n left: 0;\n right: 0;\n transition: opacity 500ms;\n\n @media only screen and (min-width: $layout-breakpoint-sm) {\n padding: 16px;\n }\n\n &.ng-enter {\n opacity: 0;\n }\n\n .ng-enter-active {\n opacity: 1;\n transition-delay: 250ms;\n }\n\n &.ng-leave-active,\n &.ng-hide {\n opacity: 0;\n }\n\n &.ng-hide-add,\n &.ng-hide-add-active,\n &.ng-hide-remove,\n &.ng-hide-remove-active {\n opacity: 0;\n }\n}\n\n.view-content--with-sidemenu {\n padding: 8px;\n\n @media only screen and (min-width: $layout-breakpoint-xs) {\n margin-left: 54px;\n padding: 16px;\n }\n}\n[dir='rtl'] .view-content--with-sidemenu {\n @media only screen and (min-width: $layout-breakpoint-xs) {\n margin-left: auto;\n margin-right: 54px;\n }\n}\n\n.content-head {\n margin: 8px 0;\n\n h1,\n h2,\n h3 {\n font-weight: 300;\n margin-top: 0;\n margin-bottom: 0;\n font-size: rem(3.6);\n\n @media only screen and (max-width: $layout-breakpoint-sm - 1) {\n font-size: rem(3.2);\n text-align: center;\n }\n }\n}\n\n.content-head__more {\n @media only screen and (max-width: $layout-breakpoint-sm - 1) {\n margin-top: 8px;\n }\n}\n\n.content-head__item,\nh2.content-head__item {\n margin: 0 8px;\n\n .md-subhead {\n padding-left: 4px;\n\n @media only screen and (max-width: $layout-breakpoint-sm - 1) {\n display: block;\n padding-left: 0;\n }\n }\n\n md-icon {\n vertical-align: text-bottom;\n }\n}\n\n.stepSelectMenuContainer md-select-menu,\n.stepSelectMenuContainer md-select-menu md-content {\n max-height: 500px;\n}\n","/*\n WISE Project Styles\n */\nbody {\n background: #eeeeee; }\n body.vle {\n overflow: hidden; }\n\na:hover, a:focus {\n color: #7e57c2; }\n\nblockquote {\n background-color: white;\n padding: 8px;\n margin: 16px 0;\n border-style: solid;\n border-color: #7e57c2;\n border-width: 0 0 0 3px; }\n\n.has-indicator:after {\n content: '';\n position: absolute;\n border-radius: 50%;\n padding: 5px;\n background-color: #F05843; }\n\n.has-indicator--icon-button:after {\n top: 25px;\n left: 5px; }\n\n.badge {\n border-radius: 4px;\n padding: 2px 6px;\n font-size: 12px;\n font-weight: 500;\n font-style: normal;\n background-color: #aaaaaa; }\n .badge.md-button {\n min-width: 0;\n min-height: 0;\n line-height: inherit; }\n .badge.md-button:hover, .badge.md-button:focus {\n background-color: #aaaaaa; }\n .badge.md-button:focus {\n outline: 1px dotted #aaaaaa; }\n\n.badge--info {\n background-color: #ef6c00;\n color: #ffffff; }\n\n.badge--warn {\n background-color: #c62828;\n color: #ffffff; }\n\n.badge--success {\n background-color: #00C853;\n color: #ffffff; }\n\n.divider--withmargin {\n margin: 16px 0; }\n\n.divider--dashed {\n border-top-style: dashed; }\n\na {\n color: #7e57c2;\n cursor: pointer; }\n\n.active {\n background-color: rgba(158, 158, 158, 0.2);\n color: rgba(0, 0, 0, 0.87); }\n\n.avatar {\n border-radius: 50%;\n box-sizing: content-box; }\n\n.avatar--square {\n border-radius: 4px; }\n\n.avatar.md-18 {\n height: 30px;\n width: 30px; }\n\n.avatar.md-24 {\n height: 36px;\n width: 36px; }\n\n.avatar.md-36 {\n height: 48px;\n width: 48px; }\n\n.avatar.md-48 {\n height: 60px;\n width: 60px; }\n\n.avatar--icon {\n background-color: #dddddd;\n white-space: normal !important; }\n .avatar--icon:not(.md-avatar) {\n padding: 6px; }\n .avatar--icon.md-18 {\n height: 18px;\n width: 18px; }\n .avatar--icon.md-24 {\n height: 24px;\n width: 24px; }\n .avatar--icon.md-36 {\n height: 36px;\n width: 36px; }\n .avatar--icon.md-48 {\n height: 48px;\n width: 48px; }\n\nmd-toolbar.md-light-theme:not(.md-menu-toolbar) md-icon {\n color: rgba(0, 0, 0, 0.54); }\n\nmd-toolbar.md-light-theme:not(.md-menu-toolbar) .md-button:disabled md-icon {\n color: rgba(0, 0, 0, 0.26); }\n\nmd-icon.primary, .md-button:not([disabled]).primary {\n color: #7e57c2 !important; }\n\nmd-icon.success, .md-button:not([disabled]).success {\n color: #00C853 !important; }\n\nmd-icon.warn, .md-button:not([disabled]).warn {\n color: #c62828 !important; }\n\nmd-icon.info, .md-button:not([disabled]).info {\n color: #ef6c00 !important; }\n\nmd-icon.accent, .md-button:not([disabled]).accent {\n color: #F05843 !important; }\n\nmd-icon.accent-1, .md-button:not([disabled]).accent-1 {\n color: #795C3A !important; }\n\nmd-icon.accent-2, .md-button:not([disabled]).accent-2 {\n color: #CAD266 !important; }\n\nmd-input-container.md-wise-theme label {\n color: rgba(0, 0, 0, 0.87); }\n\nmd-select-menu.md-default-theme md-option[selected], md-select-menu md-option[selected] {\n background-color: white; }\n\n.md-autocomplete-suggestions-container.md-default-theme li .highlight,\n.md-autocomplete-suggestions-container li .highlight {\n color: #7e57c2;\n background-color: white; }\n\n.primary {\n color: #7e57c2; }\n\n.accent {\n color: #F05843; }\n\n.accent-1 {\n color: #795C3A; }\n\n.accent-2 {\n color: #CAD266; }\n\n.warn {\n color: #c62828; }\n\n.info {\n color: #ef6c00; }\n\n.success {\n color: #00C853; }\n\n.divider {\n color: rgba(0, 0, 0, 0.12); }\n\n.gray-lightest {\n color: #f7f7f7; }\n\n.gray-lighter {\n color: #eeeeee; }\n\n.gray-light {\n color: #dddddd; }\n\n.gray {\n color: #cccccc; }\n\n.gray-dark {\n color: #aaaaaa; }\n\n.gray-darker {\n color: #757575; }\n\n.gray-darkest {\n color: #333333; }\n\n.text {\n color: rgba(0, 0, 0, 0.87); }\n\n.text-secondary {\n color: rgba(0, 0, 0, 0.54); }\n\n.text-disabled {\n color: rgba(0, 0, 0, 0.26); }\n\n.text-light {\n color: white; }\n\n.text-light-secondary {\n color: rgba(255, 255, 255, 0.7); }\n\n.text-light-disabled {\n color: rgba(255, 255, 255, 0.5); }\n\n.selected-bg {\n color: white; }\n\n.score {\n color: #FFC107; }\n\n.body {\n color: rgba(0, 0, 0, 0.87); }\n\n.body-bg {\n color: #eeeeee; }\n\n.primary-bg {\n background-color: #7e57c2; }\n\n.accent-bg {\n background-color: #F05843; }\n\n.accent-1-bg {\n background-color: #795C3A; }\n\n.accent-2-bg {\n background-color: #CAD266; }\n\n.warn-bg {\n background-color: #c62828; }\n\n.info-bg {\n background-color: #ef6c00; }\n\n.success-bg {\n background-color: #00C853; }\n\n.divider-bg {\n background-color: rgba(0, 0, 0, 0.12); }\n\n.gray-lightest-bg {\n background-color: #f7f7f7; }\n\n.gray-lighter-bg {\n background-color: #eeeeee; }\n\n.gray-light-bg {\n background-color: #dddddd; }\n\n.gray-bg {\n background-color: #cccccc; }\n\n.gray-dark-bg {\n background-color: #aaaaaa; }\n\n.gray-darker-bg {\n background-color: #757575; }\n\n.gray-darkest-bg {\n background-color: #333333; }\n\n.text-bg {\n background-color: rgba(0, 0, 0, 0.87); }\n\n.text-secondary-bg {\n background-color: rgba(0, 0, 0, 0.54); }\n\n.text-disabled-bg {\n background-color: rgba(0, 0, 0, 0.26); }\n\n.text-light-bg {\n background-color: white; }\n\n.text-light-secondary-bg {\n background-color: rgba(255, 255, 255, 0.7); }\n\n.text-light-disabled-bg {\n background-color: rgba(255, 255, 255, 0.5); }\n\n.selected-bg-bg {\n background-color: white; }\n\n.score-bg {\n background-color: #FFC107; }\n\n.body-bg {\n background-color: rgba(0, 0, 0, 0.87); }\n\n.body-bg-bg {\n background-color: #eeeeee; }\n\nmd-progress-circular.primary path {\n stroke: #7e57c2; }\n\nmd-progress-circular.accent path {\n stroke: #F05843; }\n\nmd-progress-circular.accent-1 path {\n stroke: #795C3A; }\n\nmd-progress-circular.accent-2 path {\n stroke: #CAD266; }\n\nmd-progress-circular.warn path {\n stroke: #c62828; }\n\nmd-progress-circular.info path {\n stroke: #ef6c00; }\n\nmd-progress-circular.success path {\n stroke: #00C853; }\n\nmd-progress-circular.divider path {\n stroke: rgba(0, 0, 0, 0.12); }\n\nmd-progress-circular.gray-lightest path {\n stroke: #f7f7f7; }\n\nmd-progress-circular.gray-lighter path {\n stroke: #eeeeee; }\n\nmd-progress-circular.gray-light path {\n stroke: #dddddd; }\n\nmd-progress-circular.gray path {\n stroke: #cccccc; }\n\nmd-progress-circular.gray-dark path {\n stroke: #aaaaaa; }\n\nmd-progress-circular.gray-darker path {\n stroke: #757575; }\n\nmd-progress-circular.gray-darkest path {\n stroke: #333333; }\n\nmd-progress-circular.text path {\n stroke: rgba(0, 0, 0, 0.87); }\n\nmd-progress-circular.text-secondary path {\n stroke: rgba(0, 0, 0, 0.54); }\n\nmd-progress-circular.text-disabled path {\n stroke: rgba(0, 0, 0, 0.26); }\n\nmd-progress-circular.text-light path {\n stroke: white; }\n\nmd-progress-circular.text-light-secondary path {\n stroke: rgba(255, 255, 255, 0.7); }\n\nmd-progress-circular.text-light-disabled path {\n stroke: rgba(255, 255, 255, 0.5); }\n\nmd-progress-circular.selected-bg path {\n stroke: white; }\n\nmd-progress-circular.score path {\n stroke: #FFC107; }\n\nmd-progress-circular.body path {\n stroke: rgba(0, 0, 0, 0.87); }\n\nmd-progress-circular.body-bg path {\n stroke: #eeeeee; }\n\n.l-constrained {\n margin-left: auto;\n margin-right: auto;\n max-width: 100%;\n position: relative; }\n @media (min-width: 600px) {\n .l-constrained {\n width: 1280px; } }\n\n.l-constrained-md {\n width: 960px;\n max-width: 100%; }\n\n.l-footer {\n position: fixed;\n bottom: 0;\n left: 0;\n right: 0;\n z-index: 1;\n background-color: #ffffff;\n border-top: 1px solid #eeeeee; }\n\n.button--footer {\n margin: 0;\n padding-top: 0;\n padding-bottom: 0;\n min-width: 0;\n display: flex; }\n\n.button--footer__element {\n padding-left: 8px; }\n\n.l-header {\n z-index: 3; }\n .l-header .logo {\n margin-left: 0 !important;\n height: 36px;\n width: 36px;\n vertical-align: middle; }\n .l-header .logo-link {\n min-width: auto;\n display: none;\n padding: 0 4px;\n margin-right: 12px; }\n @media only screen and (min-width: 600px) {\n .l-header .logo-link {\n display: block; } }\n .l-header .logo-link:hover, .l-header .logo-link:focus {\n border: 0 none; }\n @media only screen and (max-width: 599px) {\n .l-header .md-toolbar-tools h1, .l-header .md-toolbar-tools h2, .l-header .md-toolbar-tools h3 {\n font-size: 15px; } }\n\n.l-main {\n background-color: #eeeeee; }\n\n.l-main--with-toolbar {\n margin-top: 42px; }\n\n#content {\n transition: margin-top 0.5s; }\n\n.view-content {\n margin: 0 auto;\n padding: 8px;\n position: absolute;\n left: 0;\n right: 0;\n transition: opacity 500ms; }\n @media only screen and (min-width: 960px) {\n .view-content {\n padding: 16px; } }\n .view-content.ng-enter {\n opacity: 0; }\n .view-content .ng-enter-active {\n opacity: 1;\n transition-delay: 250ms; }\n .view-content.ng-leave-active, .view-content.ng-hide {\n opacity: 0; }\n .view-content.ng-hide-add, .view-content.ng-hide-add-active, .view-content.ng-hide-remove, .view-content.ng-hide-remove-active {\n opacity: 0; }\n\n.view-content--with-sidemenu {\n padding: 8px; }\n @media only screen and (min-width: 600px) {\n .view-content--with-sidemenu {\n margin-left: 54px;\n padding: 16px; } }\n\n@media only screen and (min-width: 600px) {\n [dir='rtl'] .view-content--with-sidemenu {\n margin-left: auto;\n margin-right: 54px; } }\n\n.content-head {\n margin: 8px 0; }\n .content-head h1,\n .content-head h2,\n .content-head h3 {\n font-weight: 300;\n margin-top: 0;\n margin-bottom: 0;\n font-size: 36px; }\n @media only screen and (max-width: 959px) {\n .content-head h1,\n .content-head h2,\n .content-head h3 {\n font-size: 32px;\n text-align: center; } }\n\n@media only screen and (max-width: 959px) {\n .content-head__more {\n margin-top: 8px; } }\n\n.content-head__item,\nh2.content-head__item {\n margin: 0 8px; }\n .content-head__item .md-subhead,\n h2.content-head__item .md-subhead {\n padding-left: 4px; }\n @media only screen and (max-width: 959px) {\n .content-head__item .md-subhead,\n h2.content-head__item .md-subhead {\n display: block;\n padding-left: 0; } }\n .content-head__item md-icon,\n h2.content-head__item md-icon {\n vertical-align: text-bottom; }\n\n.stepSelectMenuContainer md-select-menu,\n.stepSelectMenuContainer md-select-menu md-content {\n max-height: 500px; }\n\n.l-nav {\n background-color: #eeeeee !important; }\n\n.l-node {\n margin-top: 42px;\n padding: 0; }\n @media only screen and (min-width: 600px) {\n .l-node {\n padding: 0 0 16px;\n background-color: #eeeeee !important; } }\n @media only screen and (min-width: 960px) {\n .l-node {\n padding: 0 0 32px; } }\n\n.l-notebook {\n margin-top: 42px;\n background-color: #eeeeee !important; }\n\n.l-sidebar__header {\n background-color: #ffffff !important;\n color: #795C3A !important; }\n .l-sidebar__header md-select {\n color: rgba(0, 0, 0, 0.87); }\n\n.status-icon {\n margin: 0 4px;\n z-index: 1;\n vertical-align: bottom; }\n\n.md-button.status-icon {\n height: auto;\n width: auto;\n min-height: 0;\n line-height: inherit;\n margin: 0 4px;\n padding: 0; }\n\n.avatar--icon--alert {\n background-color: #ffffff; }\n\n.avatar--icon--alert__icon {\n font-size: 48px;\n margin: -4px 0 0 -4px; }\n\nmd-dialog {\n width: 600px; }\n\n.dialog--wide {\n width: 960px; }\n\n.dialog--wider {\n width: 1280px; }\n\n.help-bubble {\n border-radius: 4px;\n max-width: 320px; }\n @media (min-width: 600px) {\n .help-bubble {\n max-width: 552px; } }\n @media (min-width: 960px) {\n .help-bubble {\n max-width: 912px; } }\n @media (min-width: 1280px) {\n .help-bubble {\n max-width: 1232px; } }\n\n.help-bubble__title {\n border-top-left-radius: 4px;\n border-top-right-radius: 4px; }\n\n.help-bubble___title__content {\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n padding: 0px 0 0 12px;\n background-color: #ef6c00; }\n .help-bubble___title__content .md-icon-button {\n margin-right: 0;\n padding-top: 0;\n padding-bottom: 0; }\n\n.help-bubble__content {\n overflow: auto;\n padding: 8px 12px;\n max-height: 480px; }\n\n.help-bubble__actions {\n border-bottom-left-radius: 4px;\n border-bottom-right-radius: 4px; }\n\ndiv.hopscotch-bubble {\n border-radius: 4px;\n border: 0 none;\n font-family: Roboto, \"Helvetica Neue\", sans-serif;\n font-size: 14px;\n z-index: 6; }\n div.hopscotch-bubble .hopscotch-bubble-arrow-container {\n position: absolute;\n width: 20px;\n height: 20px; }\n div.hopscotch-bubble .hopscotch-bubble-arrow-container.up {\n top: 0;\n left: 12px; }\n div.hopscotch-bubble .hopscotch-bubble-arrow-container.up .hopscotch-bubble-arrow {\n border-bottom: 10px solid #ef6c00;\n border-left: 10px solid transparent;\n border-right: 10px solid transparent;\n position: relative;\n top: -10px; }\n div.hopscotch-bubble .hopscotch-bubble-arrow-container.up .hopscotch-bubble-arrow-border {\n border: 0 none;\n /*border-bottom: 12px solid color('gray-darker');\n border-left: 12px solid transparent;\n border-right: 12px solid transparent;*/ }\n div.hopscotch-bubble .hopscotch-bubble-arrow-container.down {\n bottom: -34px;\n left: 12px; }\n div.hopscotch-bubble .hopscotch-bubble-arrow-container.down .hopscotch-bubble-arrow {\n border-top: 10px solid #ef6c00;\n border-left: 10px solid transparent;\n border-right: 10px solid transparent;\n position: relative;\n top: -10px; }\n div.hopscotch-bubble .hopscotch-bubble-arrow-container.down .hopscotch-bubble-arrow-border {\n border: 0 none;\n /*border-top: 12px solid color('gray-darker');\n border-left: 12px solid transparent;\n border-right: 12px solid transparent;*/ }\n div.hopscotch-bubble .hopscotch-bubble-arrow-container.left {\n top: 12px;\n left: -10px; }\n div.hopscotch-bubble .hopscotch-bubble-arrow-container.left .hopscotch-bubble-arrow {\n border-right: 10px solid #ef6c00;\n border-bottom: 10px solid transparent;\n border-top: 10px solid transparent;\n position: relative;\n left: 0;\n top: -10px; }\n div.hopscotch-bubble .hopscotch-bubble-arrow-container.left .hopscotch-bubble-arrow-border {\n border: 0 none;\n /*border-right: 12px solid color('gray-darker');\n border-bottom: 12px solid transparent;\n border-top: 12px solid transparent;*/ }\n div.hopscotch-bubble .hopscotch-bubble-arrow-container.right {\n top: 12px;\n right: -30px; }\n div.hopscotch-bubble .hopscotch-bubble-arrow-container.right .hopscotch-bubble-arrow {\n border-left: 10px solid #ef6c00;\n border-bottom: 10px solid transparent;\n border-top: 10px solid transparent;\n position: relative;\n right: 0;\n top: -10px; }\n div.hopscotch-bubble .hopscotch-bubble-arrow-container.right .hopscotch-bubble-arrow-border {\n border: 0 none;\n /*border-right: 12px solid color('gray-darker');\n border-bottom: 12px solid transparent;\n border-top: 12px solid transparent;*/ }\n\n.input-container {\n padding-top: 12px; }\n\n.input-container--component {\n margin-bottom: 0; }\n\n.input-container--open-response.md-has-icon {\n padding-left: 0; }\n\n.input-container--open-response .md-errors-spacer {\n display: none; }\n\n.input-wrapper {\n position: relative; }\n\n.input-wrapper--focused .input--textarea__action md-icon {\n color: #7e57c2; }\n\n.input--textarea, .input-container textarea.input--textarea {\n padding: 8px;\n background-color: #f7f7f7;\n border: 1px solid #cccccc;\n margin-bottom: 8px; }\n .input--textarea:focus, .input-container textarea.input--textarea:focus {\n background-color: #ffffff; }\n .input--textarea[disabled], .input-container textarea.input--textarea[disabled] {\n color: rgba(0, 0, 0, 0.54); }\n\n.input-container textarea.input--textarea {\n width: 100%; }\n\n.input--textarea--disabled {\n color: rgba(0, 0, 0, 0.54); }\n\n.input--textarea__action {\n position: absolute;\n right: -4px; }\n .input--textarea__action[disabled] md-icon {\n color: rgba(0, 0, 0, 0.26) !important; }\n\n.input--textarea__action--notebook {\n top: 6px; }\n .input-wrapper--richtext .input--textarea__action--notebook {\n top: -7px; }\n\n.input--textarea__action--revision {\n bottom: 6px; }\n .input-wrapper--richtext .input--textarea__action--revision {\n bottom: -5px; }\n\n.input-label, md-input-container:not(.md-input-invalid):not(.md-input-focused).md-input-has-value label.input-label {\n line-height: 1.2;\n color: rgba(0, 0, 0, 0.87); }\n .input-label.input-label--focused, md-input-container:not(.md-input-invalid):not(.md-input-focused).md-input-has-value label.input-label.input-label--focused {\n color: #7e57c2; }\n\n.autocomplete input {\n text-overflow: ellipsis;\n overflow: hidden;\n word-wrap: none;\n font-weight: 500;\n color: rgba(0, 0, 0, 0.54); }\n\n@media only screen and (min-width: 600px) {\n .autocomplete--minwidth {\n min-width: 300px; } }\n\n@media only screen and (min-width: 960px) {\n .autocomplete--minwidth {\n min-width: 300px; } }\n\n.autocomplete--flat md-autocomplete-wrap {\n background-color: #ffffff; }\n .autocomplete--flat md-autocomplete-wrap:not(.md-menu-showing) {\n box-shadow: none;\n background-color: #eeeeee; }\n\n.select__header {\n height: 48px; }\n .select__header input {\n height: 100%;\n width: 100%;\n padding: 0 8px;\n outline: none;\n border: 0 none;\n font-size: 14px;\n font-weight: 500; }\n\n.table {\n max-width: 100%;\n width: auto;\n min-width: 100px;\n margin: 8px 0; }\n .table thead > tr > th,\n .table thead > tr > td,\n .table tbody > tr > th,\n .table tbody > tr > td,\n .table tfoot > tr > th,\n .table tfoot > tr > td {\n border: 1px solid #cccccc;\n padding: 6px;\n font-size: 15px;\n min-height: 32px;\n height: 32px;\n min-width: 32px;\n vertical-align: top; }\n .table td.inactive,\n .table th {\n background-color: #f7f7f7;\n opacity: 1;\n visibility: visible; }\n .table md-input-container {\n margin: 0; }\n .table .md-errors-spacer {\n display: none; }\n\n.table--student td.inactive {\n padding: 8px 10px; }\n\n.table--full-width {\n width: 100%; }\n\n.table--list {\n border: 0 none;\n border-collapse: collapse;\n background-color: #ffffff;\n max-width: 100%;\n overflow: auto; }\n .table--list th,\n .table--list td {\n padding: 0 4px;\n border: 0 none; }\n .table--list td {\n min-height: 56px;\n height: 56px; }\n .table--list tr.md-button {\n display: table-row;\n text-align: left;\n width: auto;\n text-transform: none;\n font-size: inherit;\n font-weight: normal; }\n\n.table--list__wrap {\n min-width: 600px; }\n\n@media only screen and (max-width: 959px) {\n .table-wrap-sticky {\n overflow-x: auto; } }\n\n.table--list__thead {\n font-size: 14px;\n font-weight: 700; }\n\n.table--list__thead__tr {\n height: 100%;\n margin: 0; }\n\n.table--list__thead__th {\n background-color: #757575;\n color: white;\n min-height: 42px;\n height: 42px; }\n\n.table--list__thead__link {\n color: #ffffff;\n text-transform: none;\n margin: 0;\n min-width: 0;\n white-space: normal;\n line-height: 1.4;\n width: 100%; }\n\n.table--list__thead__sort {\n margin: 0; }\n\n.table--list__thead__sort--reverse {\n transform: rotate(180deg); }\n\n.td--wrap {\n min-width: 180px;\n white-space: normal;\n line-height: 1.2; }\n\n@media only screen and (max-width: 959px) {\n .td--max-width {\n max-width: 180px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap; } }\n\n.md-toolbar-tools {\n font-size: 18px; }\n .md-toolbar-tools .autocomplete {\n height: 36px; }\n .md-toolbar-tools .autocomplete md-autocomplete-wrap {\n height: 36px; }\n .md-toolbar-tools .autocomplete input {\n height: 36px; }\n\n.md-toolbar--wise {\n min-height: 42px; }\n .md-toolbar--wise .md-toolbar-tools {\n height: 42px;\n max-height: 42px; }\n .md-toolbar--wise .md-button.md-icon-button {\n height: 42px;\n line-height: 42px;\n width: 42px; }\n\n.md-toolbar--wise--sm .md-toolbar-tools {\n padding: 0 8px;\n font-size: 15px; }\n\n.md-toolbar--sidenav {\n background-color: #333333 !important; }\n .md-toolbar--sidenav .md-toolbar-tools {\n font-size: 16px;\n font-weight: 500; }\n\n.toolbar {\n position: fixed;\n left: 0;\n right: 0;\n top: 52px;\n z-index: 3; }\n\n.toolbar__title {\n margin-left: 8px;\n font-size: 16px;\n font-weight: 500; }\n\n.toolbar__tools {\n padding-right: 8px; }\n\n[dir=rtl] .toolbar__tools {\n padding-right: 16px;\n padding-left: 8px; }\n\n.toolbar__nav, .md-button.toolbar__nav {\n margin: 0; }\n\n.toolbar__select, .md-button.toolbar__select {\n margin: 0 4px;\n min-height: 32px;\n background-color: #f7f7f7; }\n .toolbar__select .md-select-value, .md-button.toolbar__select .md-select-value {\n height: 32px;\n text-align: left; }\n\n[dir=rtl] .toolbar__select .md-select-value, [dir=rtl] .md-button.toolbar__select .md-select-value {\n text-align: right; }\n\n.toolbar__select--fixedwidth {\n width: 168px; }\n @media only screen and (min-width: 600px) {\n .toolbar__select--fixedwidth {\n width: 264px; } }\n @media only screen and (min-width: 960px) {\n .toolbar__select--fixedwidth {\n width: 432px; } }\n\n.list-item {\n background-color: #ffffff;\n border-bottom: 1px solid #eeeeee; }\n .list-item .md-subheader, .list-item.md-subheader {\n color: rgba(0, 0, 0, 0.87);\n background-color: #ffffff; }\n .list-item .md-subheader md-icon, .list-item.md-subheader md-icon {\n vertical-align: middle; }\n .list-item .md-subheader .md-subheader-inner, .list-item.md-subheader .md-subheader-inner {\n padding: 0; }\n .list-item .md-subheader .md-avatar, .list-item.md-subheader .md-avatar {\n margin-right: 8px; }\n .list-item .autocomplete {\n margin: 8px 0; }\n\n.list-item--info._md-button-wrap > div.md-button:first-child, .list-item--info .md-subheader-content {\n border-left: 4px solid #ef6c00 !important;\n margin-left: -4px; }\n\n.list-item--warn._md-button-wrap > div.md-button:first-child, .list-item--warn .md-subheader-content {\n border-left: 4px solid #c62828 !important;\n margin-left: -4px; }\n\n.list-item--expanded {\n border-bottom-width: 0; }\n\n.list-item--noclick, .list-item--noclick.md-button {\n cursor: default;\n background-color: #f7f7f7; }\n\n.list-item--actions {\n padding: 0 8px !important; }\n\n.list-item__subheader-button {\n text-transform: none;\n width: 100%;\n padding: 8px 16px;\n margin: 0;\n white-space: normal;\n text-align: left;\n line-height: 1.4; }\n\n.user-list {\n font-size: 15px; }\n\n#nav {\n position: relative; }\n\n.nav {\n margin-bottom: 16px; }\n\n.nav-mask {\n position: absolute;\n top: 0;\n bottom: 0;\n left: 0;\n right: 0;\n background-color: rgba(0, 0, 0, 0.25);\n z-index: 1; }\n .nav-mask.ng-hide {\n opacity: 0; }\n\n.nav-head {\n color: rgba(0, 0, 0, 0.54);\n font-weight: 500; }\n .nav-head md-icon {\n line-height: 20px; }\n\n.nav-contents--root {\n padding: 6px 6px 12px; }\n\n.nav-contents--group {\n background-color: #dddddd;\n padding: 8px; }\n\n.nav-contents--root {\n padding: 0; }\n\n.nav-contents__list {\n padding: 0; }\n @media (min-width: 600px) {\n .nav-contents__list {\n padding: 8px; } }\n\n.nav-item {\n transition: opacity 250ms ease-in-out; }\n .nav-item.prev md-list-item {\n background-color: white;\n /*&._md-button-wrap>div.md-button:first-child {\n border-left: 4px solid color('primary');\n margin-left: -4px;\n }*/ }\n\n.nav-item--card__content {\n border-top-right-radius: 4px;\n border-top-left-radius: 4px; }\n .nav-item--card__content:focus {\n outline: none; }\n .nav-item--card__content:focus .nav-item__title > span {\n border-bottom: 1px dashed #cccccc; }\n\n.nav-item--root {\n transition: margin 250ms, box-shadow 500ms; }\n .nav-item--root.expanded {\n flex-basis: 100%;\n max-width: 100%;\n max-height: none !important;\n margin: 8px auto;\n padding-left: 4px; }\n .nav-item--root.expanded:first-of-type {\n margin-top: 0; }\n\n.nav-item--list__info-item {\n padding: 0 16px 0 4px;\n display: inline-block; }\n\n.nav-item--list__reorder {\n margin-left: 8px;\n color: rgba(0, 0, 0, 0.26); }\n\n.nav-item--card--group:not(.expanded) {\n box-shadow: 0px 3px 1px -2px rgba(0, 0, 0, 0.14), 0px 2px 2px 0px rgba(0, 0, 0, 0.098), 0px 1px 5px 0px rgba(0, 0, 0, 0.084), 3px 3px 0px 1px #d5d5d5, 6px 6px 0px 1px #aaaaaa; }\n\n.nav-item__collapse {\n margin: 0; }\n\n.nav-item__more {\n border-top: 1px solid #dddddd;\n border-bottom-right-radius: 4px;\n border-bottom-left-radius: 4px;\n padding: 8px 16px;\n min-height: 40px; }\n\n.nav-item__users {\n height: auto;\n cursor: pointer;\n color: #ffffff;\n margin: 0;\n padding: 1px 6px; }\n .nav-item__users > md-icon {\n padding-right: 4px;\n color: #ffffff; }\n .nav-item__users:hover.success-bg, .nav-item__users:focus.success-bg {\n background-color: #00C853; }\n\n.nav-item__title {\n padding-left: 16px;\n line-height: 1.2;\n font-weight: 400; }\n\n[dir=rtl] .nav-item__title {\n padding-left: auto;\n padding-right: 16px; }\n\n.nav-item__info {\n padding: 0 8px; }\n\n.nav-item__progress {\n width: 48px; }\n .nav-item__progress > .md-container {\n top: 0; }\n\n.nav-item__progress-value {\n margin-left: 8px;\n width: 36px; }\n\n.progress-wrapper {\n padding: 2px 0;\n cursor: pointer; }\n\n.notice {\n text-align: center;\n padding: 8px;\n background-color: rgba(0, 0, 0, 0.04);\n width: 100%; }\n @media (min-width: 600px) {\n .notice {\n max-width: 80%;\n border-radius: 3px;\n margin: 24px auto; } }\n\n.menu-progress {\n position: absolute;\n top: 10px;\n right: 12px; }\n .menu-progress path {\n stroke: #CAD266 !important;\n stroke-width: 2px; }\n\n[dir=rtl] .menu-progress {\n right: auto;\n left: 12px; }\n\n.menu-sidenav__item {\n font-weight: 700;\n font-size: 14px; }\n\n.menu-sidenav__icon {\n margin-top: 12px !important;\n margin-right: 12px !important;\n margin-left: 12px; }\n\n.active .menu-sidenav__icon, .active .menu-sidenav__item {\n color: #7e57c2; }\n\n.menu-sidebar {\n position: absolute;\n top: 94px;\n bottom: 0;\n left: 0;\n background-color: #fff;\n width: 56px;\n overflow: hidden;\n padding: 8px 0;\n text-align: center;\n border-right: 1px solid #cccccc; }\n @media only screen and (max-width: 599px) {\n .menu-sidebar {\n display: none; } }\n\n[dir=rtl] .menu-sidebar {\n right: 0;\n left: auto; }\n\n.md-button.md-icon-button.menu-sidebar__link {\n margin-top: 6px;\n margin-bottom: 6px; }\n\n#node {\n margin: 0 auto;\n position: absolute;\n left: 0;\n right: 0; }\n @media only screen and (min-width: 600px) {\n #node {\n padding: 8px;\n padding: 24px 16px;\n margin-bottom: 32px; } }\n @media only screen and (min-width: 960px) {\n #node {\n padding: 32px; } }\n #node.ng-enter {\n transition: opacity .5s;\n opacity: 0; }\n #node.ng-enter-active {\n opacity: 1; }\n\n@media only screen and (min-width: 600px) {\n .node-notice {\n margin-top: -8px;\n margin-bottom: 16px; } }\n\n@media only screen and (min-width: 960px) {\n .node-notice {\n margin-top: -16px; } }\n\n.node-content {\n padding: 0 0 48px;\n background-color: #ffffff;\n border-radius: 3px;\n overflow: visible; }\n @media only screen and (max-width: 599px) {\n .node-content {\n box-shadow: none; } }\n @media only screen and (min-width: 600px) {\n .node-content {\n padding: 0;\n border-top: 2px solid;\n border-bottom: 2px solid; } }\n\nmd-content.node-content {\n background-color: #ffffff; }\n\n.node-content__rubric {\n position: absolute;\n top: -22px;\n left: 0;\n right: 0;\n z-index: 1; }\n .node-content__rubric .avatar--icon {\n transform: scale(0.94); }\n @media only screen and (max-width: 599px) {\n .node-content__rubric .avatar--icon {\n transform: scale(0.8); } }\n\n.node-icon {\n color: #ffffff;\n vertical-align: inherit; }\n\n.node-select {\n margin: 0 8px;\n min-width: 0;\n font-weight: 500;\n font-size: 15px; }\n .node-select .md-select-value *:first-child {\n transform: translate3d(0, 0, 0);\n flex: 1 0 0; }\n .node-select .md-select-value .node-select__icon {\n display: none; }\n .node-select .md-select-value .node-select__status {\n display: none; }\n .node-select .md-select-icon {\n margin-left: 0;\n color: rgba(0, 0, 0, 0.87); }\n\n.node-select-option--group {\n background-color: #f7f7f7;\n border-bottom: 1px solid #eeeeee;\n border-top: 1px solid #eeeeee; }\n\n.node-select-option--node {\n padding-left: 20px; }\n\n.node-select__icon {\n margin-right: 8px; }\n\n.node-select__status {\n margin-left: 8px; }\n\n.node-select__text {\n text-overflow: ellipsis;\n white-space: nowrap;\n overflow: hidden; }\n @media only screen and (min-width: 600px) {\n .node-select__text {\n margin-top: 2px; } }\n\n.node-title {\n line-height: 1.2;\n text-transform: none;\n margin-top: 3px; }\n @media only screen and (max-width: 599px) {\n .node-title {\n font-size: 15px; } }\n\n.node-content__actions {\n padding: 0 16px 16px; }\n @media only screen and (min-width: 960px) {\n .node-content__actions {\n padding: 0 24px 24px; } }\n @media only screen and (min-width: 1280px) {\n .node-content__actions {\n padding: 0 32px 32px; } }\n .node-content__actions .md-button:first-child {\n margin-left: 0; }\n .node-content__actions .md-button:last-child {\n margin-right: 0; }\n\n.node-content__actions__info {\n font-style: italic;\n margin-left: 8px;\n color: rgba(0, 0, 0, 0.54); }\n\n.node-content__actions__more {\n border-bottom: 1px dotted; }\n\n.md-button.md-icon-button.node-nav:first-of-type {\n margin-right: 0; }\n\n@media only screen and (min-width: 600px) {\n .node-sidebar-active {\n margin-right: 68px; } }\n\n@media only screen and (max-width: 599px) {\n .node-sidebar-visible {\n margin-bottom: 42px; } }\n\n.node-sidebar {\n position: absolute;\n right: 0;\n top: 0;\n width: 52px; }\n\n.node-sidebar__toolbar {\n position: fixed;\n width: 52px;\n background-color: #ffffff;\n padding: 8px 0;\n border-radius: 3px; }\n @media only screen and (max-width: 599px) {\n .node-sidebar__toolbar {\n right: 0;\n bottom: 0;\n left: 0;\n width: 100%;\n border-radius: 0;\n padding: 0;\n min-height: 0;\n height: 42px; } }\n\n.node-info {\n margin: 0; }\n @media only screen and (max-width: 599px) {\n .node-info {\n margin: -16px;\n border-radius: 0; } }\n .node-info .divider {\n margin-left: -8px;\n margin-right: -8px; }\n .node-info .component:first-child {\n margin-top: -16px; }\n .node-info .component, .node-info .component__content, .node-info .component__header {\n margin-left: -8px;\n margin-right: -8px; }\n .node-info .component__actions {\n display: none; }\n\n.node-rubric {\n border: 2px solid #7e57c2;\n margin: 8px 16px 24px;\n padding: 16px;\n background-color: #ffffff; }\n\n.node__label--vertical-alignment {\n vertical-align: middle;\n display: inline-block; }\n\n@media only screen and (min-width: 600px) {\n .notebook-launcher.md-button.md-fab {\n z-index: 61; } }\n\n.notebook-report {\n border-radius: 4px 4px 0 0;\n margin: 0;\n height: 100%; }\n .notebook-report .note-toolbar {\n margin: -8px -8px 8px;\n padding: 4px;\n border: 0 none;\n border-top: 1px solid #dddddd;\n border-bottom: 1px solid #dddddd;\n border-radius: 0; }\n .notebook-report .note-toolbar .btn-group {\n margin-top: 0; }\n .notebook-report .note-btn {\n border: 0 none;\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n border-bottom-left-radius: 0;\n border-bottom-right-radius: 0; }\n\n.notebook-report-container {\n height: 550px;\n width: 450px;\n max-height: 90%;\n bottom: 0;\n right: 96px;\n position: absolute;\n z-index: 3; }\n\n@media only screen and (min-width: 960px) {\n .notes-visible .notebook-report-container {\n right: 516px;\n transition: right 250ms; } }\n\n.notebook-report-container__full {\n top: 16px;\n bottom: 16px;\n left: 16px;\n right: 16px;\n max-height: none;\n max-width: none;\n height: auto;\n width: auto; }\n .notebook-report-container__full .notebook-report {\n height: 100%;\n width: 100%;\n margin: 0 auto;\n max-height: none;\n border-radius: 4px; }\n\n.notebook-report-container__collapsed {\n width: 300px;\n height: auto; }\n .notebook-report-container__collapsed .notebook-report__content, .notebook-report-container__collapsed .notebook-report__actions, .notebook-report-container__collapsed .notebook-report__content__header {\n display: none; }\n\n.notebook-report__toolbar {\n background-color: #333333 !important;\n border-radius: 4px 4px 0 0; }\n\n.notebook-report__toolbar__title {\n max-width: 150px; }\n\n.notebook-report__content {\n background-color: #ffffff; }\n .notebook-report__content h1, .notebook-report__content h2, .notebook-report__content h3, .notebook-report__content h4 {\n font-size: 22px; }\n .notebook-report__content .note-editor.note-frame {\n border: 0 none;\n border-radius: 0;\n padding: 0;\n box-shadow: none; }\n .notebook-report__content .note-resizebar {\n display: none; }\n\n.notebook-report__content__header {\n padding: 8px;\n background-color: #ffffff;\n font-size: 16px; }\n\n@media only screen and (max-width: 599px) {\n .notebook-report-container:not(.notebook-report-container__collapsed) {\n top: 0;\n bottom: 0;\n left: 0;\n right: 0;\n max-height: none;\n max-width: none;\n height: auto;\n width: auto; }\n .notebook-report-container:not(.notebook-report-container__collapsed) .notebook-report {\n border-radius: 0; }\n .notebook-tools--full {\n display: none; } }\n\n.notebook-report-backdrop {\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n z-index: 3;\n background-color: #212121;\n opacity: .48; }\n\n.notebook-menu {\n transition: opacity 500ms; }\n .notebook-menu.ng-enter {\n opacity: 0; }\n .notebook-menu .ng-enter-active {\n opacity: 1;\n transition-delay: 250ms; }\n .notebook-menu.ng-leave-active, .notebook-menu.ng-hide {\n opacity: 0; }\n .notebook-menu.ng-hide-add, .notebook-menu.ng-hide-add-active, .notebook-menu.ng-hide-remove, .notebook-menu.ng-hide-remove-active {\n opacity: 0; }\n\n.notebook-item {\n transition: box-shadow 250ms;\n margin: 0 16px 16px;\n display: block; }\n\n.notebook-item__content {\n height: 250px;\n min-width: 230px;\n position: relative;\n padding: 0;\n background-color: #cccccc;\n border-top-left-radius: 4px;\n border-top-right-radius: 4px; }\n\n.notebook-item__content__attachment, .notebook-item__content__text {\n position: absolute;\n left: 0;\n right: 0; }\n\n.notebook-item__content__attachment {\n background-repeat: no-repeat !important;\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n background-position: center top !important;\n background-size: cover !important;\n top: 0;\n bottom: 0; }\n\n.notebook-item__content__text {\n bottom: 0;\n padding: 8px;\n font-weight: 500;\n overflow: hidden;\n max-height: 120px;\n min-height: 56px;\n background-color: rgba(255, 255, 255, 0.95);\n border-top: 1px solid #eeeeee; }\n .notebook-item__content__text:after {\n content: \"\";\n text-align: right;\n position: absolute;\n bottom: 0;\n right: 0;\n width: 100%;\n height: 0.8em;\n background: linear-gradient(180deg, rgba(255, 255, 255, 0), rgba(255, 255, 255, 0.95) 100%); }\n\n.notebook-item__content--text-only:after {\n content: \"note\";\n font-family: 'Material Icons';\n font-weight: normal;\n font-style: normal;\n display: inline-block;\n line-height: 1;\n text-transform: none;\n letter-spacing: normal;\n word-wrap: normal;\n white-space: nowrap;\n direction: ltr;\n /* Support for all WebKit browsers. */\n -webkit-font-smoothing: antialiased;\n /* Support for Safari and Chrome. */\n text-rendering: optimizeLegibility;\n /* Support for Firefox. */\n -moz-osx-font-smoothing: grayscale;\n /* Support for IE. */\n font-feature-settings: 'liga';\n font-size: 80px;\n color: rgba(0, 0, 0, 0.26); }\n\n.notebook-item--question__content--text-only {\n content: \"live_help\"; }\n\n.notebook-item__content__location {\n opacity: 0.9;\n padding: 8px 0; }\n .notebook-item__content__location md-icon {\n font-size: 22px; }\n\n.notebook-item__edit {\n cursor: pointer; }\n\n.notebook-item__actions {\n margin: 0;\n padding: 0 8px;\n color: #ffffff;\n background-color: #333333;\n border-bottom-left-radius: 4px;\n border-bottom-right-radius: 4px; }\n .notebook-item__actions md-icon {\n color: #ffffff; }\n\n.notebook-item__text-input {\n margin: 0; }\n\n.notebook-item__text-input__textarea {\n padding-left: 0;\n padding-right: 0; }\n\n.notebook-item__attachment {\n background-color: #dddddd;\n padding: 16px;\n margin-bottom: 16px;\n text-align: center;\n position: relative; }\n\n.notebook-item__attachment__content {\n max-width: 100%;\n height: auto; }\n\n.notebook-item__attachment__delete {\n position: absolute;\n top: 4px;\n right: -2px;\n width: 34px !important;\n height: 34px !important;\n min-height: 0; }\n .notebook-item__attachment__delete md-icon {\n margin-left: -2px;\n font-size: 22px; }\n\n.notebook-item__info {\n font-style: italic;\n opacity: .8;\n color: #8a6942; }\n .notebook-item__info a, .notebook-item__info md-icon {\n color: #8a6942; }\n .notebook-item__info md-icon {\n font-size: 1.5em;\n min-width: 0;\n width: auto; }\n\n.notebook-item__upload {\n text-align: center;\n padding: 24px;\n background-color: #eeeeee;\n margin-bottom: 16px;\n color: rgba(0, 0, 0, 0.54);\n border-radius: 4px;\n cursor: pointer;\n border: 1px dashed transparent;\n transition: all 250ms; }\n .notebook-item__upload md-icon, .notebook-item__upload span {\n transition: color 250ms; }\n .notebook-item__upload:hover, .notebook-item__upload:focus, .notebook-item__upload.dragover {\n border-color: #F05843;\n background-color: #fdebe8;\n color: #F05843; }\n .notebook-item__upload:hover md-icon, .notebook-item__upload:hover span, .notebook-item__upload:focus md-icon, .notebook-item__upload:focus span, .notebook-item__upload.dragover md-icon, .notebook-item__upload.dragover span {\n color: #F05843; }\n\n.view-notebook-item {\n width: 600px; }\n\n.notebook-item--report {\n background-color: #ffffff; }\n .notebook-item--report .note-editor {\n margin-bottom: 16px;\n border-color: #cccccc; }\n\n.notebook-item--report__container.ui-scrollpoint .notebook-item--report__toolbar {\n position: fixed;\n top: 94px;\n left: 0;\n right: 0;\n z-index: 1; }\n @media only screen and (min-width: 600px) {\n .notebook-item--report__container.ui-scrollpoint .notebook-item--report__toolbar {\n left: 54px; } }\n @media only screen and (min-width: 600px) {\n .notebook-item--report__container.ui-scrollpoint .notebook-item--report__toolbar {\n padding: 0 24px; } }\n @media only screen and (min-width: 960px) {\n .notebook-item--report__container.ui-scrollpoint .notebook-item--report__toolbar {\n padding: 0 32px; } }\n .notebook-item--report__container.ui-scrollpoint .notebook-item--report__toolbar .note-toolbar {\n margin: 0 16px; }\n @media only screen and (min-width: 600px) {\n .notebook-item--report__container.ui-scrollpoint .notebook-item--report__toolbar .note-toolbar {\n margin: 0 8px; } }\n @media only screen and (min-width: 960px) {\n .notebook-item--report__container.ui-scrollpoint .notebook-item--report__toolbar .note-toolbar {\n margin: 0 16px; } }\n\n.notebook-item--report__container.ui-scrollpoint .note-editor {\n padding-top: 40px; }\n\n.notebook-item--report__toolbar .note-toolbar {\n background-color: #dddddd;\n border: 1px solid #cccccc;\n margin-bottom: -2px; }\n\n.notebook-item--report__heading {\n text-align: center;\n margin-bottom: 32px; }\n\n.notebook-item--report__add-note {\n font-weight: 700; }\n\n.notebook-item--report__note-img {\n max-width: 100%;\n height: auto !important; }\n\n@media only screen and (min-width: 600px) {\n .notebook-sidebar {\n width: 400px;\n max-width: none; } }\n\n@media only screen and (min-width: 960px) {\n .notebook-sidebar {\n width: 500px;\n max-width: none; } }\n\n.notebook-items {\n width: 100%;\n overflow: auto;\n margin-top: 16px;\n margin-bottom: 76px; }\n .notebook-items .notebook-item {\n width: 100%; }\n .notebook-items .notebook-item__content {\n height: 200px;\n min-width: 0; }\n\n.notebook-items--grading {\n margin-bottom: 0; }\n\n@media only screen and (max-width: 599px) {\n .notebook-enabled .md-fab-bottom-right, .notebook-enabled .md-fab-bottom-left {\n bottom: 50px !important; } }\n\n.notebook-grading {\n background-color: #ffffff;\n display: block; }\n\n.notification-btn {\n width: 60px !important; }\n .notification-btn md-icon {\n margin-left: 20px; }\n\n.notification-count {\n border-radius: 50%;\n position: absolute;\n background-color: #F05843;\n width: 22px;\n left: 4px;\n height: 22px;\n line-height: 18px;\n font-size: 12px;\n font-weight: 700;\n border: 2px solid; }\n .notification-count:before {\n content: \"\";\n position: absolute;\n right: -7px;\n top: 5px;\n border-left: 6px solid rgba(255, 255, 255, 0.87);\n border-top: 4px solid transparent;\n border-bottom: 4px solid transparent; }\n\n.notification-list {\n padding: 8px 0; }\n\n.notification-dismiss {\n width: 500px; }\n\n.notification-dismiss__input {\n margin-bottom: 0; }\n\nmd-list md-list-item .md-list-item-text h4.notification-list-item__source,\nmd-list md-list-item.md-2-line .md-list-item-text h4.notification-list-item__source,\nmd-list md-list-item.md-3-line .md-list-item-text h4.notification-list-item__source {\n color: rgba(0, 0, 0, 0.54);\n font-size: 12px; }\n md-list md-list-item .md-list-item-text h4.notification-list-item__source md-icon,\n md-list md-list-item.md-2-line .md-list-item-text h4.notification-list-item__source md-icon,\n md-list md-list-item.md-3-line .md-list-item-text h4.notification-list-item__source md-icon {\n font-size: 18px;\n min-width: 0;\n width: auto;\n margin-left: -4px;\n line-height: 20px; }\n\n.account-menu {\n border-radius: 4px;\n padding: 0;\n font-size: 15px;\n max-width: 380px; }\n @media (min-width: 1280px) {\n .account-menu {\n min-width: 380px !important; } }\n .account-menu h3 {\n margin: 0;\n font-weight: 300; }\n\n.account-menu--fixed-height {\n height: 304px; }\n\n.account-menu--fixed-width {\n width: 320px; }\n @media (min-width: 960px) {\n .account-menu--fixed-width {\n width: 380px; } }\n\n.account-menu__icon {\n background-color: white;\n border-radius: 50%; }\n\n.account-menu__caret {\n position: absolute;\n right: 28px;\n top: -8px;\n outline: none; }\n .account-menu__caret:before {\n content: '';\n position: absolute;\n border-bottom: 8px solid #fff;\n border-left: 8px solid transparent;\n border-right: 8px solid transparent; }\n\n.account-menu__caret--pause, .account-menu__caret--notification {\n right: 80px; }\n\n.account-menu__caret--notification--with-pause {\n right: 132px; }\n\n[dir=rtl] .account-menu__caret {\n right: auto;\n left: 28px; }\n\n[dir=rtl] .account-menu__caret--pause, [dir=rtl] .account-menu__caret--notification {\n left: 80px;\n right: auto; }\n\n[dir=rtl] .account-menu__caret--notification--with-pause {\n left: 132px;\n right: auto; }\n\n.account-menu__info {\n padding: 8px 12px; }\n\n.account-menu__info__title {\n font-weight: 500; }\n\n.account-menu__info__team {\n font-weight: 400;\n color: rgba(0, 0, 0, 0.54); }\n\n.account-menu__users {\n padding: 0; }\n .account-menu__users md-list-item {\n padding: 0; }\n .account-menu__users md-list-item .md-avatar {\n margin: 0 8px 0 0;\n height: 48px;\n width: 48px; }\n\n.account-menu__progress md-progress-linear {\n transform: rotate(270deg);\n width: 26px; }\n\n.account-menu__grade {\n position: relative;\n margin-right: 4px; }\n .account-menu__grade md-icon {\n color: #FFC107; }\n\n.account-menu__grade__overlay {\n position: absolute;\n top: 0;\n width: 100%;\n background-color: rgba(255, 255, 255, 0.6); }\n\n.account-menu__actions {\n background-color: #f7f7f7; }\n\n.account-menu__control {\n padding: 16px; }\n\n.annotations {\n margin: 16px 4px 16px 62px;\n position: relative;\n font-size: 15px; }\n .annotations hr {\n margin: 10px 0 8px;\n border-color: rgba(0, 0, 0, 0.12); }\n .annotations:after {\n content: \"\";\n position: absolute;\n width: 0;\n height: 0;\n left: -16px;\n right: auto;\n top: 0px;\n bottom: auto;\n border-top: 20px solid transparent;\n border-bottom: 20px solid transparent;\n border-right: 16px solid #757575; }\n\n.annotations-container--student--report {\n border-top: 1px solid #dddddd; }\n\n.annotations--report {\n margin-top: 0;\n margin-bottom: 0; }\n\n.annotations__header {\n position: relative;\n border-top-right-radius: 4px;\n padding: 10px 12px;\n font-weight: 700;\n transition: all 1s;\n color: #ffffff;\n background-color: #757575; }\n\n.annotations__avatar {\n background-color: #F05843;\n padding: 2px;\n position: absolute;\n top: 0;\n left: -62px; }\n\n.annotations__icon {\n transition: all 1s;\n color: #ffffff; }\n\n.annotations__body {\n padding: 12px;\n background-color: #ffffff;\n border-bottom-left-radius: 4px;\n border-bottom-right-radius: 4px;\n overflow: auto; }\n\n.annotations__status {\n background-color: #ffffff;\n color: #ef6c00;\n display: inline-block;\n margin-left: 8px;\n font-size: 12px; }\n .annotations__status.ng-enter, .annotations__status.ng-leave {\n transition: all 1s; }\n .annotations__status.ng-enter, .annotations__status.ng-leave.ng-leave-active {\n opacity: 0; }\n .annotations__status.ng-leave, .annotations__status.ng-enter.ng-enter-active {\n opacity: 1; }\n\n.annotations__score {\n font-weight: 700; }\n\n.annotations__info {\n font-style: italic;\n opacity: 0.8;\n border-bottom: 1px dotted;\n font-size: 13px; }\n\n.annotations--inside .annotations {\n margin-left: 72px; }\n\n.annotations--info {\n margin-bottom: 32px;\n margin-right: 8px;\n margin-left: 72px; }\n @media only screen and (min-width: 600px) {\n .annotations--info {\n margin: 16px 16px 32px 76px; } }\n .annotations--info:after {\n border-right: 16px solid #ef6c00; }\n .annotations--info .annotations__avatar {\n background-color: #ffffff; }\n .annotations--info .annotations__header {\n background-color: #ef6c00; }\n\n.component {\n position: relative; }\n\n.component__wrapper {\n padding: 0 16px;\n margin: 24px 0; }\n\n.component__content {\n overflow-x: auto;\n font-size: 15px;\n overflow-y: hidden; }\n @media only screen and (min-width: 600px) {\n .component__content {\n padding: 0 8px; } }\n\nh3.component__header, .component-header {\n padding: 8px 12px;\n margin: 0;\n font-size: 14px; }\n\n.component__rubric {\n position: absolute;\n left: -20px;\n top: 12px; }\n\n.notebook-enabled .component_content img {\n transition: all 250ms;\n cursor: pointer;\n cursor: copy; }\n .notebook-enabled .component_content img:hover, .notebook-enabled .component_content img:focus {\n box-shadow: 0 0 5px 1px #F05843; }\n\n.component__actions .md-button:first-child {\n margin-left: 0; }\n\n.component__actions .md-button:last-child {\n margin-right: 0; }\n\n.component__actions__info {\n font-style: italic;\n margin-left: 8px;\n color: rgba(0, 0, 0, 0.54); }\n\n.component__actions__more {\n border-bottom: 1px dotted; }\n\n.component__prompt {\n margin-bottom: 8px;\n font-weight: 500; }\n\n.component__prompt__content {\n display: inline; }\n\n.component__attachment {\n position: relative;\n margin: 0 8px;\n padding-bottom: 8px; }\n @media only screen and (min-width: 600px) {\n .component__attachment {\n padding-top: 8px; } }\n\n@media only screen and (max-width: 599px) {\n .component__add-attachment {\n width: 100%; } }\n\n.component__attachment__content {\n max-height: 100px;\n width: auto; }\n\n.component__attachment__delete {\n position: absolute;\n top: 0;\n right: 0;\n min-width: 0;\n background-color: rgba(255, 255, 255, 0.75) !important;\n border-radius: 0;\n padding: 4px;\n margin: 0; }\n .component__attachment__delete > md-icon {\n margin-top: 0; }\n\n.component__revision {\n margin: 8px 0;\n padding: 8px; }\n .component__revision:nth-child(odd) {\n background-color: #f7f7f7; }\n\n.component__revision__content {\n padding: 4px 0 8px 0;\n border-bottom: 1px solid #dddddd; }\n\n.component__revision__actions {\n color: #757575;\n padding-top: 4px; }\n\n.component__content--Discussion {\n overflow: hidden; }\n\n.discussion-content {\n background-color: #eeeeee;\n box-shadow: inset 0 0 3px #aaaaaa; }\n\n.discussion-posts {\n padding: 12px 12px 8px; }\n @media only screen and (min-width: 1280px) {\n .discussion-posts {\n padding: 16px 16px 0; } }\n\n.discussion-post {\n margin: 0 auto 16px;\n max-width: 600px; }\n @media only screen and (min-width: 600px) {\n .discussion-post {\n margin-bottom: 24px; } }\n @media only screen and (min-width: 1280px) {\n .discussion-post {\n margin-bottom: 32px; } }\n .discussion-post md-divider {\n position: relative;\n width: auto; }\n\n.discussion-post__contents {\n padding: 16px; }\n\n.discussion-post__avatar, md-list-item > .md-avatar.discussion-post__avatar {\n margin-right: 8px; }\n\n.discussion-post__avatar--reply, md-list-item > .md-avatar.discussion-post__avatar--reply {\n margin-top: 8px; }\n\n.discussion-post__user, md-list-item .md-list-item-text h3.discussion-post__user {\n padding-bottom: 4px;\n font-weight: 700;\n line-height: 1.3;\n overflow: visible;\n white-space: normal; }\n\n.discussion-post__date {\n color: #aaaaaa; }\n\n.discussion-post__date--reply {\n margin-left: 8px;\n font-weight: 400; }\n\n.discussion-post__content {\n margin-top: 16px;\n white-space: pre-wrap; }\n\n.discussion-post__attachment {\n max-width: 100%;\n height: auto !important;\n margin-top: 16px; }\n\n.discussion-new {\n background-color: #ffffff;\n max-width: 570px;\n margin-left: auto;\n margin-right: auto;\n padding: 8px;\n transition: all 250ms;\n transform: scale(0.95); }\n\n.discussion-new--focused {\n transform: scale(1); }\n\nmd-input-container.discussion-new__input-container {\n margin: 0;\n padding: 0; }\n md-input-container.discussion-new__input-container > textarea.md-input {\n min-height: 68px; }\n\n.discussion-new__input--textarea, .input-container textarea.discussion-new__input--textarea {\n padding: 8px;\n border: 0 none; }\n\n.discussion-new__actions {\n padding: 0 8px; }\n .discussion-new__actions .md-button:first-of-type {\n margin-left: 0; }\n .discussion-new__actions .md-button:last-of-type {\n margin-right: 0; }\n\n.discussion-new__attachment {\n padding: 0;\n margin: 0 0 8px; }\n\n.discussion-new__attachment__content {\n margin-top: 0;\n margin-bottom: 16px; }\n\n.discussion-comments {\n padding: 0; }\n\n.discussion-comments__contents {\n background-color: #f7f7f7; }\n\n.discussion-comments__header {\n background-color: transparent;\n padding: 0; }\n .discussion-comments__header .md-subheader-inner {\n padding-bottom: 8px; }\n\n.discussion-comments__list {\n padding: 0;\n max-height: 9999px;\n overflow-y: auto; }\n @media only screen and (min-width: 600px) {\n .discussion-comments__list {\n max-height: 400px; } }\n\n.input--textarea.discussion-reply__input, .input-container textarea.input--textarea.discussion-reply__input {\n background-color: #ffffff;\n padding: 4px;\n font-size: 14px;\n border: 0 none;\n margin-left: -1px;\n margin-bottom: 0;\n resize: none; }\n\n.discussion-reply, md-list-item.discussion-reply {\n margin: 0 12px 8px;\n padding: 0;\n min-height: 56px; }\n\n.discusstion-reply__details, md-list-item .md-list-item-text.discusstion-reply__details {\n margin: 8px 0; }\n\n.discussion-post__user--reply, md-list-item .md-list-item-text h3.discussion-post__user--reply {\n font-size: 14px;\n padding: 0;\n margin: 0; }\n\n.discusstion-reply__content {\n margin-top: 2px; }\n .discusstion-reply__content p {\n font-weight: 400 !important;\n color: rgba(0, 0, 0, 0.87) !important; }\n\n.discussion-new-reply {\n padding: 8px; }\n\n.discussion-new-reply__input-container {\n padding-top: 0;\n margin: 0; }\n .discussion-new-reply__input-container .md-errors-spacer {\n display: none; }\n\n.discussion-new-reply__actions {\n margin-left: 8px; }\n .discussion-new-reply__actions .md-button {\n margin-top: 0;\n margin-bottom: 0; }\n\n.embedded-content__iframe {\n border: 0 none; }\n\n.graph-select {\n min-width: 150px;\n max-width: 200px; }\n\n.graph-controls {\n margin: 8px 0;\n padding: 8px 0;\n border: 1px solid #eeeeee;\n border-left-width: 0;\n border-right-width: 0; }\n\n.match-content {\n background-color: #eeeeee;\n margin-bottom: 16px;\n padding: 8px;\n box-shadow: inset 0 0 3px #aaaaaa; }\n\n.match-divider {\n margin: 16px 8px 8px; }\n\n@media only screen and (min-width: 960px) {\n .match-divider--horizontal {\n display: none; } }\n\n.match-bucket__header {\n padding: 12px;\n font-weight: 500;\n color: #7e57c2; }\n\n.match-bucket__content {\n padding: 0; }\n\n.match-bucket--choices .match-bucket__header {\n color: #795C3A; }\n\n.match-bucket__contents {\n min-height: 120px;\n padding: 0 8px 8px;\n background-color: #dddddd;\n transition: background-color 250ms;\n border-bottom-left-radius: 4px;\n border-bottom-right-radius: 4px;\n column-gap: 8px; }\n @media only screen and (max-width: 599px) {\n .match-bucket__contents {\n column-count: 1 !important; } }\n .match-bucket__contents img {\n max-width: 100%;\n height: auto; }\n\n.match-bucket__contents--over {\n background-color: #7e57c2; }\n\n.match-bucket__item {\n list-style-type: none;\n cursor: move;\n padding-top: 8px;\n break-inside: avoid; }\n\n.match-bucket__item__contents {\n background-color: #ffffff;\n padding: 8px !important;\n border: 1px solid #cccccc; }\n .match-bucket__item__contents .md-list-item-text {\n width: 100%; }\n\n.match-bucket__item__contents__text {\n margin-right: 4px; }\n\n.match-feedback {\n transition: opacity 250ms;\n /*padding-left: 8px;\n border-left: 4px solid;*/\n margin: 8px -8px -8px;\n color: #ffffff;\n padding: 4px 8px; }\n .match-feedback.ng-hide {\n opacity: 0;\n transition: opacity 1ms; }\n .match-feedback md-icon {\n color: #ffffff; }\n\n.outside-content iframe {\n border: 1px solid #eeeeee; }\n\n.outside-content__source {\n margin-top: 4px;\n text-align: end; }\n .outside-content__source a {\n max-width: 240px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n display: inline-block; }\n\n.notebook-toolbar md-divider {\n margin: 8px 0; }\n\n@media only screen and (max-width: 959px) {\n .notebook-toolbar {\n border-top: 1px solid #dddddd; } }\n\n.notebook-toolbar__add-menu {\n position: absolute;\n bottom: 40px; }\n .notebook-toolbar__add-menu .md-fab-action-item {\n background-color: #ffffff; }\n\n.notebook-toolbar__add-icon {\n border-radius: 50%; }\n\n#closeNotebookSettingsButton {\n float: right; }\n\n[dir=rtl] #closeNotebookSettingsButton {\n float: left; }\n\nhighchart {\n display: block; }\n","// 1. Config\n\n// 2. Base\n.l-nav {\n background-color: color('body-bg') !important;\n}\n","// 1. Config\n\n// 2. Base\n.l-node {\n margin-top: $wise-toolbar-height;\n padding: 0;\n\n @media only screen and (min-width: $layout-breakpoint-xs) {\n padding: 0 0 16px;\n background-color: color('body-bg') !important;\n }\n\n @media only screen and (min-width: $layout-breakpoint-sm) {\n padding: 0 0 32px;\n }\n}\n","// 1. Config\n\n// 2. Base\n.l-notebook {\n margin-top: $wise-toolbar-height;\n background-color: color('body-bg') !important;\n}\n","// 1. Config\n\n// 2. Base\n.l-sidebar {\n\n}\n\n.l-sidebar__header {\n background-color: #ffffff !important;\n color: color('accent-1') !important;\n\n md-select {\n color: color('body');\n }\n}",".status-icon {\n margin: 0 4px;\n z-index: 1;\n vertical-align: bottom;\n}\n\n.md-button.status-icon {\n height: auto;\n width: auto;\n min-height: 0;\n line-height: inherit;\n margin: 0 4px;\n padding: 0;\n}\n\n.avatar--icon--alert {\n background-color: #ffffff;\n}\n\n.avatar--icon--alert__icon {\n font-size: 48px;\n margin: -4px 0 0 -4px;\n}\n","md-dialog {\n width: $layout-breakpoint-xs;\n}\n\n.dialog--wide {\n width: $layout-breakpoint-sm;\n}\n\n.dialog--wider {\n width: $layout-breakpoint-md;\n}\n",".help-bubble {\n border-radius: $card-border-radius;\n max-width: 320px;\n\n @media (min-width: $layout-breakpoint-xs) {\n max-width: ($layout-breakpoint-xs - 48);\n }\n\n @media (min-width: $layout-breakpoint-sm) {\n max-width: ($layout-breakpoint-sm - 48);\n }\n\n @media (min-width: $layout-breakpoint-md) {\n max-width: ($layout-breakpoint-md - 48);\n }\n}\n\n.help-bubble__title {\n border-top-left-radius: $card-border-radius;\n border-top-right-radius: $card-border-radius;\n}\n\n.help-bubble___title__content {\n border-top-left-radius: $card-border-radius;\n border-top-right-radius: $card-border-radius;\n padding: 0px 0 0 12px;\n background-color: color('info');\n\n .md-icon-button {\n margin-right: 0;\n padding-top: 0;\n padding-bottom: 0;\n }\n}\n\n.help-bubble__content {\n overflow: auto;\n padding: 8px 12px;\n max-height: 480px;\n}\n\n.help-bubble__actions {\n border-bottom-left-radius: $card-border-radius;\n border-bottom-right-radius: $card-border-radius;\n}\n\ndiv.hopscotch-bubble {\n border-radius: $card-border-radius;\n //border: 2px solid color('gray-darker');\n border: 0 none;\n font-family: Roboto, \"Helvetica Neue\", sans-serif;\n font-size: rem(1.4);\n z-index: 6;\n\n .hopscotch-bubble-arrow-container {\n position: absolute;\n width: 20px;\n height: 20px;\n }\n\n .hopscotch-bubble-arrow-container {\n &.up {\n top: 0;\n left: 12px;\n\n .hopscotch-bubble-arrow {\n border-bottom: 10px solid color('info');\n border-left: 10px solid transparent;\n border-right: 10px solid transparent;\n position: relative;\n top: -10px;\n }\n\n .hopscotch-bubble-arrow-border {\n border: 0 none;\n /*border-bottom: 12px solid color('gray-darker');\n border-left: 12px solid transparent;\n border-right: 12px solid transparent;*/\n }\n }\n\n &.down {\n bottom: -34px;\n left: 12px;\n\n .hopscotch-bubble-arrow {\n border-top: 10px solid color('info');\n border-left: 10px solid transparent;\n border-right: 10px solid transparent;\n position: relative;\n top: -10px;\n }\n\n .hopscotch-bubble-arrow-border {\n border: 0 none;\n /*border-top: 12px solid color('gray-darker');\n border-left: 12px solid transparent;\n border-right: 12px solid transparent;*/\n }\n }\n\n &.left {\n top: 12px;\n left: -10px;\n\n .hopscotch-bubble-arrow {\n border-right: 10px solid color('info');\n border-bottom: 10px solid transparent;\n border-top: 10px solid transparent;\n position: relative;\n left: 0;\n top: -10px;\n }\n\n .hopscotch-bubble-arrow-border {\n border: 0 none;\n /*border-right: 12px solid color('gray-darker');\n border-bottom: 12px solid transparent;\n border-top: 12px solid transparent;*/\n }\n }\n\n &.right {\n top: 12px;\n right: -30px;\n\n .hopscotch-bubble-arrow {\n border-left: 10px solid color('info');\n border-bottom: 10px solid transparent;\n border-top: 10px solid transparent;\n position: relative;\n right: 0;\n top: -10px;\n }\n\n .hopscotch-bubble-arrow-border {\n border: 0 none;\n /*border-right: 12px solid color('gray-darker');\n border-bottom: 12px solid transparent;\n border-top: 12px solid transparent;*/\n }\n }\n }\n}\n","// Variables\n$input-action-width: 44px;\n$input-action-vertical-offset: 6px;\n$input-action-vertical-offset-richtext: -7px;\n\n// Base\n.input-container {\n padding-top: 12px;\n}\n\n.input-container--component {\n margin-bottom: 0;\n}\n\n.input-container--open-response {\n &.md-has-icon {\n padding-left: 0;\n }\n\n .md-errors-spacer {\n display: none;\n }\n}\n\n.input-wrapper {\n position: relative;\n}\n\n.input-wrapper--focused {\n .input--textarea__action md-icon {\n color: color('primary');\n }\n}\n\n.input--textarea, .input-container textarea.input--textarea {\n padding: 8px;\n background-color: color('gray-lightest');\n border: 1px solid color('gray');\n margin-bottom: 8px;\n\n &:focus {\n background-color: #ffffff;\n }\n\n &[disabled] {\n color: color('text-secondary');\n }\n}\n\n.input-container textarea.input--textarea {\n width: 100%;\n}\n\n.input--textarea--disabled {\n color: color('text-secondary');\n}\n\n.input--textarea__action {\n position: absolute;\n right: -4px;\n\n &[disabled] md-icon {\n color: color('text-disabled') !important;\n }\n}\n\n.input--textarea__action--notebook {\n top: $input-action-vertical-offset;\n\n .input-wrapper--richtext & {\n top: $input-action-vertical-offset-richtext\n }\n}\n\n.input--textarea__action--revision {\n bottom: $input-action-vertical-offset;\n\n .input-wrapper--richtext & {\n bottom: ($input-action-vertical-offset-richtext + 2px);\n }\n\n}\n\n.input-label, md-input-container:not(.md-input-invalid):not(.md-input-focused).md-input-has-value label.input-label {\n line-height: 1.2;\n color: color('text');\n\n &.input-label--focused {\n color: color('primary');\n }\n}\n\n.autocomplete {\n input {\n text-overflow: ellipsis;\n overflow: hidden;\n word-wrap: none;\n font-weight: 500;\n color: color('text-secondary');\n }\n}\n\n.autocomplete--minwidth {\n @media only screen and (min-width: $layout-breakpoint-xs) {\n min-width: 300px;\n }\n\n @media only screen and (min-width: $layout-breakpoint-sm) {\n min-width: 300px;\n }\n}\n\n.autocomplete--flat {\n md-autocomplete-wrap {\n background-color: #ffffff;\n\n &:not(.md-menu-showing) {\n box-shadow: none;\n background-color: color('gray-lighter');\n }\n }\n}\n\n.select__header {\n height: $menu-item-height;\n\n input {\n height: 100%;\n width: 100%;\n padding: 0 8px;\n outline: none;\n border: 0 none;\n font-size: rem(1.4);\n font-weight: 500;\n }\n}\n",".table {\n max-width: 100%;\n width: auto;\n min-width: 100px;\n margin: 8px 0;\n\n thead,\n tbody,\n tfoot {\n // TODO: remove chaining when bootstrap dependency is removed\n > tr > th,\n > tr > td {\n border: 1px solid color('gray');\n padding: 6px;\n font-size: rem(1.5);\n min-height: 32px;\n height: 32px;\n min-width: 32px;\n vertical-align: top;\n }\n }\n\n td.inactive,\n th {\n background-color: color('gray-lightest');\n opacity: 1;\n visibility: visible;\n }\n\n md-input-container {\n margin: 0;\n }\n\n .md-errors-spacer {\n display: none;\n }\n}\n\n.table--student {\n td {\n &.inactive {\n padding: 8px 10px;\n }\n }\n}\n\n.table--full-width {\n width: 100%;\n}\n\n.table--list {\n border: 0 none;\n border-collapse: collapse;\n background-color: #ffffff;\n max-width: 100%;\n overflow: auto;\n\n th,\n td {\n padding: 0 4px;\n border: 0 none;\n }\n\n td {\n min-height: 56px;\n height: 56px;\n }\n\n tr {\n &.md-button {\n display: table-row;\n text-align: left;\n width: auto;\n text-transform: none;\n font-size: inherit;\n font-weight: normal;\n }\n }\n}\n\n.table--list__wrap {\n min-width: $layout-breakpoint-xs;\n}\n\n.table-wrap-sticky {\n @media only screen and (max-width: $layout-breakpoint-sm - 1) {\n overflow-x: auto;\n }\n}\n\n.table--list__thead {\n font-size: rem(1.4);\n font-weight: 700;\n}\n\n.table--list__thead__tr {\n height: 100%;\n margin: 0;\n}\n\n.table--list__thead__th {\n background-color: color('gray-darker');\n color: color('text-light');\n min-height: $wise-toolbar-height;\n height: $wise-toolbar-height;\n}\n\n.table--list__thead__link {\n color: #ffffff;\n text-transform: none;\n margin: 0;\n min-width: 0;\n white-space: normal;\n line-height: 1.4;\n width: 100%;\n}\n\n.table--list__thead__sort {\n margin: 0;\n}\n\n.table--list__thead__sort--reverse {\n transform: rotate(180deg);\n}\n\n.td--wrap {\n min-width: 180px;\n white-space: normal;\n line-height: 1.2;\n}\n\n.td--max-width {\n @media only screen and (max-width: $layout-breakpoint-sm - 1) {\n max-width: 180px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n}\n",".md-toolbar-tools {\n font-size: 18px;\n\n .autocomplete {\n height: 36px;\n\n md-autocomplete-wrap {\n height: 36px;\n }\n\n input {\n height: 36px;\n }\n }\n}\n\n.md-toolbar--wise {\n min-height: $wise-toolbar-height;\n\n .md-toolbar-tools {\n height: $wise-toolbar-height;\n max-height: $wise-toolbar-height;\n }\n\n .md-button.md-icon-button {\n height: $wise-toolbar-height;\n line-height: $wise-toolbar-height;\n width: $wise-toolbar-height;\n }\n}\n\n.md-toolbar--wise--sm {\n .md-toolbar-tools {\n padding: 0 8px;\n font-size: $body-font-size-base;\n }\n}\n\n.md-toolbar--sidenav {\n background-color: color('gray-darkest') !important;\n\n .md-toolbar-tools {\n font-size: rem(1.6);\n font-weight: 500;\n }\n}\n\n.toolbar {\n position: fixed;\n left: 0;\n right: 0;\n top: $md-toolbar-height;\n z-index: 3;\n}\n\n.toolbar__title {\n margin-left: 8px;\n font-size: rem(1.6);\n font-weight: 500;\n}\n\n.toolbar__tools {\n padding-right: 8px;\n}\n[dir=rtl] .toolbar__tools {\n padding-right: 16px;\n padding-left: 8px;\n}\n\n.toolbar__nav, .md-button.toolbar__nav {\n margin: 0;\n}\n\n.toolbar__select, .md-button.toolbar__select {\n margin: 0 4px;\n min-height: 32px;\n background-color: color('gray-lightest');\n\n .md-select-value {\n height: 32px;\n text-align: left;\n }\n}\n[dir=rtl] {\n .toolbar__select, .md-button.toolbar__select {\n .md-select-value {\n text-align: right;\n }\n }\n}\n\n.toolbar__select--fixedwidth {\n width: 168px;\n\n @media only screen and (min-width: $layout-breakpoint-xs) {\n width: 264px;\n }\n\n @media only screen and (min-width: $layout-breakpoint-sm) {\n width: 432px;\n }\n}\n",".list-item {\n background-color: #ffffff;\n border-bottom: 1px solid color('gray-lighter');\n\n .md-subheader, &.md-subheader {\n color: color('text');\n background-color: #ffffff;\n\n md-icon {\n vertical-align: middle;\n }\n\n .md-subheader-inner {\n padding: 0;\n }\n\n .md-avatar {\n margin-right: 8px;\n }\n }\n\n .autocomplete {\n margin: 8px 0;\n }\n}\n\n.list-item--info {\n &._md-button-wrap>div.md-button:first-child, .md-subheader-content {\n border-left: 4px solid color('info') !important;\n margin-left: -4px;\n }\n}\n\n.list-item--warn {\n //background-color: lighten(color('warn'), 56%);\n\n &._md-button-wrap>div.md-button:first-child, .md-subheader-content {\n border-left: 4px solid color('warn') !important;\n margin-left: -4px;\n }\n}\n\n.list-item--expanded {\n border-bottom-width: 0;\n}\n\n.list-item--noclick, .list-item--noclick.md-button {\n cursor: default;\n background-color: color('gray-lightest');\n}\n\n.list-item--actions {\n padding: 0 8px !important;\n}\n\n.list-item__subheader-button {\n text-transform: none;\n width: 100%;\n padding: 8px 16px;\n margin: 0;\n white-space: normal;\n text-align: left;\n line-height: 1.4;\n}\n\n.user-list {\n font-size: rem(1.5);\n}\n","#nav {\n position: relative;\n}\n\n.nav {\n margin-bottom: 16px;\n}\n\n.nav-mask {\n position: absolute;\n top: 0;\n bottom: 0;\n left: 0;\n right: 0;\n background-color: rgba(0,0,0,0.25);\n z-index: 1;\n\n &.ng-hide {\n opacity: 0;\n }\n}\n\n.nav-head {\n color: color('text-secondary');\n font-weight: 500;\n\n md-icon {\n line-height: 20px;\n }\n}\n\n.nav-contents--root {\n padding: 6px 6px 12px;\n}\n\n.nav-contents--group {\n background-color: color('gray-light');\n padding: 8px;\n}\n\n.nav-contents--root {\n padding: 0;\n}\n\n.nav-contents__list {\n padding: 0;\n\n @media (min-width: $layout-breakpoint-xs) {\n padding: 8px;\n }\n}\n\n.nav-item {\n transition: opacity 250ms ease-in-out;\n\n &.prev {\n md-list-item {\n background-color: color('selected-bg');\n\n /*&._md-button-wrap>div.md-button:first-child {\n border-left: 4px solid color('primary');\n margin-left: -4px;\n }*/\n }\n }\n}\n\n.nav-item--card__content {\n border-top-right-radius: $card-border-radius;\n border-top-left-radius: $card-border-radius;\n\n &:focus {\n outline: none;\n\n .nav-item__title > span {\n border-bottom: 1px dashed color('gray');\n }\n }\n}\n\n.nav-item--root {\n transition: margin 250ms, box-shadow 500ms;\n\n &.expanded {\n flex-basis: 100%;\n //max-width: ($layout-breakpoint-md - 100) !important;\n max-width: 100%;\n max-height: none !important;\n margin: 8px auto;\n padding-left: 4px;\n\n &:first-of-type {\n margin-top: 0;\n }\n\n //@media only screen and (min-width: $layout-breakpoint-sm) {\n //margin: 8px auto;\n //}\n }\n}\n\n//.nav-item--list {\n//}\n\n.nav-item--list__info-item {\n padding: 0 16px 0 4px;\n display: inline-block;\n}\n\n.nav-item--list__reorder {\n margin-left: 8px;\n color: color('text-disabled');\n}\n\n.nav-item--card {\n\n}\n\n.nav-item--card--group {\n &:not(.expanded) {\n box-shadow: 0px 3px 1px -2px rgba(0, 0, 0, 0.14), 0px 2px 2px 0px rgba(0, 0, 0, 0.098),\n 0px 1px 5px 0px rgba(0, 0, 0, 0.084), 3px 3px 0px 1px #d5d5d5, 6px 6px 0px 1px #aaaaaa;\n }\n}\n\n.nav-item__collapse {\n margin: 0;\n}\n\n.nav-item__more {\n border-top: 1px solid color('gray-light');\n border-bottom-right-radius: $card-border-radius;\n border-bottom-left-radius: $card-border-radius;\n padding: 8px 16px;\n min-height: 40px;\n}\n\n.nav-item__users {\n height: auto;\n cursor: pointer;\n color: #ffffff;\n margin: 0;\n padding: 1px 6px;\n\n > md-icon {\n padding-right: 4px;\n color: #ffffff;\n }\n\n &:hover, &:focus {\n &.success-bg {\n background-color: color('success');\n }\n }\n}\n\n.nav-item__title {\n padding-left: 16px;\n line-height: 1.2;\n font-weight: 400;\n}\n[dir=rtl] .nav-item__title {\n padding-left: auto;\n padding-right: 16px;\n}\n\n.nav-item__info {\n padding: 0 8px;\n}\n\n.nav-item__progress {\n width: 48px;\n\n > .md-container {\n top: 0;\n }\n}\n\n.nav-item__progress-value {\n margin-left: 8px;\n width: 36px;\n}\n\n.progress-wrapper {\n padding: 2px 0;\n cursor: pointer;\n}\n",".notice {\n text-align: center;\n padding: 8px;\n background-color: rgba(0,0,0,0.04);\n width: 100%;\n\n @media (min-width: $layout-breakpoint-xs) {\n max-width: 80%;\n border-radius: $button-border-radius;\n margin: 24px auto;\n }\n}","// 1. Variables\n$menu-sidebar-width: 56px;\n\n// 2. Base\n.menu-progress {\n position: absolute;\n top: 10px;\n right: 12px;\n\n path {\n stroke: color('accent-2') !important;\n stroke-width: 2px;\n }\n}\n[dir=rtl] .menu-progress {\n right:auto;\n left:12px;\n}\n\n.menu-sidenav {\n\n}\n\n.menu-sidenav__item {\n font-weight: 700;\n //color: color('text-secondary');\n font-size: rem(1.4);\n}\n\n.menu-sidenav__icon {\n margin-top: 12px !important;\n margin-right: 12px !important;\n margin-left: 12px;\n}\n\n.active {\n .menu-sidenav__icon, .menu-sidenav__item {\n color: color('primary');\n }\n}\n\n.menu-sidebar {\n position: absolute;\n top: $wise-toolbar-height + $md-toolbar-height;\n bottom: 0;\n left: 0;\n background-color: #fff;\n width: $menu-sidebar-width;\n overflow: hidden;\n padding: 8px 0;\n text-align: center;\n border-right: 1px solid color('gray');\n\n @media only screen and (max-width: ($layout-breakpoint-xs - 1)) {\n display: none;\n }\n}\n[dir=rtl] .menu-sidebar {\n right:0;\n left:auto;\n}\n\n.md-button.md-icon-button.menu-sidebar__link {\n margin-top: 6px;\n margin-bottom: 6px;\n}\n","// Variables\n$input-action-width: 48px;\n$input-action-vertical-offset: 0;\n$input-action-vertical-offset-richtext: -7px;\n\n// Base\n#node {\n margin: 0 auto;\n position: absolute;\n left: 0;\n right: 0;\n\n @media only screen and (min-width: $layout-breakpoint-xs) {\n padding: 8px;\n padding: 24px 16px;\n margin-bottom: 32px;\n }\n\n @media only screen and (min-width: $layout-breakpoint-sm) {\n padding: 32px;\n }\n\n &.ng-enter {\n transition: opacity .5s;\n opacity: 0;\n }\n\n &.ng-enter-active {\n opacity: 1;\n }\n}\n\n// TODO: use BEM conventions\n\n.node-notice {\n @media only screen and (min-width: $layout-breakpoint-xs) {\n margin-top: -8px;\n margin-bottom: 16px;\n }\n\n @media only screen and (min-width: $layout-breakpoint-sm) {\n margin-top: -16px;\n }\n}\n\n.node-content {\n padding: 0 0 48px;\n background-color: #ffffff;\n border-radius: $button-border-radius;\n overflow: visible;\n\n @media only screen and (max-width: $layout-breakpoint-xs - 1) {\n box-shadow: none;\n }\n\n @media only screen and (min-width: $layout-breakpoint-xs) {\n padding: 0;\n border-top: 2px solid;\n border-bottom: 2px solid;\n }\n}\n\nmd-content {\n &.node-content {\n background-color: #ffffff;\n }\n}\n\n.node-content__rubric {\n position: absolute;\n top: -22px;\n left: 0;\n right: 0;\n z-index: 1;\n\n .avatar--icon {\n transform: scale(0.94);\n\n @media only screen and (max-width: $layout-breakpoint-xs - 1) {\n transform: scale(0.8);\n }\n }\n}\n\n.node-icon {\n color: #ffffff;\n vertical-align: inherit;\n}\n\n.node-select {\n margin: 0 8px;\n min-width: 0;\n font-weight: 500;\n font-size: $body-font-size-base;\n\n .md-select-value {\n *:first-child {\n transform: translate3d(0,0,0);\n flex: 1 0 0;\n }\n\n .node-select__icon {\n display: none;\n }\n\n .node-select__status {\n display: none;\n }\n }\n\n .md-select-icon {\n margin-left: 0;\n color: color('text');\n }\n}\n\n.node-select-option--group {\n //color: rgba(0,0,0,0.54);\n background-color: color('gray-lightest');\n border-bottom: 1px solid color('gray-lighter');\n border-top: 1px solid color('gray-lighter');\n}\n\n.node-select-option--node {\n padding-left: 20px;\n}\n\n.node-select__icon {\n margin-right: 8px;\n}\n\n.node-select__status {\n margin-left: 8px;\n}\n\n.node-select__text {\n text-overflow: ellipsis;\n white-space: nowrap;\n overflow: hidden;\n\n @media only screen and (min-width: $layout-breakpoint-xs) {\n margin-top: 2px;\n }\n}\n\n.node-title {\n line-height: 1.2;\n text-transform: none;\n margin-top: 3px;\n\n @media only screen and (max-width: $layout-breakpoint-xs - 1) {\n font-size: $body-font-size-base;\n }\n}\n\n.node-content__actions {\n padding: 0 16px 16px;\n\n @media only screen and (min-width: $layout-breakpoint-sm) {\n padding: 0 24px 24px;\n }\n\n @media only screen and (min-width: $layout-breakpoint-md) {\n padding: 0 32px 32px;\n }\n\n .md-button:first-child {\n margin-left: 0;\n }\n\n .md-button:last-child {\n margin-right: 0;\n }\n}\n\n.node-content__actions__info {\n font-style: italic;\n margin-left: 8px;\n color: color('text-secondary');\n}\n\n.node-content__actions__more {\n border-bottom: 1px dotted;\n}\n\n.md-button.md-icon-button.node-nav {\n &:not(:first-of-type) {\n }\n\n &:first-of-type {\n margin-right: 0;\n }\n}\n\n.node-sidebar-active {\n @media only screen and (min-width: $layout-breakpoint-xs) {\n margin-right: ($md-toolbar-height + 16);\n }\n}\n\n.node-sidebar-visible {\n @media only screen and (max-width: $layout-breakpoint-xs - 1) {\n margin-bottom: $wise-toolbar-height;\n }\n}\n\n.node-sidebar {\n position: absolute;\n right: 0;\n top: 0;\n width: $md-toolbar-height;\n}\n\n.node-sidebar__toolbar {\n position: fixed;\n width: $md-toolbar-height;\n background-color: #ffffff;\n padding: 8px 0;\n border-radius: $button-border-radius;\n\n @media only screen and (max-width: $layout-breakpoint-xs - 1) {\n right: 0;\n bottom: 0;\n left: 0;\n width: 100%;\n border-radius: 0;\n padding: 0;\n min-height: 0;\n height: $wise-toolbar-height;\n }\n}\n\n// TODO: move to own sass module file (only gets used by teacher)\n// TODO: use BEM (.node--info)\n.node-info {\n margin: 0;\n\n @media only screen and (max-width: $layout-breakpoint-xs - 1) {\n margin: -16px;\n border-radius: 0;\n }\n\n .divider {\n margin-left: -8px;\n margin-right: -8px;\n }\n\n .component {\n &:first-child {\n margin-top: -16px;\n }\n }\n\n .component, .component__content, .component__header {\n margin-left: -8px;\n margin-right: -8px;\n }\n\n .component__actions {\n display: none;\n }\n}\n\n.node-rubric {\n border: 2px solid color('primary');\n margin: 8px 16px 24px;\n padding: 16px;\n background-color: #ffffff;\n}\n\n.node-rubric--component {\n\n}\n\n.node__label--vertical-alignment {\n vertical-align: middle;\n display: inline-block;\n}\n","// Variables\n$notebook-sidebar-width: 56px;\n\n// Base\n.notebook-launcher {\n @media only screen and (min-width: $layout-breakpoint-xs) {\n &.md-button.md-fab {\n z-index: 61;\n }\n }\n}\n\n.notebook-report {\n border-radius: 4px 4px 0 0;\n margin: 0;\n height: 100%;\n\n .note-toolbar {\n margin: -8px -8px 8px;\n padding: 4px;\n border: 0 none;\n border-top: 1px solid color('gray-light');\n border-bottom: 1px solid color('gray-light');\n border-radius: 0;\n\n .btn-group {\n margin-top: 0;\n }\n }\n\n .note-btn {\n border: 0 none;\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n border-bottom-left-radius: 0;\n border-bottom-right-radius: 0;\n }\n}\n\n.notebook-report-container {\n height: 550px;\n width: 450px;\n max-height: 90%;\n bottom: 0;\n right: 96px;\n position: absolute;\n z-index: 3;\n}\n\n.notes-visible {\n @media only screen and (min-width: $layout-breakpoint-sm) {\n .notebook-report-container {\n right: 516px;\n transition: right 250ms;\n }\n }\n}\n\n.notebook-report-container__full {\n top: 16px;\n bottom: 16px;\n left: 16px;\n right: 16px;\n max-height: none;\n max-width: none;\n height: auto;\n width: auto;\n\n .notebook-report {\n height: 100%;\n width: 100%;\n margin: 0 auto;\n max-height: none;\n border-radius: $card-border-radius;\n }\n}\n\n.notebook-report-container__collapsed {\n width: 300px;\n height: auto;\n\n .notebook-report__content, .notebook-report__actions, .notebook-report__content__header {\n display: none;\n }\n}\n\n.notebook-report__toolbar {\n background-color: color('gray-darkest') !important;\n border-radius: $card-border-radius $card-border-radius 0 0;\n}\n\n.notebook-report__toolbar__title {\n max-width: 150px;\n}\n\n.notebook-report__content {\n h1, h2, h3, h4 {\n font-size: rem(2.2);\n }\n\n background-color: #ffffff;\n\n .note-editor.note-frame {\n border: 0 none;\n border-radius: 0;\n padding: 0;\n box-shadow: none;\n }\n\n .note-resizebar {\n display: none;\n }\n}\n\n.notebook-report__content__header {\n padding: 8px;\n background-color: #ffffff;\n font-size: rem(1.6);\n}\n\n@media only screen and (max-width: $layout-breakpoint-xs - 1) {\n .notebook-report-container {\n &:not(.notebook-report-container__collapsed) {\n top: 0;\n bottom: 0;\n left: 0;\n right: 0;\n max-height: none;\n max-width: none;\n height: auto;\n width: auto;\n\n .notebook-report {\n border-radius: 0;\n }\n }\n }\n\n .notebook-tools--full {\n display: none;\n }\n}\n\n.notebook-report-backdrop {\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n z-index: 3;\n background-color: rgba(33,33,33,1.0);\n opacity: .48;\n}\n\n.notebook-menu {\n transition: opacity 500ms;\n\n &.ng-enter {\n opacity: 0;\n }\n\n .ng-enter-active {\n opacity: 1;\n transition-delay: 250ms;\n }\n\n &.ng-leave-active, &.ng-hide {\n opacity: 0;\n }\n\n &.ng-hide-add, &.ng-hide-add-active,\n &.ng-hide-remove, &.ng-hide-remove-active {\n opacity: 0;\n }\n}\n\n.notebook-item {\n transition: box-shadow 250ms;\n margin: 0 16px 16px;\n display: block;\n}\n\n.notebook-item__content {\n height: 250px;\n min-width: 230px;\n position: relative;\n padding: 0;\n background-color: color('gray');\n border-top-left-radius: $card-border-radius;\n border-top-right-radius: $card-border-radius;\n}\n\n.notebook-item__content__attachment, .notebook-item__content__text {\n position: absolute;\n left: 0;\n right: 0;\n}\n\n.notebook-item__content__attachment {\n background-repeat: no-repeat !important;\n border-top-left-radius: $card-border-radius;\n border-top-right-radius: $card-border-radius;\n background-position: center top !important;\n background-size: cover !important;\n top: 0;\n bottom: 0;\n}\n\n.notebook-item__content__text {\n bottom: 0;\n padding: 8px;\n font-weight: 500;\n overflow: hidden;\n max-height: 120px;\n min-height: 56px;\n background-color: rgba(255,255,255,0.95);\n border-top: 1px solid color('gray-lighter');\n\n &:after {\n content: \"\";\n text-align: right;\n position: absolute;\n bottom: 0;\n right: 0;\n width: 100%;\n height: 0.8em;\n background: linear-gradient(180deg,hsla(0,0%,100%,0),rgba(255,255,255,0.95) 100%);\n }\n}\n\n.notebook-item__content--text-only {\n &:after {\n content: \"note\";\n font-family: 'Material Icons';\n font-weight: normal;\n font-style: normal;\n display: inline-block;\n line-height: 1;\n text-transform: none;\n letter-spacing: normal;\n word-wrap: normal;\n white-space: nowrap;\n direction: ltr;\n\n /* Support for all WebKit browsers. */\n -webkit-font-smoothing: antialiased;\n /* Support for Safari and Chrome. */\n text-rendering: optimizeLegibility;\n\n /* Support for Firefox. */\n -moz-osx-font-smoothing: grayscale;\n\n /* Support for IE. */\n font-feature-settings: 'liga';\n //position: absolute;\n font-size: 80px;\n color: color('text-disabled');\n }\n}\n\n.notebook-item--question__content--text-only {\n content: \"live_help\";\n}\n\n.notebook-item__content__location {\n opacity: 0.9;\n padding: 8px 0;\n\n md-icon {\n font-size: 22px;\n }\n}\n\n.notebook-item__edit {\n cursor: pointer;\n}\n\n.notebook-item__actions {\n margin: 0;\n padding: 0 8px;\n color: #ffffff;\n background-color: color('gray-darkest');\n border-bottom-left-radius: $card-border-radius;\n border-bottom-right-radius: $card-border-radius;\n\n md-icon {\n color: #ffffff;\n }\n}\n\n.notebook-item__text-input {\n margin: 0;\n}\n\n.notebook-item__text-input__textarea {\n padding-left: 0;\n padding-right: 0;\n}\n\n.notebook-item__attachment {\n background-color: color('gray-light');\n padding: 16px;\n margin-bottom: 16px;\n text-align: center;\n position: relative;\n}\n\n.notebook-item__attachment__content {\n max-width: 100%;\n height: auto;\n}\n\n.notebook-item__attachment__delete {\n position: absolute;\n top: 4px;\n right: -2px;\n // TODO: generalize for on item buttons like this (delete attachment, etc)\n width: 34px !important;\n height: 34px !important;\n min-height: 0;\n\n md-icon {\n margin-left: -2px;\n font-size: 22px;\n }\n}\n\n.notebook-item__info {\n font-style: italic;\n opacity: .8;\n color: lighten(color('accent-1'), 5%);\n\n a, md-icon {\n color: lighten(color('accent-1'), 5%);\n }\n\n md-icon {\n font-size: 1.5em;\n min-width: 0;\n width: auto;\n }\n}\n\n.notebook-item__upload {\n text-align: center;\n padding: 24px;\n background-color: color('gray-lighter');\n margin-bottom: 16px;\n color: color('text-secondary');\n border-radius: 4px;\n cursor: pointer;\n border: 1px dashed transparent;\n transition: all 250ms;\n\n md-icon, span {\n transition: color 250ms;\n }\n\n &:hover, &:focus, &.dragover {\n border-color: color('accent');\n background-color: lighten(color('accent'), 35%);\n color: color('accent');\n\n md-icon, span {\n color: color('accent');\n }\n }\n}\n\n.view-notebook-item {\n width: $layout-breakpoint-xs;\n}\n\n.view-notebook-item__content {\n}\n\n.notebook-item--report {\n background-color: #ffffff;\n\n .note-editor {\n margin-bottom: 16px;\n border-color: color('gray');\n }\n}\n\n.notebook-item--report__container {\n &.ui-scrollpoint {\n .notebook-item--report__toolbar {\n position: fixed;\n top: ($wise-toolbar-height + $md-toolbar-height);\n left: 0;\n right: 0;\n z-index: 1;\n\n @media only screen and (min-width: $layout-breakpoint-xs) {\n left: ($notebook-sidebar-width - 2);\n }\n\n @media only screen and (min-width: $layout-breakpoint-xs) {\n padding: 0 24px;\n }\n\n @media only screen and (min-width: $layout-breakpoint-sm) {\n padding: 0 32px;\n }\n\n .note-toolbar {\n margin: 0 16px;\n\n @media only screen and (min-width: $layout-breakpoint-xs) {\n margin: 0 8px;\n }\n\n @media only screen and (min-width: $layout-breakpoint-sm) {\n margin: 0 16px;\n }\n }\n }\n\n .note-editor {\n padding-top: 40px;\n }\n }\n}\n\n.notebook-item--report__toolbar {\n\n .note-toolbar {\n background-color: color('gray-light');\n border: 1px solid color('gray');\n margin-bottom: -2px;\n }\n}\n\n.notebook-item--report__heading {\n text-align: center;\n margin-bottom: 32px;\n}\n\n.notebook-item--report__content {\n}\n\n.notebook-item--report__add-note {\n font-weight: 700;\n}\n\n.notebook-item--report__note-img {\n max-width: 100%;\n height: auto !important;\n}\n\n.notebook-sidebar {\n @media only screen and (min-width: $layout-breakpoint-xs) {\n width: 400px;\n max-width: none;\n }\n\n @media only screen and (min-width: $layout-breakpoint-sm) {\n width: 500px;\n max-width: none;\n }\n}\n\n.notebook-items {\n width: 100%;\n overflow: auto;\n margin-top: 16px;\n margin-bottom: 76px;\n\n .notebook-item {\n width: 100%;\n }\n\n .notebook-item__content {\n height: 200px;\n min-width: 0;\n }\n}\n\n.notebook-items--grading {\n margin-bottom: 0;\n}\n\n@media only screen and (max-width: ($layout-breakpoint-xs - 1)) {\n .notebook-enabled {\n .md-fab-bottom-right, .md-fab-bottom-left {\n bottom: ($wise-toolbar-height + 8) !important;\n }\n }\n}\n\n.notebook-grading {\n background-color: #ffffff;\n display: block;\n}\n",".notification-btn {\n width: 60px !important;\n\n md-icon {\n margin-left: 20px;\n }\n}\n\n.notification-count {\n border-radius: 50%;\n position: absolute;\n background-color: color('accent');\n width: 22px;\n left: 4px;\n height: 22px;\n line-height: 18px;\n font-size: 12px;\n font-weight: 700;\n border: 2px solid;\n\n &:before {\n content: \"\";\n position: absolute;\n right: -7px;\n top: 5px;\n border-left: 6px solid rgba(255,255,255,0.87);\n border-top: 4px solid transparent;\n border-bottom: 4px solid transparent;\n }\n}\n\n.notification-list {\n padding: 8px 0;\n}\n\n.notification-dismiss {\n width: 500px;\n}\n\n.notification-dismiss__input {\n margin-bottom: 0;\n}\n\nmd-list md-list-item .md-list-item-text h4,\nmd-list md-list-item.md-2-line .md-list-item-text h4,\nmd-list md-list-item.md-3-line .md-list-item-text h4 {\n &.notification-list-item__source {\n color: color('text-secondary');\n font-size: rem(1.2);\n\n md-icon {\n font-size: rem(1.8);\n min-width: 0;\n width: auto;\n margin-left: -4px;\n line-height: rem(2);\n }\n }\n}\n",".account-menu {\n border-radius: $card-border-radius;\n padding: 0;\n font-size: $body-font-size-base;\n max-width: 380px;\n\n @media (min-width: $layout-breakpoint-md) {\n min-width: 380px !important;\n }\n\n h3 {\n margin: 0;\n font-weight: 300;\n }\n}\n\n.account-menu--fixed-height {\n height: $max-menu-height;\n}\n\n.account-menu--fixed-width {\n width: 320px;\n\n @media (min-width: $layout-breakpoint-sm) {\n width: 380px;\n }\n}\n\n.account-menu__icon {\n background-color: color('text-light');\n border-radius: 50%;\n}\n\n.account-menu__caret {\n position: absolute;\n right: 28px;\n top: -8px;\n outline: none;\n\n &:before {\n content: '';\n position: absolute;\n border-bottom: 8px solid #fff;\n border-left: 8px solid transparent;\n border-right: 8px solid transparent;\n }\n}\n\n.account-menu__caret--pause, .account-menu__caret--notification {\n right: 80px;\n}\n\n.account-menu__caret--notification--with-pause {\n right: 132px;\n}\n\n[dir=rtl] {\n .account-menu__caret {\n right: auto;\n left: 28px;\n }\n .account-menu__caret--pause, .account-menu__caret--notification {\n left:80px;\n right:auto;\n }\n .account-menu__caret--notification--with-pause {\n left: 132px;\n right:auto;\n }\n}\n\n.account-menu__info {\n padding: 8px 12px;\n}\n\n.account-menu__info__title {\n font-weight: 500;\n}\n\n.account-menu__info__team {\n font-weight: 400;\n color: color('text-secondary');\n}\n\n.account-menu__users {\n padding: 0;\n\n md-list-item {\n padding: 0;\n\n .md-avatar {\n margin: 0 8px 0 0;\n height: 48px;\n width: 48px;\n }\n }\n}\n\n.account-menu__progress {\n md-progress-linear {\n transform: rotate(270deg);\n width: 26px;\n }\n}\n\n.account-menu__grade {\n position: relative;\n margin-right: 4px;\n\n md-icon {\n color: color('score');\n }\n}\n\n.account-menu__grade__overlay {\n position: absolute;\n top: 0;\n width: 100%;\n background-color: rgba(255, 255, 255, 0.6);\n}\n\n.account-menu__actions {\n background-color: color('gray-lightest');\n}\n\n.account-menu__control {\n padding: 16px;\n}\n",".annotations {\n margin: 16px 4px 16px 62px;\n position: relative;\n font-size: rem(1.5);\n\n hr {\n margin: 10px 0 8px;\n border-color: rgba(0,0,0,.12);\n }\n\n &:after {\n content: \"\";\n position: absolute;\n width: 0;\n height: 0;\n left: -16px;\n right: auto;\n top: 0px;\n bottom: auto;\n border-top: 20px solid transparent;\n border-bottom: 20px solid transparent;\n border-right: 16px solid color('gray-darker');\n }\n}\n\n.annotations-container--student--report {\n border-top: 1px solid color('gray-light');\n}\n\n.annotations--report {\n margin-top: 0;\n margin-bottom: 0;\n}\n\n.annotations__header {\n position: relative;\n //border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n padding: 10px 12px;\n font-weight: 700;\n transition: all 1s;\n color: #ffffff;\n background-color: color('gray-darker');\n}\n\n.annotations__avatar {\n background-color: color('accent');\n padding: 2px;\n position: absolute;\n top: 0;\n left: -62px;\n}\n\n.annotations__icon {\n transition: all 1s;\n color: #ffffff;\n}\n\n.annotations__body {\n padding: 12px;\n background-color: #ffffff;\n border-bottom-left-radius: $card-border-radius;\n border-bottom-right-radius: $card-border-radius;\n overflow: auto;\n}\n\n.annotations__status {\n background-color: #ffffff;\n color: color('info');\n display: inline-block;\n margin-left: 8px;\n font-size: rem(1.2);\n\n &.ng-enter, &.ng-leave {\n transition: all 1s;\n }\n\n &.ng-enter, &.ng-leave.ng-leave-active {\n opacity:0;\n }\n\n &.ng-leave, &.ng-enter.ng-enter-active {\n opacity:1;\n }\n}\n\n.annotations__score {\n font-weight: 700;\n}\n\n.annotations__info {\n font-style: italic;\n opacity: 0.8;\n border-bottom: 1px dotted;\n font-size: rem(1.3);\n}\n\n.annotations--inside {\n .annotations {\n margin-left: 72px;\n }\n}\n\n// TODO: move to own file\n.annotations--info {\n margin-bottom: 32px;\n margin-right: 8px;\n margin-left: 72px;\n\n @media only screen and (min-width: ($layout-breakpoint-xs)) {\n margin: 16px 16px 32px 76px;\n }\n\n &:after {\n border-right: 16px solid color('info');\n }\n\n .annotations__avatar {\n background-color: #ffffff;\n }\n\n .annotations__header {\n background-color: color('info');\n }\n}\n",".component {\n position: relative;\n}\n\n.component__wrapper {\n padding: 0 16px;\n margin: 24px 0;\n}\n\n.component__content {\n overflow-x: auto;\n font-size: rem(1.5);\n overflow-y: hidden; // TODO: figure out why this is needed after update to ng-material 1.1.1\n\n @media only screen and (min-width: $layout-breakpoint-xs) {\n padding: 0 8px;\n }\n}\n\nh3.component__header, .component-header {\n padding: 8px 12px;\n margin: 0;\n font-size: rem(1.4);\n}\n\n.component__rubric {\n position: absolute;\n left: -20px;\n top: 12px;\n}\n\n.notebook-enabled {\n .component_content {\n img {\n transition: all 250ms;\n cursor: pointer;\n cursor: copy;\n //position: relative;\n //border: 2px solid transparent;\n\n &:hover, &:focus {\n box-shadow: 0 0 5px 1px color('accent');\n //border: 2px solid #ffffff;\n }\n }\n }\n}\n\n.component__actions {\n .md-button:first-child {\n margin-left: 0;\n }\n\n .md-button:last-child {\n margin-right: 0;\n }\n}\n\n.component__actions__info {\n font-style: italic;\n margin-left: 8px;\n //color: color('accent-1');\n color: color('text-secondary');\n}\n\n.component__actions__more {\n border-bottom: 1px dotted;\n}\n\n.component__prompt {\n margin-bottom: 8px;\n font-weight: 500;\n}\n\n.component__prompt__content {\n display: inline;\n}\n\n.component__attachment {\n position: relative;\n margin: 0 8px;\n padding-bottom: 8px;\n\n @media only screen and (min-width: $layout-breakpoint-xs) {\n padding-top: 8px;\n }\n}\n\n.component__add-attachment {\n @media only screen and (max-width: ($layout-breakpoint-xs - 1)) {\n width: 100%;\n }\n}\n\n.component__attachment__content {\n max-height: 100px;\n width: auto;\n}\n\n.component__attachment__delete {\n position: absolute;\n top: 0;\n right: 0;\n min-width: 0;\n background-color: rgba(255, 255, 255, 0.75) !important;\n border-radius: 0;\n padding: 4px;\n margin: 0;\n\n //@media only screen and (min-width: $layout-breakpoint-sm) {\n //margin-top: 8px;\n //}\n\n > md-icon {\n margin-top: 0;\n }\n}\n\n.component__revision {\n margin: 8px 0;\n padding: 8px;\n\n &:nth-child(odd) {\n background-color: color('gray-lightest');\n }\n}\n\n.component__revision__content {\n padding: 4px 0 8px 0;\n border-bottom: 1px solid color('gray-light');\n}\n\n.component__revision__actions {\n color: color('gray-darker');\n padding-top: 4px;\n}\n","// Variables\n\n// Base\n.component__content--Discussion {\n overflow: hidden;\n}\n\n.discussion-content {\n background-color: color('gray-lighter');\n //margin: 0 0 -16px;\n box-shadow: inset 0 0 3px color('gray-dark');\n}\n\n.discussion-posts {\n padding: 12px 12px 8px;\n\n @media only screen and (min-width: $layout-breakpoint-md) {\n padding: 16px 16px 0;\n }\n}\n\n.discussion-post {\n margin: 0 auto 16px;\n max-width: $layout-breakpoint-xs;\n\n @media only screen and (min-width: $layout-breakpoint-xs) {\n margin-bottom: 24px;\n }\n\n @media only screen and (min-width: $layout-breakpoint-md) {\n margin-bottom: 32px;\n }\n\n // angular-material fix for when discussion posts are shown inside an md-list-item (e.g. in the grading tool)\n md-divider {\n position: relative;\n width: auto;\n }\n}\n\n.discussion-post__contents {\n padding: 16px;\n}\n\n.discussion-post__avatar, md-list-item > .md-avatar.discussion-post__avatar {\n margin-right: 8px;\n}\n\n.discussion-post__avatar--reply, md-list-item > .md-avatar.discussion-post__avatar--reply {\n margin-top: 8px;\n}\n\n\n.discussion-post__user, md-list-item .md-list-item-text h3.discussion-post__user {\n padding-bottom: 4px;\n font-weight: 700;\n line-height: 1.3;\n overflow: visible;\n white-space: normal;\n}\n\n.discussion-post__date {\n color: color('gray-dark');\n}\n\n.discussion-post__date--reply {\n margin-left: 8px;\n font-weight: 400;\n}\n\n.discussion-post__content {\n margin-top: 16px;\n white-space: pre-wrap;\n}\n\n.discussion-post__attachment {\n max-width: 100%;\n height: auto !important;\n margin-top: 16px;\n}\n\n.discussion-new {\n background-color: #ffffff;\n max-width: 570px;\n margin-left: auto;\n margin-right: auto;\n padding: 8px;\n transition: all 250ms;\n transform: scale(0.95);\n}\n\n.discussion-new--focused {\n transform: scale(1);\n}\n\nmd-input-container.discussion-new__input-container {\n margin: 0;\n padding: 0;\n\n > textarea.md-input {\n min-height: 68px;\n }\n}\n\n.discussion-new__input--textarea, .input-container textarea.discussion-new__input--textarea {\n padding: 8px;\n border: 0 none;\n}\n\n.discussion-new__actions {\n padding: 0 8px;\n\n .md-button {\n &:first-of-type {\n margin-left: 0;\n }\n\n &:last-of-type {\n margin-right: 0;\n }\n }\n}\n\n.discussion-new__attachment {\n padding: 0;\n margin: 0 0 8px;\n}\n\n.discussion-new__attachment__content {\n margin-top: 0;\n margin-bottom: 16px;\n}\n\n.discussion-comments {\n padding: 0;\n}\n\n.discussion-comments__contents {\n background-color: color('gray-lightest');\n}\n\n.discussion-comments__header {\n background-color: transparent;\n padding: 0;\n\n .md-subheader-inner {\n padding-bottom: 8px;\n }\n}\n\n.discussion-comments__list {\n padding: 0;\n max-height: 9999px;\n overflow-y: auto;\n\n @media only screen and (min-width: $layout-breakpoint-xs) {\n max-height: 400px;\n }\n}\n\n.input--textarea.discussion-reply__input, .input-container textarea.input--textarea.discussion-reply__input {\n background-color: #ffffff;\n padding: 4px;\n font-size: ($body-font-size-base) - 1;\n border: 0 none;\n margin-left: -1px;\n margin-bottom: 0;\n resize: none;\n}\n\n.discussion-reply, md-list-item.discussion-reply {\n margin: 0 12px 8px;\n padding: 0;\n min-height: 56px;\n}\n\n.discusstion-reply__details, md-list-item .md-list-item-text.discusstion-reply__details {\n margin: 8px 0;\n}\n\n.discussion-post__user--reply, md-list-item .md-list-item-text h3.discussion-post__user--reply {\n font-size: ($body-font-size-base) - 1;\n padding: 0;\n margin: 0;\n}\n\n.discusstion-reply__content {\n margin-top: 2px;\n\n p {\n font-weight: 400 !important;\n color: color('body') !important;\n }\n}\n\n.discussion-new-reply {\n padding: 8px;\n}\n\n.discussion-new-reply__input-container {\n padding-top: 0;\n margin: 0;\n\n .md-errors-spacer {\n display: none;\n }\n}\n\n.discussion-new-reply__actions {\n margin-left: 8px;\n\n .md-button {\n margin-top: 0;\n margin-bottom: 0;\n }\n}\n",".embedded-content {\n\n}\n\n.embedded-content__iframe {\n border: 0 none;\n}\n",".graph-select {\n min-width: 150px;\n max-width: 200px;\n}\n\n.graph-controls {\n margin: 8px 0;\n padding: 8px 0;\n border: 1px solid color('gray-lighter');\n border-left-width: 0;\n border-right-width: 0;\n}\n","// Variables\n\n// Base\n.match-content {\n background-color: color('gray-lighter');\n margin-bottom: 16px;\n padding: 8px;\n box-shadow: inset 0 0 3px color('gray-dark');\n}\n\n.match-divider {\n margin: 16px 8px 8px;\n}\n\n.match-divider--horizontal {\n @media only screen and (min-width: $layout-breakpoint-sm) {\n display: none;\n }\n}\n\n.match-bucket__header {\n padding: 12px;\n font-weight: 500;\n color: color('primary');\n}\n\n.match-bucket__content {\n padding: 0;\n}\n\n.match-bucket--choices {\n .match-bucket__header {\n color: color('accent-1');\n }\n}\n\n.match-bucket__contents {\n min-height: 120px;\n //margin: 0;\n padding: 0 8px 8px;\n background-color: color('gray-light');\n transition: background-color 250ms;\n border-bottom-left-radius: $card-border-radius;\n border-bottom-right-radius: $card-border-radius;\n\n column-gap: 8px;\n\n @media only screen and (max-width: ($layout-breakpoint-xs - 1)) {\n column-count: 1 !important;\n }\n\n img {\n max-width: 100%;\n height: auto;\n }\n}\n\n.match-bucket__contents--over {\n background-color: color('primary');\n}\n\n.match-bucket__item {\n list-style-type: none;\n cursor: move;\n padding-top: 8px;\n break-inside: avoid;\n\n &:hover, &:focus {\n //background-color: rgba(0,0,0,0.2);\n }\n}\n\n.match-bucket__item__contents {\n background-color: #ffffff;\n padding: 8px !important;\n border: 1px solid color('gray');\n\n .md-list-item-text {\n width: 100%;\n }\n}\n\n.match-bucket__item__contents__text {\n margin-right: 4px;\n}\n\n.match-feedback {\n transition: opacity 250ms;\n /*padding-left: 8px;\n border-left: 4px solid;*/\n margin: 8px -8px -8px;\n color: #ffffff;\n padding: 4px 8px;\n\n &.ng-hide {\n opacity: 0;\n transition: opacity 1ms;\n }\n\n md-icon {\n color: #ffffff;\n }\n}\n",".outside-content {\n iframe {\n border: 1px solid color('gray-lighter');\n }\n}\n\n.outside-content__source {\n margin-top: 4px;\n text-align: end;\n\n a {\n max-width: 240px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n display: inline-block;\n }\n}",".notebook-toolbar {\n md-divider {\n margin: 8px 0;\n }\n\n @media only screen and (max-width: ($layout-breakpoint-sm - 1)) {\n border-top: 1px solid color('gray-light');\n }\n}\n\n.notebook-toolbar__add-menu {\n position: absolute;\n bottom: 40px;\n\n .md-fab-action-item {\n background-color: #ffffff;\n }\n}\n\n.notebook-toolbar__add-icon {\n border-radius: 50%;\n}\n\n#closeNotebookSettingsButton {\n float:right;\n}\n\n[dir=rtl] #closeNotebookSettingsButton {\n float:left;\n}","highchart {\n display: block;\n}\n"]} \ No newline at end of file diff --git a/src/main/webapp/wise5/themes/default/style/modules/_alerts.scss b/src/main/webapp/wise5/themes/default/style/modules/_alerts.scss index 97030004b5..53646e770d 100644 --- a/src/main/webapp/wise5/themes/default/style/modules/_alerts.scss +++ b/src/main/webapp/wise5/themes/default/style/modules/_alerts.scss @@ -13,78 +13,6 @@ padding: 0; } -.status-corner-wrapper { - position: absolute; - z-index: 1; - overflow: hidden; -} - -.status-corner { - width: 0; - height: 0; - border-top-color: color('text-disabled'); - border-bottom-color: color('text-disabled'); - color: color('text-disabled'); - - &:after { - content: '!'; - color: #ffffff; - top: 2px; - right: 10px; - position: absolute; - font-weight: 700; - } -} - -.status-corner--warn { - border-top-color: color('warn') !important; - border-bottom-color: color('warn') !important; -} - -.status-corner-top-right { - top: 0; - right: 0; - border-top-right-radius: $card-border-radius; - - .status-corner { - border-top: 36px solid; - border-left: 36px solid transparent; - } -} - -.status-corner-top-left { - top: 0; - left: 0; - border-top-left-radius: $card-border-radius; - - .status-corner { - border-top: 36px solid; - border-right: 36px solid transparent; - } -} - -.status-corner-bottom-right { - bottom: 0; - right: 0; - border-bottom-right-radius: $card-border-radius; - - .status-corner { - border-bottom: 36px solid; - border-left: 36px solid transparent; - } -} - -.status-corner-bottom-left { - bottom: 0; - left: 0; - border-bottom-left-radius: $card-border-radius; - - .status-corner { - border-bottom: 36px solid; - border-right: 36px solid transparent; - } -} - .avatar--icon--alert { background-color: #ffffff; } diff --git a/src/main/webapp/wise5/themes/default/style/modules/_nav--grading.scss b/src/main/webapp/wise5/themes/default/style/modules/_nav--grading.scss index 0620858ce5..0b2581e956 100644 --- a/src/main/webapp/wise5/themes/default/style/modules/_nav--grading.scss +++ b/src/main/webapp/wise5/themes/default/style/modules/_nav--grading.scss @@ -1,12 +1,18 @@ .student-select { - padding-top: 0; - padding-bottom: 0; + padding-top: 0; + padding-bottom: 0; } .workgroup-progress { - margin-bottom: 8px; + margin-bottom: 8px; - @media (min-width: $layout-breakpoint-sm) { - margin-bottom: 0; - } + @media (min-width: $layout-breakpoint-sm) { + margin-bottom: 0; + } +} + +alert-status-corner { + position: absolute; + top: 0; + right: 0; } diff --git a/src/main/webapp/wise5/themes/default/style/monitor.css b/src/main/webapp/wise5/themes/default/style/monitor.css index 396d8bab25..3b9627f0f9 100644 --- a/src/main/webapp/wise5/themes/default/style/monitor.css +++ b/src/main/webapp/wise5/themes/default/style/monitor.css @@ -1,2 +1,2 @@ -body{background:#eee}body.vle{overflow:hidden}a:focus,a:hover{color:#1565c0}blockquote{background-color:#f5f9fe;padding:8px;margin:16px 0;border:solid #1565c0;border-width:0 0 0 3px}.has-indicator:after{content:"";position:absolute;border-radius:50%;padding:5px;background-color:#f05843}.has-indicator--icon-button:after{top:25px;left:5px}.badge{border-radius:4px;padding:2px 6px;font-size:12px;font-weight:500;font-style:normal;background-color:#aaa}.badge.md-button{min-width:0;min-height:0;line-height:inherit}.badge.md-button:focus,.badge.md-button:hover{background-color:#aaa}.badge.md-button:focus{outline:1px dotted #aaa}.badge--info{background-color:#ef6c00;color:#fff}.badge--warn{background-color:#c62828;color:#fff}.badge--success{background-color:#00c853;color:#fff}.divider--withmargin{margin:16px 0}.divider--dashed{border-top-style:dashed}a{color:#1565c0;cursor:pointer}.active{background-color:hsla(0,0%,62%,.2);color:rgba(0,0,0,.87)}.avatar{border-radius:50%;box-sizing:content-box}.avatar--square{border-radius:4px}.avatar.md-18{height:30px;width:30px}.avatar.md-24{height:36px;width:36px}.avatar.md-36{height:48px;width:48px}.avatar.md-48{height:60px;width:60px}.avatar--icon{background-color:#ddd;white-space:normal!important}.avatar--icon:not(.md-avatar){padding:6px}.avatar--icon.md-18{height:18px;width:18px}.avatar--icon.md-24{height:24px;width:24px}.avatar--icon.md-36{height:36px;width:36px}.avatar--icon.md-48{height:48px;width:48px}md-toolbar.md-light-theme:not(.md-menu-toolbar) md-icon{color:rgba(0,0,0,.54)}md-toolbar.md-light-theme:not(.md-menu-toolbar) .md-button:disabled md-icon{color:rgba(0,0,0,.26)}.md-button:not([disabled]).primary,md-icon.primary{color:#1565c0!important}.md-button:not([disabled]).success,md-icon.success{color:#00c853!important}.md-button:not([disabled]).warn,md-icon.warn{color:#c62828!important}.md-button:not([disabled]).info,md-icon.info{color:#ef6c00!important}.md-button:not([disabled]).accent,md-icon.accent{color:#f05843!important}.md-button:not([disabled]).accent-1,md-icon.accent-1{color:#795c3a!important}.md-button:not([disabled]).accent-2,md-icon.accent-2{color:#cad266!important}md-input-container.md-wise-theme label{color:rgba(0,0,0,.87)}md-select-menu.md-default-theme md-option[selected],md-select-menu md-option[selected]{background-color:#ecf4fd}.md-autocomplete-suggestions-container.md-default-theme li .highlight,.md-autocomplete-suggestions-container li .highlight{color:#1565c0;background-color:#ecf4fd}.primary{color:#1565c0}.accent{color:#f05843}.accent-1{color:#795c3a}.accent-2{color:#cad266}.warn{color:#c62828}.info{color:#ef6c00}.success{color:#00c853}.divider{color:rgba(0,0,0,.12)}.gray-lightest{color:#f7f7f7}.gray-lighter{color:#eee}.gray-light{color:#ddd}.gray{color:#ccc}.gray-dark{color:#aaa}.gray-darker{color:#757575}.gray-darkest{color:#333}.text{color:rgba(0,0,0,.87)}.text-secondary{color:rgba(0,0,0,.54)}.text-disabled{color:rgba(0,0,0,.26)}.text-light{color:#fff}.text-light-secondary{color:hsla(0,0%,100%,.7)}.text-light-disabled{color:hsla(0,0%,100%,.5)}.selected-bg{color:#ecf4fd}.score{color:#ffc107}.body{color:rgba(0,0,0,.87)}.body-bg{color:#eee}.primary-bg{background-color:#1565c0}.accent-bg{background-color:#f05843}.accent-1-bg{background-color:#795c3a}.accent-2-bg{background-color:#cad266}.warn-bg{background-color:#c62828}.info-bg{background-color:#ef6c00}.success-bg{background-color:#00c853}.divider-bg{background-color:rgba(0,0,0,.12)}.gray-lightest-bg{background-color:#f7f7f7}.gray-lighter-bg{background-color:#eee}.gray-light-bg{background-color:#ddd}.gray-bg{background-color:#ccc}.gray-dark-bg{background-color:#aaa}.gray-darker-bg{background-color:#757575}.gray-darkest-bg{background-color:#333}.text-bg{background-color:rgba(0,0,0,.87)}.text-secondary-bg{background-color:rgba(0,0,0,.54)}.text-disabled-bg{background-color:rgba(0,0,0,.26)}.text-light-bg{background-color:#fff}.text-light-secondary-bg{background-color:hsla(0,0%,100%,.7)}.text-light-disabled-bg{background-color:hsla(0,0%,100%,.5)}.selected-bg-bg{background-color:#ecf4fd}.score-bg{background-color:#ffc107}.body-bg{background-color:rgba(0,0,0,.87)}.body-bg-bg{background-color:#eee}md-progress-circular.primary path{stroke:#1565c0}md-progress-circular.accent path{stroke:#f05843}md-progress-circular.accent-1 path{stroke:#795c3a}md-progress-circular.accent-2 path{stroke:#cad266}md-progress-circular.warn path{stroke:#c62828}md-progress-circular.info path{stroke:#ef6c00}md-progress-circular.success path{stroke:#00c853}md-progress-circular.divider path{stroke:rgba(0,0,0,.12)}md-progress-circular.gray-lightest path{stroke:#f7f7f7}md-progress-circular.gray-lighter path{stroke:#eee}md-progress-circular.gray-light path{stroke:#ddd}md-progress-circular.gray path{stroke:#ccc}md-progress-circular.gray-dark path{stroke:#aaa}md-progress-circular.gray-darker path{stroke:#757575}md-progress-circular.gray-darkest path{stroke:#333}md-progress-circular.text path{stroke:rgba(0,0,0,.87)}md-progress-circular.text-secondary path{stroke:rgba(0,0,0,.54)}md-progress-circular.text-disabled path{stroke:rgba(0,0,0,.26)}md-progress-circular.text-light path{stroke:#fff}md-progress-circular.text-light-secondary path{stroke:hsla(0,0%,100%,.7)}md-progress-circular.text-light-disabled path{stroke:hsla(0,0%,100%,.5)}md-progress-circular.selected-bg path{stroke:#ecf4fd}md-progress-circular.score path{stroke:#ffc107}md-progress-circular.body path{stroke:rgba(0,0,0,.87)}md-progress-circular.body-bg path{stroke:#eee}.l-constrained{margin-left:auto;margin-right:auto;max-width:100%;position:relative}@media (min-width:600px){.l-constrained{width:1280px}}.l-constrained-md{width:960px;max-width:100%}.l-footer{position:fixed;bottom:0;left:0;right:0;z-index:1;background-color:#fff;border-top:1px solid #eee}.button--footer{margin:0;padding-top:0;padding-bottom:0;min-width:0;display:flex}.button--footer__element{padding-left:8px}.l-header{z-index:3}.l-header .logo{margin-left:0!important;height:36px;width:36px;vertical-align:middle}.l-header .logo-link{min-width:auto;display:none;padding:0 4px;margin-right:12px}@media only screen and (min-width:600px){.l-header .logo-link{display:block}}.l-header .logo-link:focus,.l-header .logo-link:hover{border:0}@media only screen and (max-width:599px){.l-header .md-toolbar-tools h1,.l-header .md-toolbar-tools h2,.l-header .md-toolbar-tools h3{font-size:15px}}.l-main{background-color:#eee}.l-main--with-toolbar{margin-top:42px}#content{transition:margin-top .5s}.view-content{margin:0 auto;padding:8px;position:absolute;left:0;right:0;transition:opacity .5s}@media only screen and (min-width:960px){.view-content{padding:16px}}.view-content.ng-enter{opacity:0}.view-content .ng-enter-active{opacity:1;transition-delay:.25s}.view-content.ng-hide,.view-content.ng-hide-add,.view-content.ng-hide-add-active,.view-content.ng-hide-remove,.view-content.ng-hide-remove-active,.view-content.ng-leave-active{opacity:0}.view-content--with-sidemenu{padding:8px}@media only screen and (min-width:600px){.view-content--with-sidemenu{margin-left:54px;padding:16px}}@media only screen and (min-width:600px){[dir=rtl] .view-content--with-sidemenu{margin-left:auto;margin-right:54px}}.content-head{margin:8px 0}.content-head h1,.content-head h2,.content-head h3{font-weight:300;margin-top:0;margin-bottom:0;font-size:36px}@media only screen and (max-width:959px){.content-head h1,.content-head h2,.content-head h3{font-size:32px;text-align:center}}@media only screen and (max-width:959px){.content-head__more{margin-top:8px}}.content-head__item,h2.content-head__item{margin:0 8px}.content-head__item .md-subhead,h2.content-head__item .md-subhead{padding-left:4px}@media only screen and (max-width:959px){.content-head__item .md-subhead,h2.content-head__item .md-subhead{display:block;padding-left:0}}.content-head__item md-icon,h2.content-head__item md-icon{vertical-align:text-bottom}.stepSelectMenuContainer md-select-menu,.stepSelectMenuContainer md-select-menu md-content{max-height:500px}.l-nav,.l-notebook{background-color:#eee!important}.l-notebook{margin-top:42px}.l-sidebar__header{background-color:#fff!important;color:#795c3a!important}.l-sidebar__header md-select{color:rgba(0,0,0,.87)}.status-icon{margin:0 4px;z-index:1;vertical-align:bottom}.md-button.status-icon{height:auto;width:auto;min-height:0;line-height:inherit;margin:0 4px;padding:0}.status-corner-wrapper{position:absolute;z-index:1;overflow:hidden}.status-corner{width:0;height:0;border-top-color:rgba(0,0,0,.26);border-bottom-color:rgba(0,0,0,.26);color:rgba(0,0,0,.26)}.status-corner:after{content:"!";color:#fff;top:2px;right:10px;position:absolute;font-weight:700}.status-corner--warn{border-top-color:#c62828!important;border-bottom-color:#c62828!important}.status-corner-top-right{top:0;right:0;border-top-right-radius:4px}.status-corner-top-right .status-corner{border-top:36px solid;border-left:36px solid transparent}.status-corner-top-left{top:0;left:0;border-top-left-radius:4px}.status-corner-top-left .status-corner{border-top:36px solid;border-right:36px solid transparent}.status-corner-bottom-right{bottom:0;right:0;border-bottom-right-radius:4px}.status-corner-bottom-right .status-corner{border-bottom:36px solid;border-left:36px solid transparent}.status-corner-bottom-left{bottom:0;left:0;border-bottom-left-radius:4px}.status-corner-bottom-left .status-corner{border-bottom:36px solid;border-right:36px solid transparent}.avatar--icon--alert{background-color:#fff}.avatar--icon--alert__icon{font-size:48px;margin:-4px 0 0 -4px}.gu-mirror{position:fixed!important;margin:0!important;z-index:9999!important;opacity:.8;transition:opacity .25s ease-in-out}.gu-hide{display:none!important}.gu-unselectable{-webkit-user-select:none!important;-moz-user-select:none!important;-ms-user-select:none!important;user-select:none!important}.gu-transit{opacity:.2}md-dialog{width:600px}.dialog--wide{width:960px}.dialog--wider{width:1280px}.help-bubble{border-radius:4px;max-width:320px}@media (min-width:600px){.help-bubble{max-width:552px}}@media (min-width:960px){.help-bubble{max-width:912px}}@media (min-width:1280px){.help-bubble{max-width:1232px}}.help-bubble___title__content,.help-bubble__title{border-top-left-radius:4px;border-top-right-radius:4px}.help-bubble___title__content{padding:0 0 0 12px;background-color:#ef6c00}.help-bubble___title__content .md-icon-button{margin-right:0;padding-top:0;padding-bottom:0}.help-bubble__content{overflow:auto;padding:8px 12px;max-height:480px}.help-bubble__actions{border-bottom-left-radius:4px;border-bottom-right-radius:4px}div.hopscotch-bubble{border-radius:4px;border:0;font-family:Roboto,Helvetica Neue,sans-serif;font-size:14px;z-index:6}div.hopscotch-bubble .hopscotch-bubble-arrow-container{position:absolute;width:20px;height:20px}div.hopscotch-bubble .hopscotch-bubble-arrow-container.up{top:0;left:12px}div.hopscotch-bubble .hopscotch-bubble-arrow-container.up .hopscotch-bubble-arrow{border-bottom:10px solid #ef6c00;border-left:10px solid transparent;border-right:10px solid transparent;position:relative;top:-10px}div.hopscotch-bubble .hopscotch-bubble-arrow-container.up .hopscotch-bubble-arrow-border{border:0}div.hopscotch-bubble .hopscotch-bubble-arrow-container.down{bottom:-34px;left:12px}div.hopscotch-bubble .hopscotch-bubble-arrow-container.down .hopscotch-bubble-arrow{border-top:10px solid #ef6c00;border-left:10px solid transparent;border-right:10px solid transparent;position:relative;top:-10px}div.hopscotch-bubble .hopscotch-bubble-arrow-container.down .hopscotch-bubble-arrow-border{border:0}div.hopscotch-bubble .hopscotch-bubble-arrow-container.left{top:12px;left:-10px}div.hopscotch-bubble .hopscotch-bubble-arrow-container.left .hopscotch-bubble-arrow{border-right:10px solid #ef6c00;border-bottom:10px solid transparent;border-top:10px solid transparent;position:relative;left:0;top:-10px}div.hopscotch-bubble .hopscotch-bubble-arrow-container.left .hopscotch-bubble-arrow-border{border:0}div.hopscotch-bubble .hopscotch-bubble-arrow-container.right{top:12px;right:-30px}div.hopscotch-bubble .hopscotch-bubble-arrow-container.right .hopscotch-bubble-arrow{border-left:10px solid #ef6c00;border-bottom:10px solid transparent;border-top:10px solid transparent;position:relative;right:0;top:-10px}div.hopscotch-bubble .hopscotch-bubble-arrow-container.right .hopscotch-bubble-arrow-border{border:0}.input-container{padding-top:12px}.input-container--component{margin-bottom:0}.input-container--open-response.md-has-icon{padding-left:0}.input-container--open-response .md-errors-spacer{display:none}.input-wrapper{position:relative}.input-wrapper--focused .input--textarea__action md-icon{color:#1565c0}.input--textarea,.input-container textarea.input--textarea{padding:8px;background-color:#f7f7f7;border:1px solid #ccc;margin-bottom:8px}.input--textarea:focus,.input-container textarea.input--textarea:focus{background-color:#fff}.input--textarea[disabled],.input-container textarea.input--textarea[disabled]{color:rgba(0,0,0,.54)}.input-container textarea.input--textarea{width:100%}.input--textarea--disabled{color:rgba(0,0,0,.54)}.input--textarea__action{position:absolute;right:-4px}.input--textarea__action[disabled] md-icon{color:rgba(0,0,0,.26)!important}.input--textarea__action--notebook{top:6px}.input-wrapper--richtext .input--textarea__action--notebook{top:-7px}.input--textarea__action--revision{bottom:6px}.input-wrapper--richtext .input--textarea__action--revision{bottom:-5px}.input-label,md-input-container:not(.md-input-invalid):not(.md-input-focused).md-input-has-value label.input-label{line-height:1.2;color:rgba(0,0,0,.87)}.input-label.input-label--focused,md-input-container:not(.md-input-invalid):not(.md-input-focused).md-input-has-value label.input-label.input-label--focused{color:#1565c0}.autocomplete input{text-overflow:ellipsis;overflow:hidden;word-wrap:none;font-weight:500;color:rgba(0,0,0,.54)}@media only screen and (min-width:600px){.autocomplete--minwidth{min-width:300px}}@media only screen and (min-width:960px){.autocomplete--minwidth{min-width:300px}}.autocomplete--flat md-autocomplete-wrap{background-color:#fff}.autocomplete--flat md-autocomplete-wrap:not(.md-menu-showing){box-shadow:none;background-color:#eee}.select__header{height:48px}.select__header input{height:100%;width:100%;padding:0 8px;outline:none;border:0;font-size:14px;font-weight:500}.table{max-width:100%;width:auto;min-width:100px;margin:8px 0}.table tbody>tr>td,.table tbody>tr>th,.table tfoot>tr>td,.table tfoot>tr>th,.table thead>tr>td,.table thead>tr>th{border:1px solid #ccc;padding:6px;font-size:15px;min-height:32px;height:32px;min-width:32px;vertical-align:top}.table td.inactive,.table th{background-color:#f7f7f7;opacity:1;visibility:visible}.table md-input-container{margin:0}.table .md-errors-spacer{display:none}.table--student td.inactive{padding:8px 10px}.table--full-width{width:100%}.table--list{border:0;border-collapse:collapse;background-color:#fff;max-width:100%;overflow:auto}.table--list td,.table--list th{padding:0 4px;border:0}.table--list td{min-height:56px;height:56px}.table--list tr.md-button{display:table-row;text-align:left;width:auto;text-transform:none;font-size:inherit;font-weight:400}.table--list__wrap{min-width:600px}@media only screen and (max-width:959px){.table-wrap-sticky{overflow-x:auto}}.table--list__thead{font-size:14px;font-weight:700}.table--list__thead__tr{height:100%;margin:0}.table--list__thead__th{background-color:#757575;color:#fff;min-height:42px;height:42px}.table--list__thead__link{color:#fff;text-transform:none;margin:0;min-width:0;white-space:normal;line-height:1.4;width:100%}.table--list__thead__sort{margin:0}.table--list__thead__sort--reverse{transform:rotate(180deg)}.td--wrap{min-width:180px;white-space:normal;line-height:1.2}@media only screen and (max-width:959px){.td--max-width{max-width:180px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}}.md-toolbar-tools{font-size:18px}.md-toolbar-tools .autocomplete,.md-toolbar-tools .autocomplete input,.md-toolbar-tools .autocomplete md-autocomplete-wrap{height:36px}.md-toolbar--wise{min-height:42px}.md-toolbar--wise .md-toolbar-tools{height:42px;max-height:42px}.md-toolbar--wise .md-button.md-icon-button{height:42px;line-height:42px;width:42px}.md-toolbar--wise--sm .md-toolbar-tools{padding:0 8px;font-size:15px}.md-toolbar--sidenav{background-color:#333!important}.md-toolbar--sidenav .md-toolbar-tools{font-size:16px;font-weight:500}.toolbar{position:fixed;left:0;right:0;top:52px;z-index:3}.toolbar__title{margin-left:8px;font-size:16px;font-weight:500}.toolbar__tools{padding-right:8px}[dir=rtl] .toolbar__tools{padding-right:16px;padding-left:8px}.md-button.toolbar__nav,.toolbar__nav{margin:0}.md-button.toolbar__select,.toolbar__select{margin:0 4px;min-height:32px;background-color:#f7f7f7}.md-button.toolbar__select .md-select-value,.toolbar__select .md-select-value{height:32px;text-align:left}[dir=rtl] .md-button.toolbar__select .md-select-value,[dir=rtl] .toolbar__select .md-select-value{text-align:right}.toolbar__select--fixedwidth{width:168px}@media only screen and (min-width:600px){.toolbar__select--fixedwidth{width:264px}}@media only screen and (min-width:960px){.toolbar__select--fixedwidth{width:432px}}.list-item{background-color:#fff;border-bottom:1px solid #eee}.list-item.md-subheader,.list-item .md-subheader{color:rgba(0,0,0,.87);background-color:#fff}.list-item.md-subheader md-icon,.list-item .md-subheader md-icon{vertical-align:middle}.list-item.md-subheader .md-subheader-inner,.list-item .md-subheader .md-subheader-inner{padding:0}.list-item.md-subheader .md-avatar,.list-item .md-subheader .md-avatar{margin-right:8px}.list-item .autocomplete{margin:8px 0}.list-item--info._md-button-wrap>div.md-button:first-child,.list-item--info .md-subheader-content{border-left:4px solid #ef6c00!important;margin-left:-4px}.list-item--warn._md-button-wrap>div.md-button:first-child,.list-item--warn .md-subheader-content{border-left:4px solid #c62828!important;margin-left:-4px}.list-item--expanded{border-bottom-width:0}.list-item--noclick,.list-item--noclick.md-button{cursor:default;background-color:#f7f7f7}.list-item--actions{padding:0 8px!important}.list-item__subheader-button{text-transform:none;width:100%;padding:8px 16px;margin:0;white-space:normal;text-align:left;line-height:1.4}.user-list{font-size:15px}.notice{text-align:center;padding:8px;background-color:rgba(0,0,0,.04);width:100%}@media (min-width:600px){.notice{max-width:80%;border-radius:3px;margin:24px auto}}.milestone{min-width:196px;width:196px;height:242px;background-color:#fff;padding:0}.milestone.md-button{text-transform:none}.milestone__progress{background-color:#eee;border-radius:50%;position:relative;margin-bottom:12px}.milestone__progress__percent{position:absolute;top:8px;bottom:8px;left:8px;right:8px;border-radius:50%;background-color:#fff;color:#1565c0;font-size:28px;font-weight:500}.milestone__title{font-weight:700;font-size:15px;margin-bottom:12px}.milestone-details section:not(:first-child){margin-top:8px}.milestone-details__section{background-color:#fff;padding:16px}.milestone-details__section>p{margin-top:16px;margin-bottom:0}.milestone-details__section .grading,.milestone-details__section md-list{padding:0}.milestone-details__header{padding:12px 16px;margin:-16px -16px 16px;text-transform:uppercase;font-weight:500}.milestone-details__progress{width:48px;margin-right:8px}.milestone--add.md-button{text-transform:uppercase}.milestone--add__icon{height:96px;width:96px;background-color:#eee;border-radius:50%}#nav{position:relative}.nav{margin-bottom:16px}.nav-mask{position:absolute;top:0;bottom:0;left:0;right:0;background-color:rgba(0,0,0,.25);z-index:1}.nav-mask.ng-hide{opacity:0}.nav-head{color:rgba(0,0,0,.54);font-weight:500}.nav-head md-icon{line-height:20px}.nav-contents--root{padding:6px 6px 12px}.nav-contents--group{background-color:#ddd;padding:8px}.nav-contents--root,.nav-contents__list{padding:0}@media (min-width:600px){.nav-contents__list{padding:8px}}.nav-item{transition:opacity .25s ease-in-out}.nav-item.prev md-list-item{background-color:#ecf4fd}.nav-item--card__content{border-top-right-radius:4px;border-top-left-radius:4px}.nav-item--card__content:focus{outline:none}.nav-item--card__content:focus .nav-item__title>span{border-bottom:1px dashed #ccc}.nav-item--root{transition:margin .25s,box-shadow .5s}.nav-item--root.expanded{flex-basis:100%;max-width:100%;max-height:none!important;margin:8px auto;padding-left:4px}.nav-item--root.expanded:first-of-type{margin-top:0}.nav-item--list__info-item{padding:0 16px 0 4px;display:inline-block}.nav-item--list__reorder{margin-left:8px;color:rgba(0,0,0,.26)}.nav-item--card--group:not(.expanded){box-shadow:0 3px 1px -2px rgba(0,0,0,.14),0 2px 2px 0 rgba(0,0,0,.098),0 1px 5px 0 rgba(0,0,0,.084),3px 3px 0 1px #d5d5d5,6px 6px 0 1px #aaa}.nav-item__collapse{margin:0}.nav-item__more{border-top:1px solid #ddd;border-bottom-right-radius:4px;border-bottom-left-radius:4px;padding:8px 16px;min-height:40px}.nav-item__users{height:auto;cursor:pointer;color:#fff;margin:0;padding:1px 6px}.nav-item__users>md-icon{padding-right:4px;color:#fff}.nav-item__users:focus.success-bg,.nav-item__users:hover.success-bg{background-color:#00c853}.nav-item__title{padding-left:16px;line-height:1.2;font-weight:400}[dir=rtl] .nav-item__title{padding-left:auto;padding-right:16px}.nav-item__info{padding:0 8px}.nav-item__progress{width:48px}.nav-item__progress>.md-container{top:0}.nav-item__progress-value{margin-left:8px;width:36px}.progress-wrapper{padding:2px 0;cursor:pointer}.student-select{padding-top:0;padding-bottom:0}.workgroup-progress{margin-bottom:8px}@media (min-width:960px){.workgroup-progress{margin-bottom:0}}.menu-progress{position:absolute;top:10px;right:12px}.menu-progress path{stroke:#cad266!important;stroke-width:2px}[dir=rtl] .menu-progress{right:auto;left:12px}.menu-sidenav__item{font-weight:700;font-size:14px}.menu-sidenav__icon{margin-top:12px!important;margin-right:12px!important;margin-left:12px}.active .menu-sidenav__icon,.active .menu-sidenav__item{color:#1565c0}.menu-sidebar{position:absolute;top:94px;bottom:0;left:0;background-color:#fff;width:56px;overflow:hidden;padding:8px 0;text-align:center;border-right:1px solid #ccc}@media only screen and (max-width:599px){.menu-sidebar{display:none}}[dir=rtl] .menu-sidebar{right:0;left:auto}.md-button.md-icon-button.menu-sidebar__link{margin-top:6px;margin-bottom:6px}#node{margin:0 auto;position:absolute;left:0;right:0}@media only screen and (min-width:600px){#node{padding:24px 16px;margin-bottom:32px}}@media only screen and (min-width:960px){#node{padding:32px}}#node.ng-enter{transition:opacity .5s;opacity:0}#node.ng-enter-active{opacity:1}@media only screen and (min-width:600px){.node-notice{margin-top:-8px;margin-bottom:16px}}@media only screen and (min-width:960px){.node-notice{margin-top:-16px}}.node-content{padding:0 0 48px;background-color:#fff;border-radius:3px;overflow:visible}@media only screen and (max-width:599px){.node-content{box-shadow:none}}@media only screen and (min-width:600px){.node-content{padding:0;border-top:2px solid;border-bottom:2px solid}}md-content.node-content{background-color:#fff}.node-content__rubric{position:absolute;top:-22px;left:0;right:0;z-index:1}.node-content__rubric .avatar--icon{transform:scale(.94)}@media only screen and (max-width:599px){.node-content__rubric .avatar--icon{transform:scale(.8)}}.node-icon{color:#fff;vertical-align:inherit}.node-select{margin:0 8px;min-width:0;font-weight:500;font-size:15px}.node-select .md-select-value :first-child{transform:translateZ(0);flex:1 0 0}.node-select .md-select-value .node-select__icon,.node-select .md-select-value .node-select__status{display:none}.node-select .md-select-icon{margin-left:0;color:rgba(0,0,0,.87)}.node-select-option--group{background-color:#f7f7f7;border-bottom:1px solid #eee;border-top:1px solid #eee}.node-select-option--node{padding-left:20px}.node-select__icon{margin-right:8px}.node-select__status{margin-left:8px}.node-select__text{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}@media only screen and (min-width:600px){.node-select__text{margin-top:2px}}.node-title{line-height:1.2;text-transform:none;margin-top:3px}@media only screen and (max-width:599px){.node-title{font-size:15px}}.node-content__actions{padding:0 16px 16px}@media only screen and (min-width:960px){.node-content__actions{padding:0 24px 24px}}@media only screen and (min-width:1280px){.node-content__actions{padding:0 32px 32px}}.node-content__actions .md-button:first-child{margin-left:0}.node-content__actions .md-button:last-child{margin-right:0}.node-content__actions__info{font-style:italic;margin-left:8px;color:rgba(0,0,0,.54)}.node-content__actions__more{border-bottom:1px dotted}.md-button.md-icon-button.node-nav:first-of-type{margin-right:0}@media only screen and (min-width:600px){.node-sidebar-active{margin-right:68px}}@media only screen and (max-width:599px){.node-sidebar-visible{margin-bottom:42px}}.node-sidebar{position:absolute;right:0;top:0;width:52px}.node-sidebar__toolbar{position:fixed;width:52px;background-color:#fff;padding:8px 0;border-radius:3px}@media only screen and (max-width:599px){.node-sidebar__toolbar{right:0;bottom:0;left:0;width:100%;border-radius:0;padding:0;min-height:0;height:42px}}.node-info{margin:0}@media only screen and (max-width:599px){.node-info{margin:-16px;border-radius:0}}.node-info .divider{margin-left:-8px;margin-right:-8px}.node-info .component:first-child{margin-top:-16px}.node-info .component,.node-info .component__content,.node-info .component__header{margin-left:-8px;margin-right:-8px}.node-info .component__actions{display:none}.node-rubric{border:2px solid #1565c0;margin:8px 16px 24px;padding:16px;background-color:#fff}.node__label--vertical-alignment{vertical-align:middle;display:inline-block}.grading__item-container{margin:0 0 16px;padding:0!important}.grading__item{background-color:#fff}.grading__item .component{padding:0}@media only screen and (min-width:600px){.notebook-launcher.md-button.md-fab{z-index:61}}.notebook-report{border-radius:4px 4px 0 0;margin:0;height:100%}.notebook-report .note-toolbar{margin:-8px -8px 8px;padding:4px;border-bottom:0;border-top:0;border-color:#ddd currentcolor;border-style:solid none;border-width:1px 0;border-radius:0}.notebook-report .note-toolbar .btn-group{margin-top:0}.notebook-report .note-btn{border:0;border-top-left-radius:0;border-top-right-radius:0;border-bottom-left-radius:0;border-bottom-right-radius:0}.notebook-report-container{height:550px;width:450px;max-height:90%;bottom:0;right:96px;position:absolute;z-index:3}@media only screen and (min-width:960px){.notes-visible .notebook-report-container{right:516px;transition:right .25s}}.notebook-report-container__full{top:16px;bottom:16px;left:16px;right:16px;max-height:none;max-width:none;height:auto;width:auto}.notebook-report-container__full .notebook-report{height:100%;width:100%;margin:0 auto;max-height:none;border-radius:4px}.notebook-report-container__collapsed{width:300px;height:auto}.notebook-report-container__collapsed .notebook-report__actions,.notebook-report-container__collapsed .notebook-report__content,.notebook-report-container__collapsed .notebook-report__content__header{display:none}.notebook-report__toolbar{background-color:#333!important;border-radius:4px 4px 0 0}.notebook-report__toolbar__title{max-width:150px}.notebook-report__content{background-color:#fff}.notebook-report__content h1,.notebook-report__content h2,.notebook-report__content h3,.notebook-report__content h4{font-size:22px}.notebook-report__content .note-editor.note-frame{border:0;border-radius:0;padding:0;box-shadow:none}.notebook-report__content .note-resizebar{display:none}.notebook-report__content__header{padding:8px;background-color:#fff;font-size:16px}@media only screen and (max-width:599px){.notebook-report-container:not(.notebook-report-container__collapsed){top:0;bottom:0;left:0;right:0;max-height:none;max-width:none;height:auto;width:auto}.notebook-report-container:not(.notebook-report-container__collapsed) .notebook-report{border-radius:0}.notebook-tools--full{display:none}}.notebook-report-backdrop{position:fixed;top:0;left:0;width:100%;height:100%;z-index:3;background-color:#212121;opacity:.48}.notebook-menu{transition:opacity .5s}.notebook-menu.ng-enter{opacity:0}.notebook-menu .ng-enter-active{opacity:1;transition-delay:.25s}.notebook-menu.ng-hide,.notebook-menu.ng-hide-add,.notebook-menu.ng-hide-add-active,.notebook-menu.ng-hide-remove,.notebook-menu.ng-hide-remove-active,.notebook-menu.ng-leave-active{opacity:0}.notebook-item{transition:box-shadow .25s;margin:0 16px 16px;display:block}.notebook-item__content{height:250px;min-width:230px;position:relative;padding:0;background-color:#ccc;border-top-left-radius:4px;border-top-right-radius:4px}.notebook-item__content__attachment,.notebook-item__content__text{position:absolute;left:0;right:0}.notebook-item__content__attachment{background-repeat:no-repeat!important;border-top-left-radius:4px;border-top-right-radius:4px;background-position:top!important;background-size:cover!important;top:0;bottom:0}.notebook-item__content__text{bottom:0;padding:8px;font-weight:500;overflow:hidden;max-height:120px;min-height:56px;background-color:hsla(0,0%,100%,.95);border-top:1px solid #eee}.notebook-item__content__text:after{content:"";text-align:right;position:absolute;bottom:0;right:0;width:100%;height:.8em;background:linear-gradient(180deg,hsla(0,0%,100%,0),hsla(0,0%,100%,.95) 100%)}.notebook-item__content--text-only:after{content:"note";font-family:Material Icons;font-weight:400;font-style:normal;display:inline-block;line-height:1;text-transform:none;letter-spacing:normal;word-wrap:normal;white-space:nowrap;direction:ltr;-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;-moz-osx-font-smoothing:grayscale;font-feature-settings:"liga";font-size:80px;color:rgba(0,0,0,.26)}.notebook-item--question__content--text-only{content:"live_help"}.notebook-item__content__location{opacity:.9;padding:8px 0}.notebook-item__content__location md-icon{font-size:22px}.notebook-item__edit{cursor:pointer}.notebook-item__actions{margin:0;padding:0 8px;color:#fff;background-color:#333;border-bottom-left-radius:4px;border-bottom-right-radius:4px}.notebook-item__actions md-icon{color:#fff}.notebook-item__text-input{margin:0}.notebook-item__text-input__textarea{padding-left:0;padding-right:0}.notebook-item__attachment{background-color:#ddd;padding:16px;margin-bottom:16px;text-align:center;position:relative}.notebook-item__attachment__content{max-width:100%;height:auto}.notebook-item__attachment__delete{position:absolute;top:4px;right:-2px;width:34px!important;height:34px!important;min-height:0}.notebook-item__attachment__delete md-icon{margin-left:-2px;font-size:22px}.notebook-item__info{font-style:italic;opacity:.8;color:#8a6942}.notebook-item__info a,.notebook-item__info md-icon{color:#8a6942}.notebook-item__info md-icon{font-size:1.5em;min-width:0;width:auto}.notebook-item__upload{text-align:center;padding:24px;background-color:#eee;margin-bottom:16px;color:rgba(0,0,0,.54);border-radius:4px;cursor:pointer;border:1px dashed transparent;transition:all .25s}.notebook-item__upload md-icon,.notebook-item__upload span{transition:color .25s}.notebook-item__upload.dragover,.notebook-item__upload:focus,.notebook-item__upload:hover{border-color:#f05843;background-color:#fdebe8;color:#f05843}.notebook-item__upload.dragover md-icon,.notebook-item__upload.dragover span,.notebook-item__upload:focus md-icon,.notebook-item__upload:focus span,.notebook-item__upload:hover md-icon,.notebook-item__upload:hover span{color:#f05843}.view-notebook-item{width:600px}.notebook-item--report{background-color:#fff}.notebook-item--report .note-editor{margin-bottom:16px;border-color:#ccc}.notebook-item--report__container.ui-scrollpoint .notebook-item--report__toolbar{position:fixed;top:94px;left:0;right:0;z-index:1}@media only screen and (min-width:600px){.notebook-item--report__container.ui-scrollpoint .notebook-item--report__toolbar{left:54px;padding:0 24px}}@media only screen and (min-width:960px){.notebook-item--report__container.ui-scrollpoint .notebook-item--report__toolbar{padding:0 32px}}.notebook-item--report__container.ui-scrollpoint .notebook-item--report__toolbar .note-toolbar{margin:0 16px}@media only screen and (min-width:600px){.notebook-item--report__container.ui-scrollpoint .notebook-item--report__toolbar .note-toolbar{margin:0 8px}}@media only screen and (min-width:960px){.notebook-item--report__container.ui-scrollpoint .notebook-item--report__toolbar .note-toolbar{margin:0 16px}}.notebook-item--report__container.ui-scrollpoint .note-editor{padding-top:40px}.notebook-item--report__toolbar .note-toolbar{background-color:#ddd;border:1px solid #ccc;margin-bottom:-2px}.notebook-item--report__heading{text-align:center;margin-bottom:32px}.notebook-item--report__add-note{font-weight:700}.notebook-item--report__note-img{max-width:100%;height:auto!important}@media only screen and (min-width:600px){.notebook-sidebar{width:400px;max-width:none}}@media only screen and (min-width:960px){.notebook-sidebar{width:500px;max-width:none}}.notebook-items{width:100%;overflow:auto;margin-top:16px;margin-bottom:76px}.notebook-items .notebook-item{width:100%}.notebook-items .notebook-item__content{height:200px;min-width:0}.notebook-items--grading{margin-bottom:0}@media only screen and (max-width:599px){.notebook-enabled .md-fab-bottom-left,.notebook-enabled .md-fab-bottom-right{bottom:50px!important}}.notebook-grading{background-color:#fff;display:block}.notification-btn{width:60px!important}.notification-btn md-icon{margin-left:20px}.notification-count{border-radius:50%;position:absolute;background-color:#f05843;width:22px;left:4px;height:22px;line-height:18px;font-size:12px;font-weight:700;border:2px solid}.notification-count:before{content:"";position:absolute;right:-7px;top:5px;border-left:6px solid hsla(0,0%,100%,.87);border-top:4px solid transparent;border-bottom:4px solid transparent}.notification-list{padding:8px 0}.notification-dismiss{width:500px}.notification-dismiss__input{margin-bottom:0}md-list md-list-item.md-2-line .md-list-item-text h4.notification-list-item__source,md-list md-list-item.md-3-line .md-list-item-text h4.notification-list-item__source,md-list md-list-item .md-list-item-text h4.notification-list-item__source{color:rgba(0,0,0,.54);font-size:12px}md-list md-list-item.md-2-line .md-list-item-text h4.notification-list-item__source md-icon,md-list md-list-item.md-3-line .md-list-item-text h4.notification-list-item__source md-icon,md-list md-list-item .md-list-item-text h4.notification-list-item__source md-icon{font-size:18px;min-width:0;width:auto;margin-left:-4px;line-height:20px}.account-menu{border-radius:4px;padding:0;font-size:15px;max-width:380px}@media (min-width:1280px){.account-menu{min-width:380px!important}}.account-menu h3{margin:0;font-weight:300}.account-menu--fixed-height{height:304px}.account-menu--fixed-width{width:320px}@media (min-width:960px){.account-menu--fixed-width{width:380px}}.account-menu__icon{background-color:#fff;border-radius:50%}.account-menu__caret{position:absolute;right:28px;top:-8px;outline:none}.account-menu__caret:before{content:"";position:absolute;border-bottom:8px solid #fff;border-left:8px solid transparent;border-right:8px solid transparent}.account-menu__caret--notification,.account-menu__caret--pause{right:80px}.account-menu__caret--notification--with-pause{right:132px}[dir=rtl] .account-menu__caret{right:auto;left:28px}[dir=rtl] .account-menu__caret--notification,[dir=rtl] .account-menu__caret--pause{left:80px;right:auto}[dir=rtl] .account-menu__caret--notification--with-pause{left:132px;right:auto}.account-menu__info{padding:8px 12px}.account-menu__info__title{font-weight:500}.account-menu__info__team{font-weight:400;color:rgba(0,0,0,.54)}.account-menu__users,.account-menu__users md-list-item{padding:0}.account-menu__users md-list-item .md-avatar{margin:0 8px 0 0;height:48px;width:48px}.account-menu__progress md-progress-linear{transform:rotate(270deg);width:26px}.account-menu__grade{position:relative;margin-right:4px}.account-menu__grade md-icon{color:#ffc107}.account-menu__grade__overlay{position:absolute;top:0;width:100%;background-color:hsla(0,0%,100%,.6)}.account-menu__actions{background-color:#f7f7f7}.account-menu__control{padding:16px}.annotations{margin:16px 4px 16px 62px;position:relative;font-size:15px}.annotations hr{margin:10px 0 8px;border-color:rgba(0,0,0,.12)}.annotations:after{content:"";position:absolute;width:0;height:0;left:-16px;right:auto;top:0;bottom:auto;border-top:20px solid transparent;border-bottom:20px solid transparent;border-right:16px solid #757575}.annotations-container--student--report{border-top:1px solid #ddd}.annotations--report{margin-top:0;margin-bottom:0}.annotations__header{position:relative;border-top-right-radius:4px;padding:10px 12px;font-weight:700;transition:all 1s;color:#fff;background-color:#757575}.annotations__avatar{background-color:#f05843;padding:2px;position:absolute;top:0;left:-62px}.annotations__icon{transition:all 1s;color:#fff}.annotations__body{padding:12px;background-color:#fff;border-bottom-left-radius:4px;border-bottom-right-radius:4px;overflow:auto}.annotations__status{background-color:#fff;color:#ef6c00;display:inline-block;margin-left:8px;font-size:12px}.annotations__status.ng-enter,.annotations__status.ng-leave{transition:all 1s}.annotations__status.ng-enter,.annotations__status.ng-leave.ng-leave-active{opacity:0}.annotations__status.ng-enter.ng-enter-active,.annotations__status.ng-leave{opacity:1}.annotations__score{font-weight:700}.annotations__info{font-style:italic;opacity:.8;border-bottom:1px dotted;font-size:13px}.annotations--inside .annotations{margin-left:72px}.annotations--info{margin-bottom:32px;margin-right:8px;margin-left:72px}@media only screen and (min-width:600px){.annotations--info{margin:16px 16px 32px 76px}}.annotations--info:after{border-right:16px solid #ef6c00}.annotations--info .annotations__avatar{background-color:#fff}.annotations--info .annotations__header{background-color:#ef6c00}.annotations--grading md-input-container{margin-bottom:0}.annotations--grading .md-errors-spacer{display:none}.annotations--grading input:focus,.annotations--grading textarea:focus{background-color:#fff}.annotations--grading input:disabled,.annotations--grading textarea:disabled{color:rgba(0,0,0,.87)}.annotations--grading--revision{margin:8px 0 0;padding:8px}.annotations--notebook{margin-top:16px}.annotations--grading__info{font-style:italic;margin:8px 8px 4px}.annotations--grading__item{padding:8px}.annotations--grading__score input{margin-top:0!important;font-size:18px;width:52px;text-align:center}.annotations--grading__score__label{transform:none!important;width:auto;display:block;padding:0;margin:0 8px 0 0}.annotations--grading__score__max label{display:none}.annotations--grading__score__divider{position:relative;top:12px;margin-left:4px}.annotations--grading__auto-comment{margin:0 2px}.annotations--grading__auto-comment__content{margin-top:8px}.component{position:relative}.component__wrapper{padding:0 16px;margin:24px 0}.component__content{overflow-x:auto;font-size:15px;overflow-y:hidden}@media only screen and (min-width:600px){.component__content{padding:0 8px}}.component-header,h3.component__header{padding:8px 12px;margin:0;font-size:14px}.component__rubric{position:absolute;left:-20px;top:12px}.notebook-enabled .component_content img{transition:all .25s;cursor:pointer;cursor:copy}.notebook-enabled .component_content img:focus,.notebook-enabled .component_content img:hover{box-shadow:0 0 5px 1px #f05843}.component__actions .md-button:first-child{margin-left:0}.component__actions .md-button:last-child{margin-right:0}.component__actions__info{font-style:italic;margin-left:8px;color:rgba(0,0,0,.54)}.component__actions__more{border-bottom:1px dotted}.component__prompt{margin-bottom:8px;font-weight:500}.component__prompt__content{display:inline}.component__attachment{position:relative;margin:0 8px;padding-bottom:8px}@media only screen and (min-width:600px){.component__attachment{padding-top:8px}}@media only screen and (max-width:599px){.component__add-attachment{width:100%}}.component__attachment__content{max-height:100px;width:auto}.component__attachment__delete{position:absolute;top:0;right:0;min-width:0;background-color:hsla(0,0%,100%,.75)!important;border-radius:0;padding:4px;margin:0}.component__attachment__delete>md-icon{margin-top:0}.component__revision{margin:8px 0;padding:8px}.component__revision:nth-child(odd){background-color:#f7f7f7}.component__revision__content{padding:4px 0 8px;border-bottom:1px solid #ddd}.component__revision__actions{color:#757575;padding-top:4px}.component__content--Discussion{overflow:hidden}.discussion-content{background-color:#eee;box-shadow:inset 0 0 3px #aaa}.discussion-posts{padding:12px 12px 8px}@media only screen and (min-width:1280px){.discussion-posts{padding:16px 16px 0}}.discussion-post{margin:0 auto 16px;max-width:600px}@media only screen and (min-width:600px){.discussion-post{margin-bottom:24px}}@media only screen and (min-width:1280px){.discussion-post{margin-bottom:32px}}.discussion-post md-divider{position:relative;width:auto}.discussion-post__contents{padding:16px}.discussion-post__avatar,md-list-item>.md-avatar.discussion-post__avatar{margin-right:8px}.discussion-post__avatar--reply,md-list-item>.md-avatar.discussion-post__avatar--reply{margin-top:8px}.discussion-post__user,md-list-item .md-list-item-text h3.discussion-post__user{padding-bottom:4px;font-weight:700;line-height:1.3;overflow:visible;white-space:normal}.discussion-post__date{color:#aaa}.discussion-post__date--reply{margin-left:8px;font-weight:400}.discussion-post__content{margin-top:16px;white-space:pre-wrap}.discussion-post__attachment{max-width:100%;height:auto!important;margin-top:16px}.discussion-new{background-color:#fff;max-width:570px;margin-left:auto;margin-right:auto;padding:8px;transition:all .25s;transform:scale(.95)}.discussion-new--focused{transform:scale(1)}md-input-container.discussion-new__input-container{margin:0;padding:0}md-input-container.discussion-new__input-container>textarea.md-input{min-height:68px}.discussion-new__input--textarea,.input-container textarea.discussion-new__input--textarea{padding:8px;border:0}.discussion-new__actions{padding:0 8px}.discussion-new__actions .md-button:first-of-type{margin-left:0}.discussion-new__actions .md-button:last-of-type{margin-right:0}.discussion-new__attachment{padding:0;margin:0 0 8px}.discussion-new__attachment__content{margin-top:0;margin-bottom:16px}.discussion-comments{padding:0}.discussion-comments__contents{background-color:#f7f7f7}.discussion-comments__header{background-color:transparent;padding:0}.discussion-comments__header .md-subheader-inner{padding-bottom:8px}.discussion-comments__list{padding:0;max-height:9999px;overflow-y:auto}@media only screen and (min-width:600px){.discussion-comments__list{max-height:400px}}.input--textarea.discussion-reply__input,.input-container textarea.input--textarea.discussion-reply__input{background-color:#fff;padding:4px;font-size:14px;border:0;margin-left:-1px;margin-bottom:0;resize:none}.discussion-reply,md-list-item.discussion-reply{margin:0 12px 8px;padding:0;min-height:56px}.discusstion-reply__details,md-list-item .md-list-item-text.discusstion-reply__details{margin:8px 0}.discussion-post__user--reply,md-list-item .md-list-item-text h3.discussion-post__user--reply{font-size:14px;padding:0;margin:0}.discusstion-reply__content{margin-top:2px}.discusstion-reply__content p{font-weight:400!important;color:rgba(0,0,0,.87)!important}.discussion-new-reply{padding:8px}.discussion-new-reply__input-container{padding-top:0;margin:0}.discussion-new-reply__input-container .md-errors-spacer{display:none}.discussion-new-reply__actions{margin-left:8px}.discussion-new-reply__actions .md-button{margin-top:0;margin-bottom:0}.embedded-content__iframe{border:0}.component--grading{padding:0;margin:0}.component--grading:not(:last-child)>div{border-bottom:1px solid #ddd}.component--grading .component__wrapper{padding:0;margin:0}.component--grading .component__content{padding:16px;margin:0}.component--grading__response{padding-bottom:16px}@media only screen and (min-width:960px){.component--grading__response{padding-right:16px;padding-bottom:0}}.component--grading__response__content{overflow:auto}.component--grading__annotations{background-color:#ecf4fd}.component--grading__annotations__divider{padding:4px;background-color:#fff}.component--grading__actions__info{margin:16px 0 0;padding-top:8px;border-top:1px solid #eee}.graph-select{min-width:150px;max-width:200px}.graph-controls{margin:8px 0;padding:8px 0;border-color:#eee;border-style:solid;border-width:1px 0}.match-content{background-color:#eee;margin-bottom:16px;padding:8px;box-shadow:inset 0 0 3px #aaa}.match-divider{margin:16px 8px 8px}@media only screen and (min-width:960px){.match-divider--horizontal{display:none}}.match-bucket__header{padding:12px;font-weight:500;color:#1565c0}.match-bucket__content{padding:0}.match-bucket--choices .match-bucket__header{color:#795c3a}.match-bucket__contents{min-height:120px;padding:0 8px 8px;background-color:#ddd;transition:background-color .25s;border-bottom-left-radius:4px;border-bottom-right-radius:4px;-moz-column-gap:8px;column-gap:8px}@media only screen and (max-width:599px){.match-bucket__contents{-moz-column-count:1!important;column-count:1!important}}.match-bucket__contents img{max-width:100%;height:auto}.match-bucket__contents--over{background-color:#1565c0}.match-bucket__item{list-style-type:none;cursor:move;padding-top:8px;-moz-column-break-inside:avoid;break-inside:avoid}.match-bucket__item__contents{background-color:#fff;padding:8px!important;border:1px solid #ccc}.match-bucket__item__contents .md-list-item-text{width:100%}.match-bucket__item__contents__text{margin-right:4px}.match-feedback{transition:opacity .25s;margin:8px -8px -8px;color:#fff;padding:4px 8px}.match-feedback.ng-hide{opacity:0;transition:opacity 1ms}.match-feedback md-icon{color:#fff}.outside-content iframe{border:1px solid #eee}.outside-content__source{margin-top:4px;text-align:end}.outside-content__source a{max-width:240px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;display:inline-block}.component-revisions .component{padding:0;margin:0}.component-revisions .component__content{padding:0}.component-revisions .component__wrapper{margin:16px 0}.component-revisions .md-resize-handle{display:none}.component-revisions__item,md-list-item.component-revisions__item{padding:0}.component-revisions__item--latest{margin-bottom:24px}.component-revisions__annotation-label{margin-right:8px}.component-revisions__has-auto-and-teacher{padding-top:8px;margin-top:8px;border-top:1px solid #ddd}.notebook-toolbar md-divider{margin:8px 0}@media only screen and (max-width:959px){.notebook-toolbar{border-top:1px solid #ddd}}.notebook-toolbar__add-menu{position:absolute;bottom:40px}.notebook-toolbar__add-menu .md-fab-action-item{background-color:#fff}.notebook-toolbar__add-icon{border-radius:50%}#closeNotebookSettingsButton{float:right}[dir=rtl] #closeNotebookSettingsButton{float:left}highchart{display:block} +body{background:#eee}body.vle{overflow:hidden}a:focus,a:hover{color:#1565c0}blockquote{background-color:#f5f9fe;padding:8px;margin:16px 0;border:solid #1565c0;border-width:0 0 0 3px}.has-indicator:after{content:"";position:absolute;border-radius:50%;padding:5px;background-color:#f05843}.has-indicator--icon-button:after{top:25px;left:5px}.badge{border-radius:4px;padding:2px 6px;font-size:12px;font-weight:500;font-style:normal;background-color:#aaa}.badge.md-button{min-width:0;min-height:0;line-height:inherit}.badge.md-button:focus,.badge.md-button:hover{background-color:#aaa}.badge.md-button:focus{outline:1px dotted #aaa}.badge--info{background-color:#ef6c00;color:#fff}.badge--warn{background-color:#c62828;color:#fff}.badge--success{background-color:#00c853;color:#fff}.divider--withmargin{margin:16px 0}.divider--dashed{border-top-style:dashed}a{color:#1565c0;cursor:pointer}.active{background-color:hsla(0,0%,62%,.2);color:rgba(0,0,0,.87)}.avatar{border-radius:50%;box-sizing:content-box}.avatar--square{border-radius:4px}.avatar.md-18{height:30px;width:30px}.avatar.md-24{height:36px;width:36px}.avatar.md-36{height:48px;width:48px}.avatar.md-48{height:60px;width:60px}.avatar--icon{background-color:#ddd;white-space:normal!important}.avatar--icon:not(.md-avatar){padding:6px}.avatar--icon.md-18{height:18px;width:18px}.avatar--icon.md-24{height:24px;width:24px}.avatar--icon.md-36{height:36px;width:36px}.avatar--icon.md-48{height:48px;width:48px}md-toolbar.md-light-theme:not(.md-menu-toolbar) md-icon{color:rgba(0,0,0,.54)}md-toolbar.md-light-theme:not(.md-menu-toolbar) .md-button:disabled md-icon{color:rgba(0,0,0,.26)}.md-button:not([disabled]).primary,md-icon.primary{color:#1565c0!important}.md-button:not([disabled]).success,md-icon.success{color:#00c853!important}.md-button:not([disabled]).warn,md-icon.warn{color:#c62828!important}.md-button:not([disabled]).info,md-icon.info{color:#ef6c00!important}.md-button:not([disabled]).accent,md-icon.accent{color:#f05843!important}.md-button:not([disabled]).accent-1,md-icon.accent-1{color:#795c3a!important}.md-button:not([disabled]).accent-2,md-icon.accent-2{color:#cad266!important}md-input-container.md-wise-theme label{color:rgba(0,0,0,.87)}md-select-menu.md-default-theme md-option[selected],md-select-menu md-option[selected]{background-color:#ecf4fd}.md-autocomplete-suggestions-container.md-default-theme li .highlight,.md-autocomplete-suggestions-container li .highlight{color:#1565c0;background-color:#ecf4fd}.primary{color:#1565c0}.accent{color:#f05843}.accent-1{color:#795c3a}.accent-2{color:#cad266}.warn{color:#c62828}.info{color:#ef6c00}.success{color:#00c853}.divider{color:rgba(0,0,0,.12)}.gray-lightest{color:#f7f7f7}.gray-lighter{color:#eee}.gray-light{color:#ddd}.gray{color:#ccc}.gray-dark{color:#aaa}.gray-darker{color:#757575}.gray-darkest{color:#333}.text{color:rgba(0,0,0,.87)}.text-secondary{color:rgba(0,0,0,.54)}.text-disabled{color:rgba(0,0,0,.26)}.text-light{color:#fff}.text-light-secondary{color:hsla(0,0%,100%,.7)}.text-light-disabled{color:hsla(0,0%,100%,.5)}.selected-bg{color:#ecf4fd}.score{color:#ffc107}.body{color:rgba(0,0,0,.87)}.body-bg{color:#eee}.primary-bg{background-color:#1565c0}.accent-bg{background-color:#f05843}.accent-1-bg{background-color:#795c3a}.accent-2-bg{background-color:#cad266}.warn-bg{background-color:#c62828}.info-bg{background-color:#ef6c00}.success-bg{background-color:#00c853}.divider-bg{background-color:rgba(0,0,0,.12)}.gray-lightest-bg{background-color:#f7f7f7}.gray-lighter-bg{background-color:#eee}.gray-light-bg{background-color:#ddd}.gray-bg{background-color:#ccc}.gray-dark-bg{background-color:#aaa}.gray-darker-bg{background-color:#757575}.gray-darkest-bg{background-color:#333}.text-bg{background-color:rgba(0,0,0,.87)}.text-secondary-bg{background-color:rgba(0,0,0,.54)}.text-disabled-bg{background-color:rgba(0,0,0,.26)}.text-light-bg{background-color:#fff}.text-light-secondary-bg{background-color:hsla(0,0%,100%,.7)}.text-light-disabled-bg{background-color:hsla(0,0%,100%,.5)}.selected-bg-bg{background-color:#ecf4fd}.score-bg{background-color:#ffc107}.body-bg{background-color:rgba(0,0,0,.87)}.body-bg-bg{background-color:#eee}md-progress-circular.primary path{stroke:#1565c0}md-progress-circular.accent path{stroke:#f05843}md-progress-circular.accent-1 path{stroke:#795c3a}md-progress-circular.accent-2 path{stroke:#cad266}md-progress-circular.warn path{stroke:#c62828}md-progress-circular.info path{stroke:#ef6c00}md-progress-circular.success path{stroke:#00c853}md-progress-circular.divider path{stroke:rgba(0,0,0,.12)}md-progress-circular.gray-lightest path{stroke:#f7f7f7}md-progress-circular.gray-lighter path{stroke:#eee}md-progress-circular.gray-light path{stroke:#ddd}md-progress-circular.gray path{stroke:#ccc}md-progress-circular.gray-dark path{stroke:#aaa}md-progress-circular.gray-darker path{stroke:#757575}md-progress-circular.gray-darkest path{stroke:#333}md-progress-circular.text path{stroke:rgba(0,0,0,.87)}md-progress-circular.text-secondary path{stroke:rgba(0,0,0,.54)}md-progress-circular.text-disabled path{stroke:rgba(0,0,0,.26)}md-progress-circular.text-light path{stroke:#fff}md-progress-circular.text-light-secondary path{stroke:hsla(0,0%,100%,.7)}md-progress-circular.text-light-disabled path{stroke:hsla(0,0%,100%,.5)}md-progress-circular.selected-bg path{stroke:#ecf4fd}md-progress-circular.score path{stroke:#ffc107}md-progress-circular.body path{stroke:rgba(0,0,0,.87)}md-progress-circular.body-bg path{stroke:#eee}.l-constrained{margin-left:auto;margin-right:auto;max-width:100%;position:relative}@media (min-width:600px){.l-constrained{width:1280px}}.l-constrained-md{width:960px;max-width:100%}.l-footer{position:fixed;bottom:0;left:0;right:0;z-index:1;background-color:#fff;border-top:1px solid #eee}.button--footer{margin:0;padding-top:0;padding-bottom:0;min-width:0;display:flex}.button--footer__element{padding-left:8px}.l-header{z-index:3}.l-header .logo{margin-left:0!important;height:36px;width:36px;vertical-align:middle}.l-header .logo-link{min-width:auto;display:none;padding:0 4px;margin-right:12px}@media only screen and (min-width:600px){.l-header .logo-link{display:block}}.l-header .logo-link:focus,.l-header .logo-link:hover{border:0}@media only screen and (max-width:599px){.l-header .md-toolbar-tools h1,.l-header .md-toolbar-tools h2,.l-header .md-toolbar-tools h3{font-size:15px}}.l-main{background-color:#eee}.l-main--with-toolbar{margin-top:42px}#content{transition:margin-top .5s}.view-content{margin:0 auto;padding:8px;position:absolute;left:0;right:0;transition:opacity .5s}@media only screen and (min-width:960px){.view-content{padding:16px}}.view-content.ng-enter{opacity:0}.view-content .ng-enter-active{opacity:1;transition-delay:.25s}.view-content.ng-hide,.view-content.ng-hide-add,.view-content.ng-hide-add-active,.view-content.ng-hide-remove,.view-content.ng-hide-remove-active,.view-content.ng-leave-active{opacity:0}.view-content--with-sidemenu{padding:8px}@media only screen and (min-width:600px){.view-content--with-sidemenu{margin-left:54px;padding:16px}}@media only screen and (min-width:600px){[dir=rtl] .view-content--with-sidemenu{margin-left:auto;margin-right:54px}}.content-head{margin:8px 0}.content-head h1,.content-head h2,.content-head h3{font-weight:300;margin-top:0;margin-bottom:0;font-size:36px}@media only screen and (max-width:959px){.content-head h1,.content-head h2,.content-head h3{font-size:32px;text-align:center}}@media only screen and (max-width:959px){.content-head__more{margin-top:8px}}.content-head__item,h2.content-head__item{margin:0 8px}.content-head__item .md-subhead,h2.content-head__item .md-subhead{padding-left:4px}@media only screen and (max-width:959px){.content-head__item .md-subhead,h2.content-head__item .md-subhead{display:block;padding-left:0}}.content-head__item md-icon,h2.content-head__item md-icon{vertical-align:text-bottom}.stepSelectMenuContainer md-select-menu,.stepSelectMenuContainer md-select-menu md-content{max-height:500px}.l-nav,.l-notebook{background-color:#eee!important}.l-notebook{margin-top:42px}.l-sidebar__header{background-color:#fff!important;color:#795c3a!important}.l-sidebar__header md-select{color:rgba(0,0,0,.87)}.status-icon{margin:0 4px;z-index:1;vertical-align:bottom}.md-button.status-icon{height:auto;width:auto;min-height:0;line-height:inherit;margin:0 4px;padding:0}.avatar--icon--alert{background-color:#fff}.avatar--icon--alert__icon{font-size:48px;margin:-4px 0 0 -4px}.gu-mirror{position:fixed!important;margin:0!important;z-index:9999!important;opacity:.8;transition:opacity .25s ease-in-out}.gu-hide{display:none!important}.gu-unselectable{-webkit-user-select:none!important;-moz-user-select:none!important;-ms-user-select:none!important;user-select:none!important}.gu-transit{opacity:.2}md-dialog{width:600px}.dialog--wide{width:960px}.dialog--wider{width:1280px}.help-bubble{border-radius:4px;max-width:320px}@media (min-width:600px){.help-bubble{max-width:552px}}@media (min-width:960px){.help-bubble{max-width:912px}}@media (min-width:1280px){.help-bubble{max-width:1232px}}.help-bubble___title__content,.help-bubble__title{border-top-left-radius:4px;border-top-right-radius:4px}.help-bubble___title__content{padding:0 0 0 12px;background-color:#ef6c00}.help-bubble___title__content .md-icon-button{margin-right:0;padding-top:0;padding-bottom:0}.help-bubble__content{overflow:auto;padding:8px 12px;max-height:480px}.help-bubble__actions{border-bottom-left-radius:4px;border-bottom-right-radius:4px}div.hopscotch-bubble{border-radius:4px;border:0;font-family:Roboto,Helvetica Neue,sans-serif;font-size:14px;z-index:6}div.hopscotch-bubble .hopscotch-bubble-arrow-container{position:absolute;width:20px;height:20px}div.hopscotch-bubble .hopscotch-bubble-arrow-container.up{top:0;left:12px}div.hopscotch-bubble .hopscotch-bubble-arrow-container.up .hopscotch-bubble-arrow{border-bottom:10px solid #ef6c00;border-left:10px solid transparent;border-right:10px solid transparent;position:relative;top:-10px}div.hopscotch-bubble .hopscotch-bubble-arrow-container.up .hopscotch-bubble-arrow-border{border:0}div.hopscotch-bubble .hopscotch-bubble-arrow-container.down{bottom:-34px;left:12px}div.hopscotch-bubble .hopscotch-bubble-arrow-container.down .hopscotch-bubble-arrow{border-top:10px solid #ef6c00;border-left:10px solid transparent;border-right:10px solid transparent;position:relative;top:-10px}div.hopscotch-bubble .hopscotch-bubble-arrow-container.down .hopscotch-bubble-arrow-border{border:0}div.hopscotch-bubble .hopscotch-bubble-arrow-container.left{top:12px;left:-10px}div.hopscotch-bubble .hopscotch-bubble-arrow-container.left .hopscotch-bubble-arrow{border-right:10px solid #ef6c00;border-bottom:10px solid transparent;border-top:10px solid transparent;position:relative;left:0;top:-10px}div.hopscotch-bubble .hopscotch-bubble-arrow-container.left .hopscotch-bubble-arrow-border{border:0}div.hopscotch-bubble .hopscotch-bubble-arrow-container.right{top:12px;right:-30px}div.hopscotch-bubble .hopscotch-bubble-arrow-container.right .hopscotch-bubble-arrow{border-left:10px solid #ef6c00;border-bottom:10px solid transparent;border-top:10px solid transparent;position:relative;right:0;top:-10px}div.hopscotch-bubble .hopscotch-bubble-arrow-container.right .hopscotch-bubble-arrow-border{border:0}.input-container{padding-top:12px}.input-container--component{margin-bottom:0}.input-container--open-response.md-has-icon{padding-left:0}.input-container--open-response .md-errors-spacer{display:none}.input-wrapper{position:relative}.input-wrapper--focused .input--textarea__action md-icon{color:#1565c0}.input--textarea,.input-container textarea.input--textarea{padding:8px;background-color:#f7f7f7;border:1px solid #ccc;margin-bottom:8px}.input--textarea:focus,.input-container textarea.input--textarea:focus{background-color:#fff}.input--textarea[disabled],.input-container textarea.input--textarea[disabled]{color:rgba(0,0,0,.54)}.input-container textarea.input--textarea{width:100%}.input--textarea--disabled{color:rgba(0,0,0,.54)}.input--textarea__action{position:absolute;right:-4px}.input--textarea__action[disabled] md-icon{color:rgba(0,0,0,.26)!important}.input--textarea__action--notebook{top:6px}.input-wrapper--richtext .input--textarea__action--notebook{top:-7px}.input--textarea__action--revision{bottom:6px}.input-wrapper--richtext .input--textarea__action--revision{bottom:-5px}.input-label,md-input-container:not(.md-input-invalid):not(.md-input-focused).md-input-has-value label.input-label{line-height:1.2;color:rgba(0,0,0,.87)}.input-label.input-label--focused,md-input-container:not(.md-input-invalid):not(.md-input-focused).md-input-has-value label.input-label.input-label--focused{color:#1565c0}.autocomplete input{text-overflow:ellipsis;overflow:hidden;word-wrap:none;font-weight:500;color:rgba(0,0,0,.54)}@media only screen and (min-width:600px){.autocomplete--minwidth{min-width:300px}}@media only screen and (min-width:960px){.autocomplete--minwidth{min-width:300px}}.autocomplete--flat md-autocomplete-wrap{background-color:#fff}.autocomplete--flat md-autocomplete-wrap:not(.md-menu-showing){box-shadow:none;background-color:#eee}.select__header{height:48px}.select__header input{height:100%;width:100%;padding:0 8px;outline:none;border:0;font-size:14px;font-weight:500}.table{max-width:100%;width:auto;min-width:100px;margin:8px 0}.table tbody>tr>td,.table tbody>tr>th,.table tfoot>tr>td,.table tfoot>tr>th,.table thead>tr>td,.table thead>tr>th{border:1px solid #ccc;padding:6px;font-size:15px;min-height:32px;height:32px;min-width:32px;vertical-align:top}.table td.inactive,.table th{background-color:#f7f7f7;opacity:1;visibility:visible}.table md-input-container{margin:0}.table .md-errors-spacer{display:none}.table--student td.inactive{padding:8px 10px}.table--full-width{width:100%}.table--list{border:0;border-collapse:collapse;background-color:#fff;max-width:100%;overflow:auto}.table--list td,.table--list th{padding:0 4px;border:0}.table--list td{min-height:56px;height:56px}.table--list tr.md-button{display:table-row;text-align:left;width:auto;text-transform:none;font-size:inherit;font-weight:400}.table--list__wrap{min-width:600px}@media only screen and (max-width:959px){.table-wrap-sticky{overflow-x:auto}}.table--list__thead{font-size:14px;font-weight:700}.table--list__thead__tr{height:100%;margin:0}.table--list__thead__th{background-color:#757575;color:#fff;min-height:42px;height:42px}.table--list__thead__link{color:#fff;text-transform:none;margin:0;min-width:0;white-space:normal;line-height:1.4;width:100%}.table--list__thead__sort{margin:0}.table--list__thead__sort--reverse{transform:rotate(180deg)}.td--wrap{min-width:180px;white-space:normal;line-height:1.2}@media only screen and (max-width:959px){.td--max-width{max-width:180px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}}.md-toolbar-tools{font-size:18px}.md-toolbar-tools .autocomplete,.md-toolbar-tools .autocomplete input,.md-toolbar-tools .autocomplete md-autocomplete-wrap{height:36px}.md-toolbar--wise{min-height:42px}.md-toolbar--wise .md-toolbar-tools{height:42px;max-height:42px}.md-toolbar--wise .md-button.md-icon-button{height:42px;line-height:42px;width:42px}.md-toolbar--wise--sm .md-toolbar-tools{padding:0 8px;font-size:15px}.md-toolbar--sidenav{background-color:#333!important}.md-toolbar--sidenav .md-toolbar-tools{font-size:16px;font-weight:500}.toolbar{position:fixed;left:0;right:0;top:52px;z-index:3}.toolbar__title{margin-left:8px;font-size:16px;font-weight:500}.toolbar__tools{padding-right:8px}[dir=rtl] .toolbar__tools{padding-right:16px;padding-left:8px}.md-button.toolbar__nav,.toolbar__nav{margin:0}.md-button.toolbar__select,.toolbar__select{margin:0 4px;min-height:32px;background-color:#f7f7f7}.md-button.toolbar__select .md-select-value,.toolbar__select .md-select-value{height:32px;text-align:left}[dir=rtl] .md-button.toolbar__select .md-select-value,[dir=rtl] .toolbar__select .md-select-value{text-align:right}.toolbar__select--fixedwidth{width:168px}@media only screen and (min-width:600px){.toolbar__select--fixedwidth{width:264px}}@media only screen and (min-width:960px){.toolbar__select--fixedwidth{width:432px}}.list-item{background-color:#fff;border-bottom:1px solid #eee}.list-item.md-subheader,.list-item .md-subheader{color:rgba(0,0,0,.87);background-color:#fff}.list-item.md-subheader md-icon,.list-item .md-subheader md-icon{vertical-align:middle}.list-item.md-subheader .md-subheader-inner,.list-item .md-subheader .md-subheader-inner{padding:0}.list-item.md-subheader .md-avatar,.list-item .md-subheader .md-avatar{margin-right:8px}.list-item .autocomplete{margin:8px 0}.list-item--info._md-button-wrap>div.md-button:first-child,.list-item--info .md-subheader-content{border-left:4px solid #ef6c00!important;margin-left:-4px}.list-item--warn._md-button-wrap>div.md-button:first-child,.list-item--warn .md-subheader-content{border-left:4px solid #c62828!important;margin-left:-4px}.list-item--expanded{border-bottom-width:0}.list-item--noclick,.list-item--noclick.md-button{cursor:default;background-color:#f7f7f7}.list-item--actions{padding:0 8px!important}.list-item__subheader-button{text-transform:none;width:100%;padding:8px 16px;margin:0;white-space:normal;text-align:left;line-height:1.4}.user-list{font-size:15px}.notice{text-align:center;padding:8px;background-color:rgba(0,0,0,.04);width:100%}@media (min-width:600px){.notice{max-width:80%;border-radius:3px;margin:24px auto}}.milestone{min-width:196px;width:196px;height:242px;background-color:#fff;padding:0}.milestone.md-button{text-transform:none}.milestone__progress{background-color:#eee;border-radius:50%;position:relative;margin-bottom:12px}.milestone__progress__percent{position:absolute;top:8px;bottom:8px;left:8px;right:8px;border-radius:50%;background-color:#fff;color:#1565c0;font-size:28px;font-weight:500}.milestone__title{font-weight:700;font-size:15px;margin-bottom:12px}.milestone-details section:not(:first-child){margin-top:8px}.milestone-details__section{background-color:#fff;padding:16px}.milestone-details__section>p{margin-top:16px;margin-bottom:0}.milestone-details__section .grading,.milestone-details__section md-list{padding:0}.milestone-details__header{padding:12px 16px;margin:-16px -16px 16px;text-transform:uppercase;font-weight:500}.milestone-details__progress{width:48px;margin-right:8px}.milestone--add.md-button{text-transform:uppercase}.milestone--add__icon{height:96px;width:96px;background-color:#eee;border-radius:50%}#nav{position:relative}.nav{margin-bottom:16px}.nav-mask{position:absolute;top:0;bottom:0;left:0;right:0;background-color:rgba(0,0,0,.25);z-index:1}.nav-mask.ng-hide{opacity:0}.nav-head{color:rgba(0,0,0,.54);font-weight:500}.nav-head md-icon{line-height:20px}.nav-contents--root{padding:6px 6px 12px}.nav-contents--group{background-color:#ddd;padding:8px}.nav-contents--root,.nav-contents__list{padding:0}@media (min-width:600px){.nav-contents__list{padding:8px}}.nav-item{transition:opacity .25s ease-in-out}.nav-item.prev md-list-item{background-color:#ecf4fd}.nav-item--card__content{border-top-right-radius:4px;border-top-left-radius:4px}.nav-item--card__content:focus{outline:none}.nav-item--card__content:focus .nav-item__title>span{border-bottom:1px dashed #ccc}.nav-item--root{transition:margin .25s,box-shadow .5s}.nav-item--root.expanded{flex-basis:100%;max-width:100%;max-height:none!important;margin:8px auto;padding-left:4px}.nav-item--root.expanded:first-of-type{margin-top:0}.nav-item--list__info-item{padding:0 16px 0 4px;display:inline-block}.nav-item--list__reorder{margin-left:8px;color:rgba(0,0,0,.26)}.nav-item--card--group:not(.expanded){box-shadow:0 3px 1px -2px rgba(0,0,0,.14),0 2px 2px 0 rgba(0,0,0,.098),0 1px 5px 0 rgba(0,0,0,.084),3px 3px 0 1px #d5d5d5,6px 6px 0 1px #aaa}.nav-item__collapse{margin:0}.nav-item__more{border-top:1px solid #ddd;border-bottom-right-radius:4px;border-bottom-left-radius:4px;padding:8px 16px;min-height:40px}.nav-item__users{height:auto;cursor:pointer;color:#fff;margin:0;padding:1px 6px}.nav-item__users>md-icon{padding-right:4px;color:#fff}.nav-item__users:focus.success-bg,.nav-item__users:hover.success-bg{background-color:#00c853}.nav-item__title{padding-left:16px;line-height:1.2;font-weight:400}[dir=rtl] .nav-item__title{padding-left:auto;padding-right:16px}.nav-item__info{padding:0 8px}.nav-item__progress{width:48px}.nav-item__progress>.md-container{top:0}.nav-item__progress-value{margin-left:8px;width:36px}.progress-wrapper{padding:2px 0;cursor:pointer}.student-select{padding-top:0;padding-bottom:0}.workgroup-progress{margin-bottom:8px}@media (min-width:960px){.workgroup-progress{margin-bottom:0}}alert-status-corner{position:absolute;top:0;right:0}.menu-progress{position:absolute;top:10px;right:12px}.menu-progress path{stroke:#cad266!important;stroke-width:2px}[dir=rtl] .menu-progress{right:auto;left:12px}.menu-sidenav__item{font-weight:700;font-size:14px}.menu-sidenav__icon{margin-top:12px!important;margin-right:12px!important;margin-left:12px}.active .menu-sidenav__icon,.active .menu-sidenav__item{color:#1565c0}.menu-sidebar{position:absolute;top:94px;bottom:0;left:0;background-color:#fff;width:56px;overflow:hidden;padding:8px 0;text-align:center;border-right:1px solid #ccc}@media only screen and (max-width:599px){.menu-sidebar{display:none}}[dir=rtl] .menu-sidebar{right:0;left:auto}.md-button.md-icon-button.menu-sidebar__link{margin-top:6px;margin-bottom:6px}#node{margin:0 auto;position:absolute;left:0;right:0}@media only screen and (min-width:600px){#node{padding:24px 16px;margin-bottom:32px}}@media only screen and (min-width:960px){#node{padding:32px}}#node.ng-enter{transition:opacity .5s;opacity:0}#node.ng-enter-active{opacity:1}@media only screen and (min-width:600px){.node-notice{margin-top:-8px;margin-bottom:16px}}@media only screen and (min-width:960px){.node-notice{margin-top:-16px}}.node-content{padding:0 0 48px;background-color:#fff;border-radius:3px;overflow:visible}@media only screen and (max-width:599px){.node-content{box-shadow:none}}@media only screen and (min-width:600px){.node-content{padding:0;border-top:2px solid;border-bottom:2px solid}}md-content.node-content{background-color:#fff}.node-content__rubric{position:absolute;top:-22px;left:0;right:0;z-index:1}.node-content__rubric .avatar--icon{transform:scale(.94)}@media only screen and (max-width:599px){.node-content__rubric .avatar--icon{transform:scale(.8)}}.node-icon{color:#fff;vertical-align:inherit}.node-select{margin:0 8px;min-width:0;font-weight:500;font-size:15px}.node-select .md-select-value :first-child{transform:translateZ(0);flex:1 0 0}.node-select .md-select-value .node-select__icon,.node-select .md-select-value .node-select__status{display:none}.node-select .md-select-icon{margin-left:0;color:rgba(0,0,0,.87)}.node-select-option--group{background-color:#f7f7f7;border-bottom:1px solid #eee;border-top:1px solid #eee}.node-select-option--node{padding-left:20px}.node-select__icon{margin-right:8px}.node-select__status{margin-left:8px}.node-select__text{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}@media only screen and (min-width:600px){.node-select__text{margin-top:2px}}.node-title{line-height:1.2;text-transform:none;margin-top:3px}@media only screen and (max-width:599px){.node-title{font-size:15px}}.node-content__actions{padding:0 16px 16px}@media only screen and (min-width:960px){.node-content__actions{padding:0 24px 24px}}@media only screen and (min-width:1280px){.node-content__actions{padding:0 32px 32px}}.node-content__actions .md-button:first-child{margin-left:0}.node-content__actions .md-button:last-child{margin-right:0}.node-content__actions__info{font-style:italic;margin-left:8px;color:rgba(0,0,0,.54)}.node-content__actions__more{border-bottom:1px dotted}.md-button.md-icon-button.node-nav:first-of-type{margin-right:0}@media only screen and (min-width:600px){.node-sidebar-active{margin-right:68px}}@media only screen and (max-width:599px){.node-sidebar-visible{margin-bottom:42px}}.node-sidebar{position:absolute;right:0;top:0;width:52px}.node-sidebar__toolbar{position:fixed;width:52px;background-color:#fff;padding:8px 0;border-radius:3px}@media only screen and (max-width:599px){.node-sidebar__toolbar{right:0;bottom:0;left:0;width:100%;border-radius:0;padding:0;min-height:0;height:42px}}.node-info{margin:0}@media only screen and (max-width:599px){.node-info{margin:-16px;border-radius:0}}.node-info .divider{margin-left:-8px;margin-right:-8px}.node-info .component:first-child{margin-top:-16px}.node-info .component,.node-info .component__content,.node-info .component__header{margin-left:-8px;margin-right:-8px}.node-info .component__actions{display:none}.node-rubric{border:2px solid #1565c0;margin:8px 16px 24px;padding:16px;background-color:#fff}.node__label--vertical-alignment{vertical-align:middle;display:inline-block}.grading__item-container{margin:0 0 16px;padding:0!important}.grading__item{background-color:#fff}.grading__item .component{padding:0}@media only screen and (min-width:600px){.notebook-launcher.md-button.md-fab{z-index:61}}.notebook-report{border-radius:4px 4px 0 0;margin:0;height:100%}.notebook-report .note-toolbar{margin:-8px -8px 8px;padding:4px;border-bottom:0;border-top:0;border-color:#ddd currentcolor;border-style:solid none;border-width:1px 0;border-radius:0}.notebook-report .note-toolbar .btn-group{margin-top:0}.notebook-report .note-btn{border:0;border-top-left-radius:0;border-top-right-radius:0;border-bottom-left-radius:0;border-bottom-right-radius:0}.notebook-report-container{height:550px;width:450px;max-height:90%;bottom:0;right:96px;position:absolute;z-index:3}@media only screen and (min-width:960px){.notes-visible .notebook-report-container{right:516px;transition:right .25s}}.notebook-report-container__full{top:16px;bottom:16px;left:16px;right:16px;max-height:none;max-width:none;height:auto;width:auto}.notebook-report-container__full .notebook-report{height:100%;width:100%;margin:0 auto;max-height:none;border-radius:4px}.notebook-report-container__collapsed{width:300px;height:auto}.notebook-report-container__collapsed .notebook-report__actions,.notebook-report-container__collapsed .notebook-report__content,.notebook-report-container__collapsed .notebook-report__content__header{display:none}.notebook-report__toolbar{background-color:#333!important;border-radius:4px 4px 0 0}.notebook-report__toolbar__title{max-width:150px}.notebook-report__content{background-color:#fff}.notebook-report__content h1,.notebook-report__content h2,.notebook-report__content h3,.notebook-report__content h4{font-size:22px}.notebook-report__content .note-editor.note-frame{border:0;border-radius:0;padding:0;box-shadow:none}.notebook-report__content .note-resizebar{display:none}.notebook-report__content__header{padding:8px;background-color:#fff;font-size:16px}@media only screen and (max-width:599px){.notebook-report-container:not(.notebook-report-container__collapsed){top:0;bottom:0;left:0;right:0;max-height:none;max-width:none;height:auto;width:auto}.notebook-report-container:not(.notebook-report-container__collapsed) .notebook-report{border-radius:0}.notebook-tools--full{display:none}}.notebook-report-backdrop{position:fixed;top:0;left:0;width:100%;height:100%;z-index:3;background-color:#212121;opacity:.48}.notebook-menu{transition:opacity .5s}.notebook-menu.ng-enter{opacity:0}.notebook-menu .ng-enter-active{opacity:1;transition-delay:.25s}.notebook-menu.ng-hide,.notebook-menu.ng-hide-add,.notebook-menu.ng-hide-add-active,.notebook-menu.ng-hide-remove,.notebook-menu.ng-hide-remove-active,.notebook-menu.ng-leave-active{opacity:0}.notebook-item{transition:box-shadow .25s;margin:0 16px 16px;display:block}.notebook-item__content{height:250px;min-width:230px;position:relative;padding:0;background-color:#ccc;border-top-left-radius:4px;border-top-right-radius:4px}.notebook-item__content__attachment,.notebook-item__content__text{position:absolute;left:0;right:0}.notebook-item__content__attachment{background-repeat:no-repeat!important;border-top-left-radius:4px;border-top-right-radius:4px;background-position:top!important;background-size:cover!important;top:0;bottom:0}.notebook-item__content__text{bottom:0;padding:8px;font-weight:500;overflow:hidden;max-height:120px;min-height:56px;background-color:hsla(0,0%,100%,.95);border-top:1px solid #eee}.notebook-item__content__text:after{content:"";text-align:right;position:absolute;bottom:0;right:0;width:100%;height:.8em;background:linear-gradient(180deg,hsla(0,0%,100%,0),hsla(0,0%,100%,.95) 100%)}.notebook-item__content--text-only:after{content:"note";font-family:Material Icons;font-weight:400;font-style:normal;display:inline-block;line-height:1;text-transform:none;letter-spacing:normal;word-wrap:normal;white-space:nowrap;direction:ltr;-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;-moz-osx-font-smoothing:grayscale;font-feature-settings:"liga";font-size:80px;color:rgba(0,0,0,.26)}.notebook-item--question__content--text-only{content:"live_help"}.notebook-item__content__location{opacity:.9;padding:8px 0}.notebook-item__content__location md-icon{font-size:22px}.notebook-item__edit{cursor:pointer}.notebook-item__actions{margin:0;padding:0 8px;color:#fff;background-color:#333;border-bottom-left-radius:4px;border-bottom-right-radius:4px}.notebook-item__actions md-icon{color:#fff}.notebook-item__text-input{margin:0}.notebook-item__text-input__textarea{padding-left:0;padding-right:0}.notebook-item__attachment{background-color:#ddd;padding:16px;margin-bottom:16px;text-align:center;position:relative}.notebook-item__attachment__content{max-width:100%;height:auto}.notebook-item__attachment__delete{position:absolute;top:4px;right:-2px;width:34px!important;height:34px!important;min-height:0}.notebook-item__attachment__delete md-icon{margin-left:-2px;font-size:22px}.notebook-item__info{font-style:italic;opacity:.8;color:#8a6942}.notebook-item__info a,.notebook-item__info md-icon{color:#8a6942}.notebook-item__info md-icon{font-size:1.5em;min-width:0;width:auto}.notebook-item__upload{text-align:center;padding:24px;background-color:#eee;margin-bottom:16px;color:rgba(0,0,0,.54);border-radius:4px;cursor:pointer;border:1px dashed transparent;transition:all .25s}.notebook-item__upload md-icon,.notebook-item__upload span{transition:color .25s}.notebook-item__upload.dragover,.notebook-item__upload:focus,.notebook-item__upload:hover{border-color:#f05843;background-color:#fdebe8;color:#f05843}.notebook-item__upload.dragover md-icon,.notebook-item__upload.dragover span,.notebook-item__upload:focus md-icon,.notebook-item__upload:focus span,.notebook-item__upload:hover md-icon,.notebook-item__upload:hover span{color:#f05843}.view-notebook-item{width:600px}.notebook-item--report{background-color:#fff}.notebook-item--report .note-editor{margin-bottom:16px;border-color:#ccc}.notebook-item--report__container.ui-scrollpoint .notebook-item--report__toolbar{position:fixed;top:94px;left:0;right:0;z-index:1}@media only screen and (min-width:600px){.notebook-item--report__container.ui-scrollpoint .notebook-item--report__toolbar{left:54px;padding:0 24px}}@media only screen and (min-width:960px){.notebook-item--report__container.ui-scrollpoint .notebook-item--report__toolbar{padding:0 32px}}.notebook-item--report__container.ui-scrollpoint .notebook-item--report__toolbar .note-toolbar{margin:0 16px}@media only screen and (min-width:600px){.notebook-item--report__container.ui-scrollpoint .notebook-item--report__toolbar .note-toolbar{margin:0 8px}}@media only screen and (min-width:960px){.notebook-item--report__container.ui-scrollpoint .notebook-item--report__toolbar .note-toolbar{margin:0 16px}}.notebook-item--report__container.ui-scrollpoint .note-editor{padding-top:40px}.notebook-item--report__toolbar .note-toolbar{background-color:#ddd;border:1px solid #ccc;margin-bottom:-2px}.notebook-item--report__heading{text-align:center;margin-bottom:32px}.notebook-item--report__add-note{font-weight:700}.notebook-item--report__note-img{max-width:100%;height:auto!important}@media only screen and (min-width:600px){.notebook-sidebar{width:400px;max-width:none}}@media only screen and (min-width:960px){.notebook-sidebar{width:500px;max-width:none}}.notebook-items{width:100%;overflow:auto;margin-top:16px;margin-bottom:76px}.notebook-items .notebook-item{width:100%}.notebook-items .notebook-item__content{height:200px;min-width:0}.notebook-items--grading{margin-bottom:0}@media only screen and (max-width:599px){.notebook-enabled .md-fab-bottom-left,.notebook-enabled .md-fab-bottom-right{bottom:50px!important}}.notebook-grading{background-color:#fff;display:block}.notification-btn{width:60px!important}.notification-btn md-icon{margin-left:20px}.notification-count{border-radius:50%;position:absolute;background-color:#f05843;width:22px;left:4px;height:22px;line-height:18px;font-size:12px;font-weight:700;border:2px solid}.notification-count:before{content:"";position:absolute;right:-7px;top:5px;border-left:6px solid hsla(0,0%,100%,.87);border-top:4px solid transparent;border-bottom:4px solid transparent}.notification-list{padding:8px 0}.notification-dismiss{width:500px}.notification-dismiss__input{margin-bottom:0}md-list md-list-item.md-2-line .md-list-item-text h4.notification-list-item__source,md-list md-list-item.md-3-line .md-list-item-text h4.notification-list-item__source,md-list md-list-item .md-list-item-text h4.notification-list-item__source{color:rgba(0,0,0,.54);font-size:12px}md-list md-list-item.md-2-line .md-list-item-text h4.notification-list-item__source md-icon,md-list md-list-item.md-3-line .md-list-item-text h4.notification-list-item__source md-icon,md-list md-list-item .md-list-item-text h4.notification-list-item__source md-icon{font-size:18px;min-width:0;width:auto;margin-left:-4px;line-height:20px}.account-menu{border-radius:4px;padding:0;font-size:15px;max-width:380px}@media (min-width:1280px){.account-menu{min-width:380px!important}}.account-menu h3{margin:0;font-weight:300}.account-menu--fixed-height{height:304px}.account-menu--fixed-width{width:320px}@media (min-width:960px){.account-menu--fixed-width{width:380px}}.account-menu__icon{background-color:#fff;border-radius:50%}.account-menu__caret{position:absolute;right:28px;top:-8px;outline:none}.account-menu__caret:before{content:"";position:absolute;border-bottom:8px solid #fff;border-left:8px solid transparent;border-right:8px solid transparent}.account-menu__caret--notification,.account-menu__caret--pause{right:80px}.account-menu__caret--notification--with-pause{right:132px}[dir=rtl] .account-menu__caret{right:auto;left:28px}[dir=rtl] .account-menu__caret--notification,[dir=rtl] .account-menu__caret--pause{left:80px;right:auto}[dir=rtl] .account-menu__caret--notification--with-pause{left:132px;right:auto}.account-menu__info{padding:8px 12px}.account-menu__info__title{font-weight:500}.account-menu__info__team{font-weight:400;color:rgba(0,0,0,.54)}.account-menu__users,.account-menu__users md-list-item{padding:0}.account-menu__users md-list-item .md-avatar{margin:0 8px 0 0;height:48px;width:48px}.account-menu__progress md-progress-linear{transform:rotate(270deg);width:26px}.account-menu__grade{position:relative;margin-right:4px}.account-menu__grade md-icon{color:#ffc107}.account-menu__grade__overlay{position:absolute;top:0;width:100%;background-color:hsla(0,0%,100%,.6)}.account-menu__actions{background-color:#f7f7f7}.account-menu__control{padding:16px}.annotations{margin:16px 4px 16px 62px;position:relative;font-size:15px}.annotations hr{margin:10px 0 8px;border-color:rgba(0,0,0,.12)}.annotations:after{content:"";position:absolute;width:0;height:0;left:-16px;right:auto;top:0;bottom:auto;border-top:20px solid transparent;border-bottom:20px solid transparent;border-right:16px solid #757575}.annotations-container--student--report{border-top:1px solid #ddd}.annotations--report{margin-top:0;margin-bottom:0}.annotations__header{position:relative;border-top-right-radius:4px;padding:10px 12px;font-weight:700;transition:all 1s;color:#fff;background-color:#757575}.annotations__avatar{background-color:#f05843;padding:2px;position:absolute;top:0;left:-62px}.annotations__icon{transition:all 1s;color:#fff}.annotations__body{padding:12px;background-color:#fff;border-bottom-left-radius:4px;border-bottom-right-radius:4px;overflow:auto}.annotations__status{background-color:#fff;color:#ef6c00;display:inline-block;margin-left:8px;font-size:12px}.annotations__status.ng-enter,.annotations__status.ng-leave{transition:all 1s}.annotations__status.ng-enter,.annotations__status.ng-leave.ng-leave-active{opacity:0}.annotations__status.ng-enter.ng-enter-active,.annotations__status.ng-leave{opacity:1}.annotations__score{font-weight:700}.annotations__info{font-style:italic;opacity:.8;border-bottom:1px dotted;font-size:13px}.annotations--inside .annotations{margin-left:72px}.annotations--info{margin-bottom:32px;margin-right:8px;margin-left:72px}@media only screen and (min-width:600px){.annotations--info{margin:16px 16px 32px 76px}}.annotations--info:after{border-right:16px solid #ef6c00}.annotations--info .annotations__avatar{background-color:#fff}.annotations--info .annotations__header{background-color:#ef6c00}.annotations--grading md-input-container{margin-bottom:0}.annotations--grading .md-errors-spacer{display:none}.annotations--grading input:focus,.annotations--grading textarea:focus{background-color:#fff}.annotations--grading input:disabled,.annotations--grading textarea:disabled{color:rgba(0,0,0,.87)}.annotations--grading--revision{margin:8px 0 0;padding:8px}.annotations--notebook{margin-top:16px}.annotations--grading__info{font-style:italic;margin:8px 8px 4px}.annotations--grading__item{padding:8px}.annotations--grading__score input{margin-top:0!important;font-size:18px;width:52px;text-align:center}.annotations--grading__score__label{transform:none!important;width:auto;display:block;padding:0;margin:0 8px 0 0}.annotations--grading__score__max label{display:none}.annotations--grading__score__divider{position:relative;top:12px;margin-left:4px}.annotations--grading__auto-comment{margin:0 2px}.annotations--grading__auto-comment__content{margin-top:8px}.component{position:relative}.component__wrapper{padding:0 16px;margin:24px 0}.component__content{overflow-x:auto;font-size:15px;overflow-y:hidden}@media only screen and (min-width:600px){.component__content{padding:0 8px}}.component-header,h3.component__header{padding:8px 12px;margin:0;font-size:14px}.component__rubric{position:absolute;left:-20px;top:12px}.notebook-enabled .component_content img{transition:all .25s;cursor:pointer;cursor:copy}.notebook-enabled .component_content img:focus,.notebook-enabled .component_content img:hover{box-shadow:0 0 5px 1px #f05843}.component__actions .md-button:first-child{margin-left:0}.component__actions .md-button:last-child{margin-right:0}.component__actions__info{font-style:italic;margin-left:8px;color:rgba(0,0,0,.54)}.component__actions__more{border-bottom:1px dotted}.component__prompt{margin-bottom:8px;font-weight:500}.component__prompt__content{display:inline}.component__attachment{position:relative;margin:0 8px;padding-bottom:8px}@media only screen and (min-width:600px){.component__attachment{padding-top:8px}}@media only screen and (max-width:599px){.component__add-attachment{width:100%}}.component__attachment__content{max-height:100px;width:auto}.component__attachment__delete{position:absolute;top:0;right:0;min-width:0;background-color:hsla(0,0%,100%,.75)!important;border-radius:0;padding:4px;margin:0}.component__attachment__delete>md-icon{margin-top:0}.component__revision{margin:8px 0;padding:8px}.component__revision:nth-child(odd){background-color:#f7f7f7}.component__revision__content{padding:4px 0 8px;border-bottom:1px solid #ddd}.component__revision__actions{color:#757575;padding-top:4px}.component__content--Discussion{overflow:hidden}.discussion-content{background-color:#eee;box-shadow:inset 0 0 3px #aaa}.discussion-posts{padding:12px 12px 8px}@media only screen and (min-width:1280px){.discussion-posts{padding:16px 16px 0}}.discussion-post{margin:0 auto 16px;max-width:600px}@media only screen and (min-width:600px){.discussion-post{margin-bottom:24px}}@media only screen and (min-width:1280px){.discussion-post{margin-bottom:32px}}.discussion-post md-divider{position:relative;width:auto}.discussion-post__contents{padding:16px}.discussion-post__avatar,md-list-item>.md-avatar.discussion-post__avatar{margin-right:8px}.discussion-post__avatar--reply,md-list-item>.md-avatar.discussion-post__avatar--reply{margin-top:8px}.discussion-post__user,md-list-item .md-list-item-text h3.discussion-post__user{padding-bottom:4px;font-weight:700;line-height:1.3;overflow:visible;white-space:normal}.discussion-post__date{color:#aaa}.discussion-post__date--reply{margin-left:8px;font-weight:400}.discussion-post__content{margin-top:16px;white-space:pre-wrap}.discussion-post__attachment{max-width:100%;height:auto!important;margin-top:16px}.discussion-new{background-color:#fff;max-width:570px;margin-left:auto;margin-right:auto;padding:8px;transition:all .25s;transform:scale(.95)}.discussion-new--focused{transform:scale(1)}md-input-container.discussion-new__input-container{margin:0;padding:0}md-input-container.discussion-new__input-container>textarea.md-input{min-height:68px}.discussion-new__input--textarea,.input-container textarea.discussion-new__input--textarea{padding:8px;border:0}.discussion-new__actions{padding:0 8px}.discussion-new__actions .md-button:first-of-type{margin-left:0}.discussion-new__actions .md-button:last-of-type{margin-right:0}.discussion-new__attachment{padding:0;margin:0 0 8px}.discussion-new__attachment__content{margin-top:0;margin-bottom:16px}.discussion-comments{padding:0}.discussion-comments__contents{background-color:#f7f7f7}.discussion-comments__header{background-color:transparent;padding:0}.discussion-comments__header .md-subheader-inner{padding-bottom:8px}.discussion-comments__list{padding:0;max-height:9999px;overflow-y:auto}@media only screen and (min-width:600px){.discussion-comments__list{max-height:400px}}.input--textarea.discussion-reply__input,.input-container textarea.input--textarea.discussion-reply__input{background-color:#fff;padding:4px;font-size:14px;border:0;margin-left:-1px;margin-bottom:0;resize:none}.discussion-reply,md-list-item.discussion-reply{margin:0 12px 8px;padding:0;min-height:56px}.discusstion-reply__details,md-list-item .md-list-item-text.discusstion-reply__details{margin:8px 0}.discussion-post__user--reply,md-list-item .md-list-item-text h3.discussion-post__user--reply{font-size:14px;padding:0;margin:0}.discusstion-reply__content{margin-top:2px}.discusstion-reply__content p{font-weight:400!important;color:rgba(0,0,0,.87)!important}.discussion-new-reply{padding:8px}.discussion-new-reply__input-container{padding-top:0;margin:0}.discussion-new-reply__input-container .md-errors-spacer{display:none}.discussion-new-reply__actions{margin-left:8px}.discussion-new-reply__actions .md-button{margin-top:0;margin-bottom:0}.embedded-content__iframe{border:0}.component--grading{padding:0;margin:0}.component--grading:not(:last-child)>div{border-bottom:1px solid #ddd}.component--grading .component__wrapper{padding:0;margin:0}.component--grading .component__content{padding:16px;margin:0}.component--grading__response{padding-bottom:16px}@media only screen and (min-width:960px){.component--grading__response{padding-right:16px;padding-bottom:0}}.component--grading__response__content{overflow:auto}.component--grading__annotations{background-color:#ecf4fd}.component--grading__annotations__divider{padding:4px;background-color:#fff}.component--grading__actions__info{margin:16px 0 0;padding-top:8px;border-top:1px solid #eee}.graph-select{min-width:150px;max-width:200px}.graph-controls{margin:8px 0;padding:8px 0;border-color:#eee;border-style:solid;border-width:1px 0}.match-content{background-color:#eee;margin-bottom:16px;padding:8px;box-shadow:inset 0 0 3px #aaa}.match-divider{margin:16px 8px 8px}@media only screen and (min-width:960px){.match-divider--horizontal{display:none}}.match-bucket__header{padding:12px;font-weight:500;color:#1565c0}.match-bucket__content{padding:0}.match-bucket--choices .match-bucket__header{color:#795c3a}.match-bucket__contents{min-height:120px;padding:0 8px 8px;background-color:#ddd;transition:background-color .25s;border-bottom-left-radius:4px;border-bottom-right-radius:4px;-moz-column-gap:8px;column-gap:8px}@media only screen and (max-width:599px){.match-bucket__contents{-moz-column-count:1!important;column-count:1!important}}.match-bucket__contents img{max-width:100%;height:auto}.match-bucket__contents--over{background-color:#1565c0}.match-bucket__item{list-style-type:none;cursor:move;padding-top:8px;-moz-column-break-inside:avoid;break-inside:avoid}.match-bucket__item__contents{background-color:#fff;padding:8px!important;border:1px solid #ccc}.match-bucket__item__contents .md-list-item-text{width:100%}.match-bucket__item__contents__text{margin-right:4px}.match-feedback{transition:opacity .25s;margin:8px -8px -8px;color:#fff;padding:4px 8px}.match-feedback.ng-hide{opacity:0;transition:opacity 1ms}.match-feedback md-icon{color:#fff}.outside-content iframe{border:1px solid #eee}.outside-content__source{margin-top:4px;text-align:end}.outside-content__source a{max-width:240px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;display:inline-block}.component-revisions .component{padding:0;margin:0}.component-revisions .component__content{padding:0}.component-revisions .component__wrapper{margin:16px 0}.component-revisions .md-resize-handle{display:none}.component-revisions__item,md-list-item.component-revisions__item{padding:0}.component-revisions__item--latest{margin-bottom:24px}.component-revisions__annotation-label{margin-right:8px}.component-revisions__has-auto-and-teacher{padding-top:8px;margin-top:8px;border-top:1px solid #ddd}.notebook-toolbar md-divider{margin:8px 0}@media only screen and (max-width:959px){.notebook-toolbar{border-top:1px solid #ddd}}.notebook-toolbar__add-menu{position:absolute;bottom:40px}.notebook-toolbar__add-menu .md-fab-action-item{background-color:#fff}.notebook-toolbar__add-icon{border-radius:50%}#closeNotebookSettingsButton{float:right}[dir=rtl] #closeNotebookSettingsButton{float:left}highchart{display:block} /*# sourceMappingURL=monitor.css.map */ diff --git a/src/main/webapp/wise5/themes/default/style/monitor.css.map b/src/main/webapp/wise5/themes/default/style/monitor.css.map index 95a71a31f6..3fbd1ef7e5 100644 --- a/src/main/webapp/wise5/themes/default/style/monitor.css.map +++ b/src/main/webapp/wise5/themes/default/style/monitor.css.map @@ -1 +1 @@ -{"version":3,"sources":["src/main/webapp/wise5/themes/default/style/base/_presets.scss","src/main/webapp/wise5/themes/default/style/base/_config--monitor.scss","src/main/webapp/wise5/themes/default/style/base/_config.scss","src/main/webapp/wise5/themes/default/style/base/_helpers.scss","src/main/webapp/wise5/themes/default/style/layouts/_l-default.scss","src/main/webapp/wise5/themes/default/style/material/_config.scss","src/main/webapp/wise5/themes/default/style/layouts/_l-footer.scss","src/main/webapp/wise5/themes/default/style/layouts/_l-header.scss","src/main/webapp/wise5/themes/default/style/layouts/_l-main.scss","src/main/webapp/wise5/themes/default/style/monitor.css","src/main/webapp/wise5/themes/default/style/layouts/_l-notebook.scss","src/main/webapp/wise5/themes/default/style/layouts/_l-nav.scss","src/main/webapp/wise5/themes/default/style/layouts/_l-sidebar.scss","src/main/webapp/wise5/themes/default/style/modules/_alerts.scss","src/main/webapp/wise5/themes/default/style/modules/_dragula.scss","src/main/webapp/wise5/themes/default/style/modules/_dialog.scss","src/main/webapp/wise5/themes/default/style/modules/_help.scss","src/main/webapp/wise5/themes/default/style/modules/_inputs.scss","src/main/webapp/wise5/themes/default/style/modules/_table.scss","src/main/webapp/wise5/themes/default/style/modules/_toolbar.scss","src/main/webapp/wise5/themes/default/style/modules/_list.scss","src/main/webapp/wise5/themes/default/style/modules/_notice.scss","src/main/webapp/wise5/themes/default/style/modules/_milestones.scss","src/main/webapp/wise5/themes/default/style/modules/_nav.scss","src/main/webapp/wise5/themes/default/style/modules/_nav--grading.scss","src/main/webapp/wise5/themes/default/style/modules/_menu.scss","src/main/webapp/wise5/themes/default/style/modules/_node.scss","src/main/webapp/wise5/themes/default/style/modules/_grading.scss","src/main/webapp/wise5/themes/default/style/modules/_notebook.scss","src/main/webapp/wise5/themes/default/style/modules/_notifications.scss","src/main/webapp/wise5/themes/default/style/modules/_account-menu.scss","src/main/webapp/wise5/themes/default/style/modules/_annotations.scss","src/main/webapp/wise5/themes/default/style/modules/_annotations--grading.scss","src/main/webapp/wise5/themes/default/style/modules/_component.scss","src/main/webapp/wise5/themes/default/style/modules/_component--discussion.scss","src/main/webapp/wise5/themes/default/style/modules/_component--embedded.scss","src/main/webapp/wise5/themes/default/style/modules/_component--grading.scss","src/main/webapp/wise5/themes/default/style/modules/_component--graph.scss","src/main/webapp/wise5/themes/default/style/modules/_component--match.scss","src/main/webapp/wise5/themes/default/style/modules/_component--outside.scss","src/main/webapp/wise5/themes/default/style/modules/_component--revisions.scss","src/main/webapp/wise5/themes/default/style/modules/_notebook-toolbar.scss","src/main/webapp/wise5/themes/default/style/modules/_highcharts.scss"],"names":[],"mappings":"AAIA,KACE,eCSuB,CDVzB,SAIM,eAAgB,CAItB,gBAEQ,aCbgB,CDiBxB,WACE,wBAAgD,CAChD,WAAY,CACZ,aAAc,CAGd,oBAAuB,CAAvB,sBAAuB,CAGzB,qBAEQ,UAAW,CACX,iBAAkB,CAClB,iBAAkB,CAClB,WAAY,CACZ,wBC3BW,CD+BnB,kCAEQ,QAAS,CACT,QAAS,CAKjB,OACI,iBEQoB,CFPpB,eAAgB,CAChB,cGlC8B,CHmC9B,eAAgB,CAChB,iBAAkB,CAClB,qBClCkB,CD4BtB,iBASQ,WAAY,CACZ,YAAa,CACb,mBAAoB,CAX5B,8CAcY,qBC1CU,CD4BtB,uBAkBY,uBC9CU,CDmDtB,aACI,wBC3Da,CD4Db,UAAc,CAGlB,aACI,wBCjEa,CDkEb,UAAc,CAGlB,gBACI,wBCpEgB,CDqEhB,UAAc,CAIlB,qBACI,aAAc,CAGlB,iBACI,uBAAwB,CAI5B,EACE,aC7FsB,CD8FtB,cAAe,CAGjB,QACI,kCAAuC,CACvC,qBChFyB,CDoF7B,QACE,iBAAkB,CAClB,sBAAuB,CAGzB,gBACE,iBExDsB,CF4DxB,cAEI,WAAqC,CACrC,UAAoC,CAHxC,cAMI,WAAqC,CACrC,UAAoC,CAPxC,cAUI,WAAqC,CACrC,UAAoC,CAXxC,cAcI,WAAqC,CACrC,UAAoC,CAKxC,cACE,qBCxHqB,CDyHrB,4BAA8B,CAFhC,8BAKM,WA1ImB,CAqIzB,oBASI,WAAY,CACZ,UAAW,CAVf,oBAaI,WAAY,CACZ,UAAW,CAdf,oBAiBI,WAAY,CACZ,UAAW,CAlBf,oBAqBI,WAAY,CACZ,UAAW,CAIf,wDAEI,qBC7ImC,CD2IvC,4EAOM,qBCjJgC,CDuJtC,mDAEI,uBAAkC,CAFtC,mDAKI,uBAAkC,CALtC,6CAQI,uBAA+B,CARnC,6CAWI,uBAA+B,CAXnC,iDAcI,uBAAiC,CAdrC,qDAiBI,uBAAmC,CAjBvC,qDAoBI,uBAAmC,CAKvC,uCACE,qBCnL2B,CDsL7B,uFACE,wBCzM0C,CD4M5C,2HAEE,aC/MsB,CDgNtB,wBC/M0C,CDqNxC,SACE,aCvNkB,CDsNpB,QACE,aClNa,CDiNf,UACE,aCjNe,CDgNjB,UACE,aChNe,CD+MjB,MACE,aC/MW,CD8Mb,MACE,aC9MW,CD6Mb,SACE,aC7Mc,CD4MhB,SACE,qBC5M0B,CD2M5B,eACE,aC3MoB,CD0MtB,cACE,UC1MmB,CDyMrB,YACE,UCzMiB,CDwMnB,MACE,UCxMW,CDuMb,WACE,UCvMgB,CDsMlB,aACE,aCtMkB,CDqMpB,cACE,UCrMmB,CDoMrB,MACE,qBCpMuB,CDmMzB,gBACE,qBCnMiC,CDkMnC,eACE,qBClMgC,CDiMlC,YACE,UCjMgC,CDgMlC,sBACE,wBChM6C,CD+L/C,qBACE,wBC/L4C,CD8L9C,aACE,aCtNsC,CDqNxC,OACE,aC7LY,CD4Ld,MACE,qBCpMuB,CDmMzB,SACE,UC1MmB,CDgNrB,YACE,wBC9NkB,CD6NpB,WACE,wBCzNa,CDwNf,aACE,wBCxNe,CDuNjB,aACE,wBCvNe,CDsNjB,SACE,wBCtNW,CDqNb,SACE,wBCrNW,CDoNb,YACE,wBCpNc,CDmNhB,YACE,gCCnN0B,CDkN5B,kBACE,wBClNoB,CDiNtB,iBACE,qBCjNmB,CDgNrB,eACE,qBChNiB,CD+MnB,SACE,qBC/MW,CD8Mb,cACE,qBC9MgB,CD6MlB,gBACE,wBC7MkB,CD4MpB,iBACE,qBC5MmB,CD2MrB,SACE,gCC3MuB,CD0MzB,mBACE,gCC1MiC,CDyMnC,kBACE,gCCzMgC,CDwMlC,eACE,qBCxMgC,CDuMlC,yBACE,mCCvM6C,CDsM/C,wBACE,mCCtM4C,CDqM9C,gBACE,wBC7NsC,CD4NxC,UACE,wBCpMY,CDmMd,SACE,gCC3MuB,CD0MzB,YACE,qBCjNmB,CDuNrB,kCAEQ,cCtOY,CDoOpB,iCAEQ,cCjOO,CD+Nf,mCAEQ,cChOS,CD8NjB,mCAEQ,cC/NS,CD6NjB,+BAEQ,cC9NK,CD4Nb,+BAEQ,cC7NK,CD2Nb,kCAEQ,cC5NQ,CD0NhB,kCAEQ,sBC3NoB,CDyN5B,wCAEQ,cC1Nc,CDwNtB,uCAEQ,WCzNa,CDuNrB,qCAEQ,WCxNW,CDsNnB,+BAEQ,WCvNK,CDqNb,oCAEQ,WCtNU,CDoNlB,sCAEQ,cCrNY,CDmNpB,uCAEQ,WCpNa,CDkNrB,+BAEQ,sBCnNiB,CDiNzB,yCAEQ,sBClN2B,CDgNnC,wCAEQ,sBCjN0B,CD+MlC,qCAEQ,WChN0B,CD8MlC,+CAEQ,yBC/MuC,CD6M/C,8CAEQ,yBC9MsC,CD4M9C,sCAEQ,cCrOgC,CDmOxC,gCAEQ,cC5MM,CD0Md,+BAEQ,sBCnNiB,CDiNzB,kCAEQ,WCzNa,CGXzB,eACE,gBAAiB,CACjB,iBAAkB,CAClB,cAAe,CACf,iBAAkB,CAElB,yBANF,eAOM,YCW2B,CDThC,CAED,kBACE,WCK8B,CDJ9B,cAAe,CEbjB,UACE,cAAe,CACf,QAAS,CACT,MAAO,CACP,OAAQ,CACR,SAAU,CACV,qBAAyB,CACzB,yBLIuB,CKAzB,gBACE,QAAS,CACT,aAAc,CACd,gBAAiB,CACjB,WAAY,CACZ,YAAa,CAGf,yBACE,gBAAiB,CCpBnB,UACI,SAAU,CADd,gBAIQ,uBAAyB,CACzB,WAAY,CACZ,UAAW,CACX,qBAAsB,CAP9B,qBAWQ,cAAe,CACf,YAAa,CACb,aAAc,CACd,iBAAkB,CAElB,yCAhBR,qBAiBY,aAAc,CAMrB,CAvBL,sDAqBY,QAAc,CAKtB,yCA1BJ,6FA6BgB,cJlBkB,CImBrB,CC9Bb,QACE,qBPUuB,COPzB,sBACE,eNmCwB,CMhC1B,SACE,yBAA2B,CAG7B,cACE,aAAc,CACd,WAAY,CACZ,iBAAkB,CAClB,MAAO,CACP,OAAQ,CACR,sBAAyB,CAEzB,yCARF,cASI,YAAa,CAuBhB,CAhCD,uBAaI,SAAU,CAbd,+BAiBI,SAAU,CACV,qBAAuB,CAlB3B,gLA8BI,SAAU,CAId,6BACE,WAAY,CAEZ,yCAHF,6BAII,gBAAiB,CACjB,YAAa,CAEhB,CAEC,yCCwZA,uCDvZE,gBAAiB,CACjB,iBAAkB,CAErB,CAED,cACE,YAAa,CADf,mDAMI,eAAgB,CAChB,YAAa,CACb,eAAgB,CAChB,cL3D8B,CK6D9B,yCAXJ,mDAYM,cL9D4B,CK+D5B,iBAAkB,CAErB,CAID,yCADF,oBAEI,cAAe,CAElB,CAED,0CAEE,YAAa,CAFf,kEAKI,gBAAiB,CAEjB,yCAPJ,kEAQM,aAAc,CACd,cAAe,CAElB,CAXH,0DAcI,0BAA2B,CAI/B,2FAEE,gBAAiB,CEzGnB,mBCCI,+BDC6C,CAFjD,YACI,eAC6C,CEEjD,mBACE,+BAAoC,CACpC,uBAAmC,CAFrC,6BAKI,qBXQyB,CYpB7B,aACI,YAAa,CACb,SAAU,CACV,qBAAsB,CAG1B,uBACI,WAAY,CACZ,UAAW,CACX,YAAa,CACb,mBAAoB,CACpB,YAAa,CACb,SAAU,CAGd,uBACI,iBAAkB,CAClB,SAAU,CACV,eAAgB,CAGpB,eACI,OAAQ,CACR,QAAS,CACT,gCZFkC,CYGlC,mCZHkC,CYIlC,qBZJkC,CYDtC,qBAQQ,WAAY,CACZ,UAAc,CACd,OAAQ,CACR,UAAW,CACX,iBAAkB,CAClB,eAAgB,CAIxB,qBACI,kCAA0C,CAC1C,qCAA6C,CAGjD,yBACI,KAAM,CACN,OAAQ,CACR,2BXQoB,CWXxB,wCAMQ,qBAAsB,CACtB,kCAAmC,CAI3C,wBACI,KAAM,CACN,MAAO,CACP,0BXHoB,CWAxB,uCAMQ,qBAAsB,CACtB,mCAAoC,CAI5C,4BACI,QAAS,CACT,OAAQ,CACR,8BXdoB,CWWxB,2CAMQ,wBAAyB,CACzB,kCAAmC,CAI3C,2BACI,QAAS,CACT,MAAO,CACP,6BXzBoB,CWsBxB,0CAMQ,wBAAyB,CACzB,mCAAoC,CAI5C,qBACI,qBAAyB,CAG7B,2BACI,cAAe,CACf,oBAAqB,CC7FzB,WACE,wBAA0B,CAC1B,kBAAoB,CACpB,sBAAwB,CACxB,UAAY,CACZ,mCAAqC,CAEvC,SACE,sBAAwB,CAE1B,iBACE,kCAAoC,CACpC,+BAAiC,CACjC,8BAAgC,CAChC,0BAA4B,CAE9B,YACE,UAAY,CCjBd,UACI,WVkB4B,CUfhC,cACI,WVe4B,CUZhC,eACI,YVY6B,CWrBjC,aACI,iBdqDoB,CcpDpB,eAAgB,CAEhB,yBAJJ,aAKQ,eAAuC,CAU9C,CAPG,yBARJ,aASQ,eAAuC,CAM9C,CAHG,0BAZJ,aAaQ,gBAAuC,CAE9C,CAOD,kDAJI,0BdoCoB,CcnCpB,2BfTa,CeYjB,8BAGI,kBAAqB,CACrB,wBfhBa,CeYjB,8CAOQ,cAAe,CACf,aAAc,CACd,gBAAiB,CAIzB,sBACI,aAAc,CACd,gBAAiB,CACjB,gBAAiB,CAGrB,sBACI,6BdYoB,CcXpB,8BdWoB,CcRxB,qBACI,iBdOoB,CcLpB,QAAc,CACd,4CAAiD,CACjD,cbrC8B,CasC9B,SAAU,CANd,uDASQ,iBAAkB,CAClB,UAAW,CACX,WAAY,CAXpB,0DAgBY,KAAM,CACN,SAAU,CAjBtB,kFAoBgB,gCfxDC,CeyDD,kCAAmC,CACnC,mCAAoC,CACpC,iBAAkB,CAClB,SAAU,CAxB1B,yFA4BgB,QAGuC,CA/BvD,4DAoCY,YAAa,CACb,SAAU,CArCtB,oFAwCgB,6Bf5EC,Ce6ED,kCAAmC,CACnC,mCAAoC,CACpC,iBAAkB,CAClB,SAAU,CA5C1B,2FAgDgB,QAGuC,CAnDvD,4DAwDY,QAAS,CACT,UAAW,CAzDvB,oFA4DgB,+BfhGC,CeiGD,oCAAqC,CACrC,iCAAkC,CAClC,iBAAkB,CAClB,MAAO,CACP,SAAU,CAjE1B,2FAqEgB,QAGqC,CAxErD,6DA6EY,QAAS,CACT,WAAY,CA9ExB,qFAiFgB,8BfrHC,CesHD,oCAAqC,CACrC,iCAAkC,CAClC,iBAAkB,CAClB,OAAQ,CACR,SAAU,CAtF1B,4FA0FgB,QAGqC,CCrIrD,iBACI,gBAAiB,CAGrB,4BACI,eAAgB,CAGpB,4CAEQ,cAAe,CAFvB,kDAMQ,YAAa,CAIrB,eACI,iBAAkB,CAGtB,yDAEQ,ahB7BgB,CgBiCxB,2DACI,WAAY,CACZ,wBhBvBsB,CgBwBtB,qBhBrBa,CgBsBb,iBAAkB,CAJtB,uEAOQ,qBAAyB,CAPjC,+EAWQ,qBhBxB+B,CgB4BvC,0CACI,UAAW,CAGf,2BACI,qBhBjCmC,CgBoCvC,yBACI,iBAAkB,CAClB,UAAW,CAFf,2CAKQ,+BAAwC,CAIhD,mCACI,OAjE8B,CAmE9B,4DACI,QAnEoC,CAuE5C,mCACI,UAzE8B,CA2E9B,4DACI,WAAsD,CAK9D,mHACI,eAAgB,CAChB,qBhBjEyB,CgB+D7B,6JAKQ,ahBvFgB,CgB2FxB,oBAEQ,sBAAuB,CACvB,eAAgB,CAChB,cAAe,CACf,eAAgB,CAChB,qBhB7E+B,CgBkFnC,yCADJ,wBAEQ,eAAgB,CAMvB,CAHG,yCALJ,wBAMQ,eAAgB,CAEvB,CAED,yCAEQ,qBAAyB,CAFjC,+DAKY,eAAgB,CAChB,qBhBxGa,CgB6GzB,gBACI,Wf5EiC,Ce2ErC,sBAIQ,WAAY,CACZ,UAAW,CACX,aAAc,CACd,YAAa,CACb,QAAc,CACd,cdtH0B,CcuH1B,eAAgB,CCrIxB,OACE,cAAe,CACf,UAAW,CACX,eAAgB,CAChB,YAAa,CAJf,kHAYM,qBjBIW,CiBHX,WAAY,CACZ,cfA4B,CeC5B,eAAgB,CAChB,WAAY,CACZ,cAAe,CACf,kBAAmB,CAlBzB,6BAwBI,wBjBXsB,CiBYtB,SAAU,CACV,kBAAmB,CA1BvB,0BA8BI,QAAS,CA9Bb,yBAkCI,YAAa,CAIjB,4BAGM,gBAAiB,CAKvB,mBACE,UAAW,CAGb,aACE,QAAc,CACd,wBAAyB,CACzB,qBAAyB,CACzB,cAAe,CACf,aAAc,CALhB,gCASI,aAAc,CACd,QAAc,CAVlB,gBAcI,eAAgB,CAChB,WAAY,CAfhB,0BAoBM,iBAAkB,CAClB,eAAgB,CAChB,UAAW,CACX,mBAAoB,CACpB,iBAAkB,CAClB,eAAmB,CAKzB,mBACE,eb9D8B,CakE9B,yCADF,mBAEI,eAAgB,CAEnB,CAED,oBACE,cf7EgC,Ce8EhC,eAAgB,CAGlB,wBACE,WAAY,CACZ,QAAS,CAGX,wBACE,wBjBnFsB,CiBoFtB,UjB/EoC,CiBgFpC,ehB5DwB,CgB6DxB,WhB7DwB,CgBgE1B,0BACE,UAAc,CACd,mBAAoB,CACpB,QAAS,CACT,WAAY,CACZ,kBAAmB,CACnB,eAAgB,CAChB,UAAW,CAGb,0BACE,QAAS,CAGX,mCACE,wBAAyB,CAG3B,UACE,eAAgB,CAChB,kBAAmB,CACnB,eAAgB,CAIhB,yCADF,eAEI,eAAgB,CAChB,eAAgB,CAChB,sBAAuB,CACvB,kBAAmB,CAEtB,CC1ID,kBACI,cAAe,CADnB,2HAWY,WAAY,CAKxB,kBACI,ejB0BsB,CiB3B1B,oCAIQ,WjBuBkB,CiBtBlB,ejBsBkB,CiB3B1B,4CASQ,WjBkBkB,CiBjBlB,gBjBiBkB,CiBhBlB,UjBgBkB,CiBZ1B,wCAEQ,aAAc,CACd,chBpB0B,CgBwBlC,qBACI,+BAAkD,CADtD,uCAIQ,chB5B0B,CgB6B1B,eAAgB,CAIxB,SACI,cAAe,CACf,MAAO,CACP,OAAQ,CACR,Qd5CoB,Cc6CpB,SAAU,CAGd,gBACI,eAAgB,CAChB,chB3C8B,CgB4C9B,eAAgB,CAGpB,gBACI,iBAAkB,CVu4BtB,0BUp4BI,kBAAmB,CACnB,gBAAiB,CAGrB,sCACI,QAAS,CAGb,4CACI,YAAa,CACb,eAAgB,CAChB,wBlB/DsB,CkB4D1B,8EAMQ,WAAY,CACZ,eAAgB,CVo4BxB,kGU93BU,gBAAiB,CAK3B,6BACI,WAAY,CAEZ,yCAHJ,6BAIQ,WAAY,CAMnB,CAHG,yCAPJ,6BAQQ,WAAY,CAEnB,CCrGD,WACI,qBAAyB,CACzB,4BnBYqB,CmBdzB,iDAKQ,qBnBeqB,CmBdrB,qBAAyB,CANjC,iEASY,qBAAsB,CATlC,yFAaY,SAAU,CAbtB,uEAiBY,gBAAiB,CAjB7B,yBAsBQ,YAAa,CAIrB,kGAEQ,uCAA+C,CAC/C,gBAAiB,CAIzB,kGAIQ,uCAA+C,CAC/C,gBAAiB,CAIzB,qBACI,qBAAsB,CAG1B,kDACI,cAAe,CACf,wBnBnCsB,CmBsC1B,oBACI,uBAAyB,CAG7B,6BACI,mBAAoB,CACpB,UAAW,CACX,gBAAiB,CACjB,QAAS,CACT,kBAAmB,CACnB,eAAgB,CAChB,eAAgB,CAGpB,WACI,cjBpD8B,CkBdlC,QACE,iBAAkB,CAClB,WAAY,CACZ,gCAAkC,CAClC,UAAW,CAEX,yBANF,QAOI,aAAc,CACd,iBnBiDsB,CmBhDtB,gBAAiB,CAEpB,CCXD,WACE,eAAgB,CAChB,WAAY,CACZ,YAAa,CACb,qBAAyB,CACzB,SAAU,CALZ,qBAQI,mBAAoB,CAIxB,qBACE,qBrBCuB,CqBAvB,iBAAkB,CAClB,iBAAkB,CAClB,kBAAmB,CAGrB,8BACE,iBAAkB,CAClB,OAAQ,CACR,UAAW,CACX,QAAS,CACT,SAAU,CACV,iBAAkB,CAClB,qBAAyB,CACzB,arB1BsB,CqB2BtB,cnBdgC,CmBehC,eAAgB,CAGlB,kBACE,eAAgB,CAChB,cnBpBgC,CmBqBhC,kBAAmB,CAGrB,6CAGM,cAAe,CAKrB,4BACE,qBAAyB,CACzB,YAAa,CAFf,8BAKI,eAAgB,CAChB,eAAgB,CANpB,yEAcI,SAAU,CAId,2BACE,iBAAkB,CAClB,uBAAwB,CACxB,wBAAyB,CACzB,eAAgB,CAGlB,6BACE,UAAW,CACX,gBAAiB,CAGnB,0BAEI,wBAAyB,CAI7B,sBACE,WAAY,CACZ,UAAW,CACX,qBrBvEuB,CqBwEvB,iBAAkB,CCtFpB,KACI,iBAAkB,CAGtB,KACI,kBAAmB,CAGvB,UACI,iBAAkB,CAClB,KAAM,CACN,QAAS,CACT,MAAO,CACP,OAAQ,CACR,gCAAkC,CAClC,SAAU,CAPd,kBAUQ,SAAU,CAIlB,UACI,qBtBFmC,CsBGnC,eAAgB,CAFpB,kBAKQ,gBAAiB,CAIzB,oBACI,oBAAqB,CAGzB,qBACI,qBtBrBmB,CsBsBnB,WAAY,CAOhB,wCACI,SAAU,CAEV,yBAHJ,oBAIQ,WAAY,CAEnB,CAED,UACI,mCAAqC,CADzC,4BAKY,wBAKG,CAKf,yBACI,2BrBdoB,CqBepB,0BrBfoB,CqBaxB,+BAKQ,YAAa,CALrB,qDAQY,6BtB3DK,CsBgEjB,gBACI,qCAA0C,CAD9C,yBAIQ,eAAgB,CAEhB,cAAe,CACf,yBAA2B,CAC3B,eAAgB,CAChB,gBAAiB,CATzB,uCAYY,YAAa,CAYzB,2BACI,oBAAqB,CACrB,oBAAqB,CAGzB,yBACI,eAAgB,CAChB,qBtBzFkC,CsBgGtC,sCAEQ,4IACsF,CAI9F,oBACI,QAAS,CAGb,gBACI,yBtBnHmB,CsBoHnB,8BrB7EoB,CqB8EpB,6BrB9EoB,CqB+EpB,gBAAiB,CACjB,eAAgB,CAGpB,iBACI,WAAY,CACZ,cAAe,CACf,UAAc,CACd,QAAS,CACT,eAAgB,CALpB,yBAQQ,iBAAkB,CAClB,UAAc,CATtB,oEAcY,wBtB5IQ,CsBiJpB,iBACI,iBAAkB,CAClB,eAAgB,CAChB,eAAgB,CdmiCpB,2BchiCI,iBAAkB,CAClB,kBAAmB,CAGvB,gBACI,aAAc,CAGlB,oBACI,UAAW,CADf,kCAIQ,KAAM,CAId,0BACI,eAAgB,CAChB,UAAW,CAGf,kBACI,aAAc,CACd,cAAe,CCzLnB,gBACI,aAAc,CACd,gBAAiB,CAGrB,oBACI,iBAAkB,CAElB,yBAHJ,oBAIQ,eAAgB,CAEvB,CCPD,eACI,iBAAkB,CAClB,QAAS,CACT,UAAW,CAHf,oBAMQ,wBAAoC,CACpC,gBAAiB,ChB6tCzB,yBgBztCE,UAAU,CACV,SAAS,CAOX,oBACI,eAAgB,CAEhB,ctBZ8B,CsBelC,oBACI,yBAA2B,CAC3B,2BAA6B,CAC7B,gBAAiB,CAGrB,wDAEQ,axBpCgB,CwBwCxB,cACI,iBAAkB,CAClB,QAA8C,CAC9C,QAAS,CACT,MAAO,CACP,qBAAsB,CACtB,UA9CqB,CA+CrB,eAAgB,CAChB,aAAc,CACd,iBAAkB,CAClB,2BxBnCa,CwBqCb,yCAZJ,cAaQ,YAAa,CAEpB,ChB+sCD,wBgB7sCE,OAAO,CACP,SAAS,CAGX,6CACI,cAAe,CACf,iBAAkB,CC1DtB,MACI,aAAc,CACd,iBAAkB,CAClB,MAAO,CACP,OAAQ,CAER,yCANJ,MAQQ,iBAAkB,CAClB,kBAAmB,CAe1B,CAZG,yCAZJ,MAaQ,YAAa,CAWpB,CAxBD,eAiBQ,sBAAuB,CACvB,SAAU,CAlBlB,sBAsBQ,SAAU,CAOd,yCADJ,aAEQ,eAAgB,CAChB,kBAAmB,CAM1B,CAHG,yCANJ,aAOQ,gBAAiB,CAExB,CAED,cACI,gBAAiB,CACjB,qBAAyB,CACzB,iBxBSsB,CwBRtB,gBAAiB,CAEjB,yCANJ,cAOQ,eAAgB,CAQvB,CALG,yCAVJ,cAWQ,SAAU,CACV,oBAAqB,CACrB,uBAAwB,CAE/B,CAED,wBAEQ,qBAAyB,CAIjC,sBACI,iBAAkB,CAClB,SAAU,CACV,MAAO,CACP,OAAQ,CACR,SAAU,CALd,oCAQQ,oBAAsB,CAEtB,yCAVR,oCAWY,mBAAqB,CAE5B,CAGL,WACI,UAAc,CACd,sBAAuB,CAG3B,aACI,YAAa,CACb,WAAY,CACZ,eAAgB,CAChB,cvB/E8B,CuB2ElC,2CAQY,uBAA6B,CAC7B,UAAW,CATvB,oGAiBY,YAAa,CAjBzB,6BAsBQ,aAAc,CACd,qBzB5FqB,CyBgG7B,2BAEI,wBzBzGsB,CyB0GtB,4BzBzGqB,CyB0GrB,yBzB1GqB,CyB6GzB,0BACI,iBAAkB,CAGtB,mBACI,gBAAiB,CAGrB,qBACI,eAAgB,CAGpB,mBACI,sBAAuB,CACvB,kBAAmB,CACnB,eAAgB,CAEhB,yCALJ,mBAMQ,cAAe,CAEtB,CAED,YACI,eAAgB,CAChB,mBAAoB,CACpB,cAAe,CAEf,yCALJ,YAMQ,cvBzI0B,CuB2IjC,CAED,uBACI,mBAAoB,CAEpB,yCAHJ,uBAIQ,mBAAoB,CAc3B,CAXG,0CAPJ,uBAQQ,mBAAoB,CAU3B,CAlBD,8CAYQ,aAAc,CAZtB,6CAgBQ,cAAe,CAIvB,6BACI,iBAAkB,CAClB,eAAgB,CAChB,qBzB7JmC,CyBgKvC,6BACI,wBAAyB,CAG7B,iDAKQ,cAAe,CAKnB,yCADJ,qBAEQ,iBAAuC,CAE9C,CAGG,yCADJ,sBAEQ,kBxB/JkB,CwBiKzB,CAED,cACI,iBAAkB,CAClB,OAAQ,CACR,KAAM,CACN,UrB3MoB,CqB8MxB,uBACI,cAAe,CACf,UrBhNoB,CqBiNpB,qBAAyB,CACzB,aAAc,CACd,iBxBjKsB,CwBmKtB,yCAPJ,uBAQQ,OAAQ,CACR,QAAS,CACT,MAAO,CACP,UAAW,CACX,eAAgB,CAChB,SAAU,CACV,YAAa,CACb,WxBzLkB,CwB2LzB,CAID,WACI,QAAS,CAET,yCAHJ,WAIQ,YAAa,CACb,eAAgB,CAsBvB,CA3BD,oBASQ,gBAAiB,CACjB,iBAAkB,CAV1B,kCAeY,gBAAiB,CAf7B,mFAoBQ,gBAAiB,CACjB,iBAAkB,CArB1B,+BAyBQ,YAAa,CAIrB,aACI,wBzBvQoB,CyBwQpB,oBAAqB,CACrB,YAAa,CACb,qBAAyB,CAO7B,iCACI,qBAAsB,CACtB,oBAAqB,CCjRzB,yBACE,eAAgB,CAChB,mBAAqB,CAGvB,eACE,qBAAyB,CAD3B,0BAII,SAAU,CCPV,yCADJ,oCAGY,UAAW,CACd,CAIT,iBACI,yBAA0B,CAC1B,QAAS,CACT,WAAY,CAHhB,+BAMQ,oBAAqB,CACrB,WAAY,CACZ,eAAc,CAAd,YAAc,CAEd,8B3BPe,C2BOf,uB3BPe,C2BOf,kB3BPe,C2BQf,eAAgB,CAXxB,0CAcY,YAAa,CAdzB,2BAmBQ,QAAc,CACd,wBAAyB,CACzB,yBAA0B,CAC1B,2BAA4B,CAC5B,4BAA6B,CAIrC,2BACI,YAAa,CACb,WAAY,CACZ,cAAe,CACf,QAAS,CACT,UAAW,CACX,iBAAkB,CAClB,SAAU,CAIV,yCADJ,0CAGY,WAAY,CACZ,qBAAuB,CAC1B,CAIT,iCACI,QAAS,CACT,WAAY,CACZ,SAAU,CACV,UAAW,CACX,eAAgB,CAChB,cAAe,CACf,WAAY,CACZ,UAAW,CARf,kDAWQ,WAAY,CACZ,UAAW,CACX,aAAc,CACd,eAAgB,CAChB,iB1BnBgB,C0BuBxB,sCACI,WAAY,CACZ,WAAY,CAFhB,wMAKQ,YAAa,CAIrB,0BACI,+BAAkD,CAClD,yBAA0D,CAG9D,iCACI,eAAgB,CAGpB,0BAKI,qBAAyB,CAL7B,oHAEQ,czBnF0B,CyBiFlC,kDAQQ,QAAc,CACd,eAAgB,CAChB,SAAU,CACV,eAAgB,CAXxB,0CAeQ,YAAa,CAIrB,kCACI,WAAY,CACZ,qBAAyB,CACzB,czBvG8B,CyB0GlC,yCACI,sEAEQ,KAAM,CACN,QAAS,CACT,MAAO,CACP,OAAQ,CACR,eAAgB,CAChB,cAAe,CACf,WAAY,CACZ,UAAW,CATnB,uFAYY,eAAgB,CAK5B,sBACI,YAAa,CAChB,CAGL,0BACI,cAAe,CACf,KAAM,CACN,MAAO,CACP,UAAW,CACX,WAAY,CACZ,SAAU,CACV,wBAAoC,CACpC,WAAY,CAGhB,eACI,sBAAyB,CAD7B,wBAIQ,SAAU,CAJlB,gCAQQ,SAAU,CACV,qBAAuB,CAT/B,sLAkBQ,SAAU,CAIlB,eACI,0BAA4B,CAC5B,kBAAmB,CACnB,aAAc,CAGlB,wBACI,YAAa,CACb,eAAgB,CAChB,iBAAkB,CAClB,SAAU,CACV,qB3B3Ka,C2B4Kb,0B1BtIoB,C0BuIpB,2B1BvIoB,C0B0IxB,kEACI,iBAAkB,CAClB,MAAO,CACP,OAAQ,CAGZ,oCACI,qCAAuC,CACvC,0B1BlJoB,C0BmJpB,2B1BnJoB,C0BoJpB,iCAA0C,CAC1C,+BAAiC,CACjC,KAAM,CACN,QAAS,CAGb,8BACI,QAAS,CACT,WAAY,CACZ,eAAgB,CAChB,eAAgB,CAChB,gBAAiB,CACjB,eAAgB,CAChB,oCAAwC,CACxC,yB3B1MqB,C2BkMzB,oCAWQ,UAAW,CACX,gBAAiB,CACjB,iBAAkB,CAClB,QAAS,CACT,OAAQ,CACR,UAAW,CACX,WAAa,CACb,6EAAiF,CAIzF,yCAEQ,cAAe,CACf,0BAA6B,CAC7B,eAAmB,CACnB,iBAAkB,CAClB,oBAAqB,CACrB,aAAc,CACd,mBAAoB,CACpB,qBAAsB,CACtB,gBAAiB,CACjB,kBAAmB,CACnB,aAAc,CAGd,kCAAmC,CAEnC,iCAAkC,CAGlC,iCAAkC,CAGlC,4BAA6B,CAE7B,cAAe,CACf,qB3B1O8B,C2B8OtC,6CACI,mBAAoB,CAGxB,kCACI,UAAY,CACZ,aAAc,CAFlB,0CAKQ,cAAe,CAIvB,qBACI,cAAe,CAGnB,wBACI,QAAS,CACT,aAAc,CACd,UAAc,CACd,qB3BtQqB,C2BuQrB,6B1BpOoB,C0BqOpB,8B1BrOoB,C0B+NxB,gCASQ,UAAc,CAItB,2BACI,QAAS,CAGb,qCACI,cAAe,CACf,eAAgB,CAGpB,2BACI,qB3B7RmB,C2B8RnB,YAAa,CACb,kBAAmB,CACnB,iBAAkB,CAClB,iBAAkB,CAGtB,oCACI,cAAe,CACf,WAAY,CAGhB,mCACI,iBAAkB,CAClB,OAAQ,CACR,UAAW,CAEX,oBAAsB,CACtB,qBAAuB,CACvB,YAAa,CAPjB,2CAUQ,gBAAiB,CACjB,cAAe,CAIvB,qBACI,iBAAkB,CAClB,UAAW,CACX,aAAqC,CAHzC,oDAMQ,aAAqC,CAN7C,6BAUQ,eAAgB,CAChB,WAAY,CACZ,UAAW,CAInB,uBACI,iBAAkB,CAClB,YAAa,CACb,qB3B5UqB,C2B6UrB,kBAAmB,CACnB,qB3BvUmC,C2BwUnC,iBAAkB,CAClB,cAAe,CACf,6BAA8B,CAC9B,mBAAqB,CATzB,2DAYQ,qBAAuB,CAZ/B,0FAgBQ,oB3BjWW,C2BkWX,wBAA+C,CAC/C,a3BnWW,C2BiVnB,2NAqBY,a3BtWO,C2B2WnB,oBACI,WvB/V4B,CuBqWhC,uBACI,qBAAyB,CAD7B,oCAIQ,kBAAmB,CACnB,iB3B7WS,C2BiXjB,iFAGY,cAAe,CACf,QAAgD,CAChD,MAAO,CACP,OAAQ,CACR,SAAU,CAEV,yCATZ,iFAUgB,SAAmC,CAInC,cAJmC,CAsB1C,CAfG,yCAjBZ,iFAkBgB,cAAe,CActB,CAhCT,+FAsBgB,aAAc,CAEd,yCAxBhB,+FAyBoB,YAAa,CAMpB,CAHG,yCA5BhB,+FA6BoB,aAAc,CAErB,CA/Bb,8DAmCY,gBAAiB,CAK7B,8CAGQ,qB3B7Ze,C2B8Zf,qB3B7ZS,C2B8ZT,kBAAmB,CAI3B,gCACI,iBAAkB,CAClB,kBAAmB,CAMvB,iCACI,eAAgB,CAGpB,iCACI,cAAe,CACf,qBAAuB,CAIvB,yCADJ,kBAEQ,WAAY,CACZ,cAAe,CAOtB,CAJG,yCANJ,kBAOQ,WAAY,CACZ,cAAe,CAEtB,CAED,gBACI,UAAW,CACX,aAAc,CACd,eAAgB,CAChB,kBAAmB,CAJvB,+BAOQ,UAAW,CAPnB,wCAWQ,YAAa,CACb,WAAY,CAIpB,yBACI,eAAgB,CAGpB,yCACI,6EAEQ,qBAA6C,CAChD,CAIT,kBACI,qBAAyB,CACzB,aAAc,CC7elB,kBACI,oBAAsB,CAD1B,0BAIQ,gBAAiB,CAIzB,oBACI,iBAAkB,CAClB,iBAAkB,CAClB,wB5BLe,C4BMf,UAAW,CACX,QAAS,CACT,WAAY,CACZ,gBAAiB,CACjB,cAAe,CACf,eAAgB,CAChB,gBAAiB,CAVrB,2BAaQ,UAAW,CACX,iBAAkB,CAClB,UAAW,CACX,OAAQ,CACR,yCAA6C,CAC7C,gCAAiC,CACjC,mCAAoC,CAI5C,mBACI,aAAc,CAGlB,sBACI,WAAY,CAGhB,6BACI,eAAgB,CAGpB,kPAIQ,qB5B1B+B,C4B2B/B,c1BlC0B,C0B6BlC,0QAQY,c1BrCsB,C0BsCtB,WAAY,CACZ,UAAW,CACX,gBAAiB,CACjB,gB1BzCsB,C2BdlC,cACI,iB5BqDoB,C4BpDpB,SAAU,CACV,c3BW8B,C2BV9B,eAAgB,CAEhB,0BANJ,cAOQ,yBAA2B,CAOlC,CAdD,iBAWQ,QAAS,CACT,eAAgB,CAIxB,4BACI,Y5BiCyE,C4B9B7E,2BACI,WAAY,CAEZ,yBAHJ,2BAIQ,WAAY,CAEnB,CAED,oBACI,qB7BNkC,C6BOlC,iBAAkB,CAGtB,qBACI,iBAAkB,CAClB,UAAW,CACX,QAAS,CACT,YAAa,CAJjB,4BAOQ,UAAW,CACX,iBAAkB,CAClB,4BAA6B,CAC7B,iCAAkC,CAClC,kCAAmC,CAI3C,+DACI,UAAW,CAGf,+CACI,WAAY,CrB+1DhB,+BqB11DI,UAAW,CACX,SAAU,CrB61Dd,mFqB11DI,SAAS,CACT,UAAU,CrB61Dd,yDqB11DM,UAAW,CACX,UAAU,CAIhB,oBACI,gBAAiB,CAGrB,2BACI,eAAgB,CAGpB,0BACI,eAAgB,CAChB,qB7B5DmC,C6B+DvC,uDAIQ,SAAU,CAJlB,6CAOY,gBAAiB,CACjB,WAAY,CACZ,UAAW,CAKvB,2CAEQ,wBAAyB,CACzB,UAAW,CAInB,qBACI,iBAAkB,CAClB,gBAAiB,CAFrB,6BAKQ,a7BnFU,C6BuFlB,8BACI,iBAAkB,CAClB,KAAM,CACN,UAAW,CACX,mCAA0C,CAG9C,uBACI,wB7B7GsB,C6BgH1B,uBACI,YAAa,CC9HjB,aACI,yBAA0B,CAC1B,iBAAkB,CAClB,c5BW8B,C4BdlC,gBAMQ,iBAAkB,CAClB,4BAA6B,CAPrC,mBAWQ,UAAW,CACX,iBAAkB,CAClB,OAAQ,CACR,QAAS,CACT,UAAW,CACX,UAAW,CACX,KAAQ,CACR,WAAY,CACZ,iCAAkC,CAClC,oCAAqC,CACrC,+B9BHgB,C8BOxB,wCACI,yB9BXmB,C8BcvB,qBACI,YAAa,CACb,eAAgB,CAGpB,qBACI,iBAAkB,CAElB,2BAA4B,CAC5B,iBAAkB,CAClB,eAAgB,CAChB,iBAAkB,CAClB,UAAc,CACd,wB9BxBoB,C8B2BxB,qBACI,wB9BxCe,C8ByCf,WAAY,CACZ,iBAAkB,CAClB,KAAM,CACN,UAAW,CAGf,mBACI,iBAAkB,CAClB,UAAc,CAGlB,mBACI,YAAa,CACb,qBAAyB,CACzB,6B7BPoB,C6BQpB,8B7BRoB,C6BSpB,aAAc,CAGlB,qBACI,qBAAyB,CACzB,a9B1Da,C8B2Db,oBAAqB,CACrB,eAAgB,CAChB,c5BzD8B,C4BoDlC,4DAQQ,iBAAkB,CAR1B,4EAYQ,SAAS,CAZjB,4EAgBQ,SAAS,CAIjB,oBACI,eAAgB,CAGpB,mBACI,iBAAkB,CAClB,UAAY,CACZ,wBAAyB,CACzB,c5BhF8B,C4BmFlC,kCAEQ,gBAAiB,CAKzB,mBACI,kBAAmB,CACnB,gBAAiB,CACjB,gBAAiB,CAEjB,yCALJ,mBAMQ,0BAA2B,CAclC,CApBD,yBAUQ,+B9BxGS,C8B8FjB,wCAcQ,qBAAyB,CAdjC,wCAkBQ,wB9BhHS,C+BVjB,yCAEQ,eAAgB,CAFxB,wCAMQ,YAAa,CANrB,uEAcY,qBAAyB,CAdrC,6EAkBY,qB/BEiB,C+BG7B,gCACI,cAAe,CACf,WAAY,CAGhB,uBACI,eAAgB,CAGpB,4BACI,iBAAkB,CAClB,kBAAmB,CAGvB,4BACI,WAAY,CAGhB,mCAEQ,sBAAwB,CACxB,c7B9B0B,C6B+B1B,UAAW,CACX,iBAAkB,CAI1B,oCACI,wBAA0B,CAC1B,UAAW,CACX,aAAc,CACd,SAAU,CACV,gBAAiB,CAGrB,wCAEQ,YAAa,CAIrB,sCACI,iBAAkB,CAClB,QAAS,CACT,eAAgB,CAGpB,oCACI,YAAa,CAGjB,6CACI,cAAe,CC3EnB,WACI,iBAAkB,CAGtB,oBACI,cAAe,CACf,aAAc,CAGlB,oBACI,eAAgB,CAChB,c9BG8B,C8BF9B,iBAAkB,CAElB,yCALJ,oBAMQ,aAAc,CAErB,CAED,uCACI,gBAAiB,CACjB,QAAS,CACT,c9BR8B,C8BWlC,mBACI,iBAAkB,CAClB,UAAW,CACX,QAAS,CAGb,yCAGY,mBAAqB,CACrB,cAAe,CACf,WAAY,CALxB,8FAUgB,8BhCnCG,CgC0CnB,2CAEQ,aAAc,CAFtB,0CAMQ,cAAe,CAIvB,0BACI,iBAAkB,CAClB,eAAgB,CAEhB,qBhCzCmC,CgC4CvC,0BACI,wBAAyB,CAG7B,mBACI,iBAAkB,CAClB,eAAgB,CAGpB,4BACI,cAAe,CAGnB,uBACI,iBAAkB,CAClB,YAAa,CACb,kBAAmB,CAEnB,yCALJ,uBAMQ,eAAgB,CAEvB,CAGG,yCADJ,2BAEQ,UAAW,CAElB,CAED,gCACI,gBAAiB,CACjB,UAAW,CAGf,+BACI,iBAAkB,CAClB,KAAM,CACN,OAAQ,CACR,WAAY,CACZ,8CAAsD,CACtD,eAAgB,CAChB,WAAY,CACZ,QAAS,CARb,uCAeQ,YAAa,CAIrB,qBACI,YAAa,CACb,WAAY,CAFhB,oCAKQ,wBhC9GkB,CgCkH1B,8BACI,iBAAoB,CACpB,4BhClHmB,CgCqHvB,8BACI,ahCnHoB,CgCoHpB,eAAgB,CCnIpB,gCACI,eAAgB,CAGpB,oBACI,qBjCMqB,CiCJrB,6BjCOkB,CiCJtB,kBACI,qBAAsB,CAEtB,0CAHJ,kBAIQ,mBAAoB,CAE3B,CAED,iBACI,kBAAmB,CACnB,e7BJ4B,C6BM5B,yCAJJ,iBAKQ,kBAAmB,CAY1B,CATG,0CARJ,iBASQ,kBAAmB,CAQ1B,CAjBD,4BAcQ,iBAAkB,CAClB,UAAW,CAInB,2BACI,YAAa,CAGjB,yEACI,gBAAiB,CAGrB,uFACI,cAAe,CAInB,gFACI,kBAAmB,CACnB,eAAgB,CAChB,eAAgB,CAChB,gBAAiB,CACjB,kBAAmB,CAGvB,uBACI,UjC7CkB,CiCgDtB,8BACI,eAAgB,CAChB,eAAgB,CAGpB,0BACI,eAAgB,CAChB,oBAAqB,CAGzB,6BACI,cAAe,CACf,qBAAuB,CACvB,eAAgB,CAGpB,gBACI,qBAAyB,CACzB,eAAgB,CAChB,gBAAiB,CACjB,iBAAkB,CAClB,WAAY,CACZ,mBAAqB,CACrB,oBAAsB,CAG1B,yBACI,kBAAmB,CAGvB,mDACI,QAAS,CACT,SAAU,CAFd,qEAKQ,eAAgB,CAIxB,2FACI,WAAY,CACZ,QAAc,CAGlB,yBACI,aAAc,CADlB,kDAKY,aAAc,CAL1B,iDASY,cAAe,CAK3B,4BACI,SAAU,CACV,cAAe,CAGnB,qCACI,YAAa,CACb,kBAAmB,CAGvB,qBACI,SAAU,CAGd,+BACI,wBjC7HsB,CiCgI1B,6BACI,4BAA6B,CAC7B,SAAU,CAFd,iDAKQ,kBAAmB,CAI3B,2BACI,SAAU,CACV,iBAAkB,CAClB,eAAgB,CAEhB,yCALJ,2BAMQ,gBAAiB,CAExB,CAED,2GACI,qBAAyB,CACzB,WAAY,CACZ,cAAqC,CACrC,QAAc,CACd,gBAAiB,CACjB,eAAgB,CAChB,WAAY,CAGhB,gDACI,iBAAkB,CAClB,SAAU,CACV,eAAgB,CAGpB,uFACI,YAAa,CAGjB,8FACI,cAAqC,CACrC,SAAU,CACV,QAAS,CAGb,4BACI,cAAe,CADnB,8BAIQ,yBAA2B,CAC3B,+BAA+B,CAIvC,sBACI,WAAY,CAGhB,uCACI,aAAc,CACd,QAAS,CAFb,yDAKQ,YAAa,CAIrB,+BACI,eAAgB,CADpB,0CAIQ,YAAa,CACb,eAAgB,CCjNxB,0BACI,QAAc,CCLlB,oBACI,SAAU,CACV,QAAS,CAFb,yCAMY,4BnCSW,CmCfvB,wCAWQ,SAAU,CACV,QAAS,CAZjB,wCAgBQ,YAAa,CACb,QAAS,CAIjB,8BACI,mBAAoB,CAEpB,yCAHJ,8BAIQ,kBAAmB,CACnB,gBAAiB,CAExB,CAED,uCACI,aAAc,CAGlB,iCACI,wBnCjCwC,CmCoC5C,0CACI,WAAY,CACZ,qBAAyB,CAG7B,mCACI,eAAgB,CAChB,eAAgB,CAChB,yBnChCqB,CoCdzB,cACI,eAAgB,CAChB,eAAgB,CAGpB,gBACI,YAAa,CACb,aAAc,CAGd,iBAAqB,CAArB,kBAAqB,CAArB,kBAAqB,CCPzB,eACI,qBrCUqB,CqCTrB,kBAAmB,CACnB,WAAY,CACZ,6BrCUkB,CqCPtB,eACI,mBAAoB,CAIpB,yCADJ,2BAEQ,YAAa,CAEpB,CAED,sBACI,YAAa,CACb,eAAgB,CAChB,arCtBoB,CqCyBxB,uBACI,SAAU,CAGd,6CAEQ,arCzBa,CqC6BrB,wBACI,gBAAiB,CAEjB,iBAAkB,CAClB,qBrCzBmB,CqC0BnB,gCAAkC,CAClC,6BpCYoB,CoCXpB,8BpCWoB,CoCTpB,mBAAe,CAAf,cAAe,CAEf,yCAXJ,wBAYQ,6BAA0B,CAA1B,wBAA0B,CAOjC,CAnBD,4BAgBQ,cAAe,CACf,WAAY,CAIpB,8BACI,wBrCzDoB,CqC4DxB,oBACI,oBAAqB,CACrB,WAAY,CACZ,eAAgB,CAChB,8BAAmB,CAAnB,kBAAmB,CAOvB,8BACI,qBAAyB,CACzB,qBAAuB,CACvB,qBrC3Da,CqCwDjB,iDAMQ,UAAW,CAInB,oCACI,gBAAiB,CAGrB,gBACI,uBAAyB,CAGzB,oBAAqB,CACrB,UAAc,CACd,eAAgB,CANpB,wBASQ,SAAU,CACV,sBAAuB,CAV/B,wBAcQ,UAAc,CCpGtB,wBAEI,qBtCYqB,CsCRzB,yBACE,cAAe,CACf,cAAe,CAFjB,2BAKI,eAAgB,CAChB,kBAAmB,CACnB,eAAgB,CAChB,sBAAuB,CACvB,oBAAqB,CCfzB,gCAEQ,SAAU,CACV,QAAS,CAHjB,yCAOQ,SAAU,CAPlB,yCAWQ,aAAc,CAXtB,uCAeQ,YAAa,CAIrB,kEACI,SAAU,CAGd,mCACI,kBAAmB,CAOvB,uCACI,gBAAiB,CAGrB,2CACI,eAAgB,CAChB,cAAe,CACf,yBvCvBmB,CwCfvB,6BAEQ,YAAa,CAGjB,yCALJ,kBAMQ,yBxCSe,CwCPtB,CAED,4BACI,iBAAkB,CAClB,WAAY,CAFhB,gDAKQ,qBAAyB,CAIjC,4BACI,iBAAkB,CAGtB,6BACE,WAAW,ChCk/Eb,uCgC9+EE,UAAU,CC5BZ,UACE,aAAc","file":"monitor.css","sourcesContent":["// Config\n$avatar-icon-padding: 6px !default;\n$avatar-icon-padding-lg: 8px !default;\n\nbody {\n background: color('body-bg');\n\n &.vle {\n overflow: hidden;\n }\n}\n\na {\n &:hover, &:focus { // TODO: remove when bootstrap css dependency is removed\n color: color('primary');\n }\n}\n\nblockquote {\n background-color: lighten(color('primary'), 56%);\n padding: 8px;\n margin: 16px 0;\n border-style: solid;\n border-color: color('primary');\n border-width: 0 0 0 3px;\n}\n\n.has-indicator {\n &:after {\n content: '';\n position: absolute;\n border-radius: 50%;\n padding: 5px;\n background-color: color('accent');\n }\n}\n\n.has-indicator--icon-button {\n &:after {\n top: 25px;\n left: 5px;\n }\n}\n\n// Badges\n.badge {\n border-radius: $card-border-radius;\n padding: 2px 6px;\n font-size: rem(1.2);\n font-weight: 500;\n font-style: normal;\n background-color: color('gray-dark');\n\n &.md-button {\n min-width: 0;\n min-height: 0;\n line-height: inherit;\n\n &:hover, &:focus {\n background-color: color('gray-dark');\n }\n\n &:focus {\n outline: 1px dotted color('gray-dark');\n }\n }\n}\n\n.badge--info {\n background-color: color('info');\n color: #ffffff;\n}\n\n.badge--warn {\n background-color: color('warn');\n color: #ffffff;\n}\n\n.badge--success {\n background-color: color('success');\n color: #ffffff;\n}\n\n// Dividers\n.divider--withmargin {\n margin: 16px 0;\n}\n\n.divider--dashed {\n border-top-style: dashed;\n}\n\n// Links\na {\n color: color('primary');\n cursor: pointer;\n}\n\n.active {\n background-color: rgba(158,158,158,0.2);\n color: color('text');\n}\n\n// Images & Icons\n.avatar {\n border-radius: 50%;\n box-sizing: content-box;\n}\n\n.avatar--square {\n border-radius: $card-border-radius;\n}\n\n// Rules for sizing avatars (matches material icons plus avatar-icon padding)\n.avatar {\n &.md-18 {\n height: 18px + $avatar-icon-padding*2;\n width: 18px + $avatar-icon-padding*2;\n }\n &.md-24 {\n height: 24px + $avatar-icon-padding*2;\n width: 24px + $avatar-icon-padding*2;\n }\n &.md-36 {\n height: 36px + $avatar-icon-padding*2;\n width: 36px + $avatar-icon-padding*2;\n }\n &.md-48 {\n height: 48px + $avatar-icon-padding*2;\n width: 48px + $avatar-icon-padding*2;\n }\n}\n\n// Rules for sizing avatar backgrounds (when using a child md-icon)\n.avatar--icon {\n background-color: color('gray-light');\n white-space: normal !important;\n\n &:not(.md-avatar) {\n padding: $avatar-icon-padding;\n }\n\n &.md-18 {\n height: 18px;\n width: 18px;\n }\n &.md-24 {\n height: 24px;\n width: 24px;\n }\n &.md-36 {\n height: 36px;\n width: 36px;\n }\n &.md-48 {\n height: 48px;\n width: 48px;\n }\n}\n\nmd-toolbar.md-light-theme:not(.md-menu-toolbar) {\n md-icon {\n color: color('text-secondary');\n }\n\n .md-button:disabled {\n md-icon {\n color: color('text-disabled');\n }\n }\n}\n\n// hacks for now\nmd-icon, .md-button:not([disabled]) {\n &.primary {\n color: color('primary') !important;\n }\n &.success {\n color: color('success') !important;\n }\n &.warn {\n color: color('warn') !important;\n }\n &.info {\n color: color('info') !important;\n }\n &.accent {\n color: color('accent') !important;\n }\n &.accent-1 {\n color: color('accent-1') !important;\n }\n &.accent-2 {\n color: color('accent-2') !important;\n }\n}\n\n// Theme overrides\nmd-input-container.md-wise-theme label {\n color: color('text');\n}\n\nmd-select-menu.md-default-theme md-option[selected], md-select-menu md-option[selected] {\n background-color: color('selected-bg');\n}\n\n.md-autocomplete-suggestions-container.md-default-theme li .highlight,\n.md-autocomplete-suggestions-container li .highlight {\n color: color('primary');\n background-color: color('selected-bg');\n}\n\n// Color\n// Set classes for each named color in the $colors map\n@each $key, $value in $colors {\n .#{$key} {\n color: $value;\n }\n}\n\n// Set classes for each named color in the $colors map\n@each $key, $value in $colors {\n .#{$key}-bg {\n background-color: $value;\n }\n}\n\n// Set theme colors for angular-ui elements\n@each $key, $value in $colors {\n md-progress-circular.#{$key} {\n path {\n stroke: $value;\n }\n }\n}\n","// Classroom Monitor Colors\n$_primary-color: #1565c0; // should match the default color of your ng-material default theme's 'primary' palette\n$_selected-bg: lighten($_primary-color, 54%);\n\n$color: (\n 'primary': $_primary-color,\n 'accent': #F05843, // should match the default color of your ng-material default theme's 'accent' palette\n 'accent-1': #795C3A,\n 'accent-2': #CAD266,\n 'warn': #c62828, // should match the default color of your ng-material default theme's 'warn' palette\n 'info': #ef6c00,\n 'success': #00C853,\n 'divider': rgba(0, 0, 0, 0.12),\n 'gray-lightest': #f7f7f7,\n 'gray-lighter': #eeeeee,\n 'gray-light': #dddddd,\n 'gray': #cccccc,\n 'gray-dark': #aaaaaa,\n 'gray-darker': #757575,\n 'gray-darkest': #333333,\n 'text': rgba(0, 0, 0, 0.87),\n 'text-secondary': rgba(0, 0, 0, 0.54),\n 'text-disabled': rgba(0, 0, 0, 0.26),\n 'text-light': rgba(255, 255, 255, 1),\n 'text-light-secondary': rgba(255, 255, 255, 0.70),\n 'text-light-disabled': rgba(255, 255, 255, 0.50),\n 'selected-bg': $_selected-bg,\n 'score': #FFC107\n);\n\n$body-color: (\n 'body': map-get($color, 'text'),\n 'body-bg': map-get($color, 'gray-lighter')\n);\n\n$colors: map-merge($color, $body-color);\n","// Colors\n$_primary-color: #1C8CA8; // should match the default color of your ng-material default theme's 'primary' palette\n$_selected-bg: lighten($_primary-color, 59%);\n\n$color: (\n 'primary': $_primary-color,\n 'accent': #F05843, // should match the default color of your ng-material default theme's 'accent' palette\n 'accent-1': #795C3A,\n 'accent-2': #CAD266,\n 'warn': #c62828, // should match the default color of your ng-material default theme's 'warn' palette\n 'info': #ef6c00,\n 'success': #00C853,\n 'divider': rgba(0, 0, 0, 0.12),\n 'gray-lightest': #f7f7f7,\n 'gray-lighter': #eeeeee,\n 'gray-light': #dddddd,\n 'gray': #cccccc,\n 'gray-dark': #aaaaaa,\n 'gray-darker': #757575,\n 'gray-darkest': #333333,\n 'text': rgba(0, 0, 0, 0.87),\n 'text-secondary': rgba(0, 0, 0, 0.54),\n 'text-disabled': rgba(0, 0, 0, 0.26),\n 'text-light': rgba(255, 255, 255, 1),\n 'text-light-secondary': rgba(255, 255, 255, 0.70),\n 'text-light-disabled': rgba(255, 255, 255, 0.50),\n 'selected-bg': $_selected-bg,\n 'score': #FFC107\n);\n\n$body-color: (\n 'body': map-get($color, 'text'),\n 'body-bg': map-get($color, 'gray-lighter')\n);\n\n$colors: (map-merge($color, $body-color));\n\n// Typography\n$baseline-grid: 8px;\n$body-font-size-base: rem(1.500);\n$caption-font-size-base: rem(1.300);\n\n// Layout\n$wise-toolbar-height: 42px;\n\n// Menus\n$menu-border-radius: 2px;\n$max-visible-items: 6;\n$menu-item-height: 6 * $baseline-grid !default;\n$dense-menu-item-height: 4 * $baseline-grid !default;\n$max-menu-height: 2 * $baseline-grid + $max-visible-items * $menu-item-height !default;\n$max-dense-menu-height: 2 * $baseline-grid + $max-visible-items * $dense-menu-item-height !default;\n\n// Cards\n$card-border-radius: 4px;\n\n// Buttons\n$button-border-radius: 3px;\n","// Helper functions and mixins\n\n// Get colors from $colors map\n@function color($key) {\n @if map-has-key($colors, $key) {\n @return map-get($colors, $key);\n }\n @warn \"Unknown `#{$key}` in $colors.\";\n @return null;\n}\n\n// set size in pixels given rem\n@function rem($multiplier) {\n $font-size: 10px;\n @return $multiplier * $font-size;\n}\n","// 1. Config\n\n// 2. Base\n.l-constrained {\n margin-left: auto;\n margin-right: auto;\n max-width: 100%;\n position: relative;\n\n @media (min-width: $layout-breakpoint-xs) {\n width: $layout-breakpoint-md;\n }\n}\n\n.l-constrained-md {\n width: $layout-breakpoint-sm;\n max-width: 100%;\n}\n","// Helpers\n@function rem($multiplier) {\n $font-size: 10px;\n @return $multiplier * $font-size;\n}\n\n// Angular Material variables for use and !default overrides\n$md-toolbar-height: 52px;\n$md-toolbar-medium-tall-height: 74px;\n$md-toolbar-tall-height: 104px;\n$md-toolbar-height-mobile-portrait: 52px;\n$md-toolbar-height-mobile-landscape: 52px;\n\n$caption-font-size-base: rem(1.300);\n\n//$list-item-height: 56px;\n\n$card-border-radius: 4px;\n\n$layout-breakpoint-xs: 600px;\n$layout-breakpoint-sm: 960px;\n$layout-breakpoint-md: 1280px;\n$layout-breakpoint-lg: 1920px;\n\n$button-border-radius: 3px;\n\n$tooltip-fontsize-lg: rem(1.1);\n$tooltip-fontsize-sm: rem(1.1);\n//$tooltip-height-lg: rem(2.2);\n$tooltip-height-sm: rem(2.2);\n//$tooltip-top-margin-lg: rem(1.4);\n$tooltip-top-margin-sm: rem(1.4);\n//$tooltip-lr-padding-lg: rem(0.8);\n$tooltip-lr-padding-sm: rem(0.8);\n//$tooltip-max-width: rem(3.20);\n\n$progress-linear-bar-height: 7px;\n","// 1. Config\n\n// 2. Base\n.l-footer {\n position: fixed;\n bottom: 0;\n left: 0;\n right: 0;\n z-index: 1;\n background-color: #ffffff;\n border-top: 1px solid color('gray-lighter');\n}\n\n// Buttons\n.button--footer {\n margin: 0;\n padding-top: 0;\n padding-bottom: 0;\n min-width: 0;\n display: flex;\n}\n\n.button--footer__element {\n padding-left: 8px;\n}\n","// 1. Config\n\n// 2. Base\n.l-header {\n z-index: 3;\n\n .logo {\n margin-left: 0 !important;\n height: 36px;\n width: 36px;\n vertical-align: middle;\n }\n\n .logo-link {\n min-width: auto;\n display: none;\n padding: 0 4px;\n margin-right: 12px;\n\n @media only screen and (min-width: ($layout-breakpoint-xs)) {\n display: block;\n }\n\n &:hover, &:focus {\n border: 0 none;\n }\n }\n\n // Handle mobile portrait\n @media only screen and (max-width: ($layout-breakpoint-xs - 1)) {\n .md-toolbar-tools {\n h1, h2, h3 {\n font-size: $body-font-size-base;\n }\n }\n }\n}\n","// 1. Config\n\n// 2. Base\n.l-main {\n background-color: color('body-bg');\n}\n\n.l-main--with-toolbar {\n margin-top: $wise-toolbar-height;\n}\n\n#content {\n transition: margin-top 0.5s;\n}\n\n.view-content {\n margin: 0 auto;\n padding: 8px;\n position: absolute;\n left: 0;\n right: 0;\n transition: opacity 500ms;\n\n @media only screen and (min-width: $layout-breakpoint-sm) {\n padding: 16px;\n }\n\n &.ng-enter {\n opacity: 0;\n }\n\n .ng-enter-active {\n opacity: 1;\n transition-delay: 250ms;\n }\n\n &.ng-leave-active,\n &.ng-hide {\n opacity: 0;\n }\n\n &.ng-hide-add,\n &.ng-hide-add-active,\n &.ng-hide-remove,\n &.ng-hide-remove-active {\n opacity: 0;\n }\n}\n\n.view-content--with-sidemenu {\n padding: 8px;\n\n @media only screen and (min-width: $layout-breakpoint-xs) {\n margin-left: 54px;\n padding: 16px;\n }\n}\n[dir='rtl'] .view-content--with-sidemenu {\n @media only screen and (min-width: $layout-breakpoint-xs) {\n margin-left: auto;\n margin-right: 54px;\n }\n}\n\n.content-head {\n margin: 8px 0;\n\n h1,\n h2,\n h3 {\n font-weight: 300;\n margin-top: 0;\n margin-bottom: 0;\n font-size: rem(3.6);\n\n @media only screen and (max-width: $layout-breakpoint-sm - 1) {\n font-size: rem(3.2);\n text-align: center;\n }\n }\n}\n\n.content-head__more {\n @media only screen and (max-width: $layout-breakpoint-sm - 1) {\n margin-top: 8px;\n }\n}\n\n.content-head__item,\nh2.content-head__item {\n margin: 0 8px;\n\n .md-subhead {\n padding-left: 4px;\n\n @media only screen and (max-width: $layout-breakpoint-sm - 1) {\n display: block;\n padding-left: 0;\n }\n }\n\n md-icon {\n vertical-align: text-bottom;\n }\n}\n\n.stepSelectMenuContainer md-select-menu,\n.stepSelectMenuContainer md-select-menu md-content {\n max-height: 500px;\n}\n","/*\n WISE Project Styles\n */\nbody {\n background: #eeeeee; }\n body.vle {\n overflow: hidden; }\n\na:hover, a:focus {\n color: #1565c0; }\n\nblockquote {\n background-color: #f5f9fe;\n padding: 8px;\n margin: 16px 0;\n border-style: solid;\n border-color: #1565c0;\n border-width: 0 0 0 3px; }\n\n.has-indicator:after {\n content: '';\n position: absolute;\n border-radius: 50%;\n padding: 5px;\n background-color: #F05843; }\n\n.has-indicator--icon-button:after {\n top: 25px;\n left: 5px; }\n\n.badge {\n border-radius: 4px;\n padding: 2px 6px;\n font-size: 12px;\n font-weight: 500;\n font-style: normal;\n background-color: #aaaaaa; }\n .badge.md-button {\n min-width: 0;\n min-height: 0;\n line-height: inherit; }\n .badge.md-button:hover, .badge.md-button:focus {\n background-color: #aaaaaa; }\n .badge.md-button:focus {\n outline: 1px dotted #aaaaaa; }\n\n.badge--info {\n background-color: #ef6c00;\n color: #ffffff; }\n\n.badge--warn {\n background-color: #c62828;\n color: #ffffff; }\n\n.badge--success {\n background-color: #00C853;\n color: #ffffff; }\n\n.divider--withmargin {\n margin: 16px 0; }\n\n.divider--dashed {\n border-top-style: dashed; }\n\na {\n color: #1565c0;\n cursor: pointer; }\n\n.active {\n background-color: rgba(158, 158, 158, 0.2);\n color: rgba(0, 0, 0, 0.87); }\n\n.avatar {\n border-radius: 50%;\n box-sizing: content-box; }\n\n.avatar--square {\n border-radius: 4px; }\n\n.avatar.md-18 {\n height: 30px;\n width: 30px; }\n\n.avatar.md-24 {\n height: 36px;\n width: 36px; }\n\n.avatar.md-36 {\n height: 48px;\n width: 48px; }\n\n.avatar.md-48 {\n height: 60px;\n width: 60px; }\n\n.avatar--icon {\n background-color: #dddddd;\n white-space: normal !important; }\n .avatar--icon:not(.md-avatar) {\n padding: 6px; }\n .avatar--icon.md-18 {\n height: 18px;\n width: 18px; }\n .avatar--icon.md-24 {\n height: 24px;\n width: 24px; }\n .avatar--icon.md-36 {\n height: 36px;\n width: 36px; }\n .avatar--icon.md-48 {\n height: 48px;\n width: 48px; }\n\nmd-toolbar.md-light-theme:not(.md-menu-toolbar) md-icon {\n color: rgba(0, 0, 0, 0.54); }\n\nmd-toolbar.md-light-theme:not(.md-menu-toolbar) .md-button:disabled md-icon {\n color: rgba(0, 0, 0, 0.26); }\n\nmd-icon.primary, .md-button:not([disabled]).primary {\n color: #1565c0 !important; }\n\nmd-icon.success, .md-button:not([disabled]).success {\n color: #00C853 !important; }\n\nmd-icon.warn, .md-button:not([disabled]).warn {\n color: #c62828 !important; }\n\nmd-icon.info, .md-button:not([disabled]).info {\n color: #ef6c00 !important; }\n\nmd-icon.accent, .md-button:not([disabled]).accent {\n color: #F05843 !important; }\n\nmd-icon.accent-1, .md-button:not([disabled]).accent-1 {\n color: #795C3A !important; }\n\nmd-icon.accent-2, .md-button:not([disabled]).accent-2 {\n color: #CAD266 !important; }\n\nmd-input-container.md-wise-theme label {\n color: rgba(0, 0, 0, 0.87); }\n\nmd-select-menu.md-default-theme md-option[selected], md-select-menu md-option[selected] {\n background-color: #ecf4fd; }\n\n.md-autocomplete-suggestions-container.md-default-theme li .highlight,\n.md-autocomplete-suggestions-container li .highlight {\n color: #1565c0;\n background-color: #ecf4fd; }\n\n.primary {\n color: #1565c0; }\n\n.accent {\n color: #F05843; }\n\n.accent-1 {\n color: #795C3A; }\n\n.accent-2 {\n color: #CAD266; }\n\n.warn {\n color: #c62828; }\n\n.info {\n color: #ef6c00; }\n\n.success {\n color: #00C853; }\n\n.divider {\n color: rgba(0, 0, 0, 0.12); }\n\n.gray-lightest {\n color: #f7f7f7; }\n\n.gray-lighter {\n color: #eeeeee; }\n\n.gray-light {\n color: #dddddd; }\n\n.gray {\n color: #cccccc; }\n\n.gray-dark {\n color: #aaaaaa; }\n\n.gray-darker {\n color: #757575; }\n\n.gray-darkest {\n color: #333333; }\n\n.text {\n color: rgba(0, 0, 0, 0.87); }\n\n.text-secondary {\n color: rgba(0, 0, 0, 0.54); }\n\n.text-disabled {\n color: rgba(0, 0, 0, 0.26); }\n\n.text-light {\n color: white; }\n\n.text-light-secondary {\n color: rgba(255, 255, 255, 0.7); }\n\n.text-light-disabled {\n color: rgba(255, 255, 255, 0.5); }\n\n.selected-bg {\n color: #ecf4fd; }\n\n.score {\n color: #FFC107; }\n\n.body {\n color: rgba(0, 0, 0, 0.87); }\n\n.body-bg {\n color: #eeeeee; }\n\n.primary-bg {\n background-color: #1565c0; }\n\n.accent-bg {\n background-color: #F05843; }\n\n.accent-1-bg {\n background-color: #795C3A; }\n\n.accent-2-bg {\n background-color: #CAD266; }\n\n.warn-bg {\n background-color: #c62828; }\n\n.info-bg {\n background-color: #ef6c00; }\n\n.success-bg {\n background-color: #00C853; }\n\n.divider-bg {\n background-color: rgba(0, 0, 0, 0.12); }\n\n.gray-lightest-bg {\n background-color: #f7f7f7; }\n\n.gray-lighter-bg {\n background-color: #eeeeee; }\n\n.gray-light-bg {\n background-color: #dddddd; }\n\n.gray-bg {\n background-color: #cccccc; }\n\n.gray-dark-bg {\n background-color: #aaaaaa; }\n\n.gray-darker-bg {\n background-color: #757575; }\n\n.gray-darkest-bg {\n background-color: #333333; }\n\n.text-bg {\n background-color: rgba(0, 0, 0, 0.87); }\n\n.text-secondary-bg {\n background-color: rgba(0, 0, 0, 0.54); }\n\n.text-disabled-bg {\n background-color: rgba(0, 0, 0, 0.26); }\n\n.text-light-bg {\n background-color: white; }\n\n.text-light-secondary-bg {\n background-color: rgba(255, 255, 255, 0.7); }\n\n.text-light-disabled-bg {\n background-color: rgba(255, 255, 255, 0.5); }\n\n.selected-bg-bg {\n background-color: #ecf4fd; }\n\n.score-bg {\n background-color: #FFC107; }\n\n.body-bg {\n background-color: rgba(0, 0, 0, 0.87); }\n\n.body-bg-bg {\n background-color: #eeeeee; }\n\nmd-progress-circular.primary path {\n stroke: #1565c0; }\n\nmd-progress-circular.accent path {\n stroke: #F05843; }\n\nmd-progress-circular.accent-1 path {\n stroke: #795C3A; }\n\nmd-progress-circular.accent-2 path {\n stroke: #CAD266; }\n\nmd-progress-circular.warn path {\n stroke: #c62828; }\n\nmd-progress-circular.info path {\n stroke: #ef6c00; }\n\nmd-progress-circular.success path {\n stroke: #00C853; }\n\nmd-progress-circular.divider path {\n stroke: rgba(0, 0, 0, 0.12); }\n\nmd-progress-circular.gray-lightest path {\n stroke: #f7f7f7; }\n\nmd-progress-circular.gray-lighter path {\n stroke: #eeeeee; }\n\nmd-progress-circular.gray-light path {\n stroke: #dddddd; }\n\nmd-progress-circular.gray path {\n stroke: #cccccc; }\n\nmd-progress-circular.gray-dark path {\n stroke: #aaaaaa; }\n\nmd-progress-circular.gray-darker path {\n stroke: #757575; }\n\nmd-progress-circular.gray-darkest path {\n stroke: #333333; }\n\nmd-progress-circular.text path {\n stroke: rgba(0, 0, 0, 0.87); }\n\nmd-progress-circular.text-secondary path {\n stroke: rgba(0, 0, 0, 0.54); }\n\nmd-progress-circular.text-disabled path {\n stroke: rgba(0, 0, 0, 0.26); }\n\nmd-progress-circular.text-light path {\n stroke: white; }\n\nmd-progress-circular.text-light-secondary path {\n stroke: rgba(255, 255, 255, 0.7); }\n\nmd-progress-circular.text-light-disabled path {\n stroke: rgba(255, 255, 255, 0.5); }\n\nmd-progress-circular.selected-bg path {\n stroke: #ecf4fd; }\n\nmd-progress-circular.score path {\n stroke: #FFC107; }\n\nmd-progress-circular.body path {\n stroke: rgba(0, 0, 0, 0.87); }\n\nmd-progress-circular.body-bg path {\n stroke: #eeeeee; }\n\n.l-constrained {\n margin-left: auto;\n margin-right: auto;\n max-width: 100%;\n position: relative; }\n @media (min-width: 600px) {\n .l-constrained {\n width: 1280px; } }\n\n.l-constrained-md {\n width: 960px;\n max-width: 100%; }\n\n.l-footer {\n position: fixed;\n bottom: 0;\n left: 0;\n right: 0;\n z-index: 1;\n background-color: #ffffff;\n border-top: 1px solid #eeeeee; }\n\n.button--footer {\n margin: 0;\n padding-top: 0;\n padding-bottom: 0;\n min-width: 0;\n display: flex; }\n\n.button--footer__element {\n padding-left: 8px; }\n\n.l-header {\n z-index: 3; }\n .l-header .logo {\n margin-left: 0 !important;\n height: 36px;\n width: 36px;\n vertical-align: middle; }\n .l-header .logo-link {\n min-width: auto;\n display: none;\n padding: 0 4px;\n margin-right: 12px; }\n @media only screen and (min-width: 600px) {\n .l-header .logo-link {\n display: block; } }\n .l-header .logo-link:hover, .l-header .logo-link:focus {\n border: 0 none; }\n @media only screen and (max-width: 599px) {\n .l-header .md-toolbar-tools h1, .l-header .md-toolbar-tools h2, .l-header .md-toolbar-tools h3 {\n font-size: 15px; } }\n\n.l-main {\n background-color: #eeeeee; }\n\n.l-main--with-toolbar {\n margin-top: 42px; }\n\n#content {\n transition: margin-top 0.5s; }\n\n.view-content {\n margin: 0 auto;\n padding: 8px;\n position: absolute;\n left: 0;\n right: 0;\n transition: opacity 500ms; }\n @media only screen and (min-width: 960px) {\n .view-content {\n padding: 16px; } }\n .view-content.ng-enter {\n opacity: 0; }\n .view-content .ng-enter-active {\n opacity: 1;\n transition-delay: 250ms; }\n .view-content.ng-leave-active, .view-content.ng-hide {\n opacity: 0; }\n .view-content.ng-hide-add, .view-content.ng-hide-add-active, .view-content.ng-hide-remove, .view-content.ng-hide-remove-active {\n opacity: 0; }\n\n.view-content--with-sidemenu {\n padding: 8px; }\n @media only screen and (min-width: 600px) {\n .view-content--with-sidemenu {\n margin-left: 54px;\n padding: 16px; } }\n\n@media only screen and (min-width: 600px) {\n [dir='rtl'] .view-content--with-sidemenu {\n margin-left: auto;\n margin-right: 54px; } }\n\n.content-head {\n margin: 8px 0; }\n .content-head h1,\n .content-head h2,\n .content-head h3 {\n font-weight: 300;\n margin-top: 0;\n margin-bottom: 0;\n font-size: 36px; }\n @media only screen and (max-width: 959px) {\n .content-head h1,\n .content-head h2,\n .content-head h3 {\n font-size: 32px;\n text-align: center; } }\n\n@media only screen and (max-width: 959px) {\n .content-head__more {\n margin-top: 8px; } }\n\n.content-head__item,\nh2.content-head__item {\n margin: 0 8px; }\n .content-head__item .md-subhead,\n h2.content-head__item .md-subhead {\n padding-left: 4px; }\n @media only screen and (max-width: 959px) {\n .content-head__item .md-subhead,\n h2.content-head__item .md-subhead {\n display: block;\n padding-left: 0; } }\n .content-head__item md-icon,\n h2.content-head__item md-icon {\n vertical-align: text-bottom; }\n\n.stepSelectMenuContainer md-select-menu,\n.stepSelectMenuContainer md-select-menu md-content {\n max-height: 500px; }\n\n.l-nav {\n background-color: #eeeeee !important; }\n\n.l-notebook {\n margin-top: 42px;\n background-color: #eeeeee !important; }\n\n.l-sidebar__header {\n background-color: #ffffff !important;\n color: #795C3A !important; }\n .l-sidebar__header md-select {\n color: rgba(0, 0, 0, 0.87); }\n\n.status-icon {\n margin: 0 4px;\n z-index: 1;\n vertical-align: bottom; }\n\n.md-button.status-icon {\n height: auto;\n width: auto;\n min-height: 0;\n line-height: inherit;\n margin: 0 4px;\n padding: 0; }\n\n.status-corner-wrapper {\n position: absolute;\n z-index: 1;\n overflow: hidden; }\n\n.status-corner {\n width: 0;\n height: 0;\n border-top-color: rgba(0, 0, 0, 0.26);\n border-bottom-color: rgba(0, 0, 0, 0.26);\n color: rgba(0, 0, 0, 0.26); }\n .status-corner:after {\n content: '!';\n color: #ffffff;\n top: 2px;\n right: 10px;\n position: absolute;\n font-weight: 700; }\n\n.status-corner--warn {\n border-top-color: #c62828 !important;\n border-bottom-color: #c62828 !important; }\n\n.status-corner-top-right {\n top: 0;\n right: 0;\n border-top-right-radius: 4px; }\n .status-corner-top-right .status-corner {\n border-top: 36px solid;\n border-left: 36px solid transparent; }\n\n.status-corner-top-left {\n top: 0;\n left: 0;\n border-top-left-radius: 4px; }\n .status-corner-top-left .status-corner {\n border-top: 36px solid;\n border-right: 36px solid transparent; }\n\n.status-corner-bottom-right {\n bottom: 0;\n right: 0;\n border-bottom-right-radius: 4px; }\n .status-corner-bottom-right .status-corner {\n border-bottom: 36px solid;\n border-left: 36px solid transparent; }\n\n.status-corner-bottom-left {\n bottom: 0;\n left: 0;\n border-bottom-left-radius: 4px; }\n .status-corner-bottom-left .status-corner {\n border-bottom: 36px solid;\n border-right: 36px solid transparent; }\n\n.avatar--icon--alert {\n background-color: #ffffff; }\n\n.avatar--icon--alert__icon {\n font-size: 48px;\n margin: -4px 0 0 -4px; }\n\n.gu-mirror {\n position: fixed !important;\n margin: 0 !important;\n z-index: 9999 !important;\n opacity: 0.8;\n transition: opacity 250ms ease-in-out; }\n\n.gu-hide {\n display: none !important; }\n\n.gu-unselectable {\n -webkit-user-select: none !important;\n -moz-user-select: none !important;\n -ms-user-select: none !important;\n user-select: none !important; }\n\n.gu-transit {\n opacity: 0.2; }\n\nmd-dialog {\n width: 600px; }\n\n.dialog--wide {\n width: 960px; }\n\n.dialog--wider {\n width: 1280px; }\n\n.help-bubble {\n border-radius: 4px;\n max-width: 320px; }\n @media (min-width: 600px) {\n .help-bubble {\n max-width: 552px; } }\n @media (min-width: 960px) {\n .help-bubble {\n max-width: 912px; } }\n @media (min-width: 1280px) {\n .help-bubble {\n max-width: 1232px; } }\n\n.help-bubble__title {\n border-top-left-radius: 4px;\n border-top-right-radius: 4px; }\n\n.help-bubble___title__content {\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n padding: 0px 0 0 12px;\n background-color: #ef6c00; }\n .help-bubble___title__content .md-icon-button {\n margin-right: 0;\n padding-top: 0;\n padding-bottom: 0; }\n\n.help-bubble__content {\n overflow: auto;\n padding: 8px 12px;\n max-height: 480px; }\n\n.help-bubble__actions {\n border-bottom-left-radius: 4px;\n border-bottom-right-radius: 4px; }\n\ndiv.hopscotch-bubble {\n border-radius: 4px;\n border: 0 none;\n font-family: Roboto, \"Helvetica Neue\", sans-serif;\n font-size: 14px;\n z-index: 6; }\n div.hopscotch-bubble .hopscotch-bubble-arrow-container {\n position: absolute;\n width: 20px;\n height: 20px; }\n div.hopscotch-bubble .hopscotch-bubble-arrow-container.up {\n top: 0;\n left: 12px; }\n div.hopscotch-bubble .hopscotch-bubble-arrow-container.up .hopscotch-bubble-arrow {\n border-bottom: 10px solid #ef6c00;\n border-left: 10px solid transparent;\n border-right: 10px solid transparent;\n position: relative;\n top: -10px; }\n div.hopscotch-bubble .hopscotch-bubble-arrow-container.up .hopscotch-bubble-arrow-border {\n border: 0 none;\n /*border-bottom: 12px solid color('gray-darker');\n border-left: 12px solid transparent;\n border-right: 12px solid transparent;*/ }\n div.hopscotch-bubble .hopscotch-bubble-arrow-container.down {\n bottom: -34px;\n left: 12px; }\n div.hopscotch-bubble .hopscotch-bubble-arrow-container.down .hopscotch-bubble-arrow {\n border-top: 10px solid #ef6c00;\n border-left: 10px solid transparent;\n border-right: 10px solid transparent;\n position: relative;\n top: -10px; }\n div.hopscotch-bubble .hopscotch-bubble-arrow-container.down .hopscotch-bubble-arrow-border {\n border: 0 none;\n /*border-top: 12px solid color('gray-darker');\n border-left: 12px solid transparent;\n border-right: 12px solid transparent;*/ }\n div.hopscotch-bubble .hopscotch-bubble-arrow-container.left {\n top: 12px;\n left: -10px; }\n div.hopscotch-bubble .hopscotch-bubble-arrow-container.left .hopscotch-bubble-arrow {\n border-right: 10px solid #ef6c00;\n border-bottom: 10px solid transparent;\n border-top: 10px solid transparent;\n position: relative;\n left: 0;\n top: -10px; }\n div.hopscotch-bubble .hopscotch-bubble-arrow-container.left .hopscotch-bubble-arrow-border {\n border: 0 none;\n /*border-right: 12px solid color('gray-darker');\n border-bottom: 12px solid transparent;\n border-top: 12px solid transparent;*/ }\n div.hopscotch-bubble .hopscotch-bubble-arrow-container.right {\n top: 12px;\n right: -30px; }\n div.hopscotch-bubble .hopscotch-bubble-arrow-container.right .hopscotch-bubble-arrow {\n border-left: 10px solid #ef6c00;\n border-bottom: 10px solid transparent;\n border-top: 10px solid transparent;\n position: relative;\n right: 0;\n top: -10px; }\n div.hopscotch-bubble .hopscotch-bubble-arrow-container.right .hopscotch-bubble-arrow-border {\n border: 0 none;\n /*border-right: 12px solid color('gray-darker');\n border-bottom: 12px solid transparent;\n border-top: 12px solid transparent;*/ }\n\n.input-container {\n padding-top: 12px; }\n\n.input-container--component {\n margin-bottom: 0; }\n\n.input-container--open-response.md-has-icon {\n padding-left: 0; }\n\n.input-container--open-response .md-errors-spacer {\n display: none; }\n\n.input-wrapper {\n position: relative; }\n\n.input-wrapper--focused .input--textarea__action md-icon {\n color: #1565c0; }\n\n.input--textarea, .input-container textarea.input--textarea {\n padding: 8px;\n background-color: #f7f7f7;\n border: 1px solid #cccccc;\n margin-bottom: 8px; }\n .input--textarea:focus, .input-container textarea.input--textarea:focus {\n background-color: #ffffff; }\n .input--textarea[disabled], .input-container textarea.input--textarea[disabled] {\n color: rgba(0, 0, 0, 0.54); }\n\n.input-container textarea.input--textarea {\n width: 100%; }\n\n.input--textarea--disabled {\n color: rgba(0, 0, 0, 0.54); }\n\n.input--textarea__action {\n position: absolute;\n right: -4px; }\n .input--textarea__action[disabled] md-icon {\n color: rgba(0, 0, 0, 0.26) !important; }\n\n.input--textarea__action--notebook {\n top: 6px; }\n .input-wrapper--richtext .input--textarea__action--notebook {\n top: -7px; }\n\n.input--textarea__action--revision {\n bottom: 6px; }\n .input-wrapper--richtext .input--textarea__action--revision {\n bottom: -5px; }\n\n.input-label, md-input-container:not(.md-input-invalid):not(.md-input-focused).md-input-has-value label.input-label {\n line-height: 1.2;\n color: rgba(0, 0, 0, 0.87); }\n .input-label.input-label--focused, md-input-container:not(.md-input-invalid):not(.md-input-focused).md-input-has-value label.input-label.input-label--focused {\n color: #1565c0; }\n\n.autocomplete input {\n text-overflow: ellipsis;\n overflow: hidden;\n word-wrap: none;\n font-weight: 500;\n color: rgba(0, 0, 0, 0.54); }\n\n@media only screen and (min-width: 600px) {\n .autocomplete--minwidth {\n min-width: 300px; } }\n\n@media only screen and (min-width: 960px) {\n .autocomplete--minwidth {\n min-width: 300px; } }\n\n.autocomplete--flat md-autocomplete-wrap {\n background-color: #ffffff; }\n .autocomplete--flat md-autocomplete-wrap:not(.md-menu-showing) {\n box-shadow: none;\n background-color: #eeeeee; }\n\n.select__header {\n height: 48px; }\n .select__header input {\n height: 100%;\n width: 100%;\n padding: 0 8px;\n outline: none;\n border: 0 none;\n font-size: 14px;\n font-weight: 500; }\n\n.table {\n max-width: 100%;\n width: auto;\n min-width: 100px;\n margin: 8px 0; }\n .table thead > tr > th,\n .table thead > tr > td,\n .table tbody > tr > th,\n .table tbody > tr > td,\n .table tfoot > tr > th,\n .table tfoot > tr > td {\n border: 1px solid #cccccc;\n padding: 6px;\n font-size: 15px;\n min-height: 32px;\n height: 32px;\n min-width: 32px;\n vertical-align: top; }\n .table td.inactive,\n .table th {\n background-color: #f7f7f7;\n opacity: 1;\n visibility: visible; }\n .table md-input-container {\n margin: 0; }\n .table .md-errors-spacer {\n display: none; }\n\n.table--student td.inactive {\n padding: 8px 10px; }\n\n.table--full-width {\n width: 100%; }\n\n.table--list {\n border: 0 none;\n border-collapse: collapse;\n background-color: #ffffff;\n max-width: 100%;\n overflow: auto; }\n .table--list th,\n .table--list td {\n padding: 0 4px;\n border: 0 none; }\n .table--list td {\n min-height: 56px;\n height: 56px; }\n .table--list tr.md-button {\n display: table-row;\n text-align: left;\n width: auto;\n text-transform: none;\n font-size: inherit;\n font-weight: normal; }\n\n.table--list__wrap {\n min-width: 600px; }\n\n@media only screen and (max-width: 959px) {\n .table-wrap-sticky {\n overflow-x: auto; } }\n\n.table--list__thead {\n font-size: 14px;\n font-weight: 700; }\n\n.table--list__thead__tr {\n height: 100%;\n margin: 0; }\n\n.table--list__thead__th {\n background-color: #757575;\n color: white;\n min-height: 42px;\n height: 42px; }\n\n.table--list__thead__link {\n color: #ffffff;\n text-transform: none;\n margin: 0;\n min-width: 0;\n white-space: normal;\n line-height: 1.4;\n width: 100%; }\n\n.table--list__thead__sort {\n margin: 0; }\n\n.table--list__thead__sort--reverse {\n transform: rotate(180deg); }\n\n.td--wrap {\n min-width: 180px;\n white-space: normal;\n line-height: 1.2; }\n\n@media only screen and (max-width: 959px) {\n .td--max-width {\n max-width: 180px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap; } }\n\n.md-toolbar-tools {\n font-size: 18px; }\n .md-toolbar-tools .autocomplete {\n height: 36px; }\n .md-toolbar-tools .autocomplete md-autocomplete-wrap {\n height: 36px; }\n .md-toolbar-tools .autocomplete input {\n height: 36px; }\n\n.md-toolbar--wise {\n min-height: 42px; }\n .md-toolbar--wise .md-toolbar-tools {\n height: 42px;\n max-height: 42px; }\n .md-toolbar--wise .md-button.md-icon-button {\n height: 42px;\n line-height: 42px;\n width: 42px; }\n\n.md-toolbar--wise--sm .md-toolbar-tools {\n padding: 0 8px;\n font-size: 15px; }\n\n.md-toolbar--sidenav {\n background-color: #333333 !important; }\n .md-toolbar--sidenav .md-toolbar-tools {\n font-size: 16px;\n font-weight: 500; }\n\n.toolbar {\n position: fixed;\n left: 0;\n right: 0;\n top: 52px;\n z-index: 3; }\n\n.toolbar__title {\n margin-left: 8px;\n font-size: 16px;\n font-weight: 500; }\n\n.toolbar__tools {\n padding-right: 8px; }\n\n[dir=rtl] .toolbar__tools {\n padding-right: 16px;\n padding-left: 8px; }\n\n.toolbar__nav, .md-button.toolbar__nav {\n margin: 0; }\n\n.toolbar__select, .md-button.toolbar__select {\n margin: 0 4px;\n min-height: 32px;\n background-color: #f7f7f7; }\n .toolbar__select .md-select-value, .md-button.toolbar__select .md-select-value {\n height: 32px;\n text-align: left; }\n\n[dir=rtl] .toolbar__select .md-select-value, [dir=rtl] .md-button.toolbar__select .md-select-value {\n text-align: right; }\n\n.toolbar__select--fixedwidth {\n width: 168px; }\n @media only screen and (min-width: 600px) {\n .toolbar__select--fixedwidth {\n width: 264px; } }\n @media only screen and (min-width: 960px) {\n .toolbar__select--fixedwidth {\n width: 432px; } }\n\n.list-item {\n background-color: #ffffff;\n border-bottom: 1px solid #eeeeee; }\n .list-item .md-subheader, .list-item.md-subheader {\n color: rgba(0, 0, 0, 0.87);\n background-color: #ffffff; }\n .list-item .md-subheader md-icon, .list-item.md-subheader md-icon {\n vertical-align: middle; }\n .list-item .md-subheader .md-subheader-inner, .list-item.md-subheader .md-subheader-inner {\n padding: 0; }\n .list-item .md-subheader .md-avatar, .list-item.md-subheader .md-avatar {\n margin-right: 8px; }\n .list-item .autocomplete {\n margin: 8px 0; }\n\n.list-item--info._md-button-wrap > div.md-button:first-child, .list-item--info .md-subheader-content {\n border-left: 4px solid #ef6c00 !important;\n margin-left: -4px; }\n\n.list-item--warn._md-button-wrap > div.md-button:first-child, .list-item--warn .md-subheader-content {\n border-left: 4px solid #c62828 !important;\n margin-left: -4px; }\n\n.list-item--expanded {\n border-bottom-width: 0; }\n\n.list-item--noclick, .list-item--noclick.md-button {\n cursor: default;\n background-color: #f7f7f7; }\n\n.list-item--actions {\n padding: 0 8px !important; }\n\n.list-item__subheader-button {\n text-transform: none;\n width: 100%;\n padding: 8px 16px;\n margin: 0;\n white-space: normal;\n text-align: left;\n line-height: 1.4; }\n\n.user-list {\n font-size: 15px; }\n\n.notice {\n text-align: center;\n padding: 8px;\n background-color: rgba(0, 0, 0, 0.04);\n width: 100%; }\n @media (min-width: 600px) {\n .notice {\n max-width: 80%;\n border-radius: 3px;\n margin: 24px auto; } }\n\n.milestone {\n min-width: 196px;\n width: 196px;\n height: 242px;\n background-color: #ffffff;\n padding: 0; }\n .milestone.md-button {\n text-transform: none; }\n\n.milestone__progress {\n background-color: #eeeeee;\n border-radius: 50%;\n position: relative;\n margin-bottom: 12px; }\n\n.milestone__progress__percent {\n position: absolute;\n top: 8px;\n bottom: 8px;\n left: 8px;\n right: 8px;\n border-radius: 50%;\n background-color: #ffffff;\n color: #1565c0;\n font-size: 28px;\n font-weight: 500; }\n\n.milestone__title {\n font-weight: 700;\n font-size: 15px;\n margin-bottom: 12px; }\n\n.milestone-details section:not(:first-child) {\n margin-top: 8px; }\n\n.milestone-details__section {\n background-color: #ffffff;\n padding: 16px; }\n .milestone-details__section > p {\n margin-top: 16px;\n margin-bottom: 0; }\n .milestone-details__section md-list {\n padding: 0; }\n .milestone-details__section .grading {\n padding: 0; }\n\n.milestone-details__header {\n padding: 12px 16px;\n margin: -16px -16px 16px;\n text-transform: uppercase;\n font-weight: 500; }\n\n.milestone-details__progress {\n width: 48px;\n margin-right: 8px; }\n\n.milestone--add.md-button {\n text-transform: uppercase; }\n\n.milestone--add__icon {\n height: 96px;\n width: 96px;\n background-color: #eeeeee;\n border-radius: 50%; }\n\n#nav {\n position: relative; }\n\n.nav {\n margin-bottom: 16px; }\n\n.nav-mask {\n position: absolute;\n top: 0;\n bottom: 0;\n left: 0;\n right: 0;\n background-color: rgba(0, 0, 0, 0.25);\n z-index: 1; }\n .nav-mask.ng-hide {\n opacity: 0; }\n\n.nav-head {\n color: rgba(0, 0, 0, 0.54);\n font-weight: 500; }\n .nav-head md-icon {\n line-height: 20px; }\n\n.nav-contents--root {\n padding: 6px 6px 12px; }\n\n.nav-contents--group {\n background-color: #dddddd;\n padding: 8px; }\n\n.nav-contents--root {\n padding: 0; }\n\n.nav-contents__list {\n padding: 0; }\n @media (min-width: 600px) {\n .nav-contents__list {\n padding: 8px; } }\n\n.nav-item {\n transition: opacity 250ms ease-in-out; }\n .nav-item.prev md-list-item {\n background-color: #ecf4fd;\n /*&._md-button-wrap>div.md-button:first-child {\n border-left: 4px solid color('primary');\n margin-left: -4px;\n }*/ }\n\n.nav-item--card__content {\n border-top-right-radius: 4px;\n border-top-left-radius: 4px; }\n .nav-item--card__content:focus {\n outline: none; }\n .nav-item--card__content:focus .nav-item__title > span {\n border-bottom: 1px dashed #cccccc; }\n\n.nav-item--root {\n transition: margin 250ms, box-shadow 500ms; }\n .nav-item--root.expanded {\n flex-basis: 100%;\n max-width: 100%;\n max-height: none !important;\n margin: 8px auto;\n padding-left: 4px; }\n .nav-item--root.expanded:first-of-type {\n margin-top: 0; }\n\n.nav-item--list__info-item {\n padding: 0 16px 0 4px;\n display: inline-block; }\n\n.nav-item--list__reorder {\n margin-left: 8px;\n color: rgba(0, 0, 0, 0.26); }\n\n.nav-item--card--group:not(.expanded) {\n box-shadow: 0px 3px 1px -2px rgba(0, 0, 0, 0.14), 0px 2px 2px 0px rgba(0, 0, 0, 0.098), 0px 1px 5px 0px rgba(0, 0, 0, 0.084), 3px 3px 0px 1px #d5d5d5, 6px 6px 0px 1px #aaaaaa; }\n\n.nav-item__collapse {\n margin: 0; }\n\n.nav-item__more {\n border-top: 1px solid #dddddd;\n border-bottom-right-radius: 4px;\n border-bottom-left-radius: 4px;\n padding: 8px 16px;\n min-height: 40px; }\n\n.nav-item__users {\n height: auto;\n cursor: pointer;\n color: #ffffff;\n margin: 0;\n padding: 1px 6px; }\n .nav-item__users > md-icon {\n padding-right: 4px;\n color: #ffffff; }\n .nav-item__users:hover.success-bg, .nav-item__users:focus.success-bg {\n background-color: #00C853; }\n\n.nav-item__title {\n padding-left: 16px;\n line-height: 1.2;\n font-weight: 400; }\n\n[dir=rtl] .nav-item__title {\n padding-left: auto;\n padding-right: 16px; }\n\n.nav-item__info {\n padding: 0 8px; }\n\n.nav-item__progress {\n width: 48px; }\n .nav-item__progress > .md-container {\n top: 0; }\n\n.nav-item__progress-value {\n margin-left: 8px;\n width: 36px; }\n\n.progress-wrapper {\n padding: 2px 0;\n cursor: pointer; }\n\n.student-select {\n padding-top: 0;\n padding-bottom: 0; }\n\n.workgroup-progress {\n margin-bottom: 8px; }\n @media (min-width: 960px) {\n .workgroup-progress {\n margin-bottom: 0; } }\n\n.menu-progress {\n position: absolute;\n top: 10px;\n right: 12px; }\n .menu-progress path {\n stroke: #CAD266 !important;\n stroke-width: 2px; }\n\n[dir=rtl] .menu-progress {\n right: auto;\n left: 12px; }\n\n.menu-sidenav__item {\n font-weight: 700;\n font-size: 14px; }\n\n.menu-sidenav__icon {\n margin-top: 12px !important;\n margin-right: 12px !important;\n margin-left: 12px; }\n\n.active .menu-sidenav__icon, .active .menu-sidenav__item {\n color: #1565c0; }\n\n.menu-sidebar {\n position: absolute;\n top: 94px;\n bottom: 0;\n left: 0;\n background-color: #fff;\n width: 56px;\n overflow: hidden;\n padding: 8px 0;\n text-align: center;\n border-right: 1px solid #cccccc; }\n @media only screen and (max-width: 599px) {\n .menu-sidebar {\n display: none; } }\n\n[dir=rtl] .menu-sidebar {\n right: 0;\n left: auto; }\n\n.md-button.md-icon-button.menu-sidebar__link {\n margin-top: 6px;\n margin-bottom: 6px; }\n\n#node {\n margin: 0 auto;\n position: absolute;\n left: 0;\n right: 0; }\n @media only screen and (min-width: 600px) {\n #node {\n padding: 8px;\n padding: 24px 16px;\n margin-bottom: 32px; } }\n @media only screen and (min-width: 960px) {\n #node {\n padding: 32px; } }\n #node.ng-enter {\n transition: opacity .5s;\n opacity: 0; }\n #node.ng-enter-active {\n opacity: 1; }\n\n@media only screen and (min-width: 600px) {\n .node-notice {\n margin-top: -8px;\n margin-bottom: 16px; } }\n\n@media only screen and (min-width: 960px) {\n .node-notice {\n margin-top: -16px; } }\n\n.node-content {\n padding: 0 0 48px;\n background-color: #ffffff;\n border-radius: 3px;\n overflow: visible; }\n @media only screen and (max-width: 599px) {\n .node-content {\n box-shadow: none; } }\n @media only screen and (min-width: 600px) {\n .node-content {\n padding: 0;\n border-top: 2px solid;\n border-bottom: 2px solid; } }\n\nmd-content.node-content {\n background-color: #ffffff; }\n\n.node-content__rubric {\n position: absolute;\n top: -22px;\n left: 0;\n right: 0;\n z-index: 1; }\n .node-content__rubric .avatar--icon {\n transform: scale(0.94); }\n @media only screen and (max-width: 599px) {\n .node-content__rubric .avatar--icon {\n transform: scale(0.8); } }\n\n.node-icon {\n color: #ffffff;\n vertical-align: inherit; }\n\n.node-select {\n margin: 0 8px;\n min-width: 0;\n font-weight: 500;\n font-size: 15px; }\n .node-select .md-select-value *:first-child {\n transform: translate3d(0, 0, 0);\n flex: 1 0 0; }\n .node-select .md-select-value .node-select__icon {\n display: none; }\n .node-select .md-select-value .node-select__status {\n display: none; }\n .node-select .md-select-icon {\n margin-left: 0;\n color: rgba(0, 0, 0, 0.87); }\n\n.node-select-option--group {\n background-color: #f7f7f7;\n border-bottom: 1px solid #eeeeee;\n border-top: 1px solid #eeeeee; }\n\n.node-select-option--node {\n padding-left: 20px; }\n\n.node-select__icon {\n margin-right: 8px; }\n\n.node-select__status {\n margin-left: 8px; }\n\n.node-select__text {\n text-overflow: ellipsis;\n white-space: nowrap;\n overflow: hidden; }\n @media only screen and (min-width: 600px) {\n .node-select__text {\n margin-top: 2px; } }\n\n.node-title {\n line-height: 1.2;\n text-transform: none;\n margin-top: 3px; }\n @media only screen and (max-width: 599px) {\n .node-title {\n font-size: 15px; } }\n\n.node-content__actions {\n padding: 0 16px 16px; }\n @media only screen and (min-width: 960px) {\n .node-content__actions {\n padding: 0 24px 24px; } }\n @media only screen and (min-width: 1280px) {\n .node-content__actions {\n padding: 0 32px 32px; } }\n .node-content__actions .md-button:first-child {\n margin-left: 0; }\n .node-content__actions .md-button:last-child {\n margin-right: 0; }\n\n.node-content__actions__info {\n font-style: italic;\n margin-left: 8px;\n color: rgba(0, 0, 0, 0.54); }\n\n.node-content__actions__more {\n border-bottom: 1px dotted; }\n\n.md-button.md-icon-button.node-nav:first-of-type {\n margin-right: 0; }\n\n@media only screen and (min-width: 600px) {\n .node-sidebar-active {\n margin-right: 68px; } }\n\n@media only screen and (max-width: 599px) {\n .node-sidebar-visible {\n margin-bottom: 42px; } }\n\n.node-sidebar {\n position: absolute;\n right: 0;\n top: 0;\n width: 52px; }\n\n.node-sidebar__toolbar {\n position: fixed;\n width: 52px;\n background-color: #ffffff;\n padding: 8px 0;\n border-radius: 3px; }\n @media only screen and (max-width: 599px) {\n .node-sidebar__toolbar {\n right: 0;\n bottom: 0;\n left: 0;\n width: 100%;\n border-radius: 0;\n padding: 0;\n min-height: 0;\n height: 42px; } }\n\n.node-info {\n margin: 0; }\n @media only screen and (max-width: 599px) {\n .node-info {\n margin: -16px;\n border-radius: 0; } }\n .node-info .divider {\n margin-left: -8px;\n margin-right: -8px; }\n .node-info .component:first-child {\n margin-top: -16px; }\n .node-info .component, .node-info .component__content, .node-info .component__header {\n margin-left: -8px;\n margin-right: -8px; }\n .node-info .component__actions {\n display: none; }\n\n.node-rubric {\n border: 2px solid #1565c0;\n margin: 8px 16px 24px;\n padding: 16px;\n background-color: #ffffff; }\n\n.node__label--vertical-alignment {\n vertical-align: middle;\n display: inline-block; }\n\n.grading__item-container {\n margin: 0 0 16px;\n padding: 0 !important; }\n\n.grading__item {\n background-color: #ffffff; }\n .grading__item .component {\n padding: 0; }\n\n@media only screen and (min-width: 600px) {\n .notebook-launcher.md-button.md-fab {\n z-index: 61; } }\n\n.notebook-report {\n border-radius: 4px 4px 0 0;\n margin: 0;\n height: 100%; }\n .notebook-report .note-toolbar {\n margin: -8px -8px 8px;\n padding: 4px;\n border: 0 none;\n border-top: 1px solid #dddddd;\n border-bottom: 1px solid #dddddd;\n border-radius: 0; }\n .notebook-report .note-toolbar .btn-group {\n margin-top: 0; }\n .notebook-report .note-btn {\n border: 0 none;\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n border-bottom-left-radius: 0;\n border-bottom-right-radius: 0; }\n\n.notebook-report-container {\n height: 550px;\n width: 450px;\n max-height: 90%;\n bottom: 0;\n right: 96px;\n position: absolute;\n z-index: 3; }\n\n@media only screen and (min-width: 960px) {\n .notes-visible .notebook-report-container {\n right: 516px;\n transition: right 250ms; } }\n\n.notebook-report-container__full {\n top: 16px;\n bottom: 16px;\n left: 16px;\n right: 16px;\n max-height: none;\n max-width: none;\n height: auto;\n width: auto; }\n .notebook-report-container__full .notebook-report {\n height: 100%;\n width: 100%;\n margin: 0 auto;\n max-height: none;\n border-radius: 4px; }\n\n.notebook-report-container__collapsed {\n width: 300px;\n height: auto; }\n .notebook-report-container__collapsed .notebook-report__content, .notebook-report-container__collapsed .notebook-report__actions, .notebook-report-container__collapsed .notebook-report__content__header {\n display: none; }\n\n.notebook-report__toolbar {\n background-color: #333333 !important;\n border-radius: 4px 4px 0 0; }\n\n.notebook-report__toolbar__title {\n max-width: 150px; }\n\n.notebook-report__content {\n background-color: #ffffff; }\n .notebook-report__content h1, .notebook-report__content h2, .notebook-report__content h3, .notebook-report__content h4 {\n font-size: 22px; }\n .notebook-report__content .note-editor.note-frame {\n border: 0 none;\n border-radius: 0;\n padding: 0;\n box-shadow: none; }\n .notebook-report__content .note-resizebar {\n display: none; }\n\n.notebook-report__content__header {\n padding: 8px;\n background-color: #ffffff;\n font-size: 16px; }\n\n@media only screen and (max-width: 599px) {\n .notebook-report-container:not(.notebook-report-container__collapsed) {\n top: 0;\n bottom: 0;\n left: 0;\n right: 0;\n max-height: none;\n max-width: none;\n height: auto;\n width: auto; }\n .notebook-report-container:not(.notebook-report-container__collapsed) .notebook-report {\n border-radius: 0; }\n .notebook-tools--full {\n display: none; } }\n\n.notebook-report-backdrop {\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n z-index: 3;\n background-color: #212121;\n opacity: .48; }\n\n.notebook-menu {\n transition: opacity 500ms; }\n .notebook-menu.ng-enter {\n opacity: 0; }\n .notebook-menu .ng-enter-active {\n opacity: 1;\n transition-delay: 250ms; }\n .notebook-menu.ng-leave-active, .notebook-menu.ng-hide {\n opacity: 0; }\n .notebook-menu.ng-hide-add, .notebook-menu.ng-hide-add-active, .notebook-menu.ng-hide-remove, .notebook-menu.ng-hide-remove-active {\n opacity: 0; }\n\n.notebook-item {\n transition: box-shadow 250ms;\n margin: 0 16px 16px;\n display: block; }\n\n.notebook-item__content {\n height: 250px;\n min-width: 230px;\n position: relative;\n padding: 0;\n background-color: #cccccc;\n border-top-left-radius: 4px;\n border-top-right-radius: 4px; }\n\n.notebook-item__content__attachment, .notebook-item__content__text {\n position: absolute;\n left: 0;\n right: 0; }\n\n.notebook-item__content__attachment {\n background-repeat: no-repeat !important;\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n background-position: center top !important;\n background-size: cover !important;\n top: 0;\n bottom: 0; }\n\n.notebook-item__content__text {\n bottom: 0;\n padding: 8px;\n font-weight: 500;\n overflow: hidden;\n max-height: 120px;\n min-height: 56px;\n background-color: rgba(255, 255, 255, 0.95);\n border-top: 1px solid #eeeeee; }\n .notebook-item__content__text:after {\n content: \"\";\n text-align: right;\n position: absolute;\n bottom: 0;\n right: 0;\n width: 100%;\n height: 0.8em;\n background: linear-gradient(180deg, rgba(255, 255, 255, 0), rgba(255, 255, 255, 0.95) 100%); }\n\n.notebook-item__content--text-only:after {\n content: \"note\";\n font-family: 'Material Icons';\n font-weight: normal;\n font-style: normal;\n display: inline-block;\n line-height: 1;\n text-transform: none;\n letter-spacing: normal;\n word-wrap: normal;\n white-space: nowrap;\n direction: ltr;\n /* Support for all WebKit browsers. */\n -webkit-font-smoothing: antialiased;\n /* Support for Safari and Chrome. */\n text-rendering: optimizeLegibility;\n /* Support for Firefox. */\n -moz-osx-font-smoothing: grayscale;\n /* Support for IE. */\n font-feature-settings: 'liga';\n font-size: 80px;\n color: rgba(0, 0, 0, 0.26); }\n\n.notebook-item--question__content--text-only {\n content: \"live_help\"; }\n\n.notebook-item__content__location {\n opacity: 0.9;\n padding: 8px 0; }\n .notebook-item__content__location md-icon {\n font-size: 22px; }\n\n.notebook-item__edit {\n cursor: pointer; }\n\n.notebook-item__actions {\n margin: 0;\n padding: 0 8px;\n color: #ffffff;\n background-color: #333333;\n border-bottom-left-radius: 4px;\n border-bottom-right-radius: 4px; }\n .notebook-item__actions md-icon {\n color: #ffffff; }\n\n.notebook-item__text-input {\n margin: 0; }\n\n.notebook-item__text-input__textarea {\n padding-left: 0;\n padding-right: 0; }\n\n.notebook-item__attachment {\n background-color: #dddddd;\n padding: 16px;\n margin-bottom: 16px;\n text-align: center;\n position: relative; }\n\n.notebook-item__attachment__content {\n max-width: 100%;\n height: auto; }\n\n.notebook-item__attachment__delete {\n position: absolute;\n top: 4px;\n right: -2px;\n width: 34px !important;\n height: 34px !important;\n min-height: 0; }\n .notebook-item__attachment__delete md-icon {\n margin-left: -2px;\n font-size: 22px; }\n\n.notebook-item__info {\n font-style: italic;\n opacity: .8;\n color: #8a6942; }\n .notebook-item__info a, .notebook-item__info md-icon {\n color: #8a6942; }\n .notebook-item__info md-icon {\n font-size: 1.5em;\n min-width: 0;\n width: auto; }\n\n.notebook-item__upload {\n text-align: center;\n padding: 24px;\n background-color: #eeeeee;\n margin-bottom: 16px;\n color: rgba(0, 0, 0, 0.54);\n border-radius: 4px;\n cursor: pointer;\n border: 1px dashed transparent;\n transition: all 250ms; }\n .notebook-item__upload md-icon, .notebook-item__upload span {\n transition: color 250ms; }\n .notebook-item__upload:hover, .notebook-item__upload:focus, .notebook-item__upload.dragover {\n border-color: #F05843;\n background-color: #fdebe8;\n color: #F05843; }\n .notebook-item__upload:hover md-icon, .notebook-item__upload:hover span, .notebook-item__upload:focus md-icon, .notebook-item__upload:focus span, .notebook-item__upload.dragover md-icon, .notebook-item__upload.dragover span {\n color: #F05843; }\n\n.view-notebook-item {\n width: 600px; }\n\n.notebook-item--report {\n background-color: #ffffff; }\n .notebook-item--report .note-editor {\n margin-bottom: 16px;\n border-color: #cccccc; }\n\n.notebook-item--report__container.ui-scrollpoint .notebook-item--report__toolbar {\n position: fixed;\n top: 94px;\n left: 0;\n right: 0;\n z-index: 1; }\n @media only screen and (min-width: 600px) {\n .notebook-item--report__container.ui-scrollpoint .notebook-item--report__toolbar {\n left: 54px; } }\n @media only screen and (min-width: 600px) {\n .notebook-item--report__container.ui-scrollpoint .notebook-item--report__toolbar {\n padding: 0 24px; } }\n @media only screen and (min-width: 960px) {\n .notebook-item--report__container.ui-scrollpoint .notebook-item--report__toolbar {\n padding: 0 32px; } }\n .notebook-item--report__container.ui-scrollpoint .notebook-item--report__toolbar .note-toolbar {\n margin: 0 16px; }\n @media only screen and (min-width: 600px) {\n .notebook-item--report__container.ui-scrollpoint .notebook-item--report__toolbar .note-toolbar {\n margin: 0 8px; } }\n @media only screen and (min-width: 960px) {\n .notebook-item--report__container.ui-scrollpoint .notebook-item--report__toolbar .note-toolbar {\n margin: 0 16px; } }\n\n.notebook-item--report__container.ui-scrollpoint .note-editor {\n padding-top: 40px; }\n\n.notebook-item--report__toolbar .note-toolbar {\n background-color: #dddddd;\n border: 1px solid #cccccc;\n margin-bottom: -2px; }\n\n.notebook-item--report__heading {\n text-align: center;\n margin-bottom: 32px; }\n\n.notebook-item--report__add-note {\n font-weight: 700; }\n\n.notebook-item--report__note-img {\n max-width: 100%;\n height: auto !important; }\n\n@media only screen and (min-width: 600px) {\n .notebook-sidebar {\n width: 400px;\n max-width: none; } }\n\n@media only screen and (min-width: 960px) {\n .notebook-sidebar {\n width: 500px;\n max-width: none; } }\n\n.notebook-items {\n width: 100%;\n overflow: auto;\n margin-top: 16px;\n margin-bottom: 76px; }\n .notebook-items .notebook-item {\n width: 100%; }\n .notebook-items .notebook-item__content {\n height: 200px;\n min-width: 0; }\n\n.notebook-items--grading {\n margin-bottom: 0; }\n\n@media only screen and (max-width: 599px) {\n .notebook-enabled .md-fab-bottom-right, .notebook-enabled .md-fab-bottom-left {\n bottom: 50px !important; } }\n\n.notebook-grading {\n background-color: #ffffff;\n display: block; }\n\n.notification-btn {\n width: 60px !important; }\n .notification-btn md-icon {\n margin-left: 20px; }\n\n.notification-count {\n border-radius: 50%;\n position: absolute;\n background-color: #F05843;\n width: 22px;\n left: 4px;\n height: 22px;\n line-height: 18px;\n font-size: 12px;\n font-weight: 700;\n border: 2px solid; }\n .notification-count:before {\n content: \"\";\n position: absolute;\n right: -7px;\n top: 5px;\n border-left: 6px solid rgba(255, 255, 255, 0.87);\n border-top: 4px solid transparent;\n border-bottom: 4px solid transparent; }\n\n.notification-list {\n padding: 8px 0; }\n\n.notification-dismiss {\n width: 500px; }\n\n.notification-dismiss__input {\n margin-bottom: 0; }\n\nmd-list md-list-item .md-list-item-text h4.notification-list-item__source,\nmd-list md-list-item.md-2-line .md-list-item-text h4.notification-list-item__source,\nmd-list md-list-item.md-3-line .md-list-item-text h4.notification-list-item__source {\n color: rgba(0, 0, 0, 0.54);\n font-size: 12px; }\n md-list md-list-item .md-list-item-text h4.notification-list-item__source md-icon,\n md-list md-list-item.md-2-line .md-list-item-text h4.notification-list-item__source md-icon,\n md-list md-list-item.md-3-line .md-list-item-text h4.notification-list-item__source md-icon {\n font-size: 18px;\n min-width: 0;\n width: auto;\n margin-left: -4px;\n line-height: 20px; }\n\n.account-menu {\n border-radius: 4px;\n padding: 0;\n font-size: 15px;\n max-width: 380px; }\n @media (min-width: 1280px) {\n .account-menu {\n min-width: 380px !important; } }\n .account-menu h3 {\n margin: 0;\n font-weight: 300; }\n\n.account-menu--fixed-height {\n height: 304px; }\n\n.account-menu--fixed-width {\n width: 320px; }\n @media (min-width: 960px) {\n .account-menu--fixed-width {\n width: 380px; } }\n\n.account-menu__icon {\n background-color: white;\n border-radius: 50%; }\n\n.account-menu__caret {\n position: absolute;\n right: 28px;\n top: -8px;\n outline: none; }\n .account-menu__caret:before {\n content: '';\n position: absolute;\n border-bottom: 8px solid #fff;\n border-left: 8px solid transparent;\n border-right: 8px solid transparent; }\n\n.account-menu__caret--pause, .account-menu__caret--notification {\n right: 80px; }\n\n.account-menu__caret--notification--with-pause {\n right: 132px; }\n\n[dir=rtl] .account-menu__caret {\n right: auto;\n left: 28px; }\n\n[dir=rtl] .account-menu__caret--pause, [dir=rtl] .account-menu__caret--notification {\n left: 80px;\n right: auto; }\n\n[dir=rtl] .account-menu__caret--notification--with-pause {\n left: 132px;\n right: auto; }\n\n.account-menu__info {\n padding: 8px 12px; }\n\n.account-menu__info__title {\n font-weight: 500; }\n\n.account-menu__info__team {\n font-weight: 400;\n color: rgba(0, 0, 0, 0.54); }\n\n.account-menu__users {\n padding: 0; }\n .account-menu__users md-list-item {\n padding: 0; }\n .account-menu__users md-list-item .md-avatar {\n margin: 0 8px 0 0;\n height: 48px;\n width: 48px; }\n\n.account-menu__progress md-progress-linear {\n transform: rotate(270deg);\n width: 26px; }\n\n.account-menu__grade {\n position: relative;\n margin-right: 4px; }\n .account-menu__grade md-icon {\n color: #FFC107; }\n\n.account-menu__grade__overlay {\n position: absolute;\n top: 0;\n width: 100%;\n background-color: rgba(255, 255, 255, 0.6); }\n\n.account-menu__actions {\n background-color: #f7f7f7; }\n\n.account-menu__control {\n padding: 16px; }\n\n.annotations {\n margin: 16px 4px 16px 62px;\n position: relative;\n font-size: 15px; }\n .annotations hr {\n margin: 10px 0 8px;\n border-color: rgba(0, 0, 0, 0.12); }\n .annotations:after {\n content: \"\";\n position: absolute;\n width: 0;\n height: 0;\n left: -16px;\n right: auto;\n top: 0px;\n bottom: auto;\n border-top: 20px solid transparent;\n border-bottom: 20px solid transparent;\n border-right: 16px solid #757575; }\n\n.annotations-container--student--report {\n border-top: 1px solid #dddddd; }\n\n.annotations--report {\n margin-top: 0;\n margin-bottom: 0; }\n\n.annotations__header {\n position: relative;\n border-top-right-radius: 4px;\n padding: 10px 12px;\n font-weight: 700;\n transition: all 1s;\n color: #ffffff;\n background-color: #757575; }\n\n.annotations__avatar {\n background-color: #F05843;\n padding: 2px;\n position: absolute;\n top: 0;\n left: -62px; }\n\n.annotations__icon {\n transition: all 1s;\n color: #ffffff; }\n\n.annotations__body {\n padding: 12px;\n background-color: #ffffff;\n border-bottom-left-radius: 4px;\n border-bottom-right-radius: 4px;\n overflow: auto; }\n\n.annotations__status {\n background-color: #ffffff;\n color: #ef6c00;\n display: inline-block;\n margin-left: 8px;\n font-size: 12px; }\n .annotations__status.ng-enter, .annotations__status.ng-leave {\n transition: all 1s; }\n .annotations__status.ng-enter, .annotations__status.ng-leave.ng-leave-active {\n opacity: 0; }\n .annotations__status.ng-leave, .annotations__status.ng-enter.ng-enter-active {\n opacity: 1; }\n\n.annotations__score {\n font-weight: 700; }\n\n.annotations__info {\n font-style: italic;\n opacity: 0.8;\n border-bottom: 1px dotted;\n font-size: 13px; }\n\n.annotations--inside .annotations {\n margin-left: 72px; }\n\n.annotations--info {\n margin-bottom: 32px;\n margin-right: 8px;\n margin-left: 72px; }\n @media only screen and (min-width: 600px) {\n .annotations--info {\n margin: 16px 16px 32px 76px; } }\n .annotations--info:after {\n border-right: 16px solid #ef6c00; }\n .annotations--info .annotations__avatar {\n background-color: #ffffff; }\n .annotations--info .annotations__header {\n background-color: #ef6c00; }\n\n.annotations--grading md-input-container {\n margin-bottom: 0; }\n\n.annotations--grading .md-errors-spacer {\n display: none; }\n\n.annotations--grading input:focus, .annotations--grading textarea:focus {\n background-color: #ffffff; }\n\n.annotations--grading input:disabled, .annotations--grading textarea:disabled {\n color: rgba(0, 0, 0, 0.87); }\n\n.annotations--grading--revision {\n margin: 8px 0 0;\n padding: 8px; }\n\n.annotations--notebook {\n margin-top: 16px; }\n\n.annotations--grading__info {\n font-style: italic;\n margin: 8px 8px 4px; }\n\n.annotations--grading__item {\n padding: 8px; }\n\n.annotations--grading__score input {\n margin-top: 0 !important;\n font-size: 18px;\n width: 52px;\n text-align: center; }\n\n.annotations--grading__score__label {\n transform: none !important;\n width: auto;\n display: block;\n padding: 0;\n margin: 0 8px 0 0; }\n\n.annotations--grading__score__max label {\n display: none; }\n\n.annotations--grading__score__divider {\n position: relative;\n top: 12px;\n margin-left: 4px; }\n\n.annotations--grading__auto-comment {\n margin: 0 2px; }\n\n.annotations--grading__auto-comment__content {\n margin-top: 8px; }\n\n.component {\n position: relative; }\n\n.component__wrapper {\n padding: 0 16px;\n margin: 24px 0; }\n\n.component__content {\n overflow-x: auto;\n font-size: 15px;\n overflow-y: hidden; }\n @media only screen and (min-width: 600px) {\n .component__content {\n padding: 0 8px; } }\n\nh3.component__header, .component-header {\n padding: 8px 12px;\n margin: 0;\n font-size: 14px; }\n\n.component__rubric {\n position: absolute;\n left: -20px;\n top: 12px; }\n\n.notebook-enabled .component_content img {\n transition: all 250ms;\n cursor: pointer;\n cursor: copy; }\n .notebook-enabled .component_content img:hover, .notebook-enabled .component_content img:focus {\n box-shadow: 0 0 5px 1px #F05843; }\n\n.component__actions .md-button:first-child {\n margin-left: 0; }\n\n.component__actions .md-button:last-child {\n margin-right: 0; }\n\n.component__actions__info {\n font-style: italic;\n margin-left: 8px;\n color: rgba(0, 0, 0, 0.54); }\n\n.component__actions__more {\n border-bottom: 1px dotted; }\n\n.component__prompt {\n margin-bottom: 8px;\n font-weight: 500; }\n\n.component__prompt__content {\n display: inline; }\n\n.component__attachment {\n position: relative;\n margin: 0 8px;\n padding-bottom: 8px; }\n @media only screen and (min-width: 600px) {\n .component__attachment {\n padding-top: 8px; } }\n\n@media only screen and (max-width: 599px) {\n .component__add-attachment {\n width: 100%; } }\n\n.component__attachment__content {\n max-height: 100px;\n width: auto; }\n\n.component__attachment__delete {\n position: absolute;\n top: 0;\n right: 0;\n min-width: 0;\n background-color: rgba(255, 255, 255, 0.75) !important;\n border-radius: 0;\n padding: 4px;\n margin: 0; }\n .component__attachment__delete > md-icon {\n margin-top: 0; }\n\n.component__revision {\n margin: 8px 0;\n padding: 8px; }\n .component__revision:nth-child(odd) {\n background-color: #f7f7f7; }\n\n.component__revision__content {\n padding: 4px 0 8px 0;\n border-bottom: 1px solid #dddddd; }\n\n.component__revision__actions {\n color: #757575;\n padding-top: 4px; }\n\n.component__content--Discussion {\n overflow: hidden; }\n\n.discussion-content {\n background-color: #eeeeee;\n box-shadow: inset 0 0 3px #aaaaaa; }\n\n.discussion-posts {\n padding: 12px 12px 8px; }\n @media only screen and (min-width: 1280px) {\n .discussion-posts {\n padding: 16px 16px 0; } }\n\n.discussion-post {\n margin: 0 auto 16px;\n max-width: 600px; }\n @media only screen and (min-width: 600px) {\n .discussion-post {\n margin-bottom: 24px; } }\n @media only screen and (min-width: 1280px) {\n .discussion-post {\n margin-bottom: 32px; } }\n .discussion-post md-divider {\n position: relative;\n width: auto; }\n\n.discussion-post__contents {\n padding: 16px; }\n\n.discussion-post__avatar, md-list-item > .md-avatar.discussion-post__avatar {\n margin-right: 8px; }\n\n.discussion-post__avatar--reply, md-list-item > .md-avatar.discussion-post__avatar--reply {\n margin-top: 8px; }\n\n.discussion-post__user, md-list-item .md-list-item-text h3.discussion-post__user {\n padding-bottom: 4px;\n font-weight: 700;\n line-height: 1.3;\n overflow: visible;\n white-space: normal; }\n\n.discussion-post__date {\n color: #aaaaaa; }\n\n.discussion-post__date--reply {\n margin-left: 8px;\n font-weight: 400; }\n\n.discussion-post__content {\n margin-top: 16px;\n white-space: pre-wrap; }\n\n.discussion-post__attachment {\n max-width: 100%;\n height: auto !important;\n margin-top: 16px; }\n\n.discussion-new {\n background-color: #ffffff;\n max-width: 570px;\n margin-left: auto;\n margin-right: auto;\n padding: 8px;\n transition: all 250ms;\n transform: scale(0.95); }\n\n.discussion-new--focused {\n transform: scale(1); }\n\nmd-input-container.discussion-new__input-container {\n margin: 0;\n padding: 0; }\n md-input-container.discussion-new__input-container > textarea.md-input {\n min-height: 68px; }\n\n.discussion-new__input--textarea, .input-container textarea.discussion-new__input--textarea {\n padding: 8px;\n border: 0 none; }\n\n.discussion-new__actions {\n padding: 0 8px; }\n .discussion-new__actions .md-button:first-of-type {\n margin-left: 0; }\n .discussion-new__actions .md-button:last-of-type {\n margin-right: 0; }\n\n.discussion-new__attachment {\n padding: 0;\n margin: 0 0 8px; }\n\n.discussion-new__attachment__content {\n margin-top: 0;\n margin-bottom: 16px; }\n\n.discussion-comments {\n padding: 0; }\n\n.discussion-comments__contents {\n background-color: #f7f7f7; }\n\n.discussion-comments__header {\n background-color: transparent;\n padding: 0; }\n .discussion-comments__header .md-subheader-inner {\n padding-bottom: 8px; }\n\n.discussion-comments__list {\n padding: 0;\n max-height: 9999px;\n overflow-y: auto; }\n @media only screen and (min-width: 600px) {\n .discussion-comments__list {\n max-height: 400px; } }\n\n.input--textarea.discussion-reply__input, .input-container textarea.input--textarea.discussion-reply__input {\n background-color: #ffffff;\n padding: 4px;\n font-size: 14px;\n border: 0 none;\n margin-left: -1px;\n margin-bottom: 0;\n resize: none; }\n\n.discussion-reply, md-list-item.discussion-reply {\n margin: 0 12px 8px;\n padding: 0;\n min-height: 56px; }\n\n.discusstion-reply__details, md-list-item .md-list-item-text.discusstion-reply__details {\n margin: 8px 0; }\n\n.discussion-post__user--reply, md-list-item .md-list-item-text h3.discussion-post__user--reply {\n font-size: 14px;\n padding: 0;\n margin: 0; }\n\n.discusstion-reply__content {\n margin-top: 2px; }\n .discusstion-reply__content p {\n font-weight: 400 !important;\n color: rgba(0, 0, 0, 0.87) !important; }\n\n.discussion-new-reply {\n padding: 8px; }\n\n.discussion-new-reply__input-container {\n padding-top: 0;\n margin: 0; }\n .discussion-new-reply__input-container .md-errors-spacer {\n display: none; }\n\n.discussion-new-reply__actions {\n margin-left: 8px; }\n .discussion-new-reply__actions .md-button {\n margin-top: 0;\n margin-bottom: 0; }\n\n.embedded-content__iframe {\n border: 0 none; }\n\n.component--grading {\n padding: 0;\n margin: 0; }\n .component--grading:not(:last-child) > div {\n border-bottom: 1px solid #dddddd; }\n .component--grading .component__wrapper {\n padding: 0;\n margin: 0; }\n .component--grading .component__content {\n padding: 16px;\n margin: 0; }\n\n.component--grading__response {\n padding-bottom: 16px; }\n @media only screen and (min-width: 960px) {\n .component--grading__response {\n padding-right: 16px;\n padding-bottom: 0; } }\n\n.component--grading__response__content {\n overflow: auto; }\n\n.component--grading__annotations {\n background-color: #ecf4fd; }\n\n.component--grading__annotations__divider {\n padding: 4px;\n background-color: #ffffff; }\n\n.component--grading__actions__info {\n margin: 16px 0 0;\n padding-top: 8px;\n border-top: 1px solid #eeeeee; }\n\n.graph-select {\n min-width: 150px;\n max-width: 200px; }\n\n.graph-controls {\n margin: 8px 0;\n padding: 8px 0;\n border: 1px solid #eeeeee;\n border-left-width: 0;\n border-right-width: 0; }\n\n.match-content {\n background-color: #eeeeee;\n margin-bottom: 16px;\n padding: 8px;\n box-shadow: inset 0 0 3px #aaaaaa; }\n\n.match-divider {\n margin: 16px 8px 8px; }\n\n@media only screen and (min-width: 960px) {\n .match-divider--horizontal {\n display: none; } }\n\n.match-bucket__header {\n padding: 12px;\n font-weight: 500;\n color: #1565c0; }\n\n.match-bucket__content {\n padding: 0; }\n\n.match-bucket--choices .match-bucket__header {\n color: #795C3A; }\n\n.match-bucket__contents {\n min-height: 120px;\n padding: 0 8px 8px;\n background-color: #dddddd;\n transition: background-color 250ms;\n border-bottom-left-radius: 4px;\n border-bottom-right-radius: 4px;\n column-gap: 8px; }\n @media only screen and (max-width: 599px) {\n .match-bucket__contents {\n column-count: 1 !important; } }\n .match-bucket__contents img {\n max-width: 100%;\n height: auto; }\n\n.match-bucket__contents--over {\n background-color: #1565c0; }\n\n.match-bucket__item {\n list-style-type: none;\n cursor: move;\n padding-top: 8px;\n break-inside: avoid; }\n\n.match-bucket__item__contents {\n background-color: #ffffff;\n padding: 8px !important;\n border: 1px solid #cccccc; }\n .match-bucket__item__contents .md-list-item-text {\n width: 100%; }\n\n.match-bucket__item__contents__text {\n margin-right: 4px; }\n\n.match-feedback {\n transition: opacity 250ms;\n /*padding-left: 8px;\n border-left: 4px solid;*/\n margin: 8px -8px -8px;\n color: #ffffff;\n padding: 4px 8px; }\n .match-feedback.ng-hide {\n opacity: 0;\n transition: opacity 1ms; }\n .match-feedback md-icon {\n color: #ffffff; }\n\n.outside-content iframe {\n border: 1px solid #eeeeee; }\n\n.outside-content__source {\n margin-top: 4px;\n text-align: end; }\n .outside-content__source a {\n max-width: 240px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n display: inline-block; }\n\n.component-revisions .component {\n padding: 0;\n margin: 0; }\n\n.component-revisions .component__content {\n padding: 0; }\n\n.component-revisions .component__wrapper {\n margin: 16px 0; }\n\n.component-revisions .md-resize-handle {\n display: none; }\n\n.component-revisions__item, md-list-item.component-revisions__item {\n padding: 0; }\n\n.component-revisions__item--latest {\n margin-bottom: 24px; }\n\n.component-revisions__annotation-label {\n margin-right: 8px; }\n\n.component-revisions__has-auto-and-teacher {\n padding-top: 8px;\n margin-top: 8px;\n border-top: 1px solid #dddddd; }\n\n.notebook-toolbar md-divider {\n margin: 8px 0; }\n\n@media only screen and (max-width: 959px) {\n .notebook-toolbar {\n border-top: 1px solid #dddddd; } }\n\n.notebook-toolbar__add-menu {\n position: absolute;\n bottom: 40px; }\n .notebook-toolbar__add-menu .md-fab-action-item {\n background-color: #ffffff; }\n\n.notebook-toolbar__add-icon {\n border-radius: 50%; }\n\n#closeNotebookSettingsButton {\n float: right; }\n\n[dir=rtl] #closeNotebookSettingsButton {\n float: left; }\n\nhighchart {\n display: block; }\n","// 1. Config\n\n// 2. Base\n.l-notebook {\n margin-top: $wise-toolbar-height;\n background-color: color('body-bg') !important;\n}\n","// 1. Config\n\n// 2. Base\n.l-nav {\n background-color: color('body-bg') !important;\n}\n","// 1. Config\n\n// 2. Base\n.l-sidebar {\n\n}\n\n.l-sidebar__header {\n background-color: #ffffff !important;\n color: color('accent-1') !important;\n\n md-select {\n color: color('body');\n }\n}",".status-icon {\n margin: 0 4px;\n z-index: 1;\n vertical-align: bottom;\n}\n\n.md-button.status-icon {\n height: auto;\n width: auto;\n min-height: 0;\n line-height: inherit;\n margin: 0 4px;\n padding: 0;\n}\n\n.status-corner-wrapper {\n position: absolute;\n z-index: 1;\n overflow: hidden;\n}\n\n.status-corner {\n width: 0;\n height: 0;\n border-top-color: color('text-disabled');\n border-bottom-color: color('text-disabled');\n color: color('text-disabled');\n\n &:after {\n content: '!';\n color: #ffffff;\n top: 2px;\n right: 10px;\n position: absolute;\n font-weight: 700;\n }\n}\n\n.status-corner--warn {\n border-top-color: color('warn') !important;\n border-bottom-color: color('warn') !important;\n}\n\n.status-corner-top-right {\n top: 0;\n right: 0;\n border-top-right-radius: $card-border-radius;\n\n .status-corner {\n border-top: 36px solid;\n border-left: 36px solid transparent;\n }\n}\n\n.status-corner-top-left {\n top: 0;\n left: 0;\n border-top-left-radius: $card-border-radius;\n\n .status-corner {\n border-top: 36px solid;\n border-right: 36px solid transparent;\n }\n}\n\n.status-corner-bottom-right {\n bottom: 0;\n right: 0;\n border-bottom-right-radius: $card-border-radius;\n\n .status-corner {\n border-bottom: 36px solid;\n border-left: 36px solid transparent;\n }\n}\n\n.status-corner-bottom-left {\n bottom: 0;\n left: 0;\n border-bottom-left-radius: $card-border-radius;\n\n .status-corner {\n border-bottom: 36px solid;\n border-right: 36px solid transparent;\n }\n}\n\n.avatar--icon--alert {\n background-color: #ffffff;\n}\n\n.avatar--icon--alert__icon {\n font-size: 48px;\n margin: -4px 0 0 -4px;\n}\n",".gu-mirror {\n position: fixed !important;\n margin: 0 !important;\n z-index: 9999 !important;\n opacity: 0.8;\n transition: opacity 250ms ease-in-out;\n}\n.gu-hide {\n display: none !important;\n}\n.gu-unselectable {\n -webkit-user-select: none !important;\n -moz-user-select: none !important;\n -ms-user-select: none !important;\n user-select: none !important;\n}\n.gu-transit {\n opacity: 0.2;\n}\n","md-dialog {\n width: $layout-breakpoint-xs;\n}\n\n.dialog--wide {\n width: $layout-breakpoint-sm;\n}\n\n.dialog--wider {\n width: $layout-breakpoint-md;\n}\n",".help-bubble {\n border-radius: $card-border-radius;\n max-width: 320px;\n\n @media (min-width: $layout-breakpoint-xs) {\n max-width: ($layout-breakpoint-xs - 48);\n }\n\n @media (min-width: $layout-breakpoint-sm) {\n max-width: ($layout-breakpoint-sm - 48);\n }\n\n @media (min-width: $layout-breakpoint-md) {\n max-width: ($layout-breakpoint-md - 48);\n }\n}\n\n.help-bubble__title {\n border-top-left-radius: $card-border-radius;\n border-top-right-radius: $card-border-radius;\n}\n\n.help-bubble___title__content {\n border-top-left-radius: $card-border-radius;\n border-top-right-radius: $card-border-radius;\n padding: 0px 0 0 12px;\n background-color: color('info');\n\n .md-icon-button {\n margin-right: 0;\n padding-top: 0;\n padding-bottom: 0;\n }\n}\n\n.help-bubble__content {\n overflow: auto;\n padding: 8px 12px;\n max-height: 480px;\n}\n\n.help-bubble__actions {\n border-bottom-left-radius: $card-border-radius;\n border-bottom-right-radius: $card-border-radius;\n}\n\ndiv.hopscotch-bubble {\n border-radius: $card-border-radius;\n //border: 2px solid color('gray-darker');\n border: 0 none;\n font-family: Roboto, \"Helvetica Neue\", sans-serif;\n font-size: rem(1.4);\n z-index: 6;\n\n .hopscotch-bubble-arrow-container {\n position: absolute;\n width: 20px;\n height: 20px;\n }\n\n .hopscotch-bubble-arrow-container {\n &.up {\n top: 0;\n left: 12px;\n\n .hopscotch-bubble-arrow {\n border-bottom: 10px solid color('info');\n border-left: 10px solid transparent;\n border-right: 10px solid transparent;\n position: relative;\n top: -10px;\n }\n\n .hopscotch-bubble-arrow-border {\n border: 0 none;\n /*border-bottom: 12px solid color('gray-darker');\n border-left: 12px solid transparent;\n border-right: 12px solid transparent;*/\n }\n }\n\n &.down {\n bottom: -34px;\n left: 12px;\n\n .hopscotch-bubble-arrow {\n border-top: 10px solid color('info');\n border-left: 10px solid transparent;\n border-right: 10px solid transparent;\n position: relative;\n top: -10px;\n }\n\n .hopscotch-bubble-arrow-border {\n border: 0 none;\n /*border-top: 12px solid color('gray-darker');\n border-left: 12px solid transparent;\n border-right: 12px solid transparent;*/\n }\n }\n\n &.left {\n top: 12px;\n left: -10px;\n\n .hopscotch-bubble-arrow {\n border-right: 10px solid color('info');\n border-bottom: 10px solid transparent;\n border-top: 10px solid transparent;\n position: relative;\n left: 0;\n top: -10px;\n }\n\n .hopscotch-bubble-arrow-border {\n border: 0 none;\n /*border-right: 12px solid color('gray-darker');\n border-bottom: 12px solid transparent;\n border-top: 12px solid transparent;*/\n }\n }\n\n &.right {\n top: 12px;\n right: -30px;\n\n .hopscotch-bubble-arrow {\n border-left: 10px solid color('info');\n border-bottom: 10px solid transparent;\n border-top: 10px solid transparent;\n position: relative;\n right: 0;\n top: -10px;\n }\n\n .hopscotch-bubble-arrow-border {\n border: 0 none;\n /*border-right: 12px solid color('gray-darker');\n border-bottom: 12px solid transparent;\n border-top: 12px solid transparent;*/\n }\n }\n }\n}\n","// Variables\n$input-action-width: 44px;\n$input-action-vertical-offset: 6px;\n$input-action-vertical-offset-richtext: -7px;\n\n// Base\n.input-container {\n padding-top: 12px;\n}\n\n.input-container--component {\n margin-bottom: 0;\n}\n\n.input-container--open-response {\n &.md-has-icon {\n padding-left: 0;\n }\n\n .md-errors-spacer {\n display: none;\n }\n}\n\n.input-wrapper {\n position: relative;\n}\n\n.input-wrapper--focused {\n .input--textarea__action md-icon {\n color: color('primary');\n }\n}\n\n.input--textarea, .input-container textarea.input--textarea {\n padding: 8px;\n background-color: color('gray-lightest');\n border: 1px solid color('gray');\n margin-bottom: 8px;\n\n &:focus {\n background-color: #ffffff;\n }\n\n &[disabled] {\n color: color('text-secondary');\n }\n}\n\n.input-container textarea.input--textarea {\n width: 100%;\n}\n\n.input--textarea--disabled {\n color: color('text-secondary');\n}\n\n.input--textarea__action {\n position: absolute;\n right: -4px;\n\n &[disabled] md-icon {\n color: color('text-disabled') !important;\n }\n}\n\n.input--textarea__action--notebook {\n top: $input-action-vertical-offset;\n\n .input-wrapper--richtext & {\n top: $input-action-vertical-offset-richtext\n }\n}\n\n.input--textarea__action--revision {\n bottom: $input-action-vertical-offset;\n\n .input-wrapper--richtext & {\n bottom: ($input-action-vertical-offset-richtext + 2px);\n }\n\n}\n\n.input-label, md-input-container:not(.md-input-invalid):not(.md-input-focused).md-input-has-value label.input-label {\n line-height: 1.2;\n color: color('text');\n\n &.input-label--focused {\n color: color('primary');\n }\n}\n\n.autocomplete {\n input {\n text-overflow: ellipsis;\n overflow: hidden;\n word-wrap: none;\n font-weight: 500;\n color: color('text-secondary');\n }\n}\n\n.autocomplete--minwidth {\n @media only screen and (min-width: $layout-breakpoint-xs) {\n min-width: 300px;\n }\n\n @media only screen and (min-width: $layout-breakpoint-sm) {\n min-width: 300px;\n }\n}\n\n.autocomplete--flat {\n md-autocomplete-wrap {\n background-color: #ffffff;\n\n &:not(.md-menu-showing) {\n box-shadow: none;\n background-color: color('gray-lighter');\n }\n }\n}\n\n.select__header {\n height: $menu-item-height;\n\n input {\n height: 100%;\n width: 100%;\n padding: 0 8px;\n outline: none;\n border: 0 none;\n font-size: rem(1.4);\n font-weight: 500;\n }\n}\n",".table {\n max-width: 100%;\n width: auto;\n min-width: 100px;\n margin: 8px 0;\n\n thead,\n tbody,\n tfoot {\n // TODO: remove chaining when bootstrap dependency is removed\n > tr > th,\n > tr > td {\n border: 1px solid color('gray');\n padding: 6px;\n font-size: rem(1.5);\n min-height: 32px;\n height: 32px;\n min-width: 32px;\n vertical-align: top;\n }\n }\n\n td.inactive,\n th {\n background-color: color('gray-lightest');\n opacity: 1;\n visibility: visible;\n }\n\n md-input-container {\n margin: 0;\n }\n\n .md-errors-spacer {\n display: none;\n }\n}\n\n.table--student {\n td {\n &.inactive {\n padding: 8px 10px;\n }\n }\n}\n\n.table--full-width {\n width: 100%;\n}\n\n.table--list {\n border: 0 none;\n border-collapse: collapse;\n background-color: #ffffff;\n max-width: 100%;\n overflow: auto;\n\n th,\n td {\n padding: 0 4px;\n border: 0 none;\n }\n\n td {\n min-height: 56px;\n height: 56px;\n }\n\n tr {\n &.md-button {\n display: table-row;\n text-align: left;\n width: auto;\n text-transform: none;\n font-size: inherit;\n font-weight: normal;\n }\n }\n}\n\n.table--list__wrap {\n min-width: $layout-breakpoint-xs;\n}\n\n.table-wrap-sticky {\n @media only screen and (max-width: $layout-breakpoint-sm - 1) {\n overflow-x: auto;\n }\n}\n\n.table--list__thead {\n font-size: rem(1.4);\n font-weight: 700;\n}\n\n.table--list__thead__tr {\n height: 100%;\n margin: 0;\n}\n\n.table--list__thead__th {\n background-color: color('gray-darker');\n color: color('text-light');\n min-height: $wise-toolbar-height;\n height: $wise-toolbar-height;\n}\n\n.table--list__thead__link {\n color: #ffffff;\n text-transform: none;\n margin: 0;\n min-width: 0;\n white-space: normal;\n line-height: 1.4;\n width: 100%;\n}\n\n.table--list__thead__sort {\n margin: 0;\n}\n\n.table--list__thead__sort--reverse {\n transform: rotate(180deg);\n}\n\n.td--wrap {\n min-width: 180px;\n white-space: normal;\n line-height: 1.2;\n}\n\n.td--max-width {\n @media only screen and (max-width: $layout-breakpoint-sm - 1) {\n max-width: 180px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n}\n",".md-toolbar-tools {\n font-size: 18px;\n\n .autocomplete {\n height: 36px;\n\n md-autocomplete-wrap {\n height: 36px;\n }\n\n input {\n height: 36px;\n }\n }\n}\n\n.md-toolbar--wise {\n min-height: $wise-toolbar-height;\n\n .md-toolbar-tools {\n height: $wise-toolbar-height;\n max-height: $wise-toolbar-height;\n }\n\n .md-button.md-icon-button {\n height: $wise-toolbar-height;\n line-height: $wise-toolbar-height;\n width: $wise-toolbar-height;\n }\n}\n\n.md-toolbar--wise--sm {\n .md-toolbar-tools {\n padding: 0 8px;\n font-size: $body-font-size-base;\n }\n}\n\n.md-toolbar--sidenav {\n background-color: color('gray-darkest') !important;\n\n .md-toolbar-tools {\n font-size: rem(1.6);\n font-weight: 500;\n }\n}\n\n.toolbar {\n position: fixed;\n left: 0;\n right: 0;\n top: $md-toolbar-height;\n z-index: 3;\n}\n\n.toolbar__title {\n margin-left: 8px;\n font-size: rem(1.6);\n font-weight: 500;\n}\n\n.toolbar__tools {\n padding-right: 8px;\n}\n[dir=rtl] .toolbar__tools {\n padding-right: 16px;\n padding-left: 8px;\n}\n\n.toolbar__nav, .md-button.toolbar__nav {\n margin: 0;\n}\n\n.toolbar__select, .md-button.toolbar__select {\n margin: 0 4px;\n min-height: 32px;\n background-color: color('gray-lightest');\n\n .md-select-value {\n height: 32px;\n text-align: left;\n }\n}\n[dir=rtl] {\n .toolbar__select, .md-button.toolbar__select {\n .md-select-value {\n text-align: right;\n }\n }\n}\n\n.toolbar__select--fixedwidth {\n width: 168px;\n\n @media only screen and (min-width: $layout-breakpoint-xs) {\n width: 264px;\n }\n\n @media only screen and (min-width: $layout-breakpoint-sm) {\n width: 432px;\n }\n}\n",".list-item {\n background-color: #ffffff;\n border-bottom: 1px solid color('gray-lighter');\n\n .md-subheader, &.md-subheader {\n color: color('text');\n background-color: #ffffff;\n\n md-icon {\n vertical-align: middle;\n }\n\n .md-subheader-inner {\n padding: 0;\n }\n\n .md-avatar {\n margin-right: 8px;\n }\n }\n\n .autocomplete {\n margin: 8px 0;\n }\n}\n\n.list-item--info {\n &._md-button-wrap>div.md-button:first-child, .md-subheader-content {\n border-left: 4px solid color('info') !important;\n margin-left: -4px;\n }\n}\n\n.list-item--warn {\n //background-color: lighten(color('warn'), 56%);\n\n &._md-button-wrap>div.md-button:first-child, .md-subheader-content {\n border-left: 4px solid color('warn') !important;\n margin-left: -4px;\n }\n}\n\n.list-item--expanded {\n border-bottom-width: 0;\n}\n\n.list-item--noclick, .list-item--noclick.md-button {\n cursor: default;\n background-color: color('gray-lightest');\n}\n\n.list-item--actions {\n padding: 0 8px !important;\n}\n\n.list-item__subheader-button {\n text-transform: none;\n width: 100%;\n padding: 8px 16px;\n margin: 0;\n white-space: normal;\n text-align: left;\n line-height: 1.4;\n}\n\n.user-list {\n font-size: rem(1.5);\n}\n",".notice {\n text-align: center;\n padding: 8px;\n background-color: rgba(0,0,0,0.04);\n width: 100%;\n\n @media (min-width: $layout-breakpoint-xs) {\n max-width: 80%;\n border-radius: $button-border-radius;\n margin: 24px auto;\n }\n}",".milestone {\n min-width: 196px;\n width: 196px;\n height: 242px;\n background-color: #ffffff;\n padding: 0;\n\n &.md-button {\n text-transform: none;\n }\n}\n\n.milestone__progress {\n background-color: color('gray-lighter');\n border-radius: 50%;\n position: relative;\n margin-bottom: 12px;\n}\n\n.milestone__progress__percent {\n position: absolute;\n top: 8px;\n bottom: 8px;\n left: 8px;\n right: 8px;\n border-radius: 50%;\n background-color: #ffffff;\n color: color('primary');\n font-size: rem(2.8);\n font-weight: 500;\n}\n\n.milestone__title {\n font-weight: 700;\n font-size: $body-font-size-base;\n margin-bottom: 12px;\n}\n\n.milestone-details {\n section {\n &:not(:first-child) {\n margin-top: 8px;\n }\n }\n}\n\n.milestone-details__section {\n background-color: #ffffff;\n padding: 16px;\n\n > p {\n margin-top: 16px;\n margin-bottom: 0;\n }\n\n md-list {\n padding: 0;\n }\n\n .grading {\n padding: 0;\n }\n}\n\n.milestone-details__header {\n padding: 12px 16px;\n margin: -16px -16px 16px;\n text-transform: uppercase;\n font-weight: 500;\n}\n\n.milestone-details__progress {\n width: 48px;\n margin-right: 8px;\n}\n\n.milestone--add {\n &.md-button {\n text-transform: uppercase;\n }\n}\n\n.milestone--add__icon {\n height: 96px;\n width: 96px;\n background-color: color('gray-lighter');\n border-radius: 50%;\n}\n","#nav {\n position: relative;\n}\n\n.nav {\n margin-bottom: 16px;\n}\n\n.nav-mask {\n position: absolute;\n top: 0;\n bottom: 0;\n left: 0;\n right: 0;\n background-color: rgba(0,0,0,0.25);\n z-index: 1;\n\n &.ng-hide {\n opacity: 0;\n }\n}\n\n.nav-head {\n color: color('text-secondary');\n font-weight: 500;\n\n md-icon {\n line-height: 20px;\n }\n}\n\n.nav-contents--root {\n padding: 6px 6px 12px;\n}\n\n.nav-contents--group {\n background-color: color('gray-light');\n padding: 8px;\n}\n\n.nav-contents--root {\n padding: 0;\n}\n\n.nav-contents__list {\n padding: 0;\n\n @media (min-width: $layout-breakpoint-xs) {\n padding: 8px;\n }\n}\n\n.nav-item {\n transition: opacity 250ms ease-in-out;\n\n &.prev {\n md-list-item {\n background-color: color('selected-bg');\n\n /*&._md-button-wrap>div.md-button:first-child {\n border-left: 4px solid color('primary');\n margin-left: -4px;\n }*/\n }\n }\n}\n\n.nav-item--card__content {\n border-top-right-radius: $card-border-radius;\n border-top-left-radius: $card-border-radius;\n\n &:focus {\n outline: none;\n\n .nav-item__title > span {\n border-bottom: 1px dashed color('gray');\n }\n }\n}\n\n.nav-item--root {\n transition: margin 250ms, box-shadow 500ms;\n\n &.expanded {\n flex-basis: 100%;\n //max-width: ($layout-breakpoint-md - 100) !important;\n max-width: 100%;\n max-height: none !important;\n margin: 8px auto;\n padding-left: 4px;\n\n &:first-of-type {\n margin-top: 0;\n }\n\n //@media only screen and (min-width: $layout-breakpoint-sm) {\n //margin: 8px auto;\n //}\n }\n}\n\n//.nav-item--list {\n//}\n\n.nav-item--list__info-item {\n padding: 0 16px 0 4px;\n display: inline-block;\n}\n\n.nav-item--list__reorder {\n margin-left: 8px;\n color: color('text-disabled');\n}\n\n.nav-item--card {\n\n}\n\n.nav-item--card--group {\n &:not(.expanded) {\n box-shadow: 0px 3px 1px -2px rgba(0, 0, 0, 0.14), 0px 2px 2px 0px rgba(0, 0, 0, 0.098),\n 0px 1px 5px 0px rgba(0, 0, 0, 0.084), 3px 3px 0px 1px #d5d5d5, 6px 6px 0px 1px #aaaaaa;\n }\n}\n\n.nav-item__collapse {\n margin: 0;\n}\n\n.nav-item__more {\n border-top: 1px solid color('gray-light');\n border-bottom-right-radius: $card-border-radius;\n border-bottom-left-radius: $card-border-radius;\n padding: 8px 16px;\n min-height: 40px;\n}\n\n.nav-item__users {\n height: auto;\n cursor: pointer;\n color: #ffffff;\n margin: 0;\n padding: 1px 6px;\n\n > md-icon {\n padding-right: 4px;\n color: #ffffff;\n }\n\n &:hover, &:focus {\n &.success-bg {\n background-color: color('success');\n }\n }\n}\n\n.nav-item__title {\n padding-left: 16px;\n line-height: 1.2;\n font-weight: 400;\n}\n[dir=rtl] .nav-item__title {\n padding-left: auto;\n padding-right: 16px;\n}\n\n.nav-item__info {\n padding: 0 8px;\n}\n\n.nav-item__progress {\n width: 48px;\n\n > .md-container {\n top: 0;\n }\n}\n\n.nav-item__progress-value {\n margin-left: 8px;\n width: 36px;\n}\n\n.progress-wrapper {\n padding: 2px 0;\n cursor: pointer;\n}\n",".student-select {\n padding-top: 0;\n padding-bottom: 0;\n}\n\n.workgroup-progress {\n margin-bottom: 8px;\n\n @media (min-width: $layout-breakpoint-sm) {\n margin-bottom: 0;\n }\n}\n","// 1. Variables\n$menu-sidebar-width: 56px;\n\n// 2. Base\n.menu-progress {\n position: absolute;\n top: 10px;\n right: 12px;\n\n path {\n stroke: color('accent-2') !important;\n stroke-width: 2px;\n }\n}\n[dir=rtl] .menu-progress {\n right:auto;\n left:12px;\n}\n\n.menu-sidenav {\n\n}\n\n.menu-sidenav__item {\n font-weight: 700;\n //color: color('text-secondary');\n font-size: rem(1.4);\n}\n\n.menu-sidenav__icon {\n margin-top: 12px !important;\n margin-right: 12px !important;\n margin-left: 12px;\n}\n\n.active {\n .menu-sidenav__icon, .menu-sidenav__item {\n color: color('primary');\n }\n}\n\n.menu-sidebar {\n position: absolute;\n top: $wise-toolbar-height + $md-toolbar-height;\n bottom: 0;\n left: 0;\n background-color: #fff;\n width: $menu-sidebar-width;\n overflow: hidden;\n padding: 8px 0;\n text-align: center;\n border-right: 1px solid color('gray');\n\n @media only screen and (max-width: ($layout-breakpoint-xs - 1)) {\n display: none;\n }\n}\n[dir=rtl] .menu-sidebar {\n right:0;\n left:auto;\n}\n\n.md-button.md-icon-button.menu-sidebar__link {\n margin-top: 6px;\n margin-bottom: 6px;\n}\n","// Variables\n$input-action-width: 48px;\n$input-action-vertical-offset: 0;\n$input-action-vertical-offset-richtext: -7px;\n\n// Base\n#node {\n margin: 0 auto;\n position: absolute;\n left: 0;\n right: 0;\n\n @media only screen and (min-width: $layout-breakpoint-xs) {\n padding: 8px;\n padding: 24px 16px;\n margin-bottom: 32px;\n }\n\n @media only screen and (min-width: $layout-breakpoint-sm) {\n padding: 32px;\n }\n\n &.ng-enter {\n transition: opacity .5s;\n opacity: 0;\n }\n\n &.ng-enter-active {\n opacity: 1;\n }\n}\n\n// TODO: use BEM conventions\n\n.node-notice {\n @media only screen and (min-width: $layout-breakpoint-xs) {\n margin-top: -8px;\n margin-bottom: 16px;\n }\n\n @media only screen and (min-width: $layout-breakpoint-sm) {\n margin-top: -16px;\n }\n}\n\n.node-content {\n padding: 0 0 48px;\n background-color: #ffffff;\n border-radius: $button-border-radius;\n overflow: visible;\n\n @media only screen and (max-width: $layout-breakpoint-xs - 1) {\n box-shadow: none;\n }\n\n @media only screen and (min-width: $layout-breakpoint-xs) {\n padding: 0;\n border-top: 2px solid;\n border-bottom: 2px solid;\n }\n}\n\nmd-content {\n &.node-content {\n background-color: #ffffff;\n }\n}\n\n.node-content__rubric {\n position: absolute;\n top: -22px;\n left: 0;\n right: 0;\n z-index: 1;\n\n .avatar--icon {\n transform: scale(0.94);\n\n @media only screen and (max-width: $layout-breakpoint-xs - 1) {\n transform: scale(0.8);\n }\n }\n}\n\n.node-icon {\n color: #ffffff;\n vertical-align: inherit;\n}\n\n.node-select {\n margin: 0 8px;\n min-width: 0;\n font-weight: 500;\n font-size: $body-font-size-base;\n\n .md-select-value {\n *:first-child {\n transform: translate3d(0,0,0);\n flex: 1 0 0;\n }\n\n .node-select__icon {\n display: none;\n }\n\n .node-select__status {\n display: none;\n }\n }\n\n .md-select-icon {\n margin-left: 0;\n color: color('text');\n }\n}\n\n.node-select-option--group {\n //color: rgba(0,0,0,0.54);\n background-color: color('gray-lightest');\n border-bottom: 1px solid color('gray-lighter');\n border-top: 1px solid color('gray-lighter');\n}\n\n.node-select-option--node {\n padding-left: 20px;\n}\n\n.node-select__icon {\n margin-right: 8px;\n}\n\n.node-select__status {\n margin-left: 8px;\n}\n\n.node-select__text {\n text-overflow: ellipsis;\n white-space: nowrap;\n overflow: hidden;\n\n @media only screen and (min-width: $layout-breakpoint-xs) {\n margin-top: 2px;\n }\n}\n\n.node-title {\n line-height: 1.2;\n text-transform: none;\n margin-top: 3px;\n\n @media only screen and (max-width: $layout-breakpoint-xs - 1) {\n font-size: $body-font-size-base;\n }\n}\n\n.node-content__actions {\n padding: 0 16px 16px;\n\n @media only screen and (min-width: $layout-breakpoint-sm) {\n padding: 0 24px 24px;\n }\n\n @media only screen and (min-width: $layout-breakpoint-md) {\n padding: 0 32px 32px;\n }\n\n .md-button:first-child {\n margin-left: 0;\n }\n\n .md-button:last-child {\n margin-right: 0;\n }\n}\n\n.node-content__actions__info {\n font-style: italic;\n margin-left: 8px;\n color: color('text-secondary');\n}\n\n.node-content__actions__more {\n border-bottom: 1px dotted;\n}\n\n.md-button.md-icon-button.node-nav {\n &:not(:first-of-type) {\n }\n\n &:first-of-type {\n margin-right: 0;\n }\n}\n\n.node-sidebar-active {\n @media only screen and (min-width: $layout-breakpoint-xs) {\n margin-right: ($md-toolbar-height + 16);\n }\n}\n\n.node-sidebar-visible {\n @media only screen and (max-width: $layout-breakpoint-xs - 1) {\n margin-bottom: $wise-toolbar-height;\n }\n}\n\n.node-sidebar {\n position: absolute;\n right: 0;\n top: 0;\n width: $md-toolbar-height;\n}\n\n.node-sidebar__toolbar {\n position: fixed;\n width: $md-toolbar-height;\n background-color: #ffffff;\n padding: 8px 0;\n border-radius: $button-border-radius;\n\n @media only screen and (max-width: $layout-breakpoint-xs - 1) {\n right: 0;\n bottom: 0;\n left: 0;\n width: 100%;\n border-radius: 0;\n padding: 0;\n min-height: 0;\n height: $wise-toolbar-height;\n }\n}\n\n// TODO: move to own sass module file (only gets used by teacher)\n// TODO: use BEM (.node--info)\n.node-info {\n margin: 0;\n\n @media only screen and (max-width: $layout-breakpoint-xs - 1) {\n margin: -16px;\n border-radius: 0;\n }\n\n .divider {\n margin-left: -8px;\n margin-right: -8px;\n }\n\n .component {\n &:first-child {\n margin-top: -16px;\n }\n }\n\n .component, .component__content, .component__header {\n margin-left: -8px;\n margin-right: -8px;\n }\n\n .component__actions {\n display: none;\n }\n}\n\n.node-rubric {\n border: 2px solid color('primary');\n margin: 8px 16px 24px;\n padding: 16px;\n background-color: #ffffff;\n}\n\n.node-rubric--component {\n\n}\n\n.node__label--vertical-alignment {\n vertical-align: middle;\n display: inline-block;\n}\n",".grading {\n}\n\n.grading__item-container {\n margin: 0 0 16px;\n padding: 0 !important;\n}\n\n.grading__item {\n background-color: #ffffff;\n\n .component {\n padding: 0;\n }\n}\n","// Variables\n$notebook-sidebar-width: 56px;\n\n// Base\n.notebook-launcher {\n @media only screen and (min-width: $layout-breakpoint-xs) {\n &.md-button.md-fab {\n z-index: 61;\n }\n }\n}\n\n.notebook-report {\n border-radius: 4px 4px 0 0;\n margin: 0;\n height: 100%;\n\n .note-toolbar {\n margin: -8px -8px 8px;\n padding: 4px;\n border: 0 none;\n border-top: 1px solid color('gray-light');\n border-bottom: 1px solid color('gray-light');\n border-radius: 0;\n\n .btn-group {\n margin-top: 0;\n }\n }\n\n .note-btn {\n border: 0 none;\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n border-bottom-left-radius: 0;\n border-bottom-right-radius: 0;\n }\n}\n\n.notebook-report-container {\n height: 550px;\n width: 450px;\n max-height: 90%;\n bottom: 0;\n right: 96px;\n position: absolute;\n z-index: 3;\n}\n\n.notes-visible {\n @media only screen and (min-width: $layout-breakpoint-sm) {\n .notebook-report-container {\n right: 516px;\n transition: right 250ms;\n }\n }\n}\n\n.notebook-report-container__full {\n top: 16px;\n bottom: 16px;\n left: 16px;\n right: 16px;\n max-height: none;\n max-width: none;\n height: auto;\n width: auto;\n\n .notebook-report {\n height: 100%;\n width: 100%;\n margin: 0 auto;\n max-height: none;\n border-radius: $card-border-radius;\n }\n}\n\n.notebook-report-container__collapsed {\n width: 300px;\n height: auto;\n\n .notebook-report__content, .notebook-report__actions, .notebook-report__content__header {\n display: none;\n }\n}\n\n.notebook-report__toolbar {\n background-color: color('gray-darkest') !important;\n border-radius: $card-border-radius $card-border-radius 0 0;\n}\n\n.notebook-report__toolbar__title {\n max-width: 150px;\n}\n\n.notebook-report__content {\n h1, h2, h3, h4 {\n font-size: rem(2.2);\n }\n\n background-color: #ffffff;\n\n .note-editor.note-frame {\n border: 0 none;\n border-radius: 0;\n padding: 0;\n box-shadow: none;\n }\n\n .note-resizebar {\n display: none;\n }\n}\n\n.notebook-report__content__header {\n padding: 8px;\n background-color: #ffffff;\n font-size: rem(1.6);\n}\n\n@media only screen and (max-width: $layout-breakpoint-xs - 1) {\n .notebook-report-container {\n &:not(.notebook-report-container__collapsed) {\n top: 0;\n bottom: 0;\n left: 0;\n right: 0;\n max-height: none;\n max-width: none;\n height: auto;\n width: auto;\n\n .notebook-report {\n border-radius: 0;\n }\n }\n }\n\n .notebook-tools--full {\n display: none;\n }\n}\n\n.notebook-report-backdrop {\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n z-index: 3;\n background-color: rgba(33,33,33,1.0);\n opacity: .48;\n}\n\n.notebook-menu {\n transition: opacity 500ms;\n\n &.ng-enter {\n opacity: 0;\n }\n\n .ng-enter-active {\n opacity: 1;\n transition-delay: 250ms;\n }\n\n &.ng-leave-active, &.ng-hide {\n opacity: 0;\n }\n\n &.ng-hide-add, &.ng-hide-add-active,\n &.ng-hide-remove, &.ng-hide-remove-active {\n opacity: 0;\n }\n}\n\n.notebook-item {\n transition: box-shadow 250ms;\n margin: 0 16px 16px;\n display: block;\n}\n\n.notebook-item__content {\n height: 250px;\n min-width: 230px;\n position: relative;\n padding: 0;\n background-color: color('gray');\n border-top-left-radius: $card-border-radius;\n border-top-right-radius: $card-border-radius;\n}\n\n.notebook-item__content__attachment, .notebook-item__content__text {\n position: absolute;\n left: 0;\n right: 0;\n}\n\n.notebook-item__content__attachment {\n background-repeat: no-repeat !important;\n border-top-left-radius: $card-border-radius;\n border-top-right-radius: $card-border-radius;\n background-position: center top !important;\n background-size: cover !important;\n top: 0;\n bottom: 0;\n}\n\n.notebook-item__content__text {\n bottom: 0;\n padding: 8px;\n font-weight: 500;\n overflow: hidden;\n max-height: 120px;\n min-height: 56px;\n background-color: rgba(255,255,255,0.95);\n border-top: 1px solid color('gray-lighter');\n\n &:after {\n content: \"\";\n text-align: right;\n position: absolute;\n bottom: 0;\n right: 0;\n width: 100%;\n height: 0.8em;\n background: linear-gradient(180deg,hsla(0,0%,100%,0),rgba(255,255,255,0.95) 100%);\n }\n}\n\n.notebook-item__content--text-only {\n &:after {\n content: \"note\";\n font-family: 'Material Icons';\n font-weight: normal;\n font-style: normal;\n display: inline-block;\n line-height: 1;\n text-transform: none;\n letter-spacing: normal;\n word-wrap: normal;\n white-space: nowrap;\n direction: ltr;\n\n /* Support for all WebKit browsers. */\n -webkit-font-smoothing: antialiased;\n /* Support for Safari and Chrome. */\n text-rendering: optimizeLegibility;\n\n /* Support for Firefox. */\n -moz-osx-font-smoothing: grayscale;\n\n /* Support for IE. */\n font-feature-settings: 'liga';\n //position: absolute;\n font-size: 80px;\n color: color('text-disabled');\n }\n}\n\n.notebook-item--question__content--text-only {\n content: \"live_help\";\n}\n\n.notebook-item__content__location {\n opacity: 0.9;\n padding: 8px 0;\n\n md-icon {\n font-size: 22px;\n }\n}\n\n.notebook-item__edit {\n cursor: pointer;\n}\n\n.notebook-item__actions {\n margin: 0;\n padding: 0 8px;\n color: #ffffff;\n background-color: color('gray-darkest');\n border-bottom-left-radius: $card-border-radius;\n border-bottom-right-radius: $card-border-radius;\n\n md-icon {\n color: #ffffff;\n }\n}\n\n.notebook-item__text-input {\n margin: 0;\n}\n\n.notebook-item__text-input__textarea {\n padding-left: 0;\n padding-right: 0;\n}\n\n.notebook-item__attachment {\n background-color: color('gray-light');\n padding: 16px;\n margin-bottom: 16px;\n text-align: center;\n position: relative;\n}\n\n.notebook-item__attachment__content {\n max-width: 100%;\n height: auto;\n}\n\n.notebook-item__attachment__delete {\n position: absolute;\n top: 4px;\n right: -2px;\n // TODO: generalize for on item buttons like this (delete attachment, etc)\n width: 34px !important;\n height: 34px !important;\n min-height: 0;\n\n md-icon {\n margin-left: -2px;\n font-size: 22px;\n }\n}\n\n.notebook-item__info {\n font-style: italic;\n opacity: .8;\n color: lighten(color('accent-1'), 5%);\n\n a, md-icon {\n color: lighten(color('accent-1'), 5%);\n }\n\n md-icon {\n font-size: 1.5em;\n min-width: 0;\n width: auto;\n }\n}\n\n.notebook-item__upload {\n text-align: center;\n padding: 24px;\n background-color: color('gray-lighter');\n margin-bottom: 16px;\n color: color('text-secondary');\n border-radius: 4px;\n cursor: pointer;\n border: 1px dashed transparent;\n transition: all 250ms;\n\n md-icon, span {\n transition: color 250ms;\n }\n\n &:hover, &:focus, &.dragover {\n border-color: color('accent');\n background-color: lighten(color('accent'), 35%);\n color: color('accent');\n\n md-icon, span {\n color: color('accent');\n }\n }\n}\n\n.view-notebook-item {\n width: $layout-breakpoint-xs;\n}\n\n.view-notebook-item__content {\n}\n\n.notebook-item--report {\n background-color: #ffffff;\n\n .note-editor {\n margin-bottom: 16px;\n border-color: color('gray');\n }\n}\n\n.notebook-item--report__container {\n &.ui-scrollpoint {\n .notebook-item--report__toolbar {\n position: fixed;\n top: ($wise-toolbar-height + $md-toolbar-height);\n left: 0;\n right: 0;\n z-index: 1;\n\n @media only screen and (min-width: $layout-breakpoint-xs) {\n left: ($notebook-sidebar-width - 2);\n }\n\n @media only screen and (min-width: $layout-breakpoint-xs) {\n padding: 0 24px;\n }\n\n @media only screen and (min-width: $layout-breakpoint-sm) {\n padding: 0 32px;\n }\n\n .note-toolbar {\n margin: 0 16px;\n\n @media only screen and (min-width: $layout-breakpoint-xs) {\n margin: 0 8px;\n }\n\n @media only screen and (min-width: $layout-breakpoint-sm) {\n margin: 0 16px;\n }\n }\n }\n\n .note-editor {\n padding-top: 40px;\n }\n }\n}\n\n.notebook-item--report__toolbar {\n\n .note-toolbar {\n background-color: color('gray-light');\n border: 1px solid color('gray');\n margin-bottom: -2px;\n }\n}\n\n.notebook-item--report__heading {\n text-align: center;\n margin-bottom: 32px;\n}\n\n.notebook-item--report__content {\n}\n\n.notebook-item--report__add-note {\n font-weight: 700;\n}\n\n.notebook-item--report__note-img {\n max-width: 100%;\n height: auto !important;\n}\n\n.notebook-sidebar {\n @media only screen and (min-width: $layout-breakpoint-xs) {\n width: 400px;\n max-width: none;\n }\n\n @media only screen and (min-width: $layout-breakpoint-sm) {\n width: 500px;\n max-width: none;\n }\n}\n\n.notebook-items {\n width: 100%;\n overflow: auto;\n margin-top: 16px;\n margin-bottom: 76px;\n\n .notebook-item {\n width: 100%;\n }\n\n .notebook-item__content {\n height: 200px;\n min-width: 0;\n }\n}\n\n.notebook-items--grading {\n margin-bottom: 0;\n}\n\n@media only screen and (max-width: ($layout-breakpoint-xs - 1)) {\n .notebook-enabled {\n .md-fab-bottom-right, .md-fab-bottom-left {\n bottom: ($wise-toolbar-height + 8) !important;\n }\n }\n}\n\n.notebook-grading {\n background-color: #ffffff;\n display: block;\n}\n",".notification-btn {\n width: 60px !important;\n\n md-icon {\n margin-left: 20px;\n }\n}\n\n.notification-count {\n border-radius: 50%;\n position: absolute;\n background-color: color('accent');\n width: 22px;\n left: 4px;\n height: 22px;\n line-height: 18px;\n font-size: 12px;\n font-weight: 700;\n border: 2px solid;\n\n &:before {\n content: \"\";\n position: absolute;\n right: -7px;\n top: 5px;\n border-left: 6px solid rgba(255,255,255,0.87);\n border-top: 4px solid transparent;\n border-bottom: 4px solid transparent;\n }\n}\n\n.notification-list {\n padding: 8px 0;\n}\n\n.notification-dismiss {\n width: 500px;\n}\n\n.notification-dismiss__input {\n margin-bottom: 0;\n}\n\nmd-list md-list-item .md-list-item-text h4,\nmd-list md-list-item.md-2-line .md-list-item-text h4,\nmd-list md-list-item.md-3-line .md-list-item-text h4 {\n &.notification-list-item__source {\n color: color('text-secondary');\n font-size: rem(1.2);\n\n md-icon {\n font-size: rem(1.8);\n min-width: 0;\n width: auto;\n margin-left: -4px;\n line-height: rem(2);\n }\n }\n}\n",".account-menu {\n border-radius: $card-border-radius;\n padding: 0;\n font-size: $body-font-size-base;\n max-width: 380px;\n\n @media (min-width: $layout-breakpoint-md) {\n min-width: 380px !important;\n }\n\n h3 {\n margin: 0;\n font-weight: 300;\n }\n}\n\n.account-menu--fixed-height {\n height: $max-menu-height;\n}\n\n.account-menu--fixed-width {\n width: 320px;\n\n @media (min-width: $layout-breakpoint-sm) {\n width: 380px;\n }\n}\n\n.account-menu__icon {\n background-color: color('text-light');\n border-radius: 50%;\n}\n\n.account-menu__caret {\n position: absolute;\n right: 28px;\n top: -8px;\n outline: none;\n\n &:before {\n content: '';\n position: absolute;\n border-bottom: 8px solid #fff;\n border-left: 8px solid transparent;\n border-right: 8px solid transparent;\n }\n}\n\n.account-menu__caret--pause, .account-menu__caret--notification {\n right: 80px;\n}\n\n.account-menu__caret--notification--with-pause {\n right: 132px;\n}\n\n[dir=rtl] {\n .account-menu__caret {\n right: auto;\n left: 28px;\n }\n .account-menu__caret--pause, .account-menu__caret--notification {\n left:80px;\n right:auto;\n }\n .account-menu__caret--notification--with-pause {\n left: 132px;\n right:auto;\n }\n}\n\n.account-menu__info {\n padding: 8px 12px;\n}\n\n.account-menu__info__title {\n font-weight: 500;\n}\n\n.account-menu__info__team {\n font-weight: 400;\n color: color('text-secondary');\n}\n\n.account-menu__users {\n padding: 0;\n\n md-list-item {\n padding: 0;\n\n .md-avatar {\n margin: 0 8px 0 0;\n height: 48px;\n width: 48px;\n }\n }\n}\n\n.account-menu__progress {\n md-progress-linear {\n transform: rotate(270deg);\n width: 26px;\n }\n}\n\n.account-menu__grade {\n position: relative;\n margin-right: 4px;\n\n md-icon {\n color: color('score');\n }\n}\n\n.account-menu__grade__overlay {\n position: absolute;\n top: 0;\n width: 100%;\n background-color: rgba(255, 255, 255, 0.6);\n}\n\n.account-menu__actions {\n background-color: color('gray-lightest');\n}\n\n.account-menu__control {\n padding: 16px;\n}\n",".annotations {\n margin: 16px 4px 16px 62px;\n position: relative;\n font-size: rem(1.5);\n\n hr {\n margin: 10px 0 8px;\n border-color: rgba(0,0,0,.12);\n }\n\n &:after {\n content: \"\";\n position: absolute;\n width: 0;\n height: 0;\n left: -16px;\n right: auto;\n top: 0px;\n bottom: auto;\n border-top: 20px solid transparent;\n border-bottom: 20px solid transparent;\n border-right: 16px solid color('gray-darker');\n }\n}\n\n.annotations-container--student--report {\n border-top: 1px solid color('gray-light');\n}\n\n.annotations--report {\n margin-top: 0;\n margin-bottom: 0;\n}\n\n.annotations__header {\n position: relative;\n //border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n padding: 10px 12px;\n font-weight: 700;\n transition: all 1s;\n color: #ffffff;\n background-color: color('gray-darker');\n}\n\n.annotations__avatar {\n background-color: color('accent');\n padding: 2px;\n position: absolute;\n top: 0;\n left: -62px;\n}\n\n.annotations__icon {\n transition: all 1s;\n color: #ffffff;\n}\n\n.annotations__body {\n padding: 12px;\n background-color: #ffffff;\n border-bottom-left-radius: $card-border-radius;\n border-bottom-right-radius: $card-border-radius;\n overflow: auto;\n}\n\n.annotations__status {\n background-color: #ffffff;\n color: color('info');\n display: inline-block;\n margin-left: 8px;\n font-size: rem(1.2);\n\n &.ng-enter, &.ng-leave {\n transition: all 1s;\n }\n\n &.ng-enter, &.ng-leave.ng-leave-active {\n opacity:0;\n }\n\n &.ng-leave, &.ng-enter.ng-enter-active {\n opacity:1;\n }\n}\n\n.annotations__score {\n font-weight: 700;\n}\n\n.annotations__info {\n font-style: italic;\n opacity: 0.8;\n border-bottom: 1px dotted;\n font-size: rem(1.3);\n}\n\n.annotations--inside {\n .annotations {\n margin-left: 72px;\n }\n}\n\n// TODO: move to own file\n.annotations--info {\n margin-bottom: 32px;\n margin-right: 8px;\n margin-left: 72px;\n\n @media only screen and (min-width: ($layout-breakpoint-xs)) {\n margin: 16px 16px 32px 76px;\n }\n\n &:after {\n border-right: 16px solid color('info');\n }\n\n .annotations__avatar {\n background-color: #ffffff;\n }\n\n .annotations__header {\n background-color: color('info');\n }\n}\n",".annotations--grading {\n md-input-container {\n margin-bottom: 0;\n }\n\n .md-errors-spacer {\n display: none;\n }\n\n input, textarea {\n //border-width: 0;\n //background-color: color('text-light-secondary');\n\n &:focus {\n background-color: #ffffff;\n }\n\n &:disabled {\n color: color('text');\n }\n }\n}\n\n.annotations--grading--revision {\n margin: 8px 0 0;\n padding: 8px;\n}\n\n.annotations--notebook {\n margin-top: 16px;;\n}\n\n.annotations--grading__info {\n font-style: italic;\n margin: 8px 8px 4px;\n}\n\n.annotations--grading__item {\n padding: 8px;\n}\n\n.annotations--grading__score {\n input {\n margin-top: 0 !important;\n font-size: rem(1.8);\n width: 52px;\n text-align: center;\n }\n}\n\n.annotations--grading__score__label {\n transform: none !important;\n width: auto;\n display: block;\n padding: 0;\n margin: 0 8px 0 0;\n}\n\n.annotations--grading__score__max {\n label {\n display: none;\n }\n}\n\n.annotations--grading__score__divider {\n position: relative;\n top: 12px;\n margin-left: 4px;\n}\n\n.annotations--grading__auto-comment {\n margin: 0 2px;\n}\n\n.annotations--grading__auto-comment__content {\n margin-top: 8px;\n}\n",".component {\n position: relative;\n}\n\n.component__wrapper {\n padding: 0 16px;\n margin: 24px 0;\n}\n\n.component__content {\n overflow-x: auto;\n font-size: rem(1.5);\n overflow-y: hidden; // TODO: figure out why this is needed after update to ng-material 1.1.1\n\n @media only screen and (min-width: $layout-breakpoint-xs) {\n padding: 0 8px;\n }\n}\n\nh3.component__header, .component-header {\n padding: 8px 12px;\n margin: 0;\n font-size: rem(1.4);\n}\n\n.component__rubric {\n position: absolute;\n left: -20px;\n top: 12px;\n}\n\n.notebook-enabled {\n .component_content {\n img {\n transition: all 250ms;\n cursor: pointer;\n cursor: copy;\n //position: relative;\n //border: 2px solid transparent;\n\n &:hover, &:focus {\n box-shadow: 0 0 5px 1px color('accent');\n //border: 2px solid #ffffff;\n }\n }\n }\n}\n\n.component__actions {\n .md-button:first-child {\n margin-left: 0;\n }\n\n .md-button:last-child {\n margin-right: 0;\n }\n}\n\n.component__actions__info {\n font-style: italic;\n margin-left: 8px;\n //color: color('accent-1');\n color: color('text-secondary');\n}\n\n.component__actions__more {\n border-bottom: 1px dotted;\n}\n\n.component__prompt {\n margin-bottom: 8px;\n font-weight: 500;\n}\n\n.component__prompt__content {\n display: inline;\n}\n\n.component__attachment {\n position: relative;\n margin: 0 8px;\n padding-bottom: 8px;\n\n @media only screen and (min-width: $layout-breakpoint-xs) {\n padding-top: 8px;\n }\n}\n\n.component__add-attachment {\n @media only screen and (max-width: ($layout-breakpoint-xs - 1)) {\n width: 100%;\n }\n}\n\n.component__attachment__content {\n max-height: 100px;\n width: auto;\n}\n\n.component__attachment__delete {\n position: absolute;\n top: 0;\n right: 0;\n min-width: 0;\n background-color: rgba(255, 255, 255, 0.75) !important;\n border-radius: 0;\n padding: 4px;\n margin: 0;\n\n //@media only screen and (min-width: $layout-breakpoint-sm) {\n //margin-top: 8px;\n //}\n\n > md-icon {\n margin-top: 0;\n }\n}\n\n.component__revision {\n margin: 8px 0;\n padding: 8px;\n\n &:nth-child(odd) {\n background-color: color('gray-lightest');\n }\n}\n\n.component__revision__content {\n padding: 4px 0 8px 0;\n border-bottom: 1px solid color('gray-light');\n}\n\n.component__revision__actions {\n color: color('gray-darker');\n padding-top: 4px;\n}\n","// Variables\n\n// Base\n.component__content--Discussion {\n overflow: hidden;\n}\n\n.discussion-content {\n background-color: color('gray-lighter');\n //margin: 0 0 -16px;\n box-shadow: inset 0 0 3px color('gray-dark');\n}\n\n.discussion-posts {\n padding: 12px 12px 8px;\n\n @media only screen and (min-width: $layout-breakpoint-md) {\n padding: 16px 16px 0;\n }\n}\n\n.discussion-post {\n margin: 0 auto 16px;\n max-width: $layout-breakpoint-xs;\n\n @media only screen and (min-width: $layout-breakpoint-xs) {\n margin-bottom: 24px;\n }\n\n @media only screen and (min-width: $layout-breakpoint-md) {\n margin-bottom: 32px;\n }\n\n // angular-material fix for when discussion posts are shown inside an md-list-item (e.g. in the grading tool)\n md-divider {\n position: relative;\n width: auto;\n }\n}\n\n.discussion-post__contents {\n padding: 16px;\n}\n\n.discussion-post__avatar, md-list-item > .md-avatar.discussion-post__avatar {\n margin-right: 8px;\n}\n\n.discussion-post__avatar--reply, md-list-item > .md-avatar.discussion-post__avatar--reply {\n margin-top: 8px;\n}\n\n\n.discussion-post__user, md-list-item .md-list-item-text h3.discussion-post__user {\n padding-bottom: 4px;\n font-weight: 700;\n line-height: 1.3;\n overflow: visible;\n white-space: normal;\n}\n\n.discussion-post__date {\n color: color('gray-dark');\n}\n\n.discussion-post__date--reply {\n margin-left: 8px;\n font-weight: 400;\n}\n\n.discussion-post__content {\n margin-top: 16px;\n white-space: pre-wrap;\n}\n\n.discussion-post__attachment {\n max-width: 100%;\n height: auto !important;\n margin-top: 16px;\n}\n\n.discussion-new {\n background-color: #ffffff;\n max-width: 570px;\n margin-left: auto;\n margin-right: auto;\n padding: 8px;\n transition: all 250ms;\n transform: scale(0.95);\n}\n\n.discussion-new--focused {\n transform: scale(1);\n}\n\nmd-input-container.discussion-new__input-container {\n margin: 0;\n padding: 0;\n\n > textarea.md-input {\n min-height: 68px;\n }\n}\n\n.discussion-new__input--textarea, .input-container textarea.discussion-new__input--textarea {\n padding: 8px;\n border: 0 none;\n}\n\n.discussion-new__actions {\n padding: 0 8px;\n\n .md-button {\n &:first-of-type {\n margin-left: 0;\n }\n\n &:last-of-type {\n margin-right: 0;\n }\n }\n}\n\n.discussion-new__attachment {\n padding: 0;\n margin: 0 0 8px;\n}\n\n.discussion-new__attachment__content {\n margin-top: 0;\n margin-bottom: 16px;\n}\n\n.discussion-comments {\n padding: 0;\n}\n\n.discussion-comments__contents {\n background-color: color('gray-lightest');\n}\n\n.discussion-comments__header {\n background-color: transparent;\n padding: 0;\n\n .md-subheader-inner {\n padding-bottom: 8px;\n }\n}\n\n.discussion-comments__list {\n padding: 0;\n max-height: 9999px;\n overflow-y: auto;\n\n @media only screen and (min-width: $layout-breakpoint-xs) {\n max-height: 400px;\n }\n}\n\n.input--textarea.discussion-reply__input, .input-container textarea.input--textarea.discussion-reply__input {\n background-color: #ffffff;\n padding: 4px;\n font-size: ($body-font-size-base) - 1;\n border: 0 none;\n margin-left: -1px;\n margin-bottom: 0;\n resize: none;\n}\n\n.discussion-reply, md-list-item.discussion-reply {\n margin: 0 12px 8px;\n padding: 0;\n min-height: 56px;\n}\n\n.discusstion-reply__details, md-list-item .md-list-item-text.discusstion-reply__details {\n margin: 8px 0;\n}\n\n.discussion-post__user--reply, md-list-item .md-list-item-text h3.discussion-post__user--reply {\n font-size: ($body-font-size-base) - 1;\n padding: 0;\n margin: 0;\n}\n\n.discusstion-reply__content {\n margin-top: 2px;\n\n p {\n font-weight: 400 !important;\n color: color('body') !important;\n }\n}\n\n.discussion-new-reply {\n padding: 8px;\n}\n\n.discussion-new-reply__input-container {\n padding-top: 0;\n margin: 0;\n\n .md-errors-spacer {\n display: none;\n }\n}\n\n.discussion-new-reply__actions {\n margin-left: 8px;\n\n .md-button {\n margin-top: 0;\n margin-bottom: 0;\n }\n}\n",".embedded-content {\n\n}\n\n.embedded-content__iframe {\n border: 0 none;\n}\n",".component--grading {\n padding: 0;\n margin: 0;\n\n &:not(:last-child) {\n > div {\n border-bottom: 1px solid color('gray-light');\n }\n }\n\n .component__wrapper {\n padding: 0;\n margin: 0;\n }\n\n .component__content {\n padding: 16px;\n margin: 0;\n }\n}\n\n.component--grading__response {\n padding-bottom: 16px;\n\n @media only screen and (min-width: $layout-breakpoint-sm) {\n padding-right: 16px;\n padding-bottom: 0;\n }\n}\n\n.component--grading__response__content {\n overflow: auto;\n}\n\n.component--grading__annotations {\n background-color: color('selected-bg');\n}\n\n.component--grading__annotations__divider {\n padding: 4px;\n background-color: #ffffff;\n}\n\n.component--grading__actions__info {\n margin: 16px 0 0;\n padding-top: 8px;\n border-top: 1px solid color('gray-lighter');\n}\n",".graph-select {\n min-width: 150px;\n max-width: 200px;\n}\n\n.graph-controls {\n margin: 8px 0;\n padding: 8px 0;\n border: 1px solid color('gray-lighter');\n border-left-width: 0;\n border-right-width: 0;\n}\n","// Variables\n\n// Base\n.match-content {\n background-color: color('gray-lighter');\n margin-bottom: 16px;\n padding: 8px;\n box-shadow: inset 0 0 3px color('gray-dark');\n}\n\n.match-divider {\n margin: 16px 8px 8px;\n}\n\n.match-divider--horizontal {\n @media only screen and (min-width: $layout-breakpoint-sm) {\n display: none;\n }\n}\n\n.match-bucket__header {\n padding: 12px;\n font-weight: 500;\n color: color('primary');\n}\n\n.match-bucket__content {\n padding: 0;\n}\n\n.match-bucket--choices {\n .match-bucket__header {\n color: color('accent-1');\n }\n}\n\n.match-bucket__contents {\n min-height: 120px;\n //margin: 0;\n padding: 0 8px 8px;\n background-color: color('gray-light');\n transition: background-color 250ms;\n border-bottom-left-radius: $card-border-radius;\n border-bottom-right-radius: $card-border-radius;\n\n column-gap: 8px;\n\n @media only screen and (max-width: ($layout-breakpoint-xs - 1)) {\n column-count: 1 !important;\n }\n\n img {\n max-width: 100%;\n height: auto;\n }\n}\n\n.match-bucket__contents--over {\n background-color: color('primary');\n}\n\n.match-bucket__item {\n list-style-type: none;\n cursor: move;\n padding-top: 8px;\n break-inside: avoid;\n\n &:hover, &:focus {\n //background-color: rgba(0,0,0,0.2);\n }\n}\n\n.match-bucket__item__contents {\n background-color: #ffffff;\n padding: 8px !important;\n border: 1px solid color('gray');\n\n .md-list-item-text {\n width: 100%;\n }\n}\n\n.match-bucket__item__contents__text {\n margin-right: 4px;\n}\n\n.match-feedback {\n transition: opacity 250ms;\n /*padding-left: 8px;\n border-left: 4px solid;*/\n margin: 8px -8px -8px;\n color: #ffffff;\n padding: 4px 8px;\n\n &.ng-hide {\n opacity: 0;\n transition: opacity 1ms;\n }\n\n md-icon {\n color: #ffffff;\n }\n}\n",".outside-content {\n iframe {\n border: 1px solid color('gray-lighter');\n }\n}\n\n.outside-content__source {\n margin-top: 4px;\n text-align: end;\n\n a {\n max-width: 240px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n display: inline-block;\n }\n}",".component-revisions {\n .component {\n padding: 0;\n margin: 0;\n }\n\n .component__content {\n padding: 0;\n }\n\n .component__wrapper {\n margin: 16px 0;\n }\n\n .md-resize-handle {\n display: none;\n }\n}\n\n.component-revisions__item, md-list-item.component-revisions__item {\n padding: 0;\n}\n\n.component-revisions__item--latest {\n margin-bottom: 24px;\n}\n\n.component-revisions__item__text {\n\n}\n\n.component-revisions__annotation-label {\n margin-right: 8px;\n}\n\n.component-revisions__has-auto-and-teacher {\n padding-top: 8px;\n margin-top: 8px;\n border-top: 1px solid color('gray-light');\n}\n",".notebook-toolbar {\n md-divider {\n margin: 8px 0;\n }\n\n @media only screen and (max-width: ($layout-breakpoint-sm - 1)) {\n border-top: 1px solid color('gray-light');\n }\n}\n\n.notebook-toolbar__add-menu {\n position: absolute;\n bottom: 40px;\n\n .md-fab-action-item {\n background-color: #ffffff;\n }\n}\n\n.notebook-toolbar__add-icon {\n border-radius: 50%;\n}\n\n#closeNotebookSettingsButton {\n float:right;\n}\n\n[dir=rtl] #closeNotebookSettingsButton {\n float:left;\n}","highchart {\n display: block;\n}\n"]} \ No newline at end of file +{"version":3,"sources":["src/main/webapp/wise5/themes/default/style/base/_presets.scss","src/main/webapp/wise5/themes/default/style/base/_config--monitor.scss","src/main/webapp/wise5/themes/default/style/base/_config.scss","src/main/webapp/wise5/themes/default/style/base/_helpers.scss","src/main/webapp/wise5/themes/default/style/layouts/_l-default.scss","src/main/webapp/wise5/themes/default/style/material/_config.scss","src/main/webapp/wise5/themes/default/style/layouts/_l-footer.scss","src/main/webapp/wise5/themes/default/style/layouts/_l-header.scss","src/main/webapp/wise5/themes/default/style/layouts/_l-main.scss","src/main/webapp/wise5/themes/default/style/monitor.css","src/main/webapp/wise5/themes/default/style/layouts/_l-notebook.scss","src/main/webapp/wise5/themes/default/style/layouts/_l-nav.scss","src/main/webapp/wise5/themes/default/style/layouts/_l-sidebar.scss","src/main/webapp/wise5/themes/default/style/modules/_alerts.scss","src/main/webapp/wise5/themes/default/style/modules/_dragula.scss","src/main/webapp/wise5/themes/default/style/modules/_dialog.scss","src/main/webapp/wise5/themes/default/style/modules/_help.scss","src/main/webapp/wise5/themes/default/style/modules/_inputs.scss","src/main/webapp/wise5/themes/default/style/modules/_table.scss","src/main/webapp/wise5/themes/default/style/modules/_toolbar.scss","src/main/webapp/wise5/themes/default/style/modules/_list.scss","src/main/webapp/wise5/themes/default/style/modules/_notice.scss","src/main/webapp/wise5/themes/default/style/modules/_milestones.scss","src/main/webapp/wise5/themes/default/style/modules/_nav.scss","src/main/webapp/wise5/themes/default/style/modules/_nav--grading.scss","src/main/webapp/wise5/themes/default/style/modules/_menu.scss","src/main/webapp/wise5/themes/default/style/modules/_node.scss","src/main/webapp/wise5/themes/default/style/modules/_grading.scss","src/main/webapp/wise5/themes/default/style/modules/_notebook.scss","src/main/webapp/wise5/themes/default/style/modules/_notifications.scss","src/main/webapp/wise5/themes/default/style/modules/_account-menu.scss","src/main/webapp/wise5/themes/default/style/modules/_annotations.scss","src/main/webapp/wise5/themes/default/style/modules/_annotations--grading.scss","src/main/webapp/wise5/themes/default/style/modules/_component.scss","src/main/webapp/wise5/themes/default/style/modules/_component--discussion.scss","src/main/webapp/wise5/themes/default/style/modules/_component--embedded.scss","src/main/webapp/wise5/themes/default/style/modules/_component--grading.scss","src/main/webapp/wise5/themes/default/style/modules/_component--graph.scss","src/main/webapp/wise5/themes/default/style/modules/_component--match.scss","src/main/webapp/wise5/themes/default/style/modules/_component--outside.scss","src/main/webapp/wise5/themes/default/style/modules/_component--revisions.scss","src/main/webapp/wise5/themes/default/style/modules/_notebook-toolbar.scss","src/main/webapp/wise5/themes/default/style/modules/_highcharts.scss"],"names":[],"mappings":"AAIA,KACE,eCSuB,CDVzB,SAIM,eAAgB,CAItB,gBAEQ,aCbgB,CDiBxB,WACE,wBAAgD,CAChD,WAAY,CACZ,aAAc,CAGd,oBAAuB,CAAvB,sBAAuB,CAGzB,qBAEQ,UAAW,CACX,iBAAkB,CAClB,iBAAkB,CAClB,WAAY,CACZ,wBC3BW,CD+BnB,kCAEQ,QAAS,CACT,QAAS,CAKjB,OACI,iBEQoB,CFPpB,eAAgB,CAChB,cGlC8B,CHmC9B,eAAgB,CAChB,iBAAkB,CAClB,qBClCkB,CD4BtB,iBASQ,WAAY,CACZ,YAAa,CACb,mBAAoB,CAX5B,8CAcY,qBC1CU,CD4BtB,uBAkBY,uBC9CU,CDmDtB,aACI,wBC3Da,CD4Db,UAAc,CAGlB,aACI,wBCjEa,CDkEb,UAAc,CAGlB,gBACI,wBCpEgB,CDqEhB,UAAc,CAIlB,qBACI,aAAc,CAGlB,iBACI,uBAAwB,CAI5B,EACE,aC7FsB,CD8FtB,cAAe,CAGjB,QACI,kCAAuC,CACvC,qBChFyB,CDoF7B,QACE,iBAAkB,CAClB,sBAAuB,CAGzB,gBACE,iBExDsB,CF4DxB,cAEI,WAAqC,CACrC,UAAoC,CAHxC,cAMI,WAAqC,CACrC,UAAoC,CAPxC,cAUI,WAAqC,CACrC,UAAoC,CAXxC,cAcI,WAAqC,CACrC,UAAoC,CAKxC,cACE,qBCxHqB,CDyHrB,4BAA8B,CAFhC,8BAKM,WA1ImB,CAqIzB,oBASI,WAAY,CACZ,UAAW,CAVf,oBAaI,WAAY,CACZ,UAAW,CAdf,oBAiBI,WAAY,CACZ,UAAW,CAlBf,oBAqBI,WAAY,CACZ,UAAW,CAIf,wDAEI,qBC7ImC,CD2IvC,4EAOM,qBCjJgC,CDuJtC,mDAEI,uBAAkC,CAFtC,mDAKI,uBAAkC,CALtC,6CAQI,uBAA+B,CARnC,6CAWI,uBAA+B,CAXnC,iDAcI,uBAAiC,CAdrC,qDAiBI,uBAAmC,CAjBvC,qDAoBI,uBAAmC,CAKvC,uCACE,qBCnL2B,CDsL7B,uFACE,wBCzM0C,CD4M5C,2HAEE,aC/MsB,CDgNtB,wBC/M0C,CDqNxC,SACE,aCvNkB,CDsNpB,QACE,aClNa,CDiNf,UACE,aCjNe,CDgNjB,UACE,aChNe,CD+MjB,MACE,aC/MW,CD8Mb,MACE,aC9MW,CD6Mb,SACE,aC7Mc,CD4MhB,SACE,qBC5M0B,CD2M5B,eACE,aC3MoB,CD0MtB,cACE,UC1MmB,CDyMrB,YACE,UCzMiB,CDwMnB,MACE,UCxMW,CDuMb,WACE,UCvMgB,CDsMlB,aACE,aCtMkB,CDqMpB,cACE,UCrMmB,CDoMrB,MACE,qBCpMuB,CDmMzB,gBACE,qBCnMiC,CDkMnC,eACE,qBClMgC,CDiMlC,YACE,UCjMgC,CDgMlC,sBACE,wBChM6C,CD+L/C,qBACE,wBC/L4C,CD8L9C,aACE,aCtNsC,CDqNxC,OACE,aC7LY,CD4Ld,MACE,qBCpMuB,CDmMzB,SACE,UC1MmB,CDgNrB,YACE,wBC9NkB,CD6NpB,WACE,wBCzNa,CDwNf,aACE,wBCxNe,CDuNjB,aACE,wBCvNe,CDsNjB,SACE,wBCtNW,CDqNb,SACE,wBCrNW,CDoNb,YACE,wBCpNc,CDmNhB,YACE,gCCnN0B,CDkN5B,kBACE,wBClNoB,CDiNtB,iBACE,qBCjNmB,CDgNrB,eACE,qBChNiB,CD+MnB,SACE,qBC/MW,CD8Mb,cACE,qBC9MgB,CD6MlB,gBACE,wBC7MkB,CD4MpB,iBACE,qBC5MmB,CD2MrB,SACE,gCC3MuB,CD0MzB,mBACE,gCC1MiC,CDyMnC,kBACE,gCCzMgC,CDwMlC,eACE,qBCxMgC,CDuMlC,yBACE,mCCvM6C,CDsM/C,wBACE,mCCtM4C,CDqM9C,gBACE,wBC7NsC,CD4NxC,UACE,wBCpMY,CDmMd,SACE,gCC3MuB,CD0MzB,YACE,qBCjNmB,CDuNrB,kCAEQ,cCtOY,CDoOpB,iCAEQ,cCjOO,CD+Nf,mCAEQ,cChOS,CD8NjB,mCAEQ,cC/NS,CD6NjB,+BAEQ,cC9NK,CD4Nb,+BAEQ,cC7NK,CD2Nb,kCAEQ,cC5NQ,CD0NhB,kCAEQ,sBC3NoB,CDyN5B,wCAEQ,cC1Nc,CDwNtB,uCAEQ,WCzNa,CDuNrB,qCAEQ,WCxNW,CDsNnB,+BAEQ,WCvNK,CDqNb,oCAEQ,WCtNU,CDoNlB,sCAEQ,cCrNY,CDmNpB,uCAEQ,WCpNa,CDkNrB,+BAEQ,sBCnNiB,CDiNzB,yCAEQ,sBClN2B,CDgNnC,wCAEQ,sBCjN0B,CD+MlC,qCAEQ,WChN0B,CD8MlC,+CAEQ,yBC/MuC,CD6M/C,8CAEQ,yBC9MsC,CD4M9C,sCAEQ,cCrOgC,CDmOxC,gCAEQ,cC5MM,CD0Md,+BAEQ,sBCnNiB,CDiNzB,kCAEQ,WCzNa,CGXzB,eACE,gBAAiB,CACjB,iBAAkB,CAClB,cAAe,CACf,iBAAkB,CAElB,yBANF,eAOM,YCW2B,CDThC,CAED,kBACE,WCK8B,CDJ9B,cAAe,CEbjB,UACE,cAAe,CACf,QAAS,CACT,MAAO,CACP,OAAQ,CACR,SAAU,CACV,qBAAyB,CACzB,yBLIuB,CKAzB,gBACE,QAAS,CACT,aAAc,CACd,gBAAiB,CACjB,WAAY,CACZ,YAAa,CAGf,yBACE,gBAAiB,CCpBnB,UACI,SAAU,CADd,gBAIQ,uBAAyB,CACzB,WAAY,CACZ,UAAW,CACX,qBAAsB,CAP9B,qBAWQ,cAAe,CACf,YAAa,CACb,aAAc,CACd,iBAAkB,CAElB,yCAhBR,qBAiBY,aAAc,CAMrB,CAvBL,sDAqBY,QAAc,CAKtB,yCA1BJ,6FA6BgB,cJlBkB,CImBrB,CC9Bb,QACE,qBPUuB,COPzB,sBACE,eNmCwB,CMhC1B,SACE,yBAA2B,CAG7B,cACE,aAAc,CACd,WAAY,CACZ,iBAAkB,CAClB,MAAO,CACP,OAAQ,CACR,sBAAyB,CAEzB,yCARF,cASI,YAAa,CAuBhB,CAhCD,uBAaI,SAAU,CAbd,+BAiBI,SAAU,CACV,qBAAuB,CAlB3B,gLA8BI,SAAU,CAId,6BACE,WAAY,CAEZ,yCAHF,6BAII,gBAAiB,CACjB,YAAa,CAEhB,CAEC,yCCwZA,uCDvZE,gBAAiB,CACjB,iBAAkB,CAErB,CAED,cACE,YAAa,CADf,mDAMI,eAAgB,CAChB,YAAa,CACb,eAAgB,CAChB,cL3D8B,CK6D9B,yCAXJ,mDAYM,cL9D4B,CK+D5B,iBAAkB,CAErB,CAID,yCADF,oBAEI,cAAe,CAElB,CAED,0CAEE,YAAa,CAFf,kEAKI,gBAAiB,CAEjB,yCAPJ,kEAQM,aAAc,CACd,cAAe,CAElB,CAXH,0DAcI,0BAA2B,CAI/B,2FAEE,gBAAiB,CEzGnB,mBCCI,+BDC6C,CAFjD,YACI,eAC6C,CEEjD,mBACE,+BAAoC,CACpC,uBAAmC,CAFrC,6BAKI,qBXQyB,CYpB7B,aACI,YAAa,CACb,SAAU,CACV,qBAAsB,CAG1B,uBACI,WAAY,CACZ,UAAW,CACX,YAAa,CACb,mBAAoB,CACpB,YAAa,CACb,SAAU,CAGd,qBACI,qBAAyB,CAG7B,2BACI,cAAe,CACf,oBAAqB,CCrBzB,WACE,wBAA0B,CAC1B,kBAAoB,CACpB,sBAAwB,CACxB,UAAY,CACZ,mCAAqC,CAEvC,SACE,sBAAwB,CAE1B,iBACE,kCAAoC,CACpC,+BAAiC,CACjC,8BAAgC,CAChC,0BAA4B,CAE9B,YACE,UAAY,CCjBd,UACI,WVkB4B,CUfhC,cACI,WVe4B,CUZhC,eACI,YVY6B,CWrBjC,aACI,iBdqDoB,CcpDpB,eAAgB,CAEhB,yBAJJ,aAKQ,eAAuC,CAU9C,CAPG,yBARJ,aASQ,eAAuC,CAM9C,CAHG,0BAZJ,aAaQ,gBAAuC,CAE9C,CAOD,kDAJI,0BdoCoB,CcnCpB,2BfTa,CeYjB,8BAGI,kBAAqB,CACrB,wBfhBa,CeYjB,8CAOQ,cAAe,CACf,aAAc,CACd,gBAAiB,CAIzB,sBACI,aAAc,CACd,gBAAiB,CACjB,gBAAiB,CAGrB,sBACI,6BdYoB,CcXpB,8BdWoB,CcRxB,qBACI,iBdOoB,CcLpB,QAAc,CACd,4CAAiD,CACjD,cbrC8B,CasC9B,SAAU,CANd,uDASQ,iBAAkB,CAClB,UAAW,CACX,WAAY,CAXpB,0DAgBY,KAAM,CACN,SAAU,CAjBtB,kFAoBgB,gCfxDC,CeyDD,kCAAmC,CACnC,mCAAoC,CACpC,iBAAkB,CAClB,SAAU,CAxB1B,yFA4BgB,QAGuC,CA/BvD,4DAoCY,YAAa,CACb,SAAU,CArCtB,oFAwCgB,6Bf5EC,Ce6ED,kCAAmC,CACnC,mCAAoC,CACpC,iBAAkB,CAClB,SAAU,CA5C1B,2FAgDgB,QAGuC,CAnDvD,4DAwDY,QAAS,CACT,UAAW,CAzDvB,oFA4DgB,+BfhGC,CeiGD,oCAAqC,CACrC,iCAAkC,CAClC,iBAAkB,CAClB,MAAO,CACP,SAAU,CAjE1B,2FAqEgB,QAGqC,CAxErD,6DA6EY,QAAS,CACT,WAAY,CA9ExB,qFAiFgB,8BfrHC,CesHD,oCAAqC,CACrC,iCAAkC,CAClC,iBAAkB,CAClB,OAAQ,CACR,SAAU,CAtF1B,4FA0FgB,QAGqC,CCrIrD,iBACI,gBAAiB,CAGrB,4BACI,eAAgB,CAGpB,4CAEQ,cAAe,CAFvB,kDAMQ,YAAa,CAIrB,eACI,iBAAkB,CAGtB,yDAEQ,ahB7BgB,CgBiCxB,2DACI,WAAY,CACZ,wBhBvBsB,CgBwBtB,qBhBrBa,CgBsBb,iBAAkB,CAJtB,uEAOQ,qBAAyB,CAPjC,+EAWQ,qBhBxB+B,CgB4BvC,0CACI,UAAW,CAGf,2BACI,qBhBjCmC,CgBoCvC,yBACI,iBAAkB,CAClB,UAAW,CAFf,2CAKQ,+BAAwC,CAIhD,mCACI,OAjE8B,CAmE9B,4DACI,QAnEoC,CAuE5C,mCACI,UAzE8B,CA2E9B,4DACI,WAAsD,CAK9D,mHACI,eAAgB,CAChB,qBhBjEyB,CgB+D7B,6JAKQ,ahBvFgB,CgB2FxB,oBAEQ,sBAAuB,CACvB,eAAgB,CAChB,cAAe,CACf,eAAgB,CAChB,qBhB7E+B,CgBkFnC,yCADJ,wBAEQ,eAAgB,CAMvB,CAHG,yCALJ,wBAMQ,eAAgB,CAEvB,CAED,yCAEQ,qBAAyB,CAFjC,+DAKY,eAAgB,CAChB,qBhBxGa,CgB6GzB,gBACI,Wf5EiC,Ce2ErC,sBAIQ,WAAY,CACZ,UAAW,CACX,aAAc,CACd,YAAa,CACb,QAAc,CACd,cdtH0B,CcuH1B,eAAgB,CCrIxB,OACE,cAAe,CACf,UAAW,CACX,eAAgB,CAChB,YAAa,CAJf,kHAYM,qBjBIW,CiBHX,WAAY,CACZ,cfA4B,CeC5B,eAAgB,CAChB,WAAY,CACZ,cAAe,CACf,kBAAmB,CAlBzB,6BAwBI,wBjBXsB,CiBYtB,SAAU,CACV,kBAAmB,CA1BvB,0BA8BI,QAAS,CA9Bb,yBAkCI,YAAa,CAIjB,4BAGM,gBAAiB,CAKvB,mBACE,UAAW,CAGb,aACE,QAAc,CACd,wBAAyB,CACzB,qBAAyB,CACzB,cAAe,CACf,aAAc,CALhB,gCASI,aAAc,CACd,QAAc,CAVlB,gBAcI,eAAgB,CAChB,WAAY,CAfhB,0BAoBM,iBAAkB,CAClB,eAAgB,CAChB,UAAW,CACX,mBAAoB,CACpB,iBAAkB,CAClB,eAAmB,CAKzB,mBACE,eb9D8B,CakE9B,yCADF,mBAEI,eAAgB,CAEnB,CAED,oBACE,cf7EgC,Ce8EhC,eAAgB,CAGlB,wBACE,WAAY,CACZ,QAAS,CAGX,wBACE,wBjBnFsB,CiBoFtB,UjB/EoC,CiBgFpC,ehB5DwB,CgB6DxB,WhB7DwB,CgBgE1B,0BACE,UAAc,CACd,mBAAoB,CACpB,QAAS,CACT,WAAY,CACZ,kBAAmB,CACnB,eAAgB,CAChB,UAAW,CAGb,0BACE,QAAS,CAGX,mCACE,wBAAyB,CAG3B,UACE,eAAgB,CAChB,kBAAmB,CACnB,eAAgB,CAIhB,yCADF,eAEI,eAAgB,CAChB,eAAgB,CAChB,sBAAuB,CACvB,kBAAmB,CAEtB,CC1ID,kBACI,cAAe,CADnB,2HAWY,WAAY,CAKxB,kBACI,ejB0BsB,CiB3B1B,oCAIQ,WjBuBkB,CiBtBlB,ejBsBkB,CiB3B1B,4CASQ,WjBkBkB,CiBjBlB,gBjBiBkB,CiBhBlB,UjBgBkB,CiBZ1B,wCAEQ,aAAc,CACd,chBpB0B,CgBwBlC,qBACI,+BAAkD,CADtD,uCAIQ,chB5B0B,CgB6B1B,eAAgB,CAIxB,SACI,cAAe,CACf,MAAO,CACP,OAAQ,CACR,Qd5CoB,Cc6CpB,SAAU,CAGd,gBACI,eAAgB,CAChB,chB3C8B,CgB4C9B,eAAgB,CAGpB,gBACI,iBAAkB,CVg1BtB,0BU70BI,kBAAmB,CACnB,gBAAiB,CAGrB,sCACI,QAAS,CAGb,4CACI,YAAa,CACb,eAAgB,CAChB,wBlB/DsB,CkB4D1B,8EAMQ,WAAY,CACZ,eAAgB,CV60BxB,kGUv0BU,gBAAiB,CAK3B,6BACI,WAAY,CAEZ,yCAHJ,6BAIQ,WAAY,CAMnB,CAHG,yCAPJ,6BAQQ,WAAY,CAEnB,CCrGD,WACI,qBAAyB,CACzB,4BnBYqB,CmBdzB,iDAKQ,qBnBeqB,CmBdrB,qBAAyB,CANjC,iEASY,qBAAsB,CATlC,yFAaY,SAAU,CAbtB,uEAiBY,gBAAiB,CAjB7B,yBAsBQ,YAAa,CAIrB,kGAEQ,uCAA+C,CAC/C,gBAAiB,CAIzB,kGAIQ,uCAA+C,CAC/C,gBAAiB,CAIzB,qBACI,qBAAsB,CAG1B,kDACI,cAAe,CACf,wBnBnCsB,CmBsC1B,oBACI,uBAAyB,CAG7B,6BACI,mBAAoB,CACpB,UAAW,CACX,gBAAiB,CACjB,QAAS,CACT,kBAAmB,CACnB,eAAgB,CAChB,eAAgB,CAGpB,WACI,cjBpD8B,CkBdlC,QACE,iBAAkB,CAClB,WAAY,CACZ,gCAAkC,CAClC,UAAW,CAEX,yBANF,QAOI,aAAc,CACd,iBnBiDsB,CmBhDtB,gBAAiB,CAEpB,CCXD,WACE,eAAgB,CAChB,WAAY,CACZ,YAAa,CACb,qBAAyB,CACzB,SAAU,CALZ,qBAQI,mBAAoB,CAIxB,qBACE,qBrBCuB,CqBAvB,iBAAkB,CAClB,iBAAkB,CAClB,kBAAmB,CAGrB,8BACE,iBAAkB,CAClB,OAAQ,CACR,UAAW,CACX,QAAS,CACT,SAAU,CACV,iBAAkB,CAClB,qBAAyB,CACzB,arB1BsB,CqB2BtB,cnBdgC,CmBehC,eAAgB,CAGlB,kBACE,eAAgB,CAChB,cnBpBgC,CmBqBhC,kBAAmB,CAGrB,6CAGM,cAAe,CAKrB,4BACE,qBAAyB,CACzB,YAAa,CAFf,8BAKI,eAAgB,CAChB,eAAgB,CANpB,yEAcI,SAAU,CAId,2BACE,iBAAkB,CAClB,uBAAwB,CACxB,wBAAyB,CACzB,eAAgB,CAGlB,6BACE,UAAW,CACX,gBAAiB,CAGnB,0BAEI,wBAAyB,CAI7B,sBACE,WAAY,CACZ,UAAW,CACX,qBrBvEuB,CqBwEvB,iBAAkB,CCtFpB,KACI,iBAAkB,CAGtB,KACI,kBAAmB,CAGvB,UACI,iBAAkB,CAClB,KAAM,CACN,QAAS,CACT,MAAO,CACP,OAAQ,CACR,gCAAkC,CAClC,SAAU,CAPd,kBAUQ,SAAU,CAIlB,UACI,qBtBFmC,CsBGnC,eAAgB,CAFpB,kBAKQ,gBAAiB,CAIzB,oBACI,oBAAqB,CAGzB,qBACI,qBtBrBmB,CsBsBnB,WAAY,CAOhB,wCACI,SAAU,CAEV,yBAHJ,oBAIQ,WAAY,CAEnB,CAED,UACI,mCAAqC,CADzC,4BAKY,wBAKG,CAKf,yBACI,2BrBdoB,CqBepB,0BrBfoB,CqBaxB,+BAKQ,YAAa,CALrB,qDAQY,6BtB3DK,CsBgEjB,gBACI,qCAA0C,CAD9C,yBAIQ,eAAgB,CAEhB,cAAe,CACf,yBAA2B,CAC3B,eAAgB,CAChB,gBAAiB,CATzB,uCAYY,YAAa,CAYzB,2BACI,oBAAqB,CACrB,oBAAqB,CAGzB,yBACI,eAAgB,CAChB,qBtBzFkC,CsBgGtC,sCAEQ,4IACsF,CAI9F,oBACI,QAAS,CAGb,gBACI,yBtBnHmB,CsBoHnB,8BrB7EoB,CqB8EpB,6BrB9EoB,CqB+EpB,gBAAiB,CACjB,eAAgB,CAGpB,iBACI,WAAY,CACZ,cAAe,CACf,UAAc,CACd,QAAS,CACT,eAAgB,CALpB,yBAQQ,iBAAkB,CAClB,UAAc,CATtB,oEAcY,wBtB5IQ,CsBiJpB,iBACI,iBAAkB,CAClB,eAAgB,CAChB,eAAgB,Cd4+BpB,2Bcz+BI,iBAAkB,CAClB,kBAAmB,CAGvB,gBACI,aAAc,CAGlB,oBACI,UAAW,CADf,kCAIQ,KAAM,CAId,0BACI,eAAgB,CAChB,UAAW,CAGf,kBACI,aAAc,CACd,cAAe,CCzLnB,gBACE,aAAc,CACd,gBAAiB,CAGnB,oBACE,iBAAkB,CAElB,yBAHF,oBAII,eAAgB,CAEnB,CAED,oBACE,iBAAkB,CAClB,KAAM,CACN,OAAQ,CCZV,eACI,iBAAkB,CAClB,QAAS,CACT,UAAW,CAHf,oBAMQ,wBAAoC,CACpC,gBAAiB,ChB2qCzB,yBgBvqCE,UAAU,CACV,SAAS,CAOX,oBACI,eAAgB,CAEhB,ctBZ8B,CsBelC,oBACI,yBAA2B,CAC3B,2BAA6B,CAC7B,gBAAiB,CAGrB,wDAEQ,axBpCgB,CwBwCxB,cACI,iBAAkB,CAClB,QAA8C,CAC9C,QAAS,CACT,MAAO,CACP,qBAAsB,CACtB,UA9CqB,CA+CrB,eAAgB,CAChB,aAAc,CACd,iBAAkB,CAClB,2BxBnCa,CwBqCb,yCAZJ,cAaQ,YAAa,CAEpB,ChB6pCD,wBgB3pCE,OAAO,CACP,SAAS,CAGX,6CACI,cAAe,CACf,iBAAkB,CC1DtB,MACI,aAAc,CACd,iBAAkB,CAClB,MAAO,CACP,OAAQ,CAER,yCANJ,MAQQ,iBAAkB,CAClB,kBAAmB,CAe1B,CAZG,yCAZJ,MAaQ,YAAa,CAWpB,CAxBD,eAiBQ,sBAAuB,CACvB,SAAU,CAlBlB,sBAsBQ,SAAU,CAOd,yCADJ,aAEQ,eAAgB,CAChB,kBAAmB,CAM1B,CAHG,yCANJ,aAOQ,gBAAiB,CAExB,CAED,cACI,gBAAiB,CACjB,qBAAyB,CACzB,iBxBSsB,CwBRtB,gBAAiB,CAEjB,yCANJ,cAOQ,eAAgB,CAQvB,CALG,yCAVJ,cAWQ,SAAU,CACV,oBAAqB,CACrB,uBAAwB,CAE/B,CAED,wBAEQ,qBAAyB,CAIjC,sBACI,iBAAkB,CAClB,SAAU,CACV,MAAO,CACP,OAAQ,CACR,SAAU,CALd,oCAQQ,oBAAsB,CAEtB,yCAVR,oCAWY,mBAAqB,CAE5B,CAGL,WACI,UAAc,CACd,sBAAuB,CAG3B,aACI,YAAa,CACb,WAAY,CACZ,eAAgB,CAChB,cvB/E8B,CuB2ElC,2CAQY,uBAA6B,CAC7B,UAAW,CATvB,oGAiBY,YAAa,CAjBzB,6BAsBQ,aAAc,CACd,qBzB5FqB,CyBgG7B,2BAEI,wBzBzGsB,CyB0GtB,4BzBzGqB,CyB0GrB,yBzB1GqB,CyB6GzB,0BACI,iBAAkB,CAGtB,mBACI,gBAAiB,CAGrB,qBACI,eAAgB,CAGpB,mBACI,sBAAuB,CACvB,kBAAmB,CACnB,eAAgB,CAEhB,yCALJ,mBAMQ,cAAe,CAEtB,CAED,YACI,eAAgB,CAChB,mBAAoB,CACpB,cAAe,CAEf,yCALJ,YAMQ,cvBzI0B,CuB2IjC,CAED,uBACI,mBAAoB,CAEpB,yCAHJ,uBAIQ,mBAAoB,CAc3B,CAXG,0CAPJ,uBAQQ,mBAAoB,CAU3B,CAlBD,8CAYQ,aAAc,CAZtB,6CAgBQ,cAAe,CAIvB,6BACI,iBAAkB,CAClB,eAAgB,CAChB,qBzB7JmC,CyBgKvC,6BACI,wBAAyB,CAG7B,iDAKQ,cAAe,CAKnB,yCADJ,qBAEQ,iBAAuC,CAE9C,CAGG,yCADJ,sBAEQ,kBxB/JkB,CwBiKzB,CAED,cACI,iBAAkB,CAClB,OAAQ,CACR,KAAM,CACN,UrB3MoB,CqB8MxB,uBACI,cAAe,CACf,UrBhNoB,CqBiNpB,qBAAyB,CACzB,aAAc,CACd,iBxBjKsB,CwBmKtB,yCAPJ,uBAQQ,OAAQ,CACR,QAAS,CACT,MAAO,CACP,UAAW,CACX,eAAgB,CAChB,SAAU,CACV,YAAa,CACb,WxBzLkB,CwB2LzB,CAID,WACI,QAAS,CAET,yCAHJ,WAIQ,YAAa,CACb,eAAgB,CAsBvB,CA3BD,oBASQ,gBAAiB,CACjB,iBAAkB,CAV1B,kCAeY,gBAAiB,CAf7B,mFAoBQ,gBAAiB,CACjB,iBAAkB,CArB1B,+BAyBQ,YAAa,CAIrB,aACI,wBzBvQoB,CyBwQpB,oBAAqB,CACrB,YAAa,CACb,qBAAyB,CAO7B,iCACI,qBAAsB,CACtB,oBAAqB,CCjRzB,yBACE,eAAgB,CAChB,mBAAqB,CAGvB,eACE,qBAAyB,CAD3B,0BAII,SAAU,CCPV,yCADJ,oCAGY,UAAW,CACd,CAIT,iBACI,yBAA0B,CAC1B,QAAS,CACT,WAAY,CAHhB,+BAMQ,oBAAqB,CACrB,WAAY,CACZ,eAAc,CAAd,YAAc,CAEd,8B3BPe,C2BOf,uB3BPe,C2BOf,kB3BPe,C2BQf,eAAgB,CAXxB,0CAcY,YAAa,CAdzB,2BAmBQ,QAAc,CACd,wBAAyB,CACzB,yBAA0B,CAC1B,2BAA4B,CAC5B,4BAA6B,CAIrC,2BACI,YAAa,CACb,WAAY,CACZ,cAAe,CACf,QAAS,CACT,UAAW,CACX,iBAAkB,CAClB,SAAU,CAIV,yCADJ,0CAGY,WAAY,CACZ,qBAAuB,CAC1B,CAIT,iCACI,QAAS,CACT,WAAY,CACZ,SAAU,CACV,UAAW,CACX,eAAgB,CAChB,cAAe,CACf,WAAY,CACZ,UAAW,CARf,kDAWQ,WAAY,CACZ,UAAW,CACX,aAAc,CACd,eAAgB,CAChB,iB1BnBgB,C0BuBxB,sCACI,WAAY,CACZ,WAAY,CAFhB,wMAKQ,YAAa,CAIrB,0BACI,+BAAkD,CAClD,yBAA0D,CAG9D,iCACI,eAAgB,CAGpB,0BAKI,qBAAyB,CAL7B,oHAEQ,czBnF0B,CyBiFlC,kDAQQ,QAAc,CACd,eAAgB,CAChB,SAAU,CACV,eAAgB,CAXxB,0CAeQ,YAAa,CAIrB,kCACI,WAAY,CACZ,qBAAyB,CACzB,czBvG8B,CyB0GlC,yCACI,sEAEQ,KAAM,CACN,QAAS,CACT,MAAO,CACP,OAAQ,CACR,eAAgB,CAChB,cAAe,CACf,WAAY,CACZ,UAAW,CATnB,uFAYY,eAAgB,CAK5B,sBACI,YAAa,CAChB,CAGL,0BACI,cAAe,CACf,KAAM,CACN,MAAO,CACP,UAAW,CACX,WAAY,CACZ,SAAU,CACV,wBAAoC,CACpC,WAAY,CAGhB,eACI,sBAAyB,CAD7B,wBAIQ,SAAU,CAJlB,gCAQQ,SAAU,CACV,qBAAuB,CAT/B,sLAkBQ,SAAU,CAIlB,eACI,0BAA4B,CAC5B,kBAAmB,CACnB,aAAc,CAGlB,wBACI,YAAa,CACb,eAAgB,CAChB,iBAAkB,CAClB,SAAU,CACV,qB3B3Ka,C2B4Kb,0B1BtIoB,C0BuIpB,2B1BvIoB,C0B0IxB,kEACI,iBAAkB,CAClB,MAAO,CACP,OAAQ,CAGZ,oCACI,qCAAuC,CACvC,0B1BlJoB,C0BmJpB,2B1BnJoB,C0BoJpB,iCAA0C,CAC1C,+BAAiC,CACjC,KAAM,CACN,QAAS,CAGb,8BACI,QAAS,CACT,WAAY,CACZ,eAAgB,CAChB,eAAgB,CAChB,gBAAiB,CACjB,eAAgB,CAChB,oCAAwC,CACxC,yB3B1MqB,C2BkMzB,oCAWQ,UAAW,CACX,gBAAiB,CACjB,iBAAkB,CAClB,QAAS,CACT,OAAQ,CACR,UAAW,CACX,WAAa,CACb,6EAAiF,CAIzF,yCAEQ,cAAe,CACf,0BAA6B,CAC7B,eAAmB,CACnB,iBAAkB,CAClB,oBAAqB,CACrB,aAAc,CACd,mBAAoB,CACpB,qBAAsB,CACtB,gBAAiB,CACjB,kBAAmB,CACnB,aAAc,CAGd,kCAAmC,CAEnC,iCAAkC,CAGlC,iCAAkC,CAGlC,4BAA6B,CAE7B,cAAe,CACf,qB3B1O8B,C2B8OtC,6CACI,mBAAoB,CAGxB,kCACI,UAAY,CACZ,aAAc,CAFlB,0CAKQ,cAAe,CAIvB,qBACI,cAAe,CAGnB,wBACI,QAAS,CACT,aAAc,CACd,UAAc,CACd,qB3BtQqB,C2BuQrB,6B1BpOoB,C0BqOpB,8B1BrOoB,C0B+NxB,gCASQ,UAAc,CAItB,2BACI,QAAS,CAGb,qCACI,cAAe,CACf,eAAgB,CAGpB,2BACI,qB3B7RmB,C2B8RnB,YAAa,CACb,kBAAmB,CACnB,iBAAkB,CAClB,iBAAkB,CAGtB,oCACI,cAAe,CACf,WAAY,CAGhB,mCACI,iBAAkB,CAClB,OAAQ,CACR,UAAW,CAEX,oBAAsB,CACtB,qBAAuB,CACvB,YAAa,CAPjB,2CAUQ,gBAAiB,CACjB,cAAe,CAIvB,qBACI,iBAAkB,CAClB,UAAW,CACX,aAAqC,CAHzC,oDAMQ,aAAqC,CAN7C,6BAUQ,eAAgB,CAChB,WAAY,CACZ,UAAW,CAInB,uBACI,iBAAkB,CAClB,YAAa,CACb,qB3B5UqB,C2B6UrB,kBAAmB,CACnB,qB3BvUmC,C2BwUnC,iBAAkB,CAClB,cAAe,CACf,6BAA8B,CAC9B,mBAAqB,CATzB,2DAYQ,qBAAuB,CAZ/B,0FAgBQ,oB3BjWW,C2BkWX,wBAA+C,CAC/C,a3BnWW,C2BiVnB,2NAqBY,a3BtWO,C2B2WnB,oBACI,WvB/V4B,CuBqWhC,uBACI,qBAAyB,CAD7B,oCAIQ,kBAAmB,CACnB,iB3B7WS,C2BiXjB,iFAGY,cAAe,CACf,QAAgD,CAChD,MAAO,CACP,OAAQ,CACR,SAAU,CAEV,yCATZ,iFAUgB,SAAmC,CAInC,cAJmC,CAsB1C,CAfG,yCAjBZ,iFAkBgB,cAAe,CActB,CAhCT,+FAsBgB,aAAc,CAEd,yCAxBhB,+FAyBoB,YAAa,CAMpB,CAHG,yCA5BhB,+FA6BoB,aAAc,CAErB,CA/Bb,8DAmCY,gBAAiB,CAK7B,8CAGQ,qB3B7Ze,C2B8Zf,qB3B7ZS,C2B8ZT,kBAAmB,CAI3B,gCACI,iBAAkB,CAClB,kBAAmB,CAMvB,iCACI,eAAgB,CAGpB,iCACI,cAAe,CACf,qBAAuB,CAIvB,yCADJ,kBAEQ,WAAY,CACZ,cAAe,CAOtB,CAJG,yCANJ,kBAOQ,WAAY,CACZ,cAAe,CAEtB,CAED,gBACI,UAAW,CACX,aAAc,CACd,eAAgB,CAChB,kBAAmB,CAJvB,+BAOQ,UAAW,CAPnB,wCAWQ,YAAa,CACb,WAAY,CAIpB,yBACI,eAAgB,CAGpB,yCACI,6EAEQ,qBAA6C,CAChD,CAIT,kBACI,qBAAyB,CACzB,aAAc,CC7elB,kBACI,oBAAsB,CAD1B,0BAIQ,gBAAiB,CAIzB,oBACI,iBAAkB,CAClB,iBAAkB,CAClB,wB5BLe,C4BMf,UAAW,CACX,QAAS,CACT,WAAY,CACZ,gBAAiB,CACjB,cAAe,CACf,eAAgB,CAChB,gBAAiB,CAVrB,2BAaQ,UAAW,CACX,iBAAkB,CAClB,UAAW,CACX,OAAQ,CACR,yCAA6C,CAC7C,gCAAiC,CACjC,mCAAoC,CAI5C,mBACI,aAAc,CAGlB,sBACI,WAAY,CAGhB,6BACI,eAAgB,CAGpB,kPAIQ,qB5B1B+B,C4B2B/B,c1BlC0B,C0B6BlC,0QAQY,c1BrCsB,C0BsCtB,WAAY,CACZ,UAAW,CACX,gBAAiB,CACjB,gB1BzCsB,C2BdlC,cACI,iB5BqDoB,C4BpDpB,SAAU,CACV,c3BW8B,C2BV9B,eAAgB,CAEhB,0BANJ,cAOQ,yBAA2B,CAOlC,CAdD,iBAWQ,QAAS,CACT,eAAgB,CAIxB,4BACI,Y5BiCyE,C4B9B7E,2BACI,WAAY,CAEZ,yBAHJ,2BAIQ,WAAY,CAEnB,CAED,oBACI,qB7BNkC,C6BOlC,iBAAkB,CAGtB,qBACI,iBAAkB,CAClB,UAAW,CACX,QAAS,CACT,YAAa,CAJjB,4BAOQ,UAAW,CACX,iBAAkB,CAClB,4BAA6B,CAC7B,iCAAkC,CAClC,kCAAmC,CAI3C,+DACI,UAAW,CAGf,+CACI,WAAY,CrB6yDhB,+BqBxyDI,UAAW,CACX,SAAU,CrB2yDd,mFqBxyDI,SAAS,CACT,UAAU,CrB2yDd,yDqBxyDM,UAAW,CACX,UAAU,CAIhB,oBACI,gBAAiB,CAGrB,2BACI,eAAgB,CAGpB,0BACI,eAAgB,CAChB,qB7B5DmC,C6B+DvC,uDAIQ,SAAU,CAJlB,6CAOY,gBAAiB,CACjB,WAAY,CACZ,UAAW,CAKvB,2CAEQ,wBAAyB,CACzB,UAAW,CAInB,qBACI,iBAAkB,CAClB,gBAAiB,CAFrB,6BAKQ,a7BnFU,C6BuFlB,8BACI,iBAAkB,CAClB,KAAM,CACN,UAAW,CACX,mCAA0C,CAG9C,uBACI,wB7B7GsB,C6BgH1B,uBACI,YAAa,CC9HjB,aACI,yBAA0B,CAC1B,iBAAkB,CAClB,c5BW8B,C4BdlC,gBAMQ,iBAAkB,CAClB,4BAA6B,CAPrC,mBAWQ,UAAW,CACX,iBAAkB,CAClB,OAAQ,CACR,QAAS,CACT,UAAW,CACX,UAAW,CACX,KAAQ,CACR,WAAY,CACZ,iCAAkC,CAClC,oCAAqC,CACrC,+B9BHgB,C8BOxB,wCACI,yB9BXmB,C8BcvB,qBACI,YAAa,CACb,eAAgB,CAGpB,qBACI,iBAAkB,CAElB,2BAA4B,CAC5B,iBAAkB,CAClB,eAAgB,CAChB,iBAAkB,CAClB,UAAc,CACd,wB9BxBoB,C8B2BxB,qBACI,wB9BxCe,C8ByCf,WAAY,CACZ,iBAAkB,CAClB,KAAM,CACN,UAAW,CAGf,mBACI,iBAAkB,CAClB,UAAc,CAGlB,mBACI,YAAa,CACb,qBAAyB,CACzB,6B7BPoB,C6BQpB,8B7BRoB,C6BSpB,aAAc,CAGlB,qBACI,qBAAyB,CACzB,a9B1Da,C8B2Db,oBAAqB,CACrB,eAAgB,CAChB,c5BzD8B,C4BoDlC,4DAQQ,iBAAkB,CAR1B,4EAYQ,SAAS,CAZjB,4EAgBQ,SAAS,CAIjB,oBACI,eAAgB,CAGpB,mBACI,iBAAkB,CAClB,UAAY,CACZ,wBAAyB,CACzB,c5BhF8B,C4BmFlC,kCAEQ,gBAAiB,CAKzB,mBACI,kBAAmB,CACnB,gBAAiB,CACjB,gBAAiB,CAEjB,yCALJ,mBAMQ,0BAA2B,CAclC,CApBD,yBAUQ,+B9BxGS,C8B8FjB,wCAcQ,qBAAyB,CAdjC,wCAkBQ,wB9BhHS,C+BVjB,yCAEQ,eAAgB,CAFxB,wCAMQ,YAAa,CANrB,uEAcY,qBAAyB,CAdrC,6EAkBY,qB/BEiB,C+BG7B,gCACI,cAAe,CACf,WAAY,CAGhB,uBACI,eAAgB,CAGpB,4BACI,iBAAkB,CAClB,kBAAmB,CAGvB,4BACI,WAAY,CAGhB,mCAEQ,sBAAwB,CACxB,c7B9B0B,C6B+B1B,UAAW,CACX,iBAAkB,CAI1B,oCACI,wBAA0B,CAC1B,UAAW,CACX,aAAc,CACd,SAAU,CACV,gBAAiB,CAGrB,wCAEQ,YAAa,CAIrB,sCACI,iBAAkB,CAClB,QAAS,CACT,eAAgB,CAGpB,oCACI,YAAa,CAGjB,6CACI,cAAe,CC3EnB,WACI,iBAAkB,CAGtB,oBACI,cAAe,CACf,aAAc,CAGlB,oBACI,eAAgB,CAChB,c9BG8B,C8BF9B,iBAAkB,CAElB,yCALJ,oBAMQ,aAAc,CAErB,CAED,uCACI,gBAAiB,CACjB,QAAS,CACT,c9BR8B,C8BWlC,mBACI,iBAAkB,CAClB,UAAW,CACX,QAAS,CAGb,yCAGY,mBAAqB,CACrB,cAAe,CACf,WAAY,CALxB,8FAUgB,8BhCnCG,CgC0CnB,2CAEQ,aAAc,CAFtB,0CAMQ,cAAe,CAIvB,0BACI,iBAAkB,CAClB,eAAgB,CAEhB,qBhCzCmC,CgC4CvC,0BACI,wBAAyB,CAG7B,mBACI,iBAAkB,CAClB,eAAgB,CAGpB,4BACI,cAAe,CAGnB,uBACI,iBAAkB,CAClB,YAAa,CACb,kBAAmB,CAEnB,yCALJ,uBAMQ,eAAgB,CAEvB,CAGG,yCADJ,2BAEQ,UAAW,CAElB,CAED,gCACI,gBAAiB,CACjB,UAAW,CAGf,+BACI,iBAAkB,CAClB,KAAM,CACN,OAAQ,CACR,WAAY,CACZ,8CAAsD,CACtD,eAAgB,CAChB,WAAY,CACZ,QAAS,CARb,uCAeQ,YAAa,CAIrB,qBACI,YAAa,CACb,WAAY,CAFhB,oCAKQ,wBhC9GkB,CgCkH1B,8BACI,iBAAoB,CACpB,4BhClHmB,CgCqHvB,8BACI,ahCnHoB,CgCoHpB,eAAgB,CCnIpB,gCACI,eAAgB,CAGpB,oBACI,qBjCMqB,CiCJrB,6BjCOkB,CiCJtB,kBACI,qBAAsB,CAEtB,0CAHJ,kBAIQ,mBAAoB,CAE3B,CAED,iBACI,kBAAmB,CACnB,e7BJ4B,C6BM5B,yCAJJ,iBAKQ,kBAAmB,CAY1B,CATG,0CARJ,iBASQ,kBAAmB,CAQ1B,CAjBD,4BAcQ,iBAAkB,CAClB,UAAW,CAInB,2BACI,YAAa,CAGjB,yEACI,gBAAiB,CAGrB,uFACI,cAAe,CAInB,gFACI,kBAAmB,CACnB,eAAgB,CAChB,eAAgB,CAChB,gBAAiB,CACjB,kBAAmB,CAGvB,uBACI,UjC7CkB,CiCgDtB,8BACI,eAAgB,CAChB,eAAgB,CAGpB,0BACI,eAAgB,CAChB,oBAAqB,CAGzB,6BACI,cAAe,CACf,qBAAuB,CACvB,eAAgB,CAGpB,gBACI,qBAAyB,CACzB,eAAgB,CAChB,gBAAiB,CACjB,iBAAkB,CAClB,WAAY,CACZ,mBAAqB,CACrB,oBAAsB,CAG1B,yBACI,kBAAmB,CAGvB,mDACI,QAAS,CACT,SAAU,CAFd,qEAKQ,eAAgB,CAIxB,2FACI,WAAY,CACZ,QAAc,CAGlB,yBACI,aAAc,CADlB,kDAKY,aAAc,CAL1B,iDASY,cAAe,CAK3B,4BACI,SAAU,CACV,cAAe,CAGnB,qCACI,YAAa,CACb,kBAAmB,CAGvB,qBACI,SAAU,CAGd,+BACI,wBjC7HsB,CiCgI1B,6BACI,4BAA6B,CAC7B,SAAU,CAFd,iDAKQ,kBAAmB,CAI3B,2BACI,SAAU,CACV,iBAAkB,CAClB,eAAgB,CAEhB,yCALJ,2BAMQ,gBAAiB,CAExB,CAED,2GACI,qBAAyB,CACzB,WAAY,CACZ,cAAqC,CACrC,QAAc,CACd,gBAAiB,CACjB,eAAgB,CAChB,WAAY,CAGhB,gDACI,iBAAkB,CAClB,SAAU,CACV,eAAgB,CAGpB,uFACI,YAAa,CAGjB,8FACI,cAAqC,CACrC,SAAU,CACV,QAAS,CAGb,4BACI,cAAe,CADnB,8BAIQ,yBAA2B,CAC3B,+BAA+B,CAIvC,sBACI,WAAY,CAGhB,uCACI,aAAc,CACd,QAAS,CAFb,yDAKQ,YAAa,CAIrB,+BACI,eAAgB,CADpB,0CAIQ,YAAa,CACb,eAAgB,CCjNxB,0BACI,QAAc,CCLlB,oBACI,SAAU,CACV,QAAS,CAFb,yCAMY,4BnCSW,CmCfvB,wCAWQ,SAAU,CACV,QAAS,CAZjB,wCAgBQ,YAAa,CACb,QAAS,CAIjB,8BACI,mBAAoB,CAEpB,yCAHJ,8BAIQ,kBAAmB,CACnB,gBAAiB,CAExB,CAED,uCACI,aAAc,CAGlB,iCACI,wBnCjCwC,CmCoC5C,0CACI,WAAY,CACZ,qBAAyB,CAG7B,mCACI,eAAgB,CAChB,eAAgB,CAChB,yBnChCqB,CoCdzB,cACI,eAAgB,CAChB,eAAgB,CAGpB,gBACI,YAAa,CACb,aAAc,CAGd,iBAAqB,CAArB,kBAAqB,CAArB,kBAAqB,CCPzB,eACI,qBrCUqB,CqCTrB,kBAAmB,CACnB,WAAY,CACZ,6BrCUkB,CqCPtB,eACI,mBAAoB,CAIpB,yCADJ,2BAEQ,YAAa,CAEpB,CAED,sBACI,YAAa,CACb,eAAgB,CAChB,arCtBoB,CqCyBxB,uBACI,SAAU,CAGd,6CAEQ,arCzBa,CqC6BrB,wBACI,gBAAiB,CAEjB,iBAAkB,CAClB,qBrCzBmB,CqC0BnB,gCAAkC,CAClC,6BpCYoB,CoCXpB,8BpCWoB,CoCTpB,mBAAe,CAAf,cAAe,CAEf,yCAXJ,wBAYQ,6BAA0B,CAA1B,wBAA0B,CAOjC,CAnBD,4BAgBQ,cAAe,CACf,WAAY,CAIpB,8BACI,wBrCzDoB,CqC4DxB,oBACI,oBAAqB,CACrB,WAAY,CACZ,eAAgB,CAChB,8BAAmB,CAAnB,kBAAmB,CAOvB,8BACI,qBAAyB,CACzB,qBAAuB,CACvB,qBrC3Da,CqCwDjB,iDAMQ,UAAW,CAInB,oCACI,gBAAiB,CAGrB,gBACI,uBAAyB,CAGzB,oBAAqB,CACrB,UAAc,CACd,eAAgB,CANpB,wBASQ,SAAU,CACV,sBAAuB,CAV/B,wBAcQ,UAAc,CCpGtB,wBAEI,qBtCYqB,CsCRzB,yBACE,cAAe,CACf,cAAe,CAFjB,2BAKI,eAAgB,CAChB,kBAAmB,CACnB,eAAgB,CAChB,sBAAuB,CACvB,oBAAqB,CCfzB,gCAEQ,SAAU,CACV,QAAS,CAHjB,yCAOQ,SAAU,CAPlB,yCAWQ,aAAc,CAXtB,uCAeQ,YAAa,CAIrB,kEACI,SAAU,CAGd,mCACI,kBAAmB,CAOvB,uCACI,gBAAiB,CAGrB,2CACI,eAAgB,CAChB,cAAe,CACf,yBvCvBmB,CwCfvB,6BAEQ,YAAa,CAGjB,yCALJ,kBAMQ,yBxCSe,CwCPtB,CAED,4BACI,iBAAkB,CAClB,WAAY,CAFhB,gDAKQ,qBAAyB,CAIjC,4BACI,iBAAkB,CAGtB,6BACE,WAAW,ChCg8Eb,uCgC57EE,UAAU,CC5BZ,UACE,aAAc","file":"monitor.css","sourcesContent":["// Config\n$avatar-icon-padding: 6px !default;\n$avatar-icon-padding-lg: 8px !default;\n\nbody {\n background: color('body-bg');\n\n &.vle {\n overflow: hidden;\n }\n}\n\na {\n &:hover, &:focus { // TODO: remove when bootstrap css dependency is removed\n color: color('primary');\n }\n}\n\nblockquote {\n background-color: lighten(color('primary'), 56%);\n padding: 8px;\n margin: 16px 0;\n border-style: solid;\n border-color: color('primary');\n border-width: 0 0 0 3px;\n}\n\n.has-indicator {\n &:after {\n content: '';\n position: absolute;\n border-radius: 50%;\n padding: 5px;\n background-color: color('accent');\n }\n}\n\n.has-indicator--icon-button {\n &:after {\n top: 25px;\n left: 5px;\n }\n}\n\n// Badges\n.badge {\n border-radius: $card-border-radius;\n padding: 2px 6px;\n font-size: rem(1.2);\n font-weight: 500;\n font-style: normal;\n background-color: color('gray-dark');\n\n &.md-button {\n min-width: 0;\n min-height: 0;\n line-height: inherit;\n\n &:hover, &:focus {\n background-color: color('gray-dark');\n }\n\n &:focus {\n outline: 1px dotted color('gray-dark');\n }\n }\n}\n\n.badge--info {\n background-color: color('info');\n color: #ffffff;\n}\n\n.badge--warn {\n background-color: color('warn');\n color: #ffffff;\n}\n\n.badge--success {\n background-color: color('success');\n color: #ffffff;\n}\n\n// Dividers\n.divider--withmargin {\n margin: 16px 0;\n}\n\n.divider--dashed {\n border-top-style: dashed;\n}\n\n// Links\na {\n color: color('primary');\n cursor: pointer;\n}\n\n.active {\n background-color: rgba(158,158,158,0.2);\n color: color('text');\n}\n\n// Images & Icons\n.avatar {\n border-radius: 50%;\n box-sizing: content-box;\n}\n\n.avatar--square {\n border-radius: $card-border-radius;\n}\n\n// Rules for sizing avatars (matches material icons plus avatar-icon padding)\n.avatar {\n &.md-18 {\n height: 18px + $avatar-icon-padding*2;\n width: 18px + $avatar-icon-padding*2;\n }\n &.md-24 {\n height: 24px + $avatar-icon-padding*2;\n width: 24px + $avatar-icon-padding*2;\n }\n &.md-36 {\n height: 36px + $avatar-icon-padding*2;\n width: 36px + $avatar-icon-padding*2;\n }\n &.md-48 {\n height: 48px + $avatar-icon-padding*2;\n width: 48px + $avatar-icon-padding*2;\n }\n}\n\n// Rules for sizing avatar backgrounds (when using a child md-icon)\n.avatar--icon {\n background-color: color('gray-light');\n white-space: normal !important;\n\n &:not(.md-avatar) {\n padding: $avatar-icon-padding;\n }\n\n &.md-18 {\n height: 18px;\n width: 18px;\n }\n &.md-24 {\n height: 24px;\n width: 24px;\n }\n &.md-36 {\n height: 36px;\n width: 36px;\n }\n &.md-48 {\n height: 48px;\n width: 48px;\n }\n}\n\nmd-toolbar.md-light-theme:not(.md-menu-toolbar) {\n md-icon {\n color: color('text-secondary');\n }\n\n .md-button:disabled {\n md-icon {\n color: color('text-disabled');\n }\n }\n}\n\n// hacks for now\nmd-icon, .md-button:not([disabled]) {\n &.primary {\n color: color('primary') !important;\n }\n &.success {\n color: color('success') !important;\n }\n &.warn {\n color: color('warn') !important;\n }\n &.info {\n color: color('info') !important;\n }\n &.accent {\n color: color('accent') !important;\n }\n &.accent-1 {\n color: color('accent-1') !important;\n }\n &.accent-2 {\n color: color('accent-2') !important;\n }\n}\n\n// Theme overrides\nmd-input-container.md-wise-theme label {\n color: color('text');\n}\n\nmd-select-menu.md-default-theme md-option[selected], md-select-menu md-option[selected] {\n background-color: color('selected-bg');\n}\n\n.md-autocomplete-suggestions-container.md-default-theme li .highlight,\n.md-autocomplete-suggestions-container li .highlight {\n color: color('primary');\n background-color: color('selected-bg');\n}\n\n// Color\n// Set classes for each named color in the $colors map\n@each $key, $value in $colors {\n .#{$key} {\n color: $value;\n }\n}\n\n// Set classes for each named color in the $colors map\n@each $key, $value in $colors {\n .#{$key}-bg {\n background-color: $value;\n }\n}\n\n// Set theme colors for angular-ui elements\n@each $key, $value in $colors {\n md-progress-circular.#{$key} {\n path {\n stroke: $value;\n }\n }\n}\n","// Classroom Monitor Colors\n$_primary-color: #1565c0; // should match the default color of your ng-material default theme's 'primary' palette\n$_selected-bg: lighten($_primary-color, 54%);\n\n$color: (\n 'primary': $_primary-color,\n 'accent': #F05843, // should match the default color of your ng-material default theme's 'accent' palette\n 'accent-1': #795C3A,\n 'accent-2': #CAD266,\n 'warn': #c62828, // should match the default color of your ng-material default theme's 'warn' palette\n 'info': #ef6c00,\n 'success': #00C853,\n 'divider': rgba(0, 0, 0, 0.12),\n 'gray-lightest': #f7f7f7,\n 'gray-lighter': #eeeeee,\n 'gray-light': #dddddd,\n 'gray': #cccccc,\n 'gray-dark': #aaaaaa,\n 'gray-darker': #757575,\n 'gray-darkest': #333333,\n 'text': rgba(0, 0, 0, 0.87),\n 'text-secondary': rgba(0, 0, 0, 0.54),\n 'text-disabled': rgba(0, 0, 0, 0.26),\n 'text-light': rgba(255, 255, 255, 1),\n 'text-light-secondary': rgba(255, 255, 255, 0.70),\n 'text-light-disabled': rgba(255, 255, 255, 0.50),\n 'selected-bg': $_selected-bg,\n 'score': #FFC107\n);\n\n$body-color: (\n 'body': map-get($color, 'text'),\n 'body-bg': map-get($color, 'gray-lighter')\n);\n\n$colors: map-merge($color, $body-color);\n","// Colors\n$_primary-color: #1C8CA8; // should match the default color of your ng-material default theme's 'primary' palette\n$_selected-bg: lighten($_primary-color, 59%);\n\n$color: (\n 'primary': $_primary-color,\n 'accent': #F05843, // should match the default color of your ng-material default theme's 'accent' palette\n 'accent-1': #795C3A,\n 'accent-2': #CAD266,\n 'warn': #c62828, // should match the default color of your ng-material default theme's 'warn' palette\n 'info': #ef6c00,\n 'success': #00C853,\n 'divider': rgba(0, 0, 0, 0.12),\n 'gray-lightest': #f7f7f7,\n 'gray-lighter': #eeeeee,\n 'gray-light': #dddddd,\n 'gray': #cccccc,\n 'gray-dark': #aaaaaa,\n 'gray-darker': #757575,\n 'gray-darkest': #333333,\n 'text': rgba(0, 0, 0, 0.87),\n 'text-secondary': rgba(0, 0, 0, 0.54),\n 'text-disabled': rgba(0, 0, 0, 0.26),\n 'text-light': rgba(255, 255, 255, 1),\n 'text-light-secondary': rgba(255, 255, 255, 0.70),\n 'text-light-disabled': rgba(255, 255, 255, 0.50),\n 'selected-bg': $_selected-bg,\n 'score': #FFC107\n);\n\n$body-color: (\n 'body': map-get($color, 'text'),\n 'body-bg': map-get($color, 'gray-lighter')\n);\n\n$colors: (map-merge($color, $body-color));\n\n// Typography\n$baseline-grid: 8px;\n$body-font-size-base: rem(1.500);\n$caption-font-size-base: rem(1.300);\n\n// Layout\n$wise-toolbar-height: 42px;\n\n// Menus\n$menu-border-radius: 2px;\n$max-visible-items: 6;\n$menu-item-height: 6 * $baseline-grid !default;\n$dense-menu-item-height: 4 * $baseline-grid !default;\n$max-menu-height: 2 * $baseline-grid + $max-visible-items * $menu-item-height !default;\n$max-dense-menu-height: 2 * $baseline-grid + $max-visible-items * $dense-menu-item-height !default;\n\n// Cards\n$card-border-radius: 4px;\n\n// Buttons\n$button-border-radius: 3px;\n","// Helper functions and mixins\n\n// Get colors from $colors map\n@function color($key) {\n @if map-has-key($colors, $key) {\n @return map-get($colors, $key);\n }\n @warn \"Unknown `#{$key}` in $colors.\";\n @return null;\n}\n\n// set size in pixels given rem\n@function rem($multiplier) {\n $font-size: 10px;\n @return $multiplier * $font-size;\n}\n","// 1. Config\n\n// 2. Base\n.l-constrained {\n margin-left: auto;\n margin-right: auto;\n max-width: 100%;\n position: relative;\n\n @media (min-width: $layout-breakpoint-xs) {\n width: $layout-breakpoint-md;\n }\n}\n\n.l-constrained-md {\n width: $layout-breakpoint-sm;\n max-width: 100%;\n}\n","// Helpers\n@function rem($multiplier) {\n $font-size: 10px;\n @return $multiplier * $font-size;\n}\n\n// Angular Material variables for use and !default overrides\n$md-toolbar-height: 52px;\n$md-toolbar-medium-tall-height: 74px;\n$md-toolbar-tall-height: 104px;\n$md-toolbar-height-mobile-portrait: 52px;\n$md-toolbar-height-mobile-landscape: 52px;\n\n$caption-font-size-base: rem(1.300);\n\n//$list-item-height: 56px;\n\n$card-border-radius: 4px;\n\n$layout-breakpoint-xs: 600px;\n$layout-breakpoint-sm: 960px;\n$layout-breakpoint-md: 1280px;\n$layout-breakpoint-lg: 1920px;\n\n$button-border-radius: 3px;\n\n$tooltip-fontsize-lg: rem(1.1);\n$tooltip-fontsize-sm: rem(1.1);\n//$tooltip-height-lg: rem(2.2);\n$tooltip-height-sm: rem(2.2);\n//$tooltip-top-margin-lg: rem(1.4);\n$tooltip-top-margin-sm: rem(1.4);\n//$tooltip-lr-padding-lg: rem(0.8);\n$tooltip-lr-padding-sm: rem(0.8);\n//$tooltip-max-width: rem(3.20);\n\n$progress-linear-bar-height: 7px;\n","// 1. Config\n\n// 2. Base\n.l-footer {\n position: fixed;\n bottom: 0;\n left: 0;\n right: 0;\n z-index: 1;\n background-color: #ffffff;\n border-top: 1px solid color('gray-lighter');\n}\n\n// Buttons\n.button--footer {\n margin: 0;\n padding-top: 0;\n padding-bottom: 0;\n min-width: 0;\n display: flex;\n}\n\n.button--footer__element {\n padding-left: 8px;\n}\n","// 1. Config\n\n// 2. Base\n.l-header {\n z-index: 3;\n\n .logo {\n margin-left: 0 !important;\n height: 36px;\n width: 36px;\n vertical-align: middle;\n }\n\n .logo-link {\n min-width: auto;\n display: none;\n padding: 0 4px;\n margin-right: 12px;\n\n @media only screen and (min-width: ($layout-breakpoint-xs)) {\n display: block;\n }\n\n &:hover, &:focus {\n border: 0 none;\n }\n }\n\n // Handle mobile portrait\n @media only screen and (max-width: ($layout-breakpoint-xs - 1)) {\n .md-toolbar-tools {\n h1, h2, h3 {\n font-size: $body-font-size-base;\n }\n }\n }\n}\n","// 1. Config\n\n// 2. Base\n.l-main {\n background-color: color('body-bg');\n}\n\n.l-main--with-toolbar {\n margin-top: $wise-toolbar-height;\n}\n\n#content {\n transition: margin-top 0.5s;\n}\n\n.view-content {\n margin: 0 auto;\n padding: 8px;\n position: absolute;\n left: 0;\n right: 0;\n transition: opacity 500ms;\n\n @media only screen and (min-width: $layout-breakpoint-sm) {\n padding: 16px;\n }\n\n &.ng-enter {\n opacity: 0;\n }\n\n .ng-enter-active {\n opacity: 1;\n transition-delay: 250ms;\n }\n\n &.ng-leave-active,\n &.ng-hide {\n opacity: 0;\n }\n\n &.ng-hide-add,\n &.ng-hide-add-active,\n &.ng-hide-remove,\n &.ng-hide-remove-active {\n opacity: 0;\n }\n}\n\n.view-content--with-sidemenu {\n padding: 8px;\n\n @media only screen and (min-width: $layout-breakpoint-xs) {\n margin-left: 54px;\n padding: 16px;\n }\n}\n[dir='rtl'] .view-content--with-sidemenu {\n @media only screen and (min-width: $layout-breakpoint-xs) {\n margin-left: auto;\n margin-right: 54px;\n }\n}\n\n.content-head {\n margin: 8px 0;\n\n h1,\n h2,\n h3 {\n font-weight: 300;\n margin-top: 0;\n margin-bottom: 0;\n font-size: rem(3.6);\n\n @media only screen and (max-width: $layout-breakpoint-sm - 1) {\n font-size: rem(3.2);\n text-align: center;\n }\n }\n}\n\n.content-head__more {\n @media only screen and (max-width: $layout-breakpoint-sm - 1) {\n margin-top: 8px;\n }\n}\n\n.content-head__item,\nh2.content-head__item {\n margin: 0 8px;\n\n .md-subhead {\n padding-left: 4px;\n\n @media only screen and (max-width: $layout-breakpoint-sm - 1) {\n display: block;\n padding-left: 0;\n }\n }\n\n md-icon {\n vertical-align: text-bottom;\n }\n}\n\n.stepSelectMenuContainer md-select-menu,\n.stepSelectMenuContainer md-select-menu md-content {\n max-height: 500px;\n}\n","/*\n WISE Project Styles\n */\nbody {\n background: #eeeeee; }\n body.vle {\n overflow: hidden; }\n\na:hover, a:focus {\n color: #1565c0; }\n\nblockquote {\n background-color: #f5f9fe;\n padding: 8px;\n margin: 16px 0;\n border-style: solid;\n border-color: #1565c0;\n border-width: 0 0 0 3px; }\n\n.has-indicator:after {\n content: '';\n position: absolute;\n border-radius: 50%;\n padding: 5px;\n background-color: #F05843; }\n\n.has-indicator--icon-button:after {\n top: 25px;\n left: 5px; }\n\n.badge {\n border-radius: 4px;\n padding: 2px 6px;\n font-size: 12px;\n font-weight: 500;\n font-style: normal;\n background-color: #aaaaaa; }\n .badge.md-button {\n min-width: 0;\n min-height: 0;\n line-height: inherit; }\n .badge.md-button:hover, .badge.md-button:focus {\n background-color: #aaaaaa; }\n .badge.md-button:focus {\n outline: 1px dotted #aaaaaa; }\n\n.badge--info {\n background-color: #ef6c00;\n color: #ffffff; }\n\n.badge--warn {\n background-color: #c62828;\n color: #ffffff; }\n\n.badge--success {\n background-color: #00C853;\n color: #ffffff; }\n\n.divider--withmargin {\n margin: 16px 0; }\n\n.divider--dashed {\n border-top-style: dashed; }\n\na {\n color: #1565c0;\n cursor: pointer; }\n\n.active {\n background-color: rgba(158, 158, 158, 0.2);\n color: rgba(0, 0, 0, 0.87); }\n\n.avatar {\n border-radius: 50%;\n box-sizing: content-box; }\n\n.avatar--square {\n border-radius: 4px; }\n\n.avatar.md-18 {\n height: 30px;\n width: 30px; }\n\n.avatar.md-24 {\n height: 36px;\n width: 36px; }\n\n.avatar.md-36 {\n height: 48px;\n width: 48px; }\n\n.avatar.md-48 {\n height: 60px;\n width: 60px; }\n\n.avatar--icon {\n background-color: #dddddd;\n white-space: normal !important; }\n .avatar--icon:not(.md-avatar) {\n padding: 6px; }\n .avatar--icon.md-18 {\n height: 18px;\n width: 18px; }\n .avatar--icon.md-24 {\n height: 24px;\n width: 24px; }\n .avatar--icon.md-36 {\n height: 36px;\n width: 36px; }\n .avatar--icon.md-48 {\n height: 48px;\n width: 48px; }\n\nmd-toolbar.md-light-theme:not(.md-menu-toolbar) md-icon {\n color: rgba(0, 0, 0, 0.54); }\n\nmd-toolbar.md-light-theme:not(.md-menu-toolbar) .md-button:disabled md-icon {\n color: rgba(0, 0, 0, 0.26); }\n\nmd-icon.primary, .md-button:not([disabled]).primary {\n color: #1565c0 !important; }\n\nmd-icon.success, .md-button:not([disabled]).success {\n color: #00C853 !important; }\n\nmd-icon.warn, .md-button:not([disabled]).warn {\n color: #c62828 !important; }\n\nmd-icon.info, .md-button:not([disabled]).info {\n color: #ef6c00 !important; }\n\nmd-icon.accent, .md-button:not([disabled]).accent {\n color: #F05843 !important; }\n\nmd-icon.accent-1, .md-button:not([disabled]).accent-1 {\n color: #795C3A !important; }\n\nmd-icon.accent-2, .md-button:not([disabled]).accent-2 {\n color: #CAD266 !important; }\n\nmd-input-container.md-wise-theme label {\n color: rgba(0, 0, 0, 0.87); }\n\nmd-select-menu.md-default-theme md-option[selected], md-select-menu md-option[selected] {\n background-color: #ecf4fd; }\n\n.md-autocomplete-suggestions-container.md-default-theme li .highlight,\n.md-autocomplete-suggestions-container li .highlight {\n color: #1565c0;\n background-color: #ecf4fd; }\n\n.primary {\n color: #1565c0; }\n\n.accent {\n color: #F05843; }\n\n.accent-1 {\n color: #795C3A; }\n\n.accent-2 {\n color: #CAD266; }\n\n.warn {\n color: #c62828; }\n\n.info {\n color: #ef6c00; }\n\n.success {\n color: #00C853; }\n\n.divider {\n color: rgba(0, 0, 0, 0.12); }\n\n.gray-lightest {\n color: #f7f7f7; }\n\n.gray-lighter {\n color: #eeeeee; }\n\n.gray-light {\n color: #dddddd; }\n\n.gray {\n color: #cccccc; }\n\n.gray-dark {\n color: #aaaaaa; }\n\n.gray-darker {\n color: #757575; }\n\n.gray-darkest {\n color: #333333; }\n\n.text {\n color: rgba(0, 0, 0, 0.87); }\n\n.text-secondary {\n color: rgba(0, 0, 0, 0.54); }\n\n.text-disabled {\n color: rgba(0, 0, 0, 0.26); }\n\n.text-light {\n color: white; }\n\n.text-light-secondary {\n color: rgba(255, 255, 255, 0.7); }\n\n.text-light-disabled {\n color: rgba(255, 255, 255, 0.5); }\n\n.selected-bg {\n color: #ecf4fd; }\n\n.score {\n color: #FFC107; }\n\n.body {\n color: rgba(0, 0, 0, 0.87); }\n\n.body-bg {\n color: #eeeeee; }\n\n.primary-bg {\n background-color: #1565c0; }\n\n.accent-bg {\n background-color: #F05843; }\n\n.accent-1-bg {\n background-color: #795C3A; }\n\n.accent-2-bg {\n background-color: #CAD266; }\n\n.warn-bg {\n background-color: #c62828; }\n\n.info-bg {\n background-color: #ef6c00; }\n\n.success-bg {\n background-color: #00C853; }\n\n.divider-bg {\n background-color: rgba(0, 0, 0, 0.12); }\n\n.gray-lightest-bg {\n background-color: #f7f7f7; }\n\n.gray-lighter-bg {\n background-color: #eeeeee; }\n\n.gray-light-bg {\n background-color: #dddddd; }\n\n.gray-bg {\n background-color: #cccccc; }\n\n.gray-dark-bg {\n background-color: #aaaaaa; }\n\n.gray-darker-bg {\n background-color: #757575; }\n\n.gray-darkest-bg {\n background-color: #333333; }\n\n.text-bg {\n background-color: rgba(0, 0, 0, 0.87); }\n\n.text-secondary-bg {\n background-color: rgba(0, 0, 0, 0.54); }\n\n.text-disabled-bg {\n background-color: rgba(0, 0, 0, 0.26); }\n\n.text-light-bg {\n background-color: white; }\n\n.text-light-secondary-bg {\n background-color: rgba(255, 255, 255, 0.7); }\n\n.text-light-disabled-bg {\n background-color: rgba(255, 255, 255, 0.5); }\n\n.selected-bg-bg {\n background-color: #ecf4fd; }\n\n.score-bg {\n background-color: #FFC107; }\n\n.body-bg {\n background-color: rgba(0, 0, 0, 0.87); }\n\n.body-bg-bg {\n background-color: #eeeeee; }\n\nmd-progress-circular.primary path {\n stroke: #1565c0; }\n\nmd-progress-circular.accent path {\n stroke: #F05843; }\n\nmd-progress-circular.accent-1 path {\n stroke: #795C3A; }\n\nmd-progress-circular.accent-2 path {\n stroke: #CAD266; }\n\nmd-progress-circular.warn path {\n stroke: #c62828; }\n\nmd-progress-circular.info path {\n stroke: #ef6c00; }\n\nmd-progress-circular.success path {\n stroke: #00C853; }\n\nmd-progress-circular.divider path {\n stroke: rgba(0, 0, 0, 0.12); }\n\nmd-progress-circular.gray-lightest path {\n stroke: #f7f7f7; }\n\nmd-progress-circular.gray-lighter path {\n stroke: #eeeeee; }\n\nmd-progress-circular.gray-light path {\n stroke: #dddddd; }\n\nmd-progress-circular.gray path {\n stroke: #cccccc; }\n\nmd-progress-circular.gray-dark path {\n stroke: #aaaaaa; }\n\nmd-progress-circular.gray-darker path {\n stroke: #757575; }\n\nmd-progress-circular.gray-darkest path {\n stroke: #333333; }\n\nmd-progress-circular.text path {\n stroke: rgba(0, 0, 0, 0.87); }\n\nmd-progress-circular.text-secondary path {\n stroke: rgba(0, 0, 0, 0.54); }\n\nmd-progress-circular.text-disabled path {\n stroke: rgba(0, 0, 0, 0.26); }\n\nmd-progress-circular.text-light path {\n stroke: white; }\n\nmd-progress-circular.text-light-secondary path {\n stroke: rgba(255, 255, 255, 0.7); }\n\nmd-progress-circular.text-light-disabled path {\n stroke: rgba(255, 255, 255, 0.5); }\n\nmd-progress-circular.selected-bg path {\n stroke: #ecf4fd; }\n\nmd-progress-circular.score path {\n stroke: #FFC107; }\n\nmd-progress-circular.body path {\n stroke: rgba(0, 0, 0, 0.87); }\n\nmd-progress-circular.body-bg path {\n stroke: #eeeeee; }\n\n.l-constrained {\n margin-left: auto;\n margin-right: auto;\n max-width: 100%;\n position: relative; }\n @media (min-width: 600px) {\n .l-constrained {\n width: 1280px; } }\n\n.l-constrained-md {\n width: 960px;\n max-width: 100%; }\n\n.l-footer {\n position: fixed;\n bottom: 0;\n left: 0;\n right: 0;\n z-index: 1;\n background-color: #ffffff;\n border-top: 1px solid #eeeeee; }\n\n.button--footer {\n margin: 0;\n padding-top: 0;\n padding-bottom: 0;\n min-width: 0;\n display: flex; }\n\n.button--footer__element {\n padding-left: 8px; }\n\n.l-header {\n z-index: 3; }\n .l-header .logo {\n margin-left: 0 !important;\n height: 36px;\n width: 36px;\n vertical-align: middle; }\n .l-header .logo-link {\n min-width: auto;\n display: none;\n padding: 0 4px;\n margin-right: 12px; }\n @media only screen and (min-width: 600px) {\n .l-header .logo-link {\n display: block; } }\n .l-header .logo-link:hover, .l-header .logo-link:focus {\n border: 0 none; }\n @media only screen and (max-width: 599px) {\n .l-header .md-toolbar-tools h1, .l-header .md-toolbar-tools h2, .l-header .md-toolbar-tools h3 {\n font-size: 15px; } }\n\n.l-main {\n background-color: #eeeeee; }\n\n.l-main--with-toolbar {\n margin-top: 42px; }\n\n#content {\n transition: margin-top 0.5s; }\n\n.view-content {\n margin: 0 auto;\n padding: 8px;\n position: absolute;\n left: 0;\n right: 0;\n transition: opacity 500ms; }\n @media only screen and (min-width: 960px) {\n .view-content {\n padding: 16px; } }\n .view-content.ng-enter {\n opacity: 0; }\n .view-content .ng-enter-active {\n opacity: 1;\n transition-delay: 250ms; }\n .view-content.ng-leave-active, .view-content.ng-hide {\n opacity: 0; }\n .view-content.ng-hide-add, .view-content.ng-hide-add-active, .view-content.ng-hide-remove, .view-content.ng-hide-remove-active {\n opacity: 0; }\n\n.view-content--with-sidemenu {\n padding: 8px; }\n @media only screen and (min-width: 600px) {\n .view-content--with-sidemenu {\n margin-left: 54px;\n padding: 16px; } }\n\n@media only screen and (min-width: 600px) {\n [dir='rtl'] .view-content--with-sidemenu {\n margin-left: auto;\n margin-right: 54px; } }\n\n.content-head {\n margin: 8px 0; }\n .content-head h1,\n .content-head h2,\n .content-head h3 {\n font-weight: 300;\n margin-top: 0;\n margin-bottom: 0;\n font-size: 36px; }\n @media only screen and (max-width: 959px) {\n .content-head h1,\n .content-head h2,\n .content-head h3 {\n font-size: 32px;\n text-align: center; } }\n\n@media only screen and (max-width: 959px) {\n .content-head__more {\n margin-top: 8px; } }\n\n.content-head__item,\nh2.content-head__item {\n margin: 0 8px; }\n .content-head__item .md-subhead,\n h2.content-head__item .md-subhead {\n padding-left: 4px; }\n @media only screen and (max-width: 959px) {\n .content-head__item .md-subhead,\n h2.content-head__item .md-subhead {\n display: block;\n padding-left: 0; } }\n .content-head__item md-icon,\n h2.content-head__item md-icon {\n vertical-align: text-bottom; }\n\n.stepSelectMenuContainer md-select-menu,\n.stepSelectMenuContainer md-select-menu md-content {\n max-height: 500px; }\n\n.l-nav {\n background-color: #eeeeee !important; }\n\n.l-notebook {\n margin-top: 42px;\n background-color: #eeeeee !important; }\n\n.l-sidebar__header {\n background-color: #ffffff !important;\n color: #795C3A !important; }\n .l-sidebar__header md-select {\n color: rgba(0, 0, 0, 0.87); }\n\n.status-icon {\n margin: 0 4px;\n z-index: 1;\n vertical-align: bottom; }\n\n.md-button.status-icon {\n height: auto;\n width: auto;\n min-height: 0;\n line-height: inherit;\n margin: 0 4px;\n padding: 0; }\n\n.avatar--icon--alert {\n background-color: #ffffff; }\n\n.avatar--icon--alert__icon {\n font-size: 48px;\n margin: -4px 0 0 -4px; }\n\n.gu-mirror {\n position: fixed !important;\n margin: 0 !important;\n z-index: 9999 !important;\n opacity: 0.8;\n transition: opacity 250ms ease-in-out; }\n\n.gu-hide {\n display: none !important; }\n\n.gu-unselectable {\n -webkit-user-select: none !important;\n -moz-user-select: none !important;\n -ms-user-select: none !important;\n user-select: none !important; }\n\n.gu-transit {\n opacity: 0.2; }\n\nmd-dialog {\n width: 600px; }\n\n.dialog--wide {\n width: 960px; }\n\n.dialog--wider {\n width: 1280px; }\n\n.help-bubble {\n border-radius: 4px;\n max-width: 320px; }\n @media (min-width: 600px) {\n .help-bubble {\n max-width: 552px; } }\n @media (min-width: 960px) {\n .help-bubble {\n max-width: 912px; } }\n @media (min-width: 1280px) {\n .help-bubble {\n max-width: 1232px; } }\n\n.help-bubble__title {\n border-top-left-radius: 4px;\n border-top-right-radius: 4px; }\n\n.help-bubble___title__content {\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n padding: 0px 0 0 12px;\n background-color: #ef6c00; }\n .help-bubble___title__content .md-icon-button {\n margin-right: 0;\n padding-top: 0;\n padding-bottom: 0; }\n\n.help-bubble__content {\n overflow: auto;\n padding: 8px 12px;\n max-height: 480px; }\n\n.help-bubble__actions {\n border-bottom-left-radius: 4px;\n border-bottom-right-radius: 4px; }\n\ndiv.hopscotch-bubble {\n border-radius: 4px;\n border: 0 none;\n font-family: Roboto, \"Helvetica Neue\", sans-serif;\n font-size: 14px;\n z-index: 6; }\n div.hopscotch-bubble .hopscotch-bubble-arrow-container {\n position: absolute;\n width: 20px;\n height: 20px; }\n div.hopscotch-bubble .hopscotch-bubble-arrow-container.up {\n top: 0;\n left: 12px; }\n div.hopscotch-bubble .hopscotch-bubble-arrow-container.up .hopscotch-bubble-arrow {\n border-bottom: 10px solid #ef6c00;\n border-left: 10px solid transparent;\n border-right: 10px solid transparent;\n position: relative;\n top: -10px; }\n div.hopscotch-bubble .hopscotch-bubble-arrow-container.up .hopscotch-bubble-arrow-border {\n border: 0 none;\n /*border-bottom: 12px solid color('gray-darker');\n border-left: 12px solid transparent;\n border-right: 12px solid transparent;*/ }\n div.hopscotch-bubble .hopscotch-bubble-arrow-container.down {\n bottom: -34px;\n left: 12px; }\n div.hopscotch-bubble .hopscotch-bubble-arrow-container.down .hopscotch-bubble-arrow {\n border-top: 10px solid #ef6c00;\n border-left: 10px solid transparent;\n border-right: 10px solid transparent;\n position: relative;\n top: -10px; }\n div.hopscotch-bubble .hopscotch-bubble-arrow-container.down .hopscotch-bubble-arrow-border {\n border: 0 none;\n /*border-top: 12px solid color('gray-darker');\n border-left: 12px solid transparent;\n border-right: 12px solid transparent;*/ }\n div.hopscotch-bubble .hopscotch-bubble-arrow-container.left {\n top: 12px;\n left: -10px; }\n div.hopscotch-bubble .hopscotch-bubble-arrow-container.left .hopscotch-bubble-arrow {\n border-right: 10px solid #ef6c00;\n border-bottom: 10px solid transparent;\n border-top: 10px solid transparent;\n position: relative;\n left: 0;\n top: -10px; }\n div.hopscotch-bubble .hopscotch-bubble-arrow-container.left .hopscotch-bubble-arrow-border {\n border: 0 none;\n /*border-right: 12px solid color('gray-darker');\n border-bottom: 12px solid transparent;\n border-top: 12px solid transparent;*/ }\n div.hopscotch-bubble .hopscotch-bubble-arrow-container.right {\n top: 12px;\n right: -30px; }\n div.hopscotch-bubble .hopscotch-bubble-arrow-container.right .hopscotch-bubble-arrow {\n border-left: 10px solid #ef6c00;\n border-bottom: 10px solid transparent;\n border-top: 10px solid transparent;\n position: relative;\n right: 0;\n top: -10px; }\n div.hopscotch-bubble .hopscotch-bubble-arrow-container.right .hopscotch-bubble-arrow-border {\n border: 0 none;\n /*border-right: 12px solid color('gray-darker');\n border-bottom: 12px solid transparent;\n border-top: 12px solid transparent;*/ }\n\n.input-container {\n padding-top: 12px; }\n\n.input-container--component {\n margin-bottom: 0; }\n\n.input-container--open-response.md-has-icon {\n padding-left: 0; }\n\n.input-container--open-response .md-errors-spacer {\n display: none; }\n\n.input-wrapper {\n position: relative; }\n\n.input-wrapper--focused .input--textarea__action md-icon {\n color: #1565c0; }\n\n.input--textarea, .input-container textarea.input--textarea {\n padding: 8px;\n background-color: #f7f7f7;\n border: 1px solid #cccccc;\n margin-bottom: 8px; }\n .input--textarea:focus, .input-container textarea.input--textarea:focus {\n background-color: #ffffff; }\n .input--textarea[disabled], .input-container textarea.input--textarea[disabled] {\n color: rgba(0, 0, 0, 0.54); }\n\n.input-container textarea.input--textarea {\n width: 100%; }\n\n.input--textarea--disabled {\n color: rgba(0, 0, 0, 0.54); }\n\n.input--textarea__action {\n position: absolute;\n right: -4px; }\n .input--textarea__action[disabled] md-icon {\n color: rgba(0, 0, 0, 0.26) !important; }\n\n.input--textarea__action--notebook {\n top: 6px; }\n .input-wrapper--richtext .input--textarea__action--notebook {\n top: -7px; }\n\n.input--textarea__action--revision {\n bottom: 6px; }\n .input-wrapper--richtext .input--textarea__action--revision {\n bottom: -5px; }\n\n.input-label, md-input-container:not(.md-input-invalid):not(.md-input-focused).md-input-has-value label.input-label {\n line-height: 1.2;\n color: rgba(0, 0, 0, 0.87); }\n .input-label.input-label--focused, md-input-container:not(.md-input-invalid):not(.md-input-focused).md-input-has-value label.input-label.input-label--focused {\n color: #1565c0; }\n\n.autocomplete input {\n text-overflow: ellipsis;\n overflow: hidden;\n word-wrap: none;\n font-weight: 500;\n color: rgba(0, 0, 0, 0.54); }\n\n@media only screen and (min-width: 600px) {\n .autocomplete--minwidth {\n min-width: 300px; } }\n\n@media only screen and (min-width: 960px) {\n .autocomplete--minwidth {\n min-width: 300px; } }\n\n.autocomplete--flat md-autocomplete-wrap {\n background-color: #ffffff; }\n .autocomplete--flat md-autocomplete-wrap:not(.md-menu-showing) {\n box-shadow: none;\n background-color: #eeeeee; }\n\n.select__header {\n height: 48px; }\n .select__header input {\n height: 100%;\n width: 100%;\n padding: 0 8px;\n outline: none;\n border: 0 none;\n font-size: 14px;\n font-weight: 500; }\n\n.table {\n max-width: 100%;\n width: auto;\n min-width: 100px;\n margin: 8px 0; }\n .table thead > tr > th,\n .table thead > tr > td,\n .table tbody > tr > th,\n .table tbody > tr > td,\n .table tfoot > tr > th,\n .table tfoot > tr > td {\n border: 1px solid #cccccc;\n padding: 6px;\n font-size: 15px;\n min-height: 32px;\n height: 32px;\n min-width: 32px;\n vertical-align: top; }\n .table td.inactive,\n .table th {\n background-color: #f7f7f7;\n opacity: 1;\n visibility: visible; }\n .table md-input-container {\n margin: 0; }\n .table .md-errors-spacer {\n display: none; }\n\n.table--student td.inactive {\n padding: 8px 10px; }\n\n.table--full-width {\n width: 100%; }\n\n.table--list {\n border: 0 none;\n border-collapse: collapse;\n background-color: #ffffff;\n max-width: 100%;\n overflow: auto; }\n .table--list th,\n .table--list td {\n padding: 0 4px;\n border: 0 none; }\n .table--list td {\n min-height: 56px;\n height: 56px; }\n .table--list tr.md-button {\n display: table-row;\n text-align: left;\n width: auto;\n text-transform: none;\n font-size: inherit;\n font-weight: normal; }\n\n.table--list__wrap {\n min-width: 600px; }\n\n@media only screen and (max-width: 959px) {\n .table-wrap-sticky {\n overflow-x: auto; } }\n\n.table--list__thead {\n font-size: 14px;\n font-weight: 700; }\n\n.table--list__thead__tr {\n height: 100%;\n margin: 0; }\n\n.table--list__thead__th {\n background-color: #757575;\n color: white;\n min-height: 42px;\n height: 42px; }\n\n.table--list__thead__link {\n color: #ffffff;\n text-transform: none;\n margin: 0;\n min-width: 0;\n white-space: normal;\n line-height: 1.4;\n width: 100%; }\n\n.table--list__thead__sort {\n margin: 0; }\n\n.table--list__thead__sort--reverse {\n transform: rotate(180deg); }\n\n.td--wrap {\n min-width: 180px;\n white-space: normal;\n line-height: 1.2; }\n\n@media only screen and (max-width: 959px) {\n .td--max-width {\n max-width: 180px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap; } }\n\n.md-toolbar-tools {\n font-size: 18px; }\n .md-toolbar-tools .autocomplete {\n height: 36px; }\n .md-toolbar-tools .autocomplete md-autocomplete-wrap {\n height: 36px; }\n .md-toolbar-tools .autocomplete input {\n height: 36px; }\n\n.md-toolbar--wise {\n min-height: 42px; }\n .md-toolbar--wise .md-toolbar-tools {\n height: 42px;\n max-height: 42px; }\n .md-toolbar--wise .md-button.md-icon-button {\n height: 42px;\n line-height: 42px;\n width: 42px; }\n\n.md-toolbar--wise--sm .md-toolbar-tools {\n padding: 0 8px;\n font-size: 15px; }\n\n.md-toolbar--sidenav {\n background-color: #333333 !important; }\n .md-toolbar--sidenav .md-toolbar-tools {\n font-size: 16px;\n font-weight: 500; }\n\n.toolbar {\n position: fixed;\n left: 0;\n right: 0;\n top: 52px;\n z-index: 3; }\n\n.toolbar__title {\n margin-left: 8px;\n font-size: 16px;\n font-weight: 500; }\n\n.toolbar__tools {\n padding-right: 8px; }\n\n[dir=rtl] .toolbar__tools {\n padding-right: 16px;\n padding-left: 8px; }\n\n.toolbar__nav, .md-button.toolbar__nav {\n margin: 0; }\n\n.toolbar__select, .md-button.toolbar__select {\n margin: 0 4px;\n min-height: 32px;\n background-color: #f7f7f7; }\n .toolbar__select .md-select-value, .md-button.toolbar__select .md-select-value {\n height: 32px;\n text-align: left; }\n\n[dir=rtl] .toolbar__select .md-select-value, [dir=rtl] .md-button.toolbar__select .md-select-value {\n text-align: right; }\n\n.toolbar__select--fixedwidth {\n width: 168px; }\n @media only screen and (min-width: 600px) {\n .toolbar__select--fixedwidth {\n width: 264px; } }\n @media only screen and (min-width: 960px) {\n .toolbar__select--fixedwidth {\n width: 432px; } }\n\n.list-item {\n background-color: #ffffff;\n border-bottom: 1px solid #eeeeee; }\n .list-item .md-subheader, .list-item.md-subheader {\n color: rgba(0, 0, 0, 0.87);\n background-color: #ffffff; }\n .list-item .md-subheader md-icon, .list-item.md-subheader md-icon {\n vertical-align: middle; }\n .list-item .md-subheader .md-subheader-inner, .list-item.md-subheader .md-subheader-inner {\n padding: 0; }\n .list-item .md-subheader .md-avatar, .list-item.md-subheader .md-avatar {\n margin-right: 8px; }\n .list-item .autocomplete {\n margin: 8px 0; }\n\n.list-item--info._md-button-wrap > div.md-button:first-child, .list-item--info .md-subheader-content {\n border-left: 4px solid #ef6c00 !important;\n margin-left: -4px; }\n\n.list-item--warn._md-button-wrap > div.md-button:first-child, .list-item--warn .md-subheader-content {\n border-left: 4px solid #c62828 !important;\n margin-left: -4px; }\n\n.list-item--expanded {\n border-bottom-width: 0; }\n\n.list-item--noclick, .list-item--noclick.md-button {\n cursor: default;\n background-color: #f7f7f7; }\n\n.list-item--actions {\n padding: 0 8px !important; }\n\n.list-item__subheader-button {\n text-transform: none;\n width: 100%;\n padding: 8px 16px;\n margin: 0;\n white-space: normal;\n text-align: left;\n line-height: 1.4; }\n\n.user-list {\n font-size: 15px; }\n\n.notice {\n text-align: center;\n padding: 8px;\n background-color: rgba(0, 0, 0, 0.04);\n width: 100%; }\n @media (min-width: 600px) {\n .notice {\n max-width: 80%;\n border-radius: 3px;\n margin: 24px auto; } }\n\n.milestone {\n min-width: 196px;\n width: 196px;\n height: 242px;\n background-color: #ffffff;\n padding: 0; }\n .milestone.md-button {\n text-transform: none; }\n\n.milestone__progress {\n background-color: #eeeeee;\n border-radius: 50%;\n position: relative;\n margin-bottom: 12px; }\n\n.milestone__progress__percent {\n position: absolute;\n top: 8px;\n bottom: 8px;\n left: 8px;\n right: 8px;\n border-radius: 50%;\n background-color: #ffffff;\n color: #1565c0;\n font-size: 28px;\n font-weight: 500; }\n\n.milestone__title {\n font-weight: 700;\n font-size: 15px;\n margin-bottom: 12px; }\n\n.milestone-details section:not(:first-child) {\n margin-top: 8px; }\n\n.milestone-details__section {\n background-color: #ffffff;\n padding: 16px; }\n .milestone-details__section > p {\n margin-top: 16px;\n margin-bottom: 0; }\n .milestone-details__section md-list {\n padding: 0; }\n .milestone-details__section .grading {\n padding: 0; }\n\n.milestone-details__header {\n padding: 12px 16px;\n margin: -16px -16px 16px;\n text-transform: uppercase;\n font-weight: 500; }\n\n.milestone-details__progress {\n width: 48px;\n margin-right: 8px; }\n\n.milestone--add.md-button {\n text-transform: uppercase; }\n\n.milestone--add__icon {\n height: 96px;\n width: 96px;\n background-color: #eeeeee;\n border-radius: 50%; }\n\n#nav {\n position: relative; }\n\n.nav {\n margin-bottom: 16px; }\n\n.nav-mask {\n position: absolute;\n top: 0;\n bottom: 0;\n left: 0;\n right: 0;\n background-color: rgba(0, 0, 0, 0.25);\n z-index: 1; }\n .nav-mask.ng-hide {\n opacity: 0; }\n\n.nav-head {\n color: rgba(0, 0, 0, 0.54);\n font-weight: 500; }\n .nav-head md-icon {\n line-height: 20px; }\n\n.nav-contents--root {\n padding: 6px 6px 12px; }\n\n.nav-contents--group {\n background-color: #dddddd;\n padding: 8px; }\n\n.nav-contents--root {\n padding: 0; }\n\n.nav-contents__list {\n padding: 0; }\n @media (min-width: 600px) {\n .nav-contents__list {\n padding: 8px; } }\n\n.nav-item {\n transition: opacity 250ms ease-in-out; }\n .nav-item.prev md-list-item {\n background-color: #ecf4fd;\n /*&._md-button-wrap>div.md-button:first-child {\n border-left: 4px solid color('primary');\n margin-left: -4px;\n }*/ }\n\n.nav-item--card__content {\n border-top-right-radius: 4px;\n border-top-left-radius: 4px; }\n .nav-item--card__content:focus {\n outline: none; }\n .nav-item--card__content:focus .nav-item__title > span {\n border-bottom: 1px dashed #cccccc; }\n\n.nav-item--root {\n transition: margin 250ms, box-shadow 500ms; }\n .nav-item--root.expanded {\n flex-basis: 100%;\n max-width: 100%;\n max-height: none !important;\n margin: 8px auto;\n padding-left: 4px; }\n .nav-item--root.expanded:first-of-type {\n margin-top: 0; }\n\n.nav-item--list__info-item {\n padding: 0 16px 0 4px;\n display: inline-block; }\n\n.nav-item--list__reorder {\n margin-left: 8px;\n color: rgba(0, 0, 0, 0.26); }\n\n.nav-item--card--group:not(.expanded) {\n box-shadow: 0px 3px 1px -2px rgba(0, 0, 0, 0.14), 0px 2px 2px 0px rgba(0, 0, 0, 0.098), 0px 1px 5px 0px rgba(0, 0, 0, 0.084), 3px 3px 0px 1px #d5d5d5, 6px 6px 0px 1px #aaaaaa; }\n\n.nav-item__collapse {\n margin: 0; }\n\n.nav-item__more {\n border-top: 1px solid #dddddd;\n border-bottom-right-radius: 4px;\n border-bottom-left-radius: 4px;\n padding: 8px 16px;\n min-height: 40px; }\n\n.nav-item__users {\n height: auto;\n cursor: pointer;\n color: #ffffff;\n margin: 0;\n padding: 1px 6px; }\n .nav-item__users > md-icon {\n padding-right: 4px;\n color: #ffffff; }\n .nav-item__users:hover.success-bg, .nav-item__users:focus.success-bg {\n background-color: #00C853; }\n\n.nav-item__title {\n padding-left: 16px;\n line-height: 1.2;\n font-weight: 400; }\n\n[dir=rtl] .nav-item__title {\n padding-left: auto;\n padding-right: 16px; }\n\n.nav-item__info {\n padding: 0 8px; }\n\n.nav-item__progress {\n width: 48px; }\n .nav-item__progress > .md-container {\n top: 0; }\n\n.nav-item__progress-value {\n margin-left: 8px;\n width: 36px; }\n\n.progress-wrapper {\n padding: 2px 0;\n cursor: pointer; }\n\n.student-select {\n padding-top: 0;\n padding-bottom: 0; }\n\n.workgroup-progress {\n margin-bottom: 8px; }\n @media (min-width: 960px) {\n .workgroup-progress {\n margin-bottom: 0; } }\n\nalert-status-corner {\n position: absolute;\n top: 0;\n right: 0; }\n\n.menu-progress {\n position: absolute;\n top: 10px;\n right: 12px; }\n .menu-progress path {\n stroke: #CAD266 !important;\n stroke-width: 2px; }\n\n[dir=rtl] .menu-progress {\n right: auto;\n left: 12px; }\n\n.menu-sidenav__item {\n font-weight: 700;\n font-size: 14px; }\n\n.menu-sidenav__icon {\n margin-top: 12px !important;\n margin-right: 12px !important;\n margin-left: 12px; }\n\n.active .menu-sidenav__icon, .active .menu-sidenav__item {\n color: #1565c0; }\n\n.menu-sidebar {\n position: absolute;\n top: 94px;\n bottom: 0;\n left: 0;\n background-color: #fff;\n width: 56px;\n overflow: hidden;\n padding: 8px 0;\n text-align: center;\n border-right: 1px solid #cccccc; }\n @media only screen and (max-width: 599px) {\n .menu-sidebar {\n display: none; } }\n\n[dir=rtl] .menu-sidebar {\n right: 0;\n left: auto; }\n\n.md-button.md-icon-button.menu-sidebar__link {\n margin-top: 6px;\n margin-bottom: 6px; }\n\n#node {\n margin: 0 auto;\n position: absolute;\n left: 0;\n right: 0; }\n @media only screen and (min-width: 600px) {\n #node {\n padding: 8px;\n padding: 24px 16px;\n margin-bottom: 32px; } }\n @media only screen and (min-width: 960px) {\n #node {\n padding: 32px; } }\n #node.ng-enter {\n transition: opacity .5s;\n opacity: 0; }\n #node.ng-enter-active {\n opacity: 1; }\n\n@media only screen and (min-width: 600px) {\n .node-notice {\n margin-top: -8px;\n margin-bottom: 16px; } }\n\n@media only screen and (min-width: 960px) {\n .node-notice {\n margin-top: -16px; } }\n\n.node-content {\n padding: 0 0 48px;\n background-color: #ffffff;\n border-radius: 3px;\n overflow: visible; }\n @media only screen and (max-width: 599px) {\n .node-content {\n box-shadow: none; } }\n @media only screen and (min-width: 600px) {\n .node-content {\n padding: 0;\n border-top: 2px solid;\n border-bottom: 2px solid; } }\n\nmd-content.node-content {\n background-color: #ffffff; }\n\n.node-content__rubric {\n position: absolute;\n top: -22px;\n left: 0;\n right: 0;\n z-index: 1; }\n .node-content__rubric .avatar--icon {\n transform: scale(0.94); }\n @media only screen and (max-width: 599px) {\n .node-content__rubric .avatar--icon {\n transform: scale(0.8); } }\n\n.node-icon {\n color: #ffffff;\n vertical-align: inherit; }\n\n.node-select {\n margin: 0 8px;\n min-width: 0;\n font-weight: 500;\n font-size: 15px; }\n .node-select .md-select-value *:first-child {\n transform: translate3d(0, 0, 0);\n flex: 1 0 0; }\n .node-select .md-select-value .node-select__icon {\n display: none; }\n .node-select .md-select-value .node-select__status {\n display: none; }\n .node-select .md-select-icon {\n margin-left: 0;\n color: rgba(0, 0, 0, 0.87); }\n\n.node-select-option--group {\n background-color: #f7f7f7;\n border-bottom: 1px solid #eeeeee;\n border-top: 1px solid #eeeeee; }\n\n.node-select-option--node {\n padding-left: 20px; }\n\n.node-select__icon {\n margin-right: 8px; }\n\n.node-select__status {\n margin-left: 8px; }\n\n.node-select__text {\n text-overflow: ellipsis;\n white-space: nowrap;\n overflow: hidden; }\n @media only screen and (min-width: 600px) {\n .node-select__text {\n margin-top: 2px; } }\n\n.node-title {\n line-height: 1.2;\n text-transform: none;\n margin-top: 3px; }\n @media only screen and (max-width: 599px) {\n .node-title {\n font-size: 15px; } }\n\n.node-content__actions {\n padding: 0 16px 16px; }\n @media only screen and (min-width: 960px) {\n .node-content__actions {\n padding: 0 24px 24px; } }\n @media only screen and (min-width: 1280px) {\n .node-content__actions {\n padding: 0 32px 32px; } }\n .node-content__actions .md-button:first-child {\n margin-left: 0; }\n .node-content__actions .md-button:last-child {\n margin-right: 0; }\n\n.node-content__actions__info {\n font-style: italic;\n margin-left: 8px;\n color: rgba(0, 0, 0, 0.54); }\n\n.node-content__actions__more {\n border-bottom: 1px dotted; }\n\n.md-button.md-icon-button.node-nav:first-of-type {\n margin-right: 0; }\n\n@media only screen and (min-width: 600px) {\n .node-sidebar-active {\n margin-right: 68px; } }\n\n@media only screen and (max-width: 599px) {\n .node-sidebar-visible {\n margin-bottom: 42px; } }\n\n.node-sidebar {\n position: absolute;\n right: 0;\n top: 0;\n width: 52px; }\n\n.node-sidebar__toolbar {\n position: fixed;\n width: 52px;\n background-color: #ffffff;\n padding: 8px 0;\n border-radius: 3px; }\n @media only screen and (max-width: 599px) {\n .node-sidebar__toolbar {\n right: 0;\n bottom: 0;\n left: 0;\n width: 100%;\n border-radius: 0;\n padding: 0;\n min-height: 0;\n height: 42px; } }\n\n.node-info {\n margin: 0; }\n @media only screen and (max-width: 599px) {\n .node-info {\n margin: -16px;\n border-radius: 0; } }\n .node-info .divider {\n margin-left: -8px;\n margin-right: -8px; }\n .node-info .component:first-child {\n margin-top: -16px; }\n .node-info .component, .node-info .component__content, .node-info .component__header {\n margin-left: -8px;\n margin-right: -8px; }\n .node-info .component__actions {\n display: none; }\n\n.node-rubric {\n border: 2px solid #1565c0;\n margin: 8px 16px 24px;\n padding: 16px;\n background-color: #ffffff; }\n\n.node__label--vertical-alignment {\n vertical-align: middle;\n display: inline-block; }\n\n.grading__item-container {\n margin: 0 0 16px;\n padding: 0 !important; }\n\n.grading__item {\n background-color: #ffffff; }\n .grading__item .component {\n padding: 0; }\n\n@media only screen and (min-width: 600px) {\n .notebook-launcher.md-button.md-fab {\n z-index: 61; } }\n\n.notebook-report {\n border-radius: 4px 4px 0 0;\n margin: 0;\n height: 100%; }\n .notebook-report .note-toolbar {\n margin: -8px -8px 8px;\n padding: 4px;\n border: 0 none;\n border-top: 1px solid #dddddd;\n border-bottom: 1px solid #dddddd;\n border-radius: 0; }\n .notebook-report .note-toolbar .btn-group {\n margin-top: 0; }\n .notebook-report .note-btn {\n border: 0 none;\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n border-bottom-left-radius: 0;\n border-bottom-right-radius: 0; }\n\n.notebook-report-container {\n height: 550px;\n width: 450px;\n max-height: 90%;\n bottom: 0;\n right: 96px;\n position: absolute;\n z-index: 3; }\n\n@media only screen and (min-width: 960px) {\n .notes-visible .notebook-report-container {\n right: 516px;\n transition: right 250ms; } }\n\n.notebook-report-container__full {\n top: 16px;\n bottom: 16px;\n left: 16px;\n right: 16px;\n max-height: none;\n max-width: none;\n height: auto;\n width: auto; }\n .notebook-report-container__full .notebook-report {\n height: 100%;\n width: 100%;\n margin: 0 auto;\n max-height: none;\n border-radius: 4px; }\n\n.notebook-report-container__collapsed {\n width: 300px;\n height: auto; }\n .notebook-report-container__collapsed .notebook-report__content, .notebook-report-container__collapsed .notebook-report__actions, .notebook-report-container__collapsed .notebook-report__content__header {\n display: none; }\n\n.notebook-report__toolbar {\n background-color: #333333 !important;\n border-radius: 4px 4px 0 0; }\n\n.notebook-report__toolbar__title {\n max-width: 150px; }\n\n.notebook-report__content {\n background-color: #ffffff; }\n .notebook-report__content h1, .notebook-report__content h2, .notebook-report__content h3, .notebook-report__content h4 {\n font-size: 22px; }\n .notebook-report__content .note-editor.note-frame {\n border: 0 none;\n border-radius: 0;\n padding: 0;\n box-shadow: none; }\n .notebook-report__content .note-resizebar {\n display: none; }\n\n.notebook-report__content__header {\n padding: 8px;\n background-color: #ffffff;\n font-size: 16px; }\n\n@media only screen and (max-width: 599px) {\n .notebook-report-container:not(.notebook-report-container__collapsed) {\n top: 0;\n bottom: 0;\n left: 0;\n right: 0;\n max-height: none;\n max-width: none;\n height: auto;\n width: auto; }\n .notebook-report-container:not(.notebook-report-container__collapsed) .notebook-report {\n border-radius: 0; }\n .notebook-tools--full {\n display: none; } }\n\n.notebook-report-backdrop {\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n z-index: 3;\n background-color: #212121;\n opacity: .48; }\n\n.notebook-menu {\n transition: opacity 500ms; }\n .notebook-menu.ng-enter {\n opacity: 0; }\n .notebook-menu .ng-enter-active {\n opacity: 1;\n transition-delay: 250ms; }\n .notebook-menu.ng-leave-active, .notebook-menu.ng-hide {\n opacity: 0; }\n .notebook-menu.ng-hide-add, .notebook-menu.ng-hide-add-active, .notebook-menu.ng-hide-remove, .notebook-menu.ng-hide-remove-active {\n opacity: 0; }\n\n.notebook-item {\n transition: box-shadow 250ms;\n margin: 0 16px 16px;\n display: block; }\n\n.notebook-item__content {\n height: 250px;\n min-width: 230px;\n position: relative;\n padding: 0;\n background-color: #cccccc;\n border-top-left-radius: 4px;\n border-top-right-radius: 4px; }\n\n.notebook-item__content__attachment, .notebook-item__content__text {\n position: absolute;\n left: 0;\n right: 0; }\n\n.notebook-item__content__attachment {\n background-repeat: no-repeat !important;\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n background-position: center top !important;\n background-size: cover !important;\n top: 0;\n bottom: 0; }\n\n.notebook-item__content__text {\n bottom: 0;\n padding: 8px;\n font-weight: 500;\n overflow: hidden;\n max-height: 120px;\n min-height: 56px;\n background-color: rgba(255, 255, 255, 0.95);\n border-top: 1px solid #eeeeee; }\n .notebook-item__content__text:after {\n content: \"\";\n text-align: right;\n position: absolute;\n bottom: 0;\n right: 0;\n width: 100%;\n height: 0.8em;\n background: linear-gradient(180deg, rgba(255, 255, 255, 0), rgba(255, 255, 255, 0.95) 100%); }\n\n.notebook-item__content--text-only:after {\n content: \"note\";\n font-family: 'Material Icons';\n font-weight: normal;\n font-style: normal;\n display: inline-block;\n line-height: 1;\n text-transform: none;\n letter-spacing: normal;\n word-wrap: normal;\n white-space: nowrap;\n direction: ltr;\n /* Support for all WebKit browsers. */\n -webkit-font-smoothing: antialiased;\n /* Support for Safari and Chrome. */\n text-rendering: optimizeLegibility;\n /* Support for Firefox. */\n -moz-osx-font-smoothing: grayscale;\n /* Support for IE. */\n font-feature-settings: 'liga';\n font-size: 80px;\n color: rgba(0, 0, 0, 0.26); }\n\n.notebook-item--question__content--text-only {\n content: \"live_help\"; }\n\n.notebook-item__content__location {\n opacity: 0.9;\n padding: 8px 0; }\n .notebook-item__content__location md-icon {\n font-size: 22px; }\n\n.notebook-item__edit {\n cursor: pointer; }\n\n.notebook-item__actions {\n margin: 0;\n padding: 0 8px;\n color: #ffffff;\n background-color: #333333;\n border-bottom-left-radius: 4px;\n border-bottom-right-radius: 4px; }\n .notebook-item__actions md-icon {\n color: #ffffff; }\n\n.notebook-item__text-input {\n margin: 0; }\n\n.notebook-item__text-input__textarea {\n padding-left: 0;\n padding-right: 0; }\n\n.notebook-item__attachment {\n background-color: #dddddd;\n padding: 16px;\n margin-bottom: 16px;\n text-align: center;\n position: relative; }\n\n.notebook-item__attachment__content {\n max-width: 100%;\n height: auto; }\n\n.notebook-item__attachment__delete {\n position: absolute;\n top: 4px;\n right: -2px;\n width: 34px !important;\n height: 34px !important;\n min-height: 0; }\n .notebook-item__attachment__delete md-icon {\n margin-left: -2px;\n font-size: 22px; }\n\n.notebook-item__info {\n font-style: italic;\n opacity: .8;\n color: #8a6942; }\n .notebook-item__info a, .notebook-item__info md-icon {\n color: #8a6942; }\n .notebook-item__info md-icon {\n font-size: 1.5em;\n min-width: 0;\n width: auto; }\n\n.notebook-item__upload {\n text-align: center;\n padding: 24px;\n background-color: #eeeeee;\n margin-bottom: 16px;\n color: rgba(0, 0, 0, 0.54);\n border-radius: 4px;\n cursor: pointer;\n border: 1px dashed transparent;\n transition: all 250ms; }\n .notebook-item__upload md-icon, .notebook-item__upload span {\n transition: color 250ms; }\n .notebook-item__upload:hover, .notebook-item__upload:focus, .notebook-item__upload.dragover {\n border-color: #F05843;\n background-color: #fdebe8;\n color: #F05843; }\n .notebook-item__upload:hover md-icon, .notebook-item__upload:hover span, .notebook-item__upload:focus md-icon, .notebook-item__upload:focus span, .notebook-item__upload.dragover md-icon, .notebook-item__upload.dragover span {\n color: #F05843; }\n\n.view-notebook-item {\n width: 600px; }\n\n.notebook-item--report {\n background-color: #ffffff; }\n .notebook-item--report .note-editor {\n margin-bottom: 16px;\n border-color: #cccccc; }\n\n.notebook-item--report__container.ui-scrollpoint .notebook-item--report__toolbar {\n position: fixed;\n top: 94px;\n left: 0;\n right: 0;\n z-index: 1; }\n @media only screen and (min-width: 600px) {\n .notebook-item--report__container.ui-scrollpoint .notebook-item--report__toolbar {\n left: 54px; } }\n @media only screen and (min-width: 600px) {\n .notebook-item--report__container.ui-scrollpoint .notebook-item--report__toolbar {\n padding: 0 24px; } }\n @media only screen and (min-width: 960px) {\n .notebook-item--report__container.ui-scrollpoint .notebook-item--report__toolbar {\n padding: 0 32px; } }\n .notebook-item--report__container.ui-scrollpoint .notebook-item--report__toolbar .note-toolbar {\n margin: 0 16px; }\n @media only screen and (min-width: 600px) {\n .notebook-item--report__container.ui-scrollpoint .notebook-item--report__toolbar .note-toolbar {\n margin: 0 8px; } }\n @media only screen and (min-width: 960px) {\n .notebook-item--report__container.ui-scrollpoint .notebook-item--report__toolbar .note-toolbar {\n margin: 0 16px; } }\n\n.notebook-item--report__container.ui-scrollpoint .note-editor {\n padding-top: 40px; }\n\n.notebook-item--report__toolbar .note-toolbar {\n background-color: #dddddd;\n border: 1px solid #cccccc;\n margin-bottom: -2px; }\n\n.notebook-item--report__heading {\n text-align: center;\n margin-bottom: 32px; }\n\n.notebook-item--report__add-note {\n font-weight: 700; }\n\n.notebook-item--report__note-img {\n max-width: 100%;\n height: auto !important; }\n\n@media only screen and (min-width: 600px) {\n .notebook-sidebar {\n width: 400px;\n max-width: none; } }\n\n@media only screen and (min-width: 960px) {\n .notebook-sidebar {\n width: 500px;\n max-width: none; } }\n\n.notebook-items {\n width: 100%;\n overflow: auto;\n margin-top: 16px;\n margin-bottom: 76px; }\n .notebook-items .notebook-item {\n width: 100%; }\n .notebook-items .notebook-item__content {\n height: 200px;\n min-width: 0; }\n\n.notebook-items--grading {\n margin-bottom: 0; }\n\n@media only screen and (max-width: 599px) {\n .notebook-enabled .md-fab-bottom-right, .notebook-enabled .md-fab-bottom-left {\n bottom: 50px !important; } }\n\n.notebook-grading {\n background-color: #ffffff;\n display: block; }\n\n.notification-btn {\n width: 60px !important; }\n .notification-btn md-icon {\n margin-left: 20px; }\n\n.notification-count {\n border-radius: 50%;\n position: absolute;\n background-color: #F05843;\n width: 22px;\n left: 4px;\n height: 22px;\n line-height: 18px;\n font-size: 12px;\n font-weight: 700;\n border: 2px solid; }\n .notification-count:before {\n content: \"\";\n position: absolute;\n right: -7px;\n top: 5px;\n border-left: 6px solid rgba(255, 255, 255, 0.87);\n border-top: 4px solid transparent;\n border-bottom: 4px solid transparent; }\n\n.notification-list {\n padding: 8px 0; }\n\n.notification-dismiss {\n width: 500px; }\n\n.notification-dismiss__input {\n margin-bottom: 0; }\n\nmd-list md-list-item .md-list-item-text h4.notification-list-item__source,\nmd-list md-list-item.md-2-line .md-list-item-text h4.notification-list-item__source,\nmd-list md-list-item.md-3-line .md-list-item-text h4.notification-list-item__source {\n color: rgba(0, 0, 0, 0.54);\n font-size: 12px; }\n md-list md-list-item .md-list-item-text h4.notification-list-item__source md-icon,\n md-list md-list-item.md-2-line .md-list-item-text h4.notification-list-item__source md-icon,\n md-list md-list-item.md-3-line .md-list-item-text h4.notification-list-item__source md-icon {\n font-size: 18px;\n min-width: 0;\n width: auto;\n margin-left: -4px;\n line-height: 20px; }\n\n.account-menu {\n border-radius: 4px;\n padding: 0;\n font-size: 15px;\n max-width: 380px; }\n @media (min-width: 1280px) {\n .account-menu {\n min-width: 380px !important; } }\n .account-menu h3 {\n margin: 0;\n font-weight: 300; }\n\n.account-menu--fixed-height {\n height: 304px; }\n\n.account-menu--fixed-width {\n width: 320px; }\n @media (min-width: 960px) {\n .account-menu--fixed-width {\n width: 380px; } }\n\n.account-menu__icon {\n background-color: white;\n border-radius: 50%; }\n\n.account-menu__caret {\n position: absolute;\n right: 28px;\n top: -8px;\n outline: none; }\n .account-menu__caret:before {\n content: '';\n position: absolute;\n border-bottom: 8px solid #fff;\n border-left: 8px solid transparent;\n border-right: 8px solid transparent; }\n\n.account-menu__caret--pause, .account-menu__caret--notification {\n right: 80px; }\n\n.account-menu__caret--notification--with-pause {\n right: 132px; }\n\n[dir=rtl] .account-menu__caret {\n right: auto;\n left: 28px; }\n\n[dir=rtl] .account-menu__caret--pause, [dir=rtl] .account-menu__caret--notification {\n left: 80px;\n right: auto; }\n\n[dir=rtl] .account-menu__caret--notification--with-pause {\n left: 132px;\n right: auto; }\n\n.account-menu__info {\n padding: 8px 12px; }\n\n.account-menu__info__title {\n font-weight: 500; }\n\n.account-menu__info__team {\n font-weight: 400;\n color: rgba(0, 0, 0, 0.54); }\n\n.account-menu__users {\n padding: 0; }\n .account-menu__users md-list-item {\n padding: 0; }\n .account-menu__users md-list-item .md-avatar {\n margin: 0 8px 0 0;\n height: 48px;\n width: 48px; }\n\n.account-menu__progress md-progress-linear {\n transform: rotate(270deg);\n width: 26px; }\n\n.account-menu__grade {\n position: relative;\n margin-right: 4px; }\n .account-menu__grade md-icon {\n color: #FFC107; }\n\n.account-menu__grade__overlay {\n position: absolute;\n top: 0;\n width: 100%;\n background-color: rgba(255, 255, 255, 0.6); }\n\n.account-menu__actions {\n background-color: #f7f7f7; }\n\n.account-menu__control {\n padding: 16px; }\n\n.annotations {\n margin: 16px 4px 16px 62px;\n position: relative;\n font-size: 15px; }\n .annotations hr {\n margin: 10px 0 8px;\n border-color: rgba(0, 0, 0, 0.12); }\n .annotations:after {\n content: \"\";\n position: absolute;\n width: 0;\n height: 0;\n left: -16px;\n right: auto;\n top: 0px;\n bottom: auto;\n border-top: 20px solid transparent;\n border-bottom: 20px solid transparent;\n border-right: 16px solid #757575; }\n\n.annotations-container--student--report {\n border-top: 1px solid #dddddd; }\n\n.annotations--report {\n margin-top: 0;\n margin-bottom: 0; }\n\n.annotations__header {\n position: relative;\n border-top-right-radius: 4px;\n padding: 10px 12px;\n font-weight: 700;\n transition: all 1s;\n color: #ffffff;\n background-color: #757575; }\n\n.annotations__avatar {\n background-color: #F05843;\n padding: 2px;\n position: absolute;\n top: 0;\n left: -62px; }\n\n.annotations__icon {\n transition: all 1s;\n color: #ffffff; }\n\n.annotations__body {\n padding: 12px;\n background-color: #ffffff;\n border-bottom-left-radius: 4px;\n border-bottom-right-radius: 4px;\n overflow: auto; }\n\n.annotations__status {\n background-color: #ffffff;\n color: #ef6c00;\n display: inline-block;\n margin-left: 8px;\n font-size: 12px; }\n .annotations__status.ng-enter, .annotations__status.ng-leave {\n transition: all 1s; }\n .annotations__status.ng-enter, .annotations__status.ng-leave.ng-leave-active {\n opacity: 0; }\n .annotations__status.ng-leave, .annotations__status.ng-enter.ng-enter-active {\n opacity: 1; }\n\n.annotations__score {\n font-weight: 700; }\n\n.annotations__info {\n font-style: italic;\n opacity: 0.8;\n border-bottom: 1px dotted;\n font-size: 13px; }\n\n.annotations--inside .annotations {\n margin-left: 72px; }\n\n.annotations--info {\n margin-bottom: 32px;\n margin-right: 8px;\n margin-left: 72px; }\n @media only screen and (min-width: 600px) {\n .annotations--info {\n margin: 16px 16px 32px 76px; } }\n .annotations--info:after {\n border-right: 16px solid #ef6c00; }\n .annotations--info .annotations__avatar {\n background-color: #ffffff; }\n .annotations--info .annotations__header {\n background-color: #ef6c00; }\n\n.annotations--grading md-input-container {\n margin-bottom: 0; }\n\n.annotations--grading .md-errors-spacer {\n display: none; }\n\n.annotations--grading input:focus, .annotations--grading textarea:focus {\n background-color: #ffffff; }\n\n.annotations--grading input:disabled, .annotations--grading textarea:disabled {\n color: rgba(0, 0, 0, 0.87); }\n\n.annotations--grading--revision {\n margin: 8px 0 0;\n padding: 8px; }\n\n.annotations--notebook {\n margin-top: 16px; }\n\n.annotations--grading__info {\n font-style: italic;\n margin: 8px 8px 4px; }\n\n.annotations--grading__item {\n padding: 8px; }\n\n.annotations--grading__score input {\n margin-top: 0 !important;\n font-size: 18px;\n width: 52px;\n text-align: center; }\n\n.annotations--grading__score__label {\n transform: none !important;\n width: auto;\n display: block;\n padding: 0;\n margin: 0 8px 0 0; }\n\n.annotations--grading__score__max label {\n display: none; }\n\n.annotations--grading__score__divider {\n position: relative;\n top: 12px;\n margin-left: 4px; }\n\n.annotations--grading__auto-comment {\n margin: 0 2px; }\n\n.annotations--grading__auto-comment__content {\n margin-top: 8px; }\n\n.component {\n position: relative; }\n\n.component__wrapper {\n padding: 0 16px;\n margin: 24px 0; }\n\n.component__content {\n overflow-x: auto;\n font-size: 15px;\n overflow-y: hidden; }\n @media only screen and (min-width: 600px) {\n .component__content {\n padding: 0 8px; } }\n\nh3.component__header, .component-header {\n padding: 8px 12px;\n margin: 0;\n font-size: 14px; }\n\n.component__rubric {\n position: absolute;\n left: -20px;\n top: 12px; }\n\n.notebook-enabled .component_content img {\n transition: all 250ms;\n cursor: pointer;\n cursor: copy; }\n .notebook-enabled .component_content img:hover, .notebook-enabled .component_content img:focus {\n box-shadow: 0 0 5px 1px #F05843; }\n\n.component__actions .md-button:first-child {\n margin-left: 0; }\n\n.component__actions .md-button:last-child {\n margin-right: 0; }\n\n.component__actions__info {\n font-style: italic;\n margin-left: 8px;\n color: rgba(0, 0, 0, 0.54); }\n\n.component__actions__more {\n border-bottom: 1px dotted; }\n\n.component__prompt {\n margin-bottom: 8px;\n font-weight: 500; }\n\n.component__prompt__content {\n display: inline; }\n\n.component__attachment {\n position: relative;\n margin: 0 8px;\n padding-bottom: 8px; }\n @media only screen and (min-width: 600px) {\n .component__attachment {\n padding-top: 8px; } }\n\n@media only screen and (max-width: 599px) {\n .component__add-attachment {\n width: 100%; } }\n\n.component__attachment__content {\n max-height: 100px;\n width: auto; }\n\n.component__attachment__delete {\n position: absolute;\n top: 0;\n right: 0;\n min-width: 0;\n background-color: rgba(255, 255, 255, 0.75) !important;\n border-radius: 0;\n padding: 4px;\n margin: 0; }\n .component__attachment__delete > md-icon {\n margin-top: 0; }\n\n.component__revision {\n margin: 8px 0;\n padding: 8px; }\n .component__revision:nth-child(odd) {\n background-color: #f7f7f7; }\n\n.component__revision__content {\n padding: 4px 0 8px 0;\n border-bottom: 1px solid #dddddd; }\n\n.component__revision__actions {\n color: #757575;\n padding-top: 4px; }\n\n.component__content--Discussion {\n overflow: hidden; }\n\n.discussion-content {\n background-color: #eeeeee;\n box-shadow: inset 0 0 3px #aaaaaa; }\n\n.discussion-posts {\n padding: 12px 12px 8px; }\n @media only screen and (min-width: 1280px) {\n .discussion-posts {\n padding: 16px 16px 0; } }\n\n.discussion-post {\n margin: 0 auto 16px;\n max-width: 600px; }\n @media only screen and (min-width: 600px) {\n .discussion-post {\n margin-bottom: 24px; } }\n @media only screen and (min-width: 1280px) {\n .discussion-post {\n margin-bottom: 32px; } }\n .discussion-post md-divider {\n position: relative;\n width: auto; }\n\n.discussion-post__contents {\n padding: 16px; }\n\n.discussion-post__avatar, md-list-item > .md-avatar.discussion-post__avatar {\n margin-right: 8px; }\n\n.discussion-post__avatar--reply, md-list-item > .md-avatar.discussion-post__avatar--reply {\n margin-top: 8px; }\n\n.discussion-post__user, md-list-item .md-list-item-text h3.discussion-post__user {\n padding-bottom: 4px;\n font-weight: 700;\n line-height: 1.3;\n overflow: visible;\n white-space: normal; }\n\n.discussion-post__date {\n color: #aaaaaa; }\n\n.discussion-post__date--reply {\n margin-left: 8px;\n font-weight: 400; }\n\n.discussion-post__content {\n margin-top: 16px;\n white-space: pre-wrap; }\n\n.discussion-post__attachment {\n max-width: 100%;\n height: auto !important;\n margin-top: 16px; }\n\n.discussion-new {\n background-color: #ffffff;\n max-width: 570px;\n margin-left: auto;\n margin-right: auto;\n padding: 8px;\n transition: all 250ms;\n transform: scale(0.95); }\n\n.discussion-new--focused {\n transform: scale(1); }\n\nmd-input-container.discussion-new__input-container {\n margin: 0;\n padding: 0; }\n md-input-container.discussion-new__input-container > textarea.md-input {\n min-height: 68px; }\n\n.discussion-new__input--textarea, .input-container textarea.discussion-new__input--textarea {\n padding: 8px;\n border: 0 none; }\n\n.discussion-new__actions {\n padding: 0 8px; }\n .discussion-new__actions .md-button:first-of-type {\n margin-left: 0; }\n .discussion-new__actions .md-button:last-of-type {\n margin-right: 0; }\n\n.discussion-new__attachment {\n padding: 0;\n margin: 0 0 8px; }\n\n.discussion-new__attachment__content {\n margin-top: 0;\n margin-bottom: 16px; }\n\n.discussion-comments {\n padding: 0; }\n\n.discussion-comments__contents {\n background-color: #f7f7f7; }\n\n.discussion-comments__header {\n background-color: transparent;\n padding: 0; }\n .discussion-comments__header .md-subheader-inner {\n padding-bottom: 8px; }\n\n.discussion-comments__list {\n padding: 0;\n max-height: 9999px;\n overflow-y: auto; }\n @media only screen and (min-width: 600px) {\n .discussion-comments__list {\n max-height: 400px; } }\n\n.input--textarea.discussion-reply__input, .input-container textarea.input--textarea.discussion-reply__input {\n background-color: #ffffff;\n padding: 4px;\n font-size: 14px;\n border: 0 none;\n margin-left: -1px;\n margin-bottom: 0;\n resize: none; }\n\n.discussion-reply, md-list-item.discussion-reply {\n margin: 0 12px 8px;\n padding: 0;\n min-height: 56px; }\n\n.discusstion-reply__details, md-list-item .md-list-item-text.discusstion-reply__details {\n margin: 8px 0; }\n\n.discussion-post__user--reply, md-list-item .md-list-item-text h3.discussion-post__user--reply {\n font-size: 14px;\n padding: 0;\n margin: 0; }\n\n.discusstion-reply__content {\n margin-top: 2px; }\n .discusstion-reply__content p {\n font-weight: 400 !important;\n color: rgba(0, 0, 0, 0.87) !important; }\n\n.discussion-new-reply {\n padding: 8px; }\n\n.discussion-new-reply__input-container {\n padding-top: 0;\n margin: 0; }\n .discussion-new-reply__input-container .md-errors-spacer {\n display: none; }\n\n.discussion-new-reply__actions {\n margin-left: 8px; }\n .discussion-new-reply__actions .md-button {\n margin-top: 0;\n margin-bottom: 0; }\n\n.embedded-content__iframe {\n border: 0 none; }\n\n.component--grading {\n padding: 0;\n margin: 0; }\n .component--grading:not(:last-child) > div {\n border-bottom: 1px solid #dddddd; }\n .component--grading .component__wrapper {\n padding: 0;\n margin: 0; }\n .component--grading .component__content {\n padding: 16px;\n margin: 0; }\n\n.component--grading__response {\n padding-bottom: 16px; }\n @media only screen and (min-width: 960px) {\n .component--grading__response {\n padding-right: 16px;\n padding-bottom: 0; } }\n\n.component--grading__response__content {\n overflow: auto; }\n\n.component--grading__annotations {\n background-color: #ecf4fd; }\n\n.component--grading__annotations__divider {\n padding: 4px;\n background-color: #ffffff; }\n\n.component--grading__actions__info {\n margin: 16px 0 0;\n padding-top: 8px;\n border-top: 1px solid #eeeeee; }\n\n.graph-select {\n min-width: 150px;\n max-width: 200px; }\n\n.graph-controls {\n margin: 8px 0;\n padding: 8px 0;\n border: 1px solid #eeeeee;\n border-left-width: 0;\n border-right-width: 0; }\n\n.match-content {\n background-color: #eeeeee;\n margin-bottom: 16px;\n padding: 8px;\n box-shadow: inset 0 0 3px #aaaaaa; }\n\n.match-divider {\n margin: 16px 8px 8px; }\n\n@media only screen and (min-width: 960px) {\n .match-divider--horizontal {\n display: none; } }\n\n.match-bucket__header {\n padding: 12px;\n font-weight: 500;\n color: #1565c0; }\n\n.match-bucket__content {\n padding: 0; }\n\n.match-bucket--choices .match-bucket__header {\n color: #795C3A; }\n\n.match-bucket__contents {\n min-height: 120px;\n padding: 0 8px 8px;\n background-color: #dddddd;\n transition: background-color 250ms;\n border-bottom-left-radius: 4px;\n border-bottom-right-radius: 4px;\n column-gap: 8px; }\n @media only screen and (max-width: 599px) {\n .match-bucket__contents {\n column-count: 1 !important; } }\n .match-bucket__contents img {\n max-width: 100%;\n height: auto; }\n\n.match-bucket__contents--over {\n background-color: #1565c0; }\n\n.match-bucket__item {\n list-style-type: none;\n cursor: move;\n padding-top: 8px;\n break-inside: avoid; }\n\n.match-bucket__item__contents {\n background-color: #ffffff;\n padding: 8px !important;\n border: 1px solid #cccccc; }\n .match-bucket__item__contents .md-list-item-text {\n width: 100%; }\n\n.match-bucket__item__contents__text {\n margin-right: 4px; }\n\n.match-feedback {\n transition: opacity 250ms;\n /*padding-left: 8px;\n border-left: 4px solid;*/\n margin: 8px -8px -8px;\n color: #ffffff;\n padding: 4px 8px; }\n .match-feedback.ng-hide {\n opacity: 0;\n transition: opacity 1ms; }\n .match-feedback md-icon {\n color: #ffffff; }\n\n.outside-content iframe {\n border: 1px solid #eeeeee; }\n\n.outside-content__source {\n margin-top: 4px;\n text-align: end; }\n .outside-content__source a {\n max-width: 240px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n display: inline-block; }\n\n.component-revisions .component {\n padding: 0;\n margin: 0; }\n\n.component-revisions .component__content {\n padding: 0; }\n\n.component-revisions .component__wrapper {\n margin: 16px 0; }\n\n.component-revisions .md-resize-handle {\n display: none; }\n\n.component-revisions__item, md-list-item.component-revisions__item {\n padding: 0; }\n\n.component-revisions__item--latest {\n margin-bottom: 24px; }\n\n.component-revisions__annotation-label {\n margin-right: 8px; }\n\n.component-revisions__has-auto-and-teacher {\n padding-top: 8px;\n margin-top: 8px;\n border-top: 1px solid #dddddd; }\n\n.notebook-toolbar md-divider {\n margin: 8px 0; }\n\n@media only screen and (max-width: 959px) {\n .notebook-toolbar {\n border-top: 1px solid #dddddd; } }\n\n.notebook-toolbar__add-menu {\n position: absolute;\n bottom: 40px; }\n .notebook-toolbar__add-menu .md-fab-action-item {\n background-color: #ffffff; }\n\n.notebook-toolbar__add-icon {\n border-radius: 50%; }\n\n#closeNotebookSettingsButton {\n float: right; }\n\n[dir=rtl] #closeNotebookSettingsButton {\n float: left; }\n\nhighchart {\n display: block; }\n","// 1. Config\n\n// 2. Base\n.l-notebook {\n margin-top: $wise-toolbar-height;\n background-color: color('body-bg') !important;\n}\n","// 1. Config\n\n// 2. Base\n.l-nav {\n background-color: color('body-bg') !important;\n}\n","// 1. Config\n\n// 2. Base\n.l-sidebar {\n\n}\n\n.l-sidebar__header {\n background-color: #ffffff !important;\n color: color('accent-1') !important;\n\n md-select {\n color: color('body');\n }\n}",".status-icon {\n margin: 0 4px;\n z-index: 1;\n vertical-align: bottom;\n}\n\n.md-button.status-icon {\n height: auto;\n width: auto;\n min-height: 0;\n line-height: inherit;\n margin: 0 4px;\n padding: 0;\n}\n\n.avatar--icon--alert {\n background-color: #ffffff;\n}\n\n.avatar--icon--alert__icon {\n font-size: 48px;\n margin: -4px 0 0 -4px;\n}\n",".gu-mirror {\n position: fixed !important;\n margin: 0 !important;\n z-index: 9999 !important;\n opacity: 0.8;\n transition: opacity 250ms ease-in-out;\n}\n.gu-hide {\n display: none !important;\n}\n.gu-unselectable {\n -webkit-user-select: none !important;\n -moz-user-select: none !important;\n -ms-user-select: none !important;\n user-select: none !important;\n}\n.gu-transit {\n opacity: 0.2;\n}\n","md-dialog {\n width: $layout-breakpoint-xs;\n}\n\n.dialog--wide {\n width: $layout-breakpoint-sm;\n}\n\n.dialog--wider {\n width: $layout-breakpoint-md;\n}\n",".help-bubble {\n border-radius: $card-border-radius;\n max-width: 320px;\n\n @media (min-width: $layout-breakpoint-xs) {\n max-width: ($layout-breakpoint-xs - 48);\n }\n\n @media (min-width: $layout-breakpoint-sm) {\n max-width: ($layout-breakpoint-sm - 48);\n }\n\n @media (min-width: $layout-breakpoint-md) {\n max-width: ($layout-breakpoint-md - 48);\n }\n}\n\n.help-bubble__title {\n border-top-left-radius: $card-border-radius;\n border-top-right-radius: $card-border-radius;\n}\n\n.help-bubble___title__content {\n border-top-left-radius: $card-border-radius;\n border-top-right-radius: $card-border-radius;\n padding: 0px 0 0 12px;\n background-color: color('info');\n\n .md-icon-button {\n margin-right: 0;\n padding-top: 0;\n padding-bottom: 0;\n }\n}\n\n.help-bubble__content {\n overflow: auto;\n padding: 8px 12px;\n max-height: 480px;\n}\n\n.help-bubble__actions {\n border-bottom-left-radius: $card-border-radius;\n border-bottom-right-radius: $card-border-radius;\n}\n\ndiv.hopscotch-bubble {\n border-radius: $card-border-radius;\n //border: 2px solid color('gray-darker');\n border: 0 none;\n font-family: Roboto, \"Helvetica Neue\", sans-serif;\n font-size: rem(1.4);\n z-index: 6;\n\n .hopscotch-bubble-arrow-container {\n position: absolute;\n width: 20px;\n height: 20px;\n }\n\n .hopscotch-bubble-arrow-container {\n &.up {\n top: 0;\n left: 12px;\n\n .hopscotch-bubble-arrow {\n border-bottom: 10px solid color('info');\n border-left: 10px solid transparent;\n border-right: 10px solid transparent;\n position: relative;\n top: -10px;\n }\n\n .hopscotch-bubble-arrow-border {\n border: 0 none;\n /*border-bottom: 12px solid color('gray-darker');\n border-left: 12px solid transparent;\n border-right: 12px solid transparent;*/\n }\n }\n\n &.down {\n bottom: -34px;\n left: 12px;\n\n .hopscotch-bubble-arrow {\n border-top: 10px solid color('info');\n border-left: 10px solid transparent;\n border-right: 10px solid transparent;\n position: relative;\n top: -10px;\n }\n\n .hopscotch-bubble-arrow-border {\n border: 0 none;\n /*border-top: 12px solid color('gray-darker');\n border-left: 12px solid transparent;\n border-right: 12px solid transparent;*/\n }\n }\n\n &.left {\n top: 12px;\n left: -10px;\n\n .hopscotch-bubble-arrow {\n border-right: 10px solid color('info');\n border-bottom: 10px solid transparent;\n border-top: 10px solid transparent;\n position: relative;\n left: 0;\n top: -10px;\n }\n\n .hopscotch-bubble-arrow-border {\n border: 0 none;\n /*border-right: 12px solid color('gray-darker');\n border-bottom: 12px solid transparent;\n border-top: 12px solid transparent;*/\n }\n }\n\n &.right {\n top: 12px;\n right: -30px;\n\n .hopscotch-bubble-arrow {\n border-left: 10px solid color('info');\n border-bottom: 10px solid transparent;\n border-top: 10px solid transparent;\n position: relative;\n right: 0;\n top: -10px;\n }\n\n .hopscotch-bubble-arrow-border {\n border: 0 none;\n /*border-right: 12px solid color('gray-darker');\n border-bottom: 12px solid transparent;\n border-top: 12px solid transparent;*/\n }\n }\n }\n}\n","// Variables\n$input-action-width: 44px;\n$input-action-vertical-offset: 6px;\n$input-action-vertical-offset-richtext: -7px;\n\n// Base\n.input-container {\n padding-top: 12px;\n}\n\n.input-container--component {\n margin-bottom: 0;\n}\n\n.input-container--open-response {\n &.md-has-icon {\n padding-left: 0;\n }\n\n .md-errors-spacer {\n display: none;\n }\n}\n\n.input-wrapper {\n position: relative;\n}\n\n.input-wrapper--focused {\n .input--textarea__action md-icon {\n color: color('primary');\n }\n}\n\n.input--textarea, .input-container textarea.input--textarea {\n padding: 8px;\n background-color: color('gray-lightest');\n border: 1px solid color('gray');\n margin-bottom: 8px;\n\n &:focus {\n background-color: #ffffff;\n }\n\n &[disabled] {\n color: color('text-secondary');\n }\n}\n\n.input-container textarea.input--textarea {\n width: 100%;\n}\n\n.input--textarea--disabled {\n color: color('text-secondary');\n}\n\n.input--textarea__action {\n position: absolute;\n right: -4px;\n\n &[disabled] md-icon {\n color: color('text-disabled') !important;\n }\n}\n\n.input--textarea__action--notebook {\n top: $input-action-vertical-offset;\n\n .input-wrapper--richtext & {\n top: $input-action-vertical-offset-richtext\n }\n}\n\n.input--textarea__action--revision {\n bottom: $input-action-vertical-offset;\n\n .input-wrapper--richtext & {\n bottom: ($input-action-vertical-offset-richtext + 2px);\n }\n\n}\n\n.input-label, md-input-container:not(.md-input-invalid):not(.md-input-focused).md-input-has-value label.input-label {\n line-height: 1.2;\n color: color('text');\n\n &.input-label--focused {\n color: color('primary');\n }\n}\n\n.autocomplete {\n input {\n text-overflow: ellipsis;\n overflow: hidden;\n word-wrap: none;\n font-weight: 500;\n color: color('text-secondary');\n }\n}\n\n.autocomplete--minwidth {\n @media only screen and (min-width: $layout-breakpoint-xs) {\n min-width: 300px;\n }\n\n @media only screen and (min-width: $layout-breakpoint-sm) {\n min-width: 300px;\n }\n}\n\n.autocomplete--flat {\n md-autocomplete-wrap {\n background-color: #ffffff;\n\n &:not(.md-menu-showing) {\n box-shadow: none;\n background-color: color('gray-lighter');\n }\n }\n}\n\n.select__header {\n height: $menu-item-height;\n\n input {\n height: 100%;\n width: 100%;\n padding: 0 8px;\n outline: none;\n border: 0 none;\n font-size: rem(1.4);\n font-weight: 500;\n }\n}\n",".table {\n max-width: 100%;\n width: auto;\n min-width: 100px;\n margin: 8px 0;\n\n thead,\n tbody,\n tfoot {\n // TODO: remove chaining when bootstrap dependency is removed\n > tr > th,\n > tr > td {\n border: 1px solid color('gray');\n padding: 6px;\n font-size: rem(1.5);\n min-height: 32px;\n height: 32px;\n min-width: 32px;\n vertical-align: top;\n }\n }\n\n td.inactive,\n th {\n background-color: color('gray-lightest');\n opacity: 1;\n visibility: visible;\n }\n\n md-input-container {\n margin: 0;\n }\n\n .md-errors-spacer {\n display: none;\n }\n}\n\n.table--student {\n td {\n &.inactive {\n padding: 8px 10px;\n }\n }\n}\n\n.table--full-width {\n width: 100%;\n}\n\n.table--list {\n border: 0 none;\n border-collapse: collapse;\n background-color: #ffffff;\n max-width: 100%;\n overflow: auto;\n\n th,\n td {\n padding: 0 4px;\n border: 0 none;\n }\n\n td {\n min-height: 56px;\n height: 56px;\n }\n\n tr {\n &.md-button {\n display: table-row;\n text-align: left;\n width: auto;\n text-transform: none;\n font-size: inherit;\n font-weight: normal;\n }\n }\n}\n\n.table--list__wrap {\n min-width: $layout-breakpoint-xs;\n}\n\n.table-wrap-sticky {\n @media only screen and (max-width: $layout-breakpoint-sm - 1) {\n overflow-x: auto;\n }\n}\n\n.table--list__thead {\n font-size: rem(1.4);\n font-weight: 700;\n}\n\n.table--list__thead__tr {\n height: 100%;\n margin: 0;\n}\n\n.table--list__thead__th {\n background-color: color('gray-darker');\n color: color('text-light');\n min-height: $wise-toolbar-height;\n height: $wise-toolbar-height;\n}\n\n.table--list__thead__link {\n color: #ffffff;\n text-transform: none;\n margin: 0;\n min-width: 0;\n white-space: normal;\n line-height: 1.4;\n width: 100%;\n}\n\n.table--list__thead__sort {\n margin: 0;\n}\n\n.table--list__thead__sort--reverse {\n transform: rotate(180deg);\n}\n\n.td--wrap {\n min-width: 180px;\n white-space: normal;\n line-height: 1.2;\n}\n\n.td--max-width {\n @media only screen and (max-width: $layout-breakpoint-sm - 1) {\n max-width: 180px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n}\n",".md-toolbar-tools {\n font-size: 18px;\n\n .autocomplete {\n height: 36px;\n\n md-autocomplete-wrap {\n height: 36px;\n }\n\n input {\n height: 36px;\n }\n }\n}\n\n.md-toolbar--wise {\n min-height: $wise-toolbar-height;\n\n .md-toolbar-tools {\n height: $wise-toolbar-height;\n max-height: $wise-toolbar-height;\n }\n\n .md-button.md-icon-button {\n height: $wise-toolbar-height;\n line-height: $wise-toolbar-height;\n width: $wise-toolbar-height;\n }\n}\n\n.md-toolbar--wise--sm {\n .md-toolbar-tools {\n padding: 0 8px;\n font-size: $body-font-size-base;\n }\n}\n\n.md-toolbar--sidenav {\n background-color: color('gray-darkest') !important;\n\n .md-toolbar-tools {\n font-size: rem(1.6);\n font-weight: 500;\n }\n}\n\n.toolbar {\n position: fixed;\n left: 0;\n right: 0;\n top: $md-toolbar-height;\n z-index: 3;\n}\n\n.toolbar__title {\n margin-left: 8px;\n font-size: rem(1.6);\n font-weight: 500;\n}\n\n.toolbar__tools {\n padding-right: 8px;\n}\n[dir=rtl] .toolbar__tools {\n padding-right: 16px;\n padding-left: 8px;\n}\n\n.toolbar__nav, .md-button.toolbar__nav {\n margin: 0;\n}\n\n.toolbar__select, .md-button.toolbar__select {\n margin: 0 4px;\n min-height: 32px;\n background-color: color('gray-lightest');\n\n .md-select-value {\n height: 32px;\n text-align: left;\n }\n}\n[dir=rtl] {\n .toolbar__select, .md-button.toolbar__select {\n .md-select-value {\n text-align: right;\n }\n }\n}\n\n.toolbar__select--fixedwidth {\n width: 168px;\n\n @media only screen and (min-width: $layout-breakpoint-xs) {\n width: 264px;\n }\n\n @media only screen and (min-width: $layout-breakpoint-sm) {\n width: 432px;\n }\n}\n",".list-item {\n background-color: #ffffff;\n border-bottom: 1px solid color('gray-lighter');\n\n .md-subheader, &.md-subheader {\n color: color('text');\n background-color: #ffffff;\n\n md-icon {\n vertical-align: middle;\n }\n\n .md-subheader-inner {\n padding: 0;\n }\n\n .md-avatar {\n margin-right: 8px;\n }\n }\n\n .autocomplete {\n margin: 8px 0;\n }\n}\n\n.list-item--info {\n &._md-button-wrap>div.md-button:first-child, .md-subheader-content {\n border-left: 4px solid color('info') !important;\n margin-left: -4px;\n }\n}\n\n.list-item--warn {\n //background-color: lighten(color('warn'), 56%);\n\n &._md-button-wrap>div.md-button:first-child, .md-subheader-content {\n border-left: 4px solid color('warn') !important;\n margin-left: -4px;\n }\n}\n\n.list-item--expanded {\n border-bottom-width: 0;\n}\n\n.list-item--noclick, .list-item--noclick.md-button {\n cursor: default;\n background-color: color('gray-lightest');\n}\n\n.list-item--actions {\n padding: 0 8px !important;\n}\n\n.list-item__subheader-button {\n text-transform: none;\n width: 100%;\n padding: 8px 16px;\n margin: 0;\n white-space: normal;\n text-align: left;\n line-height: 1.4;\n}\n\n.user-list {\n font-size: rem(1.5);\n}\n",".notice {\n text-align: center;\n padding: 8px;\n background-color: rgba(0,0,0,0.04);\n width: 100%;\n\n @media (min-width: $layout-breakpoint-xs) {\n max-width: 80%;\n border-radius: $button-border-radius;\n margin: 24px auto;\n }\n}",".milestone {\n min-width: 196px;\n width: 196px;\n height: 242px;\n background-color: #ffffff;\n padding: 0;\n\n &.md-button {\n text-transform: none;\n }\n}\n\n.milestone__progress {\n background-color: color('gray-lighter');\n border-radius: 50%;\n position: relative;\n margin-bottom: 12px;\n}\n\n.milestone__progress__percent {\n position: absolute;\n top: 8px;\n bottom: 8px;\n left: 8px;\n right: 8px;\n border-radius: 50%;\n background-color: #ffffff;\n color: color('primary');\n font-size: rem(2.8);\n font-weight: 500;\n}\n\n.milestone__title {\n font-weight: 700;\n font-size: $body-font-size-base;\n margin-bottom: 12px;\n}\n\n.milestone-details {\n section {\n &:not(:first-child) {\n margin-top: 8px;\n }\n }\n}\n\n.milestone-details__section {\n background-color: #ffffff;\n padding: 16px;\n\n > p {\n margin-top: 16px;\n margin-bottom: 0;\n }\n\n md-list {\n padding: 0;\n }\n\n .grading {\n padding: 0;\n }\n}\n\n.milestone-details__header {\n padding: 12px 16px;\n margin: -16px -16px 16px;\n text-transform: uppercase;\n font-weight: 500;\n}\n\n.milestone-details__progress {\n width: 48px;\n margin-right: 8px;\n}\n\n.milestone--add {\n &.md-button {\n text-transform: uppercase;\n }\n}\n\n.milestone--add__icon {\n height: 96px;\n width: 96px;\n background-color: color('gray-lighter');\n border-radius: 50%;\n}\n","#nav {\n position: relative;\n}\n\n.nav {\n margin-bottom: 16px;\n}\n\n.nav-mask {\n position: absolute;\n top: 0;\n bottom: 0;\n left: 0;\n right: 0;\n background-color: rgba(0,0,0,0.25);\n z-index: 1;\n\n &.ng-hide {\n opacity: 0;\n }\n}\n\n.nav-head {\n color: color('text-secondary');\n font-weight: 500;\n\n md-icon {\n line-height: 20px;\n }\n}\n\n.nav-contents--root {\n padding: 6px 6px 12px;\n}\n\n.nav-contents--group {\n background-color: color('gray-light');\n padding: 8px;\n}\n\n.nav-contents--root {\n padding: 0;\n}\n\n.nav-contents__list {\n padding: 0;\n\n @media (min-width: $layout-breakpoint-xs) {\n padding: 8px;\n }\n}\n\n.nav-item {\n transition: opacity 250ms ease-in-out;\n\n &.prev {\n md-list-item {\n background-color: color('selected-bg');\n\n /*&._md-button-wrap>div.md-button:first-child {\n border-left: 4px solid color('primary');\n margin-left: -4px;\n }*/\n }\n }\n}\n\n.nav-item--card__content {\n border-top-right-radius: $card-border-radius;\n border-top-left-radius: $card-border-radius;\n\n &:focus {\n outline: none;\n\n .nav-item__title > span {\n border-bottom: 1px dashed color('gray');\n }\n }\n}\n\n.nav-item--root {\n transition: margin 250ms, box-shadow 500ms;\n\n &.expanded {\n flex-basis: 100%;\n //max-width: ($layout-breakpoint-md - 100) !important;\n max-width: 100%;\n max-height: none !important;\n margin: 8px auto;\n padding-left: 4px;\n\n &:first-of-type {\n margin-top: 0;\n }\n\n //@media only screen and (min-width: $layout-breakpoint-sm) {\n //margin: 8px auto;\n //}\n }\n}\n\n//.nav-item--list {\n//}\n\n.nav-item--list__info-item {\n padding: 0 16px 0 4px;\n display: inline-block;\n}\n\n.nav-item--list__reorder {\n margin-left: 8px;\n color: color('text-disabled');\n}\n\n.nav-item--card {\n\n}\n\n.nav-item--card--group {\n &:not(.expanded) {\n box-shadow: 0px 3px 1px -2px rgba(0, 0, 0, 0.14), 0px 2px 2px 0px rgba(0, 0, 0, 0.098),\n 0px 1px 5px 0px rgba(0, 0, 0, 0.084), 3px 3px 0px 1px #d5d5d5, 6px 6px 0px 1px #aaaaaa;\n }\n}\n\n.nav-item__collapse {\n margin: 0;\n}\n\n.nav-item__more {\n border-top: 1px solid color('gray-light');\n border-bottom-right-radius: $card-border-radius;\n border-bottom-left-radius: $card-border-radius;\n padding: 8px 16px;\n min-height: 40px;\n}\n\n.nav-item__users {\n height: auto;\n cursor: pointer;\n color: #ffffff;\n margin: 0;\n padding: 1px 6px;\n\n > md-icon {\n padding-right: 4px;\n color: #ffffff;\n }\n\n &:hover, &:focus {\n &.success-bg {\n background-color: color('success');\n }\n }\n}\n\n.nav-item__title {\n padding-left: 16px;\n line-height: 1.2;\n font-weight: 400;\n}\n[dir=rtl] .nav-item__title {\n padding-left: auto;\n padding-right: 16px;\n}\n\n.nav-item__info {\n padding: 0 8px;\n}\n\n.nav-item__progress {\n width: 48px;\n\n > .md-container {\n top: 0;\n }\n}\n\n.nav-item__progress-value {\n margin-left: 8px;\n width: 36px;\n}\n\n.progress-wrapper {\n padding: 2px 0;\n cursor: pointer;\n}\n",".student-select {\n padding-top: 0;\n padding-bottom: 0;\n}\n\n.workgroup-progress {\n margin-bottom: 8px;\n\n @media (min-width: $layout-breakpoint-sm) {\n margin-bottom: 0;\n }\n}\n\nalert-status-corner {\n position: absolute;\n top: 0;\n right: 0;\n}\n","// 1. Variables\n$menu-sidebar-width: 56px;\n\n// 2. Base\n.menu-progress {\n position: absolute;\n top: 10px;\n right: 12px;\n\n path {\n stroke: color('accent-2') !important;\n stroke-width: 2px;\n }\n}\n[dir=rtl] .menu-progress {\n right:auto;\n left:12px;\n}\n\n.menu-sidenav {\n\n}\n\n.menu-sidenav__item {\n font-weight: 700;\n //color: color('text-secondary');\n font-size: rem(1.4);\n}\n\n.menu-sidenav__icon {\n margin-top: 12px !important;\n margin-right: 12px !important;\n margin-left: 12px;\n}\n\n.active {\n .menu-sidenav__icon, .menu-sidenav__item {\n color: color('primary');\n }\n}\n\n.menu-sidebar {\n position: absolute;\n top: $wise-toolbar-height + $md-toolbar-height;\n bottom: 0;\n left: 0;\n background-color: #fff;\n width: $menu-sidebar-width;\n overflow: hidden;\n padding: 8px 0;\n text-align: center;\n border-right: 1px solid color('gray');\n\n @media only screen and (max-width: ($layout-breakpoint-xs - 1)) {\n display: none;\n }\n}\n[dir=rtl] .menu-sidebar {\n right:0;\n left:auto;\n}\n\n.md-button.md-icon-button.menu-sidebar__link {\n margin-top: 6px;\n margin-bottom: 6px;\n}\n","// Variables\n$input-action-width: 48px;\n$input-action-vertical-offset: 0;\n$input-action-vertical-offset-richtext: -7px;\n\n// Base\n#node {\n margin: 0 auto;\n position: absolute;\n left: 0;\n right: 0;\n\n @media only screen and (min-width: $layout-breakpoint-xs) {\n padding: 8px;\n padding: 24px 16px;\n margin-bottom: 32px;\n }\n\n @media only screen and (min-width: $layout-breakpoint-sm) {\n padding: 32px;\n }\n\n &.ng-enter {\n transition: opacity .5s;\n opacity: 0;\n }\n\n &.ng-enter-active {\n opacity: 1;\n }\n}\n\n// TODO: use BEM conventions\n\n.node-notice {\n @media only screen and (min-width: $layout-breakpoint-xs) {\n margin-top: -8px;\n margin-bottom: 16px;\n }\n\n @media only screen and (min-width: $layout-breakpoint-sm) {\n margin-top: -16px;\n }\n}\n\n.node-content {\n padding: 0 0 48px;\n background-color: #ffffff;\n border-radius: $button-border-radius;\n overflow: visible;\n\n @media only screen and (max-width: $layout-breakpoint-xs - 1) {\n box-shadow: none;\n }\n\n @media only screen and (min-width: $layout-breakpoint-xs) {\n padding: 0;\n border-top: 2px solid;\n border-bottom: 2px solid;\n }\n}\n\nmd-content {\n &.node-content {\n background-color: #ffffff;\n }\n}\n\n.node-content__rubric {\n position: absolute;\n top: -22px;\n left: 0;\n right: 0;\n z-index: 1;\n\n .avatar--icon {\n transform: scale(0.94);\n\n @media only screen and (max-width: $layout-breakpoint-xs - 1) {\n transform: scale(0.8);\n }\n }\n}\n\n.node-icon {\n color: #ffffff;\n vertical-align: inherit;\n}\n\n.node-select {\n margin: 0 8px;\n min-width: 0;\n font-weight: 500;\n font-size: $body-font-size-base;\n\n .md-select-value {\n *:first-child {\n transform: translate3d(0,0,0);\n flex: 1 0 0;\n }\n\n .node-select__icon {\n display: none;\n }\n\n .node-select__status {\n display: none;\n }\n }\n\n .md-select-icon {\n margin-left: 0;\n color: color('text');\n }\n}\n\n.node-select-option--group {\n //color: rgba(0,0,0,0.54);\n background-color: color('gray-lightest');\n border-bottom: 1px solid color('gray-lighter');\n border-top: 1px solid color('gray-lighter');\n}\n\n.node-select-option--node {\n padding-left: 20px;\n}\n\n.node-select__icon {\n margin-right: 8px;\n}\n\n.node-select__status {\n margin-left: 8px;\n}\n\n.node-select__text {\n text-overflow: ellipsis;\n white-space: nowrap;\n overflow: hidden;\n\n @media only screen and (min-width: $layout-breakpoint-xs) {\n margin-top: 2px;\n }\n}\n\n.node-title {\n line-height: 1.2;\n text-transform: none;\n margin-top: 3px;\n\n @media only screen and (max-width: $layout-breakpoint-xs - 1) {\n font-size: $body-font-size-base;\n }\n}\n\n.node-content__actions {\n padding: 0 16px 16px;\n\n @media only screen and (min-width: $layout-breakpoint-sm) {\n padding: 0 24px 24px;\n }\n\n @media only screen and (min-width: $layout-breakpoint-md) {\n padding: 0 32px 32px;\n }\n\n .md-button:first-child {\n margin-left: 0;\n }\n\n .md-button:last-child {\n margin-right: 0;\n }\n}\n\n.node-content__actions__info {\n font-style: italic;\n margin-left: 8px;\n color: color('text-secondary');\n}\n\n.node-content__actions__more {\n border-bottom: 1px dotted;\n}\n\n.md-button.md-icon-button.node-nav {\n &:not(:first-of-type) {\n }\n\n &:first-of-type {\n margin-right: 0;\n }\n}\n\n.node-sidebar-active {\n @media only screen and (min-width: $layout-breakpoint-xs) {\n margin-right: ($md-toolbar-height + 16);\n }\n}\n\n.node-sidebar-visible {\n @media only screen and (max-width: $layout-breakpoint-xs - 1) {\n margin-bottom: $wise-toolbar-height;\n }\n}\n\n.node-sidebar {\n position: absolute;\n right: 0;\n top: 0;\n width: $md-toolbar-height;\n}\n\n.node-sidebar__toolbar {\n position: fixed;\n width: $md-toolbar-height;\n background-color: #ffffff;\n padding: 8px 0;\n border-radius: $button-border-radius;\n\n @media only screen and (max-width: $layout-breakpoint-xs - 1) {\n right: 0;\n bottom: 0;\n left: 0;\n width: 100%;\n border-radius: 0;\n padding: 0;\n min-height: 0;\n height: $wise-toolbar-height;\n }\n}\n\n// TODO: move to own sass module file (only gets used by teacher)\n// TODO: use BEM (.node--info)\n.node-info {\n margin: 0;\n\n @media only screen and (max-width: $layout-breakpoint-xs - 1) {\n margin: -16px;\n border-radius: 0;\n }\n\n .divider {\n margin-left: -8px;\n margin-right: -8px;\n }\n\n .component {\n &:first-child {\n margin-top: -16px;\n }\n }\n\n .component, .component__content, .component__header {\n margin-left: -8px;\n margin-right: -8px;\n }\n\n .component__actions {\n display: none;\n }\n}\n\n.node-rubric {\n border: 2px solid color('primary');\n margin: 8px 16px 24px;\n padding: 16px;\n background-color: #ffffff;\n}\n\n.node-rubric--component {\n\n}\n\n.node__label--vertical-alignment {\n vertical-align: middle;\n display: inline-block;\n}\n",".grading {\n}\n\n.grading__item-container {\n margin: 0 0 16px;\n padding: 0 !important;\n}\n\n.grading__item {\n background-color: #ffffff;\n\n .component {\n padding: 0;\n }\n}\n","// Variables\n$notebook-sidebar-width: 56px;\n\n// Base\n.notebook-launcher {\n @media only screen and (min-width: $layout-breakpoint-xs) {\n &.md-button.md-fab {\n z-index: 61;\n }\n }\n}\n\n.notebook-report {\n border-radius: 4px 4px 0 0;\n margin: 0;\n height: 100%;\n\n .note-toolbar {\n margin: -8px -8px 8px;\n padding: 4px;\n border: 0 none;\n border-top: 1px solid color('gray-light');\n border-bottom: 1px solid color('gray-light');\n border-radius: 0;\n\n .btn-group {\n margin-top: 0;\n }\n }\n\n .note-btn {\n border: 0 none;\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n border-bottom-left-radius: 0;\n border-bottom-right-radius: 0;\n }\n}\n\n.notebook-report-container {\n height: 550px;\n width: 450px;\n max-height: 90%;\n bottom: 0;\n right: 96px;\n position: absolute;\n z-index: 3;\n}\n\n.notes-visible {\n @media only screen and (min-width: $layout-breakpoint-sm) {\n .notebook-report-container {\n right: 516px;\n transition: right 250ms;\n }\n }\n}\n\n.notebook-report-container__full {\n top: 16px;\n bottom: 16px;\n left: 16px;\n right: 16px;\n max-height: none;\n max-width: none;\n height: auto;\n width: auto;\n\n .notebook-report {\n height: 100%;\n width: 100%;\n margin: 0 auto;\n max-height: none;\n border-radius: $card-border-radius;\n }\n}\n\n.notebook-report-container__collapsed {\n width: 300px;\n height: auto;\n\n .notebook-report__content, .notebook-report__actions, .notebook-report__content__header {\n display: none;\n }\n}\n\n.notebook-report__toolbar {\n background-color: color('gray-darkest') !important;\n border-radius: $card-border-radius $card-border-radius 0 0;\n}\n\n.notebook-report__toolbar__title {\n max-width: 150px;\n}\n\n.notebook-report__content {\n h1, h2, h3, h4 {\n font-size: rem(2.2);\n }\n\n background-color: #ffffff;\n\n .note-editor.note-frame {\n border: 0 none;\n border-radius: 0;\n padding: 0;\n box-shadow: none;\n }\n\n .note-resizebar {\n display: none;\n }\n}\n\n.notebook-report__content__header {\n padding: 8px;\n background-color: #ffffff;\n font-size: rem(1.6);\n}\n\n@media only screen and (max-width: $layout-breakpoint-xs - 1) {\n .notebook-report-container {\n &:not(.notebook-report-container__collapsed) {\n top: 0;\n bottom: 0;\n left: 0;\n right: 0;\n max-height: none;\n max-width: none;\n height: auto;\n width: auto;\n\n .notebook-report {\n border-radius: 0;\n }\n }\n }\n\n .notebook-tools--full {\n display: none;\n }\n}\n\n.notebook-report-backdrop {\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n z-index: 3;\n background-color: rgba(33,33,33,1.0);\n opacity: .48;\n}\n\n.notebook-menu {\n transition: opacity 500ms;\n\n &.ng-enter {\n opacity: 0;\n }\n\n .ng-enter-active {\n opacity: 1;\n transition-delay: 250ms;\n }\n\n &.ng-leave-active, &.ng-hide {\n opacity: 0;\n }\n\n &.ng-hide-add, &.ng-hide-add-active,\n &.ng-hide-remove, &.ng-hide-remove-active {\n opacity: 0;\n }\n}\n\n.notebook-item {\n transition: box-shadow 250ms;\n margin: 0 16px 16px;\n display: block;\n}\n\n.notebook-item__content {\n height: 250px;\n min-width: 230px;\n position: relative;\n padding: 0;\n background-color: color('gray');\n border-top-left-radius: $card-border-radius;\n border-top-right-radius: $card-border-radius;\n}\n\n.notebook-item__content__attachment, .notebook-item__content__text {\n position: absolute;\n left: 0;\n right: 0;\n}\n\n.notebook-item__content__attachment {\n background-repeat: no-repeat !important;\n border-top-left-radius: $card-border-radius;\n border-top-right-radius: $card-border-radius;\n background-position: center top !important;\n background-size: cover !important;\n top: 0;\n bottom: 0;\n}\n\n.notebook-item__content__text {\n bottom: 0;\n padding: 8px;\n font-weight: 500;\n overflow: hidden;\n max-height: 120px;\n min-height: 56px;\n background-color: rgba(255,255,255,0.95);\n border-top: 1px solid color('gray-lighter');\n\n &:after {\n content: \"\";\n text-align: right;\n position: absolute;\n bottom: 0;\n right: 0;\n width: 100%;\n height: 0.8em;\n background: linear-gradient(180deg,hsla(0,0%,100%,0),rgba(255,255,255,0.95) 100%);\n }\n}\n\n.notebook-item__content--text-only {\n &:after {\n content: \"note\";\n font-family: 'Material Icons';\n font-weight: normal;\n font-style: normal;\n display: inline-block;\n line-height: 1;\n text-transform: none;\n letter-spacing: normal;\n word-wrap: normal;\n white-space: nowrap;\n direction: ltr;\n\n /* Support for all WebKit browsers. */\n -webkit-font-smoothing: antialiased;\n /* Support for Safari and Chrome. */\n text-rendering: optimizeLegibility;\n\n /* Support for Firefox. */\n -moz-osx-font-smoothing: grayscale;\n\n /* Support for IE. */\n font-feature-settings: 'liga';\n //position: absolute;\n font-size: 80px;\n color: color('text-disabled');\n }\n}\n\n.notebook-item--question__content--text-only {\n content: \"live_help\";\n}\n\n.notebook-item__content__location {\n opacity: 0.9;\n padding: 8px 0;\n\n md-icon {\n font-size: 22px;\n }\n}\n\n.notebook-item__edit {\n cursor: pointer;\n}\n\n.notebook-item__actions {\n margin: 0;\n padding: 0 8px;\n color: #ffffff;\n background-color: color('gray-darkest');\n border-bottom-left-radius: $card-border-radius;\n border-bottom-right-radius: $card-border-radius;\n\n md-icon {\n color: #ffffff;\n }\n}\n\n.notebook-item__text-input {\n margin: 0;\n}\n\n.notebook-item__text-input__textarea {\n padding-left: 0;\n padding-right: 0;\n}\n\n.notebook-item__attachment {\n background-color: color('gray-light');\n padding: 16px;\n margin-bottom: 16px;\n text-align: center;\n position: relative;\n}\n\n.notebook-item__attachment__content {\n max-width: 100%;\n height: auto;\n}\n\n.notebook-item__attachment__delete {\n position: absolute;\n top: 4px;\n right: -2px;\n // TODO: generalize for on item buttons like this (delete attachment, etc)\n width: 34px !important;\n height: 34px !important;\n min-height: 0;\n\n md-icon {\n margin-left: -2px;\n font-size: 22px;\n }\n}\n\n.notebook-item__info {\n font-style: italic;\n opacity: .8;\n color: lighten(color('accent-1'), 5%);\n\n a, md-icon {\n color: lighten(color('accent-1'), 5%);\n }\n\n md-icon {\n font-size: 1.5em;\n min-width: 0;\n width: auto;\n }\n}\n\n.notebook-item__upload {\n text-align: center;\n padding: 24px;\n background-color: color('gray-lighter');\n margin-bottom: 16px;\n color: color('text-secondary');\n border-radius: 4px;\n cursor: pointer;\n border: 1px dashed transparent;\n transition: all 250ms;\n\n md-icon, span {\n transition: color 250ms;\n }\n\n &:hover, &:focus, &.dragover {\n border-color: color('accent');\n background-color: lighten(color('accent'), 35%);\n color: color('accent');\n\n md-icon, span {\n color: color('accent');\n }\n }\n}\n\n.view-notebook-item {\n width: $layout-breakpoint-xs;\n}\n\n.view-notebook-item__content {\n}\n\n.notebook-item--report {\n background-color: #ffffff;\n\n .note-editor {\n margin-bottom: 16px;\n border-color: color('gray');\n }\n}\n\n.notebook-item--report__container {\n &.ui-scrollpoint {\n .notebook-item--report__toolbar {\n position: fixed;\n top: ($wise-toolbar-height + $md-toolbar-height);\n left: 0;\n right: 0;\n z-index: 1;\n\n @media only screen and (min-width: $layout-breakpoint-xs) {\n left: ($notebook-sidebar-width - 2);\n }\n\n @media only screen and (min-width: $layout-breakpoint-xs) {\n padding: 0 24px;\n }\n\n @media only screen and (min-width: $layout-breakpoint-sm) {\n padding: 0 32px;\n }\n\n .note-toolbar {\n margin: 0 16px;\n\n @media only screen and (min-width: $layout-breakpoint-xs) {\n margin: 0 8px;\n }\n\n @media only screen and (min-width: $layout-breakpoint-sm) {\n margin: 0 16px;\n }\n }\n }\n\n .note-editor {\n padding-top: 40px;\n }\n }\n}\n\n.notebook-item--report__toolbar {\n\n .note-toolbar {\n background-color: color('gray-light');\n border: 1px solid color('gray');\n margin-bottom: -2px;\n }\n}\n\n.notebook-item--report__heading {\n text-align: center;\n margin-bottom: 32px;\n}\n\n.notebook-item--report__content {\n}\n\n.notebook-item--report__add-note {\n font-weight: 700;\n}\n\n.notebook-item--report__note-img {\n max-width: 100%;\n height: auto !important;\n}\n\n.notebook-sidebar {\n @media only screen and (min-width: $layout-breakpoint-xs) {\n width: 400px;\n max-width: none;\n }\n\n @media only screen and (min-width: $layout-breakpoint-sm) {\n width: 500px;\n max-width: none;\n }\n}\n\n.notebook-items {\n width: 100%;\n overflow: auto;\n margin-top: 16px;\n margin-bottom: 76px;\n\n .notebook-item {\n width: 100%;\n }\n\n .notebook-item__content {\n height: 200px;\n min-width: 0;\n }\n}\n\n.notebook-items--grading {\n margin-bottom: 0;\n}\n\n@media only screen and (max-width: ($layout-breakpoint-xs - 1)) {\n .notebook-enabled {\n .md-fab-bottom-right, .md-fab-bottom-left {\n bottom: ($wise-toolbar-height + 8) !important;\n }\n }\n}\n\n.notebook-grading {\n background-color: #ffffff;\n display: block;\n}\n",".notification-btn {\n width: 60px !important;\n\n md-icon {\n margin-left: 20px;\n }\n}\n\n.notification-count {\n border-radius: 50%;\n position: absolute;\n background-color: color('accent');\n width: 22px;\n left: 4px;\n height: 22px;\n line-height: 18px;\n font-size: 12px;\n font-weight: 700;\n border: 2px solid;\n\n &:before {\n content: \"\";\n position: absolute;\n right: -7px;\n top: 5px;\n border-left: 6px solid rgba(255,255,255,0.87);\n border-top: 4px solid transparent;\n border-bottom: 4px solid transparent;\n }\n}\n\n.notification-list {\n padding: 8px 0;\n}\n\n.notification-dismiss {\n width: 500px;\n}\n\n.notification-dismiss__input {\n margin-bottom: 0;\n}\n\nmd-list md-list-item .md-list-item-text h4,\nmd-list md-list-item.md-2-line .md-list-item-text h4,\nmd-list md-list-item.md-3-line .md-list-item-text h4 {\n &.notification-list-item__source {\n color: color('text-secondary');\n font-size: rem(1.2);\n\n md-icon {\n font-size: rem(1.8);\n min-width: 0;\n width: auto;\n margin-left: -4px;\n line-height: rem(2);\n }\n }\n}\n",".account-menu {\n border-radius: $card-border-radius;\n padding: 0;\n font-size: $body-font-size-base;\n max-width: 380px;\n\n @media (min-width: $layout-breakpoint-md) {\n min-width: 380px !important;\n }\n\n h3 {\n margin: 0;\n font-weight: 300;\n }\n}\n\n.account-menu--fixed-height {\n height: $max-menu-height;\n}\n\n.account-menu--fixed-width {\n width: 320px;\n\n @media (min-width: $layout-breakpoint-sm) {\n width: 380px;\n }\n}\n\n.account-menu__icon {\n background-color: color('text-light');\n border-radius: 50%;\n}\n\n.account-menu__caret {\n position: absolute;\n right: 28px;\n top: -8px;\n outline: none;\n\n &:before {\n content: '';\n position: absolute;\n border-bottom: 8px solid #fff;\n border-left: 8px solid transparent;\n border-right: 8px solid transparent;\n }\n}\n\n.account-menu__caret--pause, .account-menu__caret--notification {\n right: 80px;\n}\n\n.account-menu__caret--notification--with-pause {\n right: 132px;\n}\n\n[dir=rtl] {\n .account-menu__caret {\n right: auto;\n left: 28px;\n }\n .account-menu__caret--pause, .account-menu__caret--notification {\n left:80px;\n right:auto;\n }\n .account-menu__caret--notification--with-pause {\n left: 132px;\n right:auto;\n }\n}\n\n.account-menu__info {\n padding: 8px 12px;\n}\n\n.account-menu__info__title {\n font-weight: 500;\n}\n\n.account-menu__info__team {\n font-weight: 400;\n color: color('text-secondary');\n}\n\n.account-menu__users {\n padding: 0;\n\n md-list-item {\n padding: 0;\n\n .md-avatar {\n margin: 0 8px 0 0;\n height: 48px;\n width: 48px;\n }\n }\n}\n\n.account-menu__progress {\n md-progress-linear {\n transform: rotate(270deg);\n width: 26px;\n }\n}\n\n.account-menu__grade {\n position: relative;\n margin-right: 4px;\n\n md-icon {\n color: color('score');\n }\n}\n\n.account-menu__grade__overlay {\n position: absolute;\n top: 0;\n width: 100%;\n background-color: rgba(255, 255, 255, 0.6);\n}\n\n.account-menu__actions {\n background-color: color('gray-lightest');\n}\n\n.account-menu__control {\n padding: 16px;\n}\n",".annotations {\n margin: 16px 4px 16px 62px;\n position: relative;\n font-size: rem(1.5);\n\n hr {\n margin: 10px 0 8px;\n border-color: rgba(0,0,0,.12);\n }\n\n &:after {\n content: \"\";\n position: absolute;\n width: 0;\n height: 0;\n left: -16px;\n right: auto;\n top: 0px;\n bottom: auto;\n border-top: 20px solid transparent;\n border-bottom: 20px solid transparent;\n border-right: 16px solid color('gray-darker');\n }\n}\n\n.annotations-container--student--report {\n border-top: 1px solid color('gray-light');\n}\n\n.annotations--report {\n margin-top: 0;\n margin-bottom: 0;\n}\n\n.annotations__header {\n position: relative;\n //border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n padding: 10px 12px;\n font-weight: 700;\n transition: all 1s;\n color: #ffffff;\n background-color: color('gray-darker');\n}\n\n.annotations__avatar {\n background-color: color('accent');\n padding: 2px;\n position: absolute;\n top: 0;\n left: -62px;\n}\n\n.annotations__icon {\n transition: all 1s;\n color: #ffffff;\n}\n\n.annotations__body {\n padding: 12px;\n background-color: #ffffff;\n border-bottom-left-radius: $card-border-radius;\n border-bottom-right-radius: $card-border-radius;\n overflow: auto;\n}\n\n.annotations__status {\n background-color: #ffffff;\n color: color('info');\n display: inline-block;\n margin-left: 8px;\n font-size: rem(1.2);\n\n &.ng-enter, &.ng-leave {\n transition: all 1s;\n }\n\n &.ng-enter, &.ng-leave.ng-leave-active {\n opacity:0;\n }\n\n &.ng-leave, &.ng-enter.ng-enter-active {\n opacity:1;\n }\n}\n\n.annotations__score {\n font-weight: 700;\n}\n\n.annotations__info {\n font-style: italic;\n opacity: 0.8;\n border-bottom: 1px dotted;\n font-size: rem(1.3);\n}\n\n.annotations--inside {\n .annotations {\n margin-left: 72px;\n }\n}\n\n// TODO: move to own file\n.annotations--info {\n margin-bottom: 32px;\n margin-right: 8px;\n margin-left: 72px;\n\n @media only screen and (min-width: ($layout-breakpoint-xs)) {\n margin: 16px 16px 32px 76px;\n }\n\n &:after {\n border-right: 16px solid color('info');\n }\n\n .annotations__avatar {\n background-color: #ffffff;\n }\n\n .annotations__header {\n background-color: color('info');\n }\n}\n",".annotations--grading {\n md-input-container {\n margin-bottom: 0;\n }\n\n .md-errors-spacer {\n display: none;\n }\n\n input, textarea {\n //border-width: 0;\n //background-color: color('text-light-secondary');\n\n &:focus {\n background-color: #ffffff;\n }\n\n &:disabled {\n color: color('text');\n }\n }\n}\n\n.annotations--grading--revision {\n margin: 8px 0 0;\n padding: 8px;\n}\n\n.annotations--notebook {\n margin-top: 16px;;\n}\n\n.annotations--grading__info {\n font-style: italic;\n margin: 8px 8px 4px;\n}\n\n.annotations--grading__item {\n padding: 8px;\n}\n\n.annotations--grading__score {\n input {\n margin-top: 0 !important;\n font-size: rem(1.8);\n width: 52px;\n text-align: center;\n }\n}\n\n.annotations--grading__score__label {\n transform: none !important;\n width: auto;\n display: block;\n padding: 0;\n margin: 0 8px 0 0;\n}\n\n.annotations--grading__score__max {\n label {\n display: none;\n }\n}\n\n.annotations--grading__score__divider {\n position: relative;\n top: 12px;\n margin-left: 4px;\n}\n\n.annotations--grading__auto-comment {\n margin: 0 2px;\n}\n\n.annotations--grading__auto-comment__content {\n margin-top: 8px;\n}\n",".component {\n position: relative;\n}\n\n.component__wrapper {\n padding: 0 16px;\n margin: 24px 0;\n}\n\n.component__content {\n overflow-x: auto;\n font-size: rem(1.5);\n overflow-y: hidden; // TODO: figure out why this is needed after update to ng-material 1.1.1\n\n @media only screen and (min-width: $layout-breakpoint-xs) {\n padding: 0 8px;\n }\n}\n\nh3.component__header, .component-header {\n padding: 8px 12px;\n margin: 0;\n font-size: rem(1.4);\n}\n\n.component__rubric {\n position: absolute;\n left: -20px;\n top: 12px;\n}\n\n.notebook-enabled {\n .component_content {\n img {\n transition: all 250ms;\n cursor: pointer;\n cursor: copy;\n //position: relative;\n //border: 2px solid transparent;\n\n &:hover, &:focus {\n box-shadow: 0 0 5px 1px color('accent');\n //border: 2px solid #ffffff;\n }\n }\n }\n}\n\n.component__actions {\n .md-button:first-child {\n margin-left: 0;\n }\n\n .md-button:last-child {\n margin-right: 0;\n }\n}\n\n.component__actions__info {\n font-style: italic;\n margin-left: 8px;\n //color: color('accent-1');\n color: color('text-secondary');\n}\n\n.component__actions__more {\n border-bottom: 1px dotted;\n}\n\n.component__prompt {\n margin-bottom: 8px;\n font-weight: 500;\n}\n\n.component__prompt__content {\n display: inline;\n}\n\n.component__attachment {\n position: relative;\n margin: 0 8px;\n padding-bottom: 8px;\n\n @media only screen and (min-width: $layout-breakpoint-xs) {\n padding-top: 8px;\n }\n}\n\n.component__add-attachment {\n @media only screen and (max-width: ($layout-breakpoint-xs - 1)) {\n width: 100%;\n }\n}\n\n.component__attachment__content {\n max-height: 100px;\n width: auto;\n}\n\n.component__attachment__delete {\n position: absolute;\n top: 0;\n right: 0;\n min-width: 0;\n background-color: rgba(255, 255, 255, 0.75) !important;\n border-radius: 0;\n padding: 4px;\n margin: 0;\n\n //@media only screen and (min-width: $layout-breakpoint-sm) {\n //margin-top: 8px;\n //}\n\n > md-icon {\n margin-top: 0;\n }\n}\n\n.component__revision {\n margin: 8px 0;\n padding: 8px;\n\n &:nth-child(odd) {\n background-color: color('gray-lightest');\n }\n}\n\n.component__revision__content {\n padding: 4px 0 8px 0;\n border-bottom: 1px solid color('gray-light');\n}\n\n.component__revision__actions {\n color: color('gray-darker');\n padding-top: 4px;\n}\n","// Variables\n\n// Base\n.component__content--Discussion {\n overflow: hidden;\n}\n\n.discussion-content {\n background-color: color('gray-lighter');\n //margin: 0 0 -16px;\n box-shadow: inset 0 0 3px color('gray-dark');\n}\n\n.discussion-posts {\n padding: 12px 12px 8px;\n\n @media only screen and (min-width: $layout-breakpoint-md) {\n padding: 16px 16px 0;\n }\n}\n\n.discussion-post {\n margin: 0 auto 16px;\n max-width: $layout-breakpoint-xs;\n\n @media only screen and (min-width: $layout-breakpoint-xs) {\n margin-bottom: 24px;\n }\n\n @media only screen and (min-width: $layout-breakpoint-md) {\n margin-bottom: 32px;\n }\n\n // angular-material fix for when discussion posts are shown inside an md-list-item (e.g. in the grading tool)\n md-divider {\n position: relative;\n width: auto;\n }\n}\n\n.discussion-post__contents {\n padding: 16px;\n}\n\n.discussion-post__avatar, md-list-item > .md-avatar.discussion-post__avatar {\n margin-right: 8px;\n}\n\n.discussion-post__avatar--reply, md-list-item > .md-avatar.discussion-post__avatar--reply {\n margin-top: 8px;\n}\n\n\n.discussion-post__user, md-list-item .md-list-item-text h3.discussion-post__user {\n padding-bottom: 4px;\n font-weight: 700;\n line-height: 1.3;\n overflow: visible;\n white-space: normal;\n}\n\n.discussion-post__date {\n color: color('gray-dark');\n}\n\n.discussion-post__date--reply {\n margin-left: 8px;\n font-weight: 400;\n}\n\n.discussion-post__content {\n margin-top: 16px;\n white-space: pre-wrap;\n}\n\n.discussion-post__attachment {\n max-width: 100%;\n height: auto !important;\n margin-top: 16px;\n}\n\n.discussion-new {\n background-color: #ffffff;\n max-width: 570px;\n margin-left: auto;\n margin-right: auto;\n padding: 8px;\n transition: all 250ms;\n transform: scale(0.95);\n}\n\n.discussion-new--focused {\n transform: scale(1);\n}\n\nmd-input-container.discussion-new__input-container {\n margin: 0;\n padding: 0;\n\n > textarea.md-input {\n min-height: 68px;\n }\n}\n\n.discussion-new__input--textarea, .input-container textarea.discussion-new__input--textarea {\n padding: 8px;\n border: 0 none;\n}\n\n.discussion-new__actions {\n padding: 0 8px;\n\n .md-button {\n &:first-of-type {\n margin-left: 0;\n }\n\n &:last-of-type {\n margin-right: 0;\n }\n }\n}\n\n.discussion-new__attachment {\n padding: 0;\n margin: 0 0 8px;\n}\n\n.discussion-new__attachment__content {\n margin-top: 0;\n margin-bottom: 16px;\n}\n\n.discussion-comments {\n padding: 0;\n}\n\n.discussion-comments__contents {\n background-color: color('gray-lightest');\n}\n\n.discussion-comments__header {\n background-color: transparent;\n padding: 0;\n\n .md-subheader-inner {\n padding-bottom: 8px;\n }\n}\n\n.discussion-comments__list {\n padding: 0;\n max-height: 9999px;\n overflow-y: auto;\n\n @media only screen and (min-width: $layout-breakpoint-xs) {\n max-height: 400px;\n }\n}\n\n.input--textarea.discussion-reply__input, .input-container textarea.input--textarea.discussion-reply__input {\n background-color: #ffffff;\n padding: 4px;\n font-size: ($body-font-size-base) - 1;\n border: 0 none;\n margin-left: -1px;\n margin-bottom: 0;\n resize: none;\n}\n\n.discussion-reply, md-list-item.discussion-reply {\n margin: 0 12px 8px;\n padding: 0;\n min-height: 56px;\n}\n\n.discusstion-reply__details, md-list-item .md-list-item-text.discusstion-reply__details {\n margin: 8px 0;\n}\n\n.discussion-post__user--reply, md-list-item .md-list-item-text h3.discussion-post__user--reply {\n font-size: ($body-font-size-base) - 1;\n padding: 0;\n margin: 0;\n}\n\n.discusstion-reply__content {\n margin-top: 2px;\n\n p {\n font-weight: 400 !important;\n color: color('body') !important;\n }\n}\n\n.discussion-new-reply {\n padding: 8px;\n}\n\n.discussion-new-reply__input-container {\n padding-top: 0;\n margin: 0;\n\n .md-errors-spacer {\n display: none;\n }\n}\n\n.discussion-new-reply__actions {\n margin-left: 8px;\n\n .md-button {\n margin-top: 0;\n margin-bottom: 0;\n }\n}\n",".embedded-content {\n\n}\n\n.embedded-content__iframe {\n border: 0 none;\n}\n",".component--grading {\n padding: 0;\n margin: 0;\n\n &:not(:last-child) {\n > div {\n border-bottom: 1px solid color('gray-light');\n }\n }\n\n .component__wrapper {\n padding: 0;\n margin: 0;\n }\n\n .component__content {\n padding: 16px;\n margin: 0;\n }\n}\n\n.component--grading__response {\n padding-bottom: 16px;\n\n @media only screen and (min-width: $layout-breakpoint-sm) {\n padding-right: 16px;\n padding-bottom: 0;\n }\n}\n\n.component--grading__response__content {\n overflow: auto;\n}\n\n.component--grading__annotations {\n background-color: color('selected-bg');\n}\n\n.component--grading__annotations__divider {\n padding: 4px;\n background-color: #ffffff;\n}\n\n.component--grading__actions__info {\n margin: 16px 0 0;\n padding-top: 8px;\n border-top: 1px solid color('gray-lighter');\n}\n",".graph-select {\n min-width: 150px;\n max-width: 200px;\n}\n\n.graph-controls {\n margin: 8px 0;\n padding: 8px 0;\n border: 1px solid color('gray-lighter');\n border-left-width: 0;\n border-right-width: 0;\n}\n","// Variables\n\n// Base\n.match-content {\n background-color: color('gray-lighter');\n margin-bottom: 16px;\n padding: 8px;\n box-shadow: inset 0 0 3px color('gray-dark');\n}\n\n.match-divider {\n margin: 16px 8px 8px;\n}\n\n.match-divider--horizontal {\n @media only screen and (min-width: $layout-breakpoint-sm) {\n display: none;\n }\n}\n\n.match-bucket__header {\n padding: 12px;\n font-weight: 500;\n color: color('primary');\n}\n\n.match-bucket__content {\n padding: 0;\n}\n\n.match-bucket--choices {\n .match-bucket__header {\n color: color('accent-1');\n }\n}\n\n.match-bucket__contents {\n min-height: 120px;\n //margin: 0;\n padding: 0 8px 8px;\n background-color: color('gray-light');\n transition: background-color 250ms;\n border-bottom-left-radius: $card-border-radius;\n border-bottom-right-radius: $card-border-radius;\n\n column-gap: 8px;\n\n @media only screen and (max-width: ($layout-breakpoint-xs - 1)) {\n column-count: 1 !important;\n }\n\n img {\n max-width: 100%;\n height: auto;\n }\n}\n\n.match-bucket__contents--over {\n background-color: color('primary');\n}\n\n.match-bucket__item {\n list-style-type: none;\n cursor: move;\n padding-top: 8px;\n break-inside: avoid;\n\n &:hover, &:focus {\n //background-color: rgba(0,0,0,0.2);\n }\n}\n\n.match-bucket__item__contents {\n background-color: #ffffff;\n padding: 8px !important;\n border: 1px solid color('gray');\n\n .md-list-item-text {\n width: 100%;\n }\n}\n\n.match-bucket__item__contents__text {\n margin-right: 4px;\n}\n\n.match-feedback {\n transition: opacity 250ms;\n /*padding-left: 8px;\n border-left: 4px solid;*/\n margin: 8px -8px -8px;\n color: #ffffff;\n padding: 4px 8px;\n\n &.ng-hide {\n opacity: 0;\n transition: opacity 1ms;\n }\n\n md-icon {\n color: #ffffff;\n }\n}\n",".outside-content {\n iframe {\n border: 1px solid color('gray-lighter');\n }\n}\n\n.outside-content__source {\n margin-top: 4px;\n text-align: end;\n\n a {\n max-width: 240px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n display: inline-block;\n }\n}",".component-revisions {\n .component {\n padding: 0;\n margin: 0;\n }\n\n .component__content {\n padding: 0;\n }\n\n .component__wrapper {\n margin: 16px 0;\n }\n\n .md-resize-handle {\n display: none;\n }\n}\n\n.component-revisions__item, md-list-item.component-revisions__item {\n padding: 0;\n}\n\n.component-revisions__item--latest {\n margin-bottom: 24px;\n}\n\n.component-revisions__item__text {\n\n}\n\n.component-revisions__annotation-label {\n margin-right: 8px;\n}\n\n.component-revisions__has-auto-and-teacher {\n padding-top: 8px;\n margin-top: 8px;\n border-top: 1px solid color('gray-light');\n}\n",".notebook-toolbar {\n md-divider {\n margin: 8px 0;\n }\n\n @media only screen and (max-width: ($layout-breakpoint-sm - 1)) {\n border-top: 1px solid color('gray-light');\n }\n}\n\n.notebook-toolbar__add-menu {\n position: absolute;\n bottom: 40px;\n\n .md-fab-action-item {\n background-color: #ffffff;\n }\n}\n\n.notebook-toolbar__add-icon {\n border-radius: 50%;\n}\n\n#closeNotebookSettingsButton {\n float:right;\n}\n\n[dir=rtl] #closeNotebookSettingsButton {\n float:left;\n}","highchart {\n display: block;\n}\n"]} \ No newline at end of file diff --git a/src/main/webapp/wise5/themes/default/style/vle.css b/src/main/webapp/wise5/themes/default/style/vle.css index abe5d7e02f..6820cf1f3f 100644 --- a/src/main/webapp/wise5/themes/default/style/vle.css +++ b/src/main/webapp/wise5/themes/default/style/vle.css @@ -1,2 +1,2 @@ -body{background:#eee}body.vle{overflow:hidden}a:focus,a:hover{color:#1c8ca8}blockquote{background-color:#e7f7fb;padding:8px;margin:16px 0;border:solid #1c8ca8;border-width:0 0 0 3px}.has-indicator:after{content:"";position:absolute;border-radius:50%;padding:5px;background-color:#f05843}.has-indicator--icon-button:after{top:25px;left:5px}.badge{border-radius:4px;padding:2px 6px;font-size:12px;font-weight:500;font-style:normal;background-color:#aaa}.badge.md-button{min-width:0;min-height:0;line-height:inherit}.badge.md-button:focus,.badge.md-button:hover{background-color:#aaa}.badge.md-button:focus{outline:1px dotted #aaa}.badge--info{background-color:#ef6c00;color:#fff}.badge--warn{background-color:#c62828;color:#fff}.badge--success{background-color:#00c853;color:#fff}.divider--withmargin{margin:16px 0}.divider--dashed{border-top-style:dashed}a{color:#1c8ca8;cursor:pointer}.active{background-color:hsla(0,0%,62%,.2);color:rgba(0,0,0,.87)}.avatar{border-radius:50%;box-sizing:content-box}.avatar--square{border-radius:4px}.avatar.md-18{height:30px;width:30px}.avatar.md-24{height:36px;width:36px}.avatar.md-36{height:48px;width:48px}.avatar.md-48{height:60px;width:60px}.avatar--icon{background-color:#ddd;white-space:normal!important}.avatar--icon:not(.md-avatar){padding:6px}.avatar--icon.md-18{height:18px;width:18px}.avatar--icon.md-24{height:24px;width:24px}.avatar--icon.md-36{height:36px;width:36px}.avatar--icon.md-48{height:48px;width:48px}md-toolbar.md-light-theme:not(.md-menu-toolbar) md-icon{color:rgba(0,0,0,.54)}md-toolbar.md-light-theme:not(.md-menu-toolbar) .md-button:disabled md-icon{color:rgba(0,0,0,.26)}.md-button:not([disabled]).primary,md-icon.primary{color:#1c8ca8!important}.md-button:not([disabled]).success,md-icon.success{color:#00c853!important}.md-button:not([disabled]).warn,md-icon.warn{color:#c62828!important}.md-button:not([disabled]).info,md-icon.info{color:#ef6c00!important}.md-button:not([disabled]).accent,md-icon.accent{color:#f05843!important}.md-button:not([disabled]).accent-1,md-icon.accent-1{color:#795c3a!important}.md-button:not([disabled]).accent-2,md-icon.accent-2{color:#cad266!important}md-input-container.md-wise-theme label{color:rgba(0,0,0,.87)}md-select-menu.md-default-theme md-option[selected],md-select-menu md-option[selected]{background-color:#f4fbfd}.md-autocomplete-suggestions-container.md-default-theme li .highlight,.md-autocomplete-suggestions-container li .highlight{color:#1c8ca8;background-color:#f4fbfd}.primary{color:#1c8ca8}.accent{color:#f05843}.accent-1{color:#795c3a}.accent-2{color:#cad266}.warn{color:#c62828}.info{color:#ef6c00}.success{color:#00c853}.divider{color:rgba(0,0,0,.12)}.gray-lightest{color:#f7f7f7}.gray-lighter{color:#eee}.gray-light{color:#ddd}.gray{color:#ccc}.gray-dark{color:#aaa}.gray-darker{color:#757575}.gray-darkest{color:#333}.text{color:rgba(0,0,0,.87)}.text-secondary{color:rgba(0,0,0,.54)}.text-disabled{color:rgba(0,0,0,.26)}.text-light{color:#fff}.text-light-secondary{color:hsla(0,0%,100%,.7)}.text-light-disabled{color:hsla(0,0%,100%,.5)}.selected-bg{color:#f4fbfd}.score{color:#ffc107}.body{color:rgba(0,0,0,.87)}.body-bg{color:#eee}.primary-bg{background-color:#1c8ca8}.accent-bg{background-color:#f05843}.accent-1-bg{background-color:#795c3a}.accent-2-bg{background-color:#cad266}.warn-bg{background-color:#c62828}.info-bg{background-color:#ef6c00}.success-bg{background-color:#00c853}.divider-bg{background-color:rgba(0,0,0,.12)}.gray-lightest-bg{background-color:#f7f7f7}.gray-lighter-bg{background-color:#eee}.gray-light-bg{background-color:#ddd}.gray-bg{background-color:#ccc}.gray-dark-bg{background-color:#aaa}.gray-darker-bg{background-color:#757575}.gray-darkest-bg{background-color:#333}.text-bg{background-color:rgba(0,0,0,.87)}.text-secondary-bg{background-color:rgba(0,0,0,.54)}.text-disabled-bg{background-color:rgba(0,0,0,.26)}.text-light-bg{background-color:#fff}.text-light-secondary-bg{background-color:hsla(0,0%,100%,.7)}.text-light-disabled-bg{background-color:hsla(0,0%,100%,.5)}.selected-bg-bg{background-color:#f4fbfd}.score-bg{background-color:#ffc107}.body-bg{background-color:rgba(0,0,0,.87)}.body-bg-bg{background-color:#eee}md-progress-circular.primary path{stroke:#1c8ca8}md-progress-circular.accent path{stroke:#f05843}md-progress-circular.accent-1 path{stroke:#795c3a}md-progress-circular.accent-2 path{stroke:#cad266}md-progress-circular.warn path{stroke:#c62828}md-progress-circular.info path{stroke:#ef6c00}md-progress-circular.success path{stroke:#00c853}md-progress-circular.divider path{stroke:rgba(0,0,0,.12)}md-progress-circular.gray-lightest path{stroke:#f7f7f7}md-progress-circular.gray-lighter path{stroke:#eee}md-progress-circular.gray-light path{stroke:#ddd}md-progress-circular.gray path{stroke:#ccc}md-progress-circular.gray-dark path{stroke:#aaa}md-progress-circular.gray-darker path{stroke:#757575}md-progress-circular.gray-darkest path{stroke:#333}md-progress-circular.text path{stroke:rgba(0,0,0,.87)}md-progress-circular.text-secondary path{stroke:rgba(0,0,0,.54)}md-progress-circular.text-disabled path{stroke:rgba(0,0,0,.26)}md-progress-circular.text-light path{stroke:#fff}md-progress-circular.text-light-secondary path{stroke:hsla(0,0%,100%,.7)}md-progress-circular.text-light-disabled path{stroke:hsla(0,0%,100%,.5)}md-progress-circular.selected-bg path{stroke:#f4fbfd}md-progress-circular.score path{stroke:#ffc107}md-progress-circular.body path{stroke:rgba(0,0,0,.87)}md-progress-circular.body-bg path{stroke:#eee}.l-constrained{margin-left:auto;margin-right:auto;max-width:100%;position:relative}@media (min-width:600px){.l-constrained{width:1280px}}.l-constrained-md{width:960px;max-width:100%}.l-footer{position:fixed;bottom:0;left:0;right:0;z-index:1;background-color:#fff;border-top:1px solid #eee}.button--footer{margin:0;padding-top:0;padding-bottom:0;min-width:0;display:flex}.button--footer__element{padding-left:8px}.l-header{z-index:3}.l-header .logo{margin-left:0!important;height:36px;width:36px;vertical-align:middle}.l-header .logo-link{min-width:auto;display:none;padding:0 4px;margin-right:12px}@media only screen and (min-width:600px){.l-header .logo-link{display:block}}.l-header .logo-link:focus,.l-header .logo-link:hover{border:0}@media only screen and (max-width:599px){.l-header .md-toolbar-tools h1,.l-header .md-toolbar-tools h2,.l-header .md-toolbar-tools h3{font-size:15px}}.l-main{background-color:#eee}.l-main--with-toolbar{margin-top:42px}#content{transition:margin-top .5s}.view-content{margin:0 auto;padding:8px;position:absolute;left:0;right:0;transition:opacity .5s}@media only screen and (min-width:960px){.view-content{padding:16px}}.view-content.ng-enter{opacity:0}.view-content .ng-enter-active{opacity:1;transition-delay:.25s}.view-content.ng-hide,.view-content.ng-hide-add,.view-content.ng-hide-add-active,.view-content.ng-hide-remove,.view-content.ng-hide-remove-active,.view-content.ng-leave-active{opacity:0}.view-content--with-sidemenu{padding:8px}@media only screen and (min-width:600px){.view-content--with-sidemenu{margin-left:54px;padding:16px}}@media only screen and (min-width:600px){[dir=rtl] .view-content--with-sidemenu{margin-left:auto;margin-right:54px}}.content-head{margin:8px 0}.content-head h1,.content-head h2,.content-head h3{font-weight:300;margin-top:0;margin-bottom:0;font-size:36px}@media only screen and (max-width:959px){.content-head h1,.content-head h2,.content-head h3{font-size:32px;text-align:center}}@media only screen and (max-width:959px){.content-head__more{margin-top:8px}}.content-head__item,h2.content-head__item{margin:0 8px}.content-head__item .md-subhead,h2.content-head__item .md-subhead{padding-left:4px}@media only screen and (max-width:959px){.content-head__item .md-subhead,h2.content-head__item .md-subhead{display:block;padding-left:0}}.content-head__item md-icon,h2.content-head__item md-icon{vertical-align:text-bottom}.stepSelectMenuContainer md-select-menu,.stepSelectMenuContainer md-select-menu md-content{max-height:500px}.l-nav{background-color:#eee!important}.l-node{margin-top:42px;padding:0}@media only screen and (min-width:600px){.l-node{padding:0 0 16px;background-color:#eee!important}}@media only screen and (min-width:960px){.l-node{padding:0 0 32px}}.l-notebook{margin-top:42px;background-color:#eee!important}.l-sidebar__header{background-color:#fff!important;color:#795c3a!important}.l-sidebar__header md-select{color:rgba(0,0,0,.87)}.status-icon{margin:0 4px;z-index:1;vertical-align:bottom}.md-button.status-icon{height:auto;width:auto;min-height:0;line-height:inherit;margin:0 4px;padding:0}.status-corner-wrapper{position:absolute;z-index:1;overflow:hidden}.status-corner{width:0;height:0;border-top-color:rgba(0,0,0,.26);border-bottom-color:rgba(0,0,0,.26);color:rgba(0,0,0,.26)}.status-corner:after{content:"!";color:#fff;top:2px;right:10px;position:absolute;font-weight:700}.status-corner--warn{border-top-color:#c62828!important;border-bottom-color:#c62828!important}.status-corner-top-right{top:0;right:0;border-top-right-radius:4px}.status-corner-top-right .status-corner{border-top:36px solid;border-left:36px solid transparent}.status-corner-top-left{top:0;left:0;border-top-left-radius:4px}.status-corner-top-left .status-corner{border-top:36px solid;border-right:36px solid transparent}.status-corner-bottom-right{bottom:0;right:0;border-bottom-right-radius:4px}.status-corner-bottom-right .status-corner{border-bottom:36px solid;border-left:36px solid transparent}.status-corner-bottom-left{bottom:0;left:0;border-bottom-left-radius:4px}.status-corner-bottom-left .status-corner{border-bottom:36px solid;border-right:36px solid transparent}.avatar--icon--alert{background-color:#fff}.avatar--icon--alert__icon{font-size:48px;margin:-4px 0 0 -4px}.gu-mirror{position:fixed!important;margin:0!important;z-index:9999!important;opacity:.8;transition:opacity .25s ease-in-out}.gu-hide{display:none!important}.gu-unselectable{-webkit-user-select:none!important;-moz-user-select:none!important;-ms-user-select:none!important;user-select:none!important}.gu-transit{opacity:.2}md-dialog{width:600px}.dialog--wide{width:960px}.dialog--wider{width:1280px}.help-bubble{border-radius:4px;max-width:320px}@media (min-width:600px){.help-bubble{max-width:552px}}@media (min-width:960px){.help-bubble{max-width:912px}}@media (min-width:1280px){.help-bubble{max-width:1232px}}.help-bubble___title__content,.help-bubble__title{border-top-left-radius:4px;border-top-right-radius:4px}.help-bubble___title__content{padding:0 0 0 12px;background-color:#ef6c00}.help-bubble___title__content .md-icon-button{margin-right:0;padding-top:0;padding-bottom:0}.help-bubble__content{overflow:auto;padding:8px 12px;max-height:480px}.help-bubble__actions{border-bottom-left-radius:4px;border-bottom-right-radius:4px}div.hopscotch-bubble{border-radius:4px;border:0;font-family:Roboto,Helvetica Neue,sans-serif;font-size:14px;z-index:6}div.hopscotch-bubble .hopscotch-bubble-arrow-container{position:absolute;width:20px;height:20px}div.hopscotch-bubble .hopscotch-bubble-arrow-container.up{top:0;left:12px}div.hopscotch-bubble .hopscotch-bubble-arrow-container.up .hopscotch-bubble-arrow{border-bottom:10px solid #ef6c00;border-left:10px solid transparent;border-right:10px solid transparent;position:relative;top:-10px}div.hopscotch-bubble .hopscotch-bubble-arrow-container.up .hopscotch-bubble-arrow-border{border:0}div.hopscotch-bubble .hopscotch-bubble-arrow-container.down{bottom:-34px;left:12px}div.hopscotch-bubble .hopscotch-bubble-arrow-container.down .hopscotch-bubble-arrow{border-top:10px solid #ef6c00;border-left:10px solid transparent;border-right:10px solid transparent;position:relative;top:-10px}div.hopscotch-bubble .hopscotch-bubble-arrow-container.down .hopscotch-bubble-arrow-border{border:0}div.hopscotch-bubble .hopscotch-bubble-arrow-container.left{top:12px;left:-10px}div.hopscotch-bubble .hopscotch-bubble-arrow-container.left .hopscotch-bubble-arrow{border-right:10px solid #ef6c00;border-bottom:10px solid transparent;border-top:10px solid transparent;position:relative;left:0;top:-10px}div.hopscotch-bubble .hopscotch-bubble-arrow-container.left .hopscotch-bubble-arrow-border{border:0}div.hopscotch-bubble .hopscotch-bubble-arrow-container.right{top:12px;right:-30px}div.hopscotch-bubble .hopscotch-bubble-arrow-container.right .hopscotch-bubble-arrow{border-left:10px solid #ef6c00;border-bottom:10px solid transparent;border-top:10px solid transparent;position:relative;right:0;top:-10px}div.hopscotch-bubble .hopscotch-bubble-arrow-container.right .hopscotch-bubble-arrow-border{border:0}.input-container{padding-top:12px}.input-container--component{margin-bottom:0}.input-container--open-response.md-has-icon{padding-left:0}.input-container--open-response .md-errors-spacer{display:none}.input-wrapper{position:relative}.input-wrapper--focused .input--textarea__action md-icon{color:#1c8ca8}.input--textarea,.input-container textarea.input--textarea{padding:8px;background-color:#f7f7f7;border:1px solid #ccc;margin-bottom:8px}.input--textarea:focus,.input-container textarea.input--textarea:focus{background-color:#fff}.input--textarea[disabled],.input-container textarea.input--textarea[disabled]{color:rgba(0,0,0,.54)}.input-container textarea.input--textarea{width:100%}.input--textarea--disabled{color:rgba(0,0,0,.54)}.input--textarea__action{position:absolute;right:-4px}.input--textarea__action[disabled] md-icon{color:rgba(0,0,0,.26)!important}.input--textarea__action--notebook{top:6px}.input-wrapper--richtext .input--textarea__action--notebook{top:-7px}.input--textarea__action--revision{bottom:6px}.input-wrapper--richtext .input--textarea__action--revision{bottom:-5px}.input-label,md-input-container:not(.md-input-invalid):not(.md-input-focused).md-input-has-value label.input-label{line-height:1.2;color:rgba(0,0,0,.87)}.input-label.input-label--focused,md-input-container:not(.md-input-invalid):not(.md-input-focused).md-input-has-value label.input-label.input-label--focused{color:#1c8ca8}.autocomplete input{text-overflow:ellipsis;overflow:hidden;word-wrap:none;font-weight:500;color:rgba(0,0,0,.54)}@media only screen and (min-width:600px){.autocomplete--minwidth{min-width:300px}}@media only screen and (min-width:960px){.autocomplete--minwidth{min-width:300px}}.autocomplete--flat md-autocomplete-wrap{background-color:#fff}.autocomplete--flat md-autocomplete-wrap:not(.md-menu-showing){box-shadow:none;background-color:#eee}.select__header{height:48px}.select__header input{height:100%;width:100%;padding:0 8px;outline:none;border:0;font-size:14px;font-weight:500}.table{max-width:100%;width:auto;min-width:100px;margin:8px 0}.table tbody>tr>td,.table tbody>tr>th,.table tfoot>tr>td,.table tfoot>tr>th,.table thead>tr>td,.table thead>tr>th{border:1px solid #ccc;padding:6px;font-size:15px;min-height:32px;height:32px;min-width:32px;vertical-align:top}.table td.inactive,.table th{background-color:#f7f7f7;opacity:1;visibility:visible}.table md-input-container{margin:0}.table .md-errors-spacer{display:none}.table--student td.inactive{padding:8px 10px}.table--full-width{width:100%}.table--list{border:0;border-collapse:collapse;background-color:#fff;max-width:100%;overflow:auto}.table--list td,.table--list th{padding:0 4px;border:0}.table--list td{min-height:56px;height:56px}.table--list tr.md-button{display:table-row;text-align:left;width:auto;text-transform:none;font-size:inherit;font-weight:400}.table--list__wrap{min-width:600px}@media only screen and (max-width:959px){.table-wrap-sticky{overflow-x:auto}}.table--list__thead{font-size:14px;font-weight:700}.table--list__thead__tr{height:100%;margin:0}.table--list__thead__th{background-color:#757575;color:#fff;min-height:42px;height:42px}.table--list__thead__link{color:#fff;text-transform:none;margin:0;min-width:0;white-space:normal;line-height:1.4;width:100%}.table--list__thead__sort{margin:0}.table--list__thead__sort--reverse{transform:rotate(180deg)}.td--wrap{min-width:180px;white-space:normal;line-height:1.2}@media only screen and (max-width:959px){.td--max-width{max-width:180px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}}.md-toolbar-tools{font-size:18px}.md-toolbar-tools .autocomplete,.md-toolbar-tools .autocomplete input,.md-toolbar-tools .autocomplete md-autocomplete-wrap{height:36px}.md-toolbar--wise{min-height:42px}.md-toolbar--wise .md-toolbar-tools{height:42px;max-height:42px}.md-toolbar--wise .md-button.md-icon-button{height:42px;line-height:42px;width:42px}.md-toolbar--wise--sm .md-toolbar-tools{padding:0 8px;font-size:15px}.md-toolbar--sidenav{background-color:#333!important}.md-toolbar--sidenav .md-toolbar-tools{font-size:16px;font-weight:500}.toolbar{position:fixed;left:0;right:0;top:52px;z-index:3}.toolbar__title{margin-left:8px;font-size:16px;font-weight:500}.toolbar__tools{padding-right:8px}[dir=rtl] .toolbar__tools{padding-right:16px;padding-left:8px}.md-button.toolbar__nav,.toolbar__nav{margin:0}.md-button.toolbar__select,.toolbar__select{margin:0 4px;min-height:32px;background-color:#f7f7f7}.md-button.toolbar__select .md-select-value,.toolbar__select .md-select-value{height:32px;text-align:left}[dir=rtl] .md-button.toolbar__select .md-select-value,[dir=rtl] .toolbar__select .md-select-value{text-align:right}.toolbar__select--fixedwidth{width:168px}@media only screen and (min-width:600px){.toolbar__select--fixedwidth{width:264px}}@media only screen and (min-width:960px){.toolbar__select--fixedwidth{width:432px}}.list-item{background-color:#fff;border-bottom:1px solid #eee}.list-item.md-subheader,.list-item .md-subheader{color:rgba(0,0,0,.87);background-color:#fff}.list-item.md-subheader md-icon,.list-item .md-subheader md-icon{vertical-align:middle}.list-item.md-subheader .md-subheader-inner,.list-item .md-subheader .md-subheader-inner{padding:0}.list-item.md-subheader .md-avatar,.list-item .md-subheader .md-avatar{margin-right:8px}.list-item .autocomplete{margin:8px 0}.list-item--info._md-button-wrap>div.md-button:first-child,.list-item--info .md-subheader-content{border-left:4px solid #ef6c00!important;margin-left:-4px}.list-item--warn._md-button-wrap>div.md-button:first-child,.list-item--warn .md-subheader-content{border-left:4px solid #c62828!important;margin-left:-4px}.list-item--expanded{border-bottom-width:0}.list-item--noclick,.list-item--noclick.md-button{cursor:default;background-color:#f7f7f7}.list-item--actions{padding:0 8px!important}.list-item__subheader-button{text-transform:none;width:100%;padding:8px 16px;margin:0;white-space:normal;text-align:left;line-height:1.4}.user-list{font-size:15px}#nav{position:relative}.nav{margin-bottom:16px}.nav-mask{position:absolute;top:0;bottom:0;left:0;right:0;background-color:rgba(0,0,0,.25);z-index:1}.nav-mask.ng-hide{opacity:0}.nav-head{color:rgba(0,0,0,.54);font-weight:500}.nav-head md-icon{line-height:20px}.nav-contents--root{padding:6px 6px 12px}.nav-contents--group{background-color:#ddd;padding:8px}.nav-contents--root,.nav-contents__list{padding:0}@media (min-width:600px){.nav-contents__list{padding:8px}}.nav-item{transition:opacity .25s ease-in-out}.nav-item.prev md-list-item{background-color:#f4fbfd}.nav-item--card__content{border-top-right-radius:4px;border-top-left-radius:4px}.nav-item--card__content:focus{outline:none}.nav-item--card__content:focus .nav-item__title>span{border-bottom:1px dashed #ccc}.nav-item--root{transition:margin .25s,box-shadow .5s}.nav-item--root.expanded{flex-basis:100%;max-width:100%;max-height:none!important;margin:8px auto;padding-left:4px}.nav-item--root.expanded:first-of-type{margin-top:0}.nav-item--list__info-item{padding:0 16px 0 4px;display:inline-block}.nav-item--list__reorder{margin-left:8px;color:rgba(0,0,0,.26)}.nav-item--card--group:not(.expanded){box-shadow:0 3px 1px -2px rgba(0,0,0,.14),0 2px 2px 0 rgba(0,0,0,.098),0 1px 5px 0 rgba(0,0,0,.084),3px 3px 0 1px #d5d5d5,6px 6px 0 1px #aaa}.nav-item__collapse{margin:0}.nav-item__more{border-top:1px solid #ddd;border-bottom-right-radius:4px;border-bottom-left-radius:4px;padding:8px 16px;min-height:40px}.nav-item__users{height:auto;cursor:pointer;color:#fff;margin:0;padding:1px 6px}.nav-item__users>md-icon{padding-right:4px;color:#fff}.nav-item__users:focus.success-bg,.nav-item__users:hover.success-bg{background-color:#00c853}.nav-item__title{padding-left:16px;line-height:1.2;font-weight:400}[dir=rtl] .nav-item__title{padding-left:auto;padding-right:16px}.nav-item__info{padding:0 8px}.nav-item__progress{width:48px}.nav-item__progress>.md-container{top:0}.nav-item__progress-value{margin-left:8px;width:36px}.progress-wrapper{padding:2px 0;cursor:pointer}.menu-progress{position:absolute;top:10px;right:12px}.menu-progress path{stroke:#cad266!important;stroke-width:2px}[dir=rtl] .menu-progress{right:auto;left:12px}.menu-sidenav__item{font-weight:700;font-size:14px}.menu-sidenav__icon{margin-top:12px!important;margin-right:12px!important;margin-left:12px}.active .menu-sidenav__icon,.active .menu-sidenav__item{color:#1c8ca8}.menu-sidebar{position:absolute;top:94px;bottom:0;left:0;background-color:#fff;width:56px;overflow:hidden;padding:8px 0;text-align:center;border-right:1px solid #ccc}@media only screen and (max-width:599px){.menu-sidebar{display:none}}[dir=rtl] .menu-sidebar{right:0;left:auto}.md-button.md-icon-button.menu-sidebar__link{margin-top:6px;margin-bottom:6px}.notice{text-align:center;padding:8px;background-color:rgba(0,0,0,.04);width:100%}@media (min-width:600px){.notice{max-width:80%;border-radius:3px;margin:24px auto}}#node{margin:0 auto;position:absolute;left:0;right:0}@media only screen and (min-width:600px){#node{padding:24px 16px;margin-bottom:32px}}@media only screen and (min-width:960px){#node{padding:32px}}#node.ng-enter{transition:opacity .5s;opacity:0}#node.ng-enter-active{opacity:1}@media only screen and (min-width:600px){.node-notice{margin-top:-8px;margin-bottom:16px}}@media only screen and (min-width:960px){.node-notice{margin-top:-16px}}.node-content{padding:0 0 48px;background-color:#fff;border-radius:3px;overflow:visible}@media only screen and (max-width:599px){.node-content{box-shadow:none}}@media only screen and (min-width:600px){.node-content{padding:0;border-top:2px solid;border-bottom:2px solid}}md-content.node-content{background-color:#fff}.node-content__rubric{position:absolute;top:-22px;left:0;right:0;z-index:1}.node-content__rubric .avatar--icon{transform:scale(.94)}@media only screen and (max-width:599px){.node-content__rubric .avatar--icon{transform:scale(.8)}}.node-icon{color:#fff;vertical-align:inherit}.node-select{margin:0 8px;min-width:0;font-weight:500;font-size:15px}.node-select .md-select-value :first-child{transform:translateZ(0);flex:1 0 0}.node-select .md-select-value .node-select__icon,.node-select .md-select-value .node-select__status{display:none}.node-select .md-select-icon{margin-left:0;color:rgba(0,0,0,.87)}.node-select-option--group{background-color:#f7f7f7;border-bottom:1px solid #eee;border-top:1px solid #eee}.node-select-option--node{padding-left:20px}.node-select__icon{margin-right:8px}.node-select__status{margin-left:8px}.node-select__text{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}@media only screen and (min-width:600px){.node-select__text{margin-top:2px}}.node-title{line-height:1.2;text-transform:none;margin-top:3px}@media only screen and (max-width:599px){.node-title{font-size:15px}}.node-content__actions{padding:0 16px 16px}@media only screen and (min-width:960px){.node-content__actions{padding:0 24px 24px}}@media only screen and (min-width:1280px){.node-content__actions{padding:0 32px 32px}}.node-content__actions .md-button:first-child{margin-left:0}.node-content__actions .md-button:last-child{margin-right:0}.node-content__actions__info{font-style:italic;margin-left:8px;color:rgba(0,0,0,.54)}.node-content__actions__more{border-bottom:1px dotted}.md-button.md-icon-button.node-nav:first-of-type{margin-right:0}@media only screen and (min-width:600px){.node-sidebar-active{margin-right:68px}}@media only screen and (max-width:599px){.node-sidebar-visible{margin-bottom:42px}}.node-sidebar{position:absolute;right:0;top:0;width:52px}.node-sidebar__toolbar{position:fixed;width:52px;background-color:#fff;padding:8px 0;border-radius:3px}@media only screen and (max-width:599px){.node-sidebar__toolbar{right:0;bottom:0;left:0;width:100%;border-radius:0;padding:0;min-height:0;height:42px}}.node-info{margin:0}@media only screen and (max-width:599px){.node-info{margin:-16px;border-radius:0}}.node-info .divider{margin-left:-8px;margin-right:-8px}.node-info .component:first-child{margin-top:-16px}.node-info .component,.node-info .component__content,.node-info .component__header{margin-left:-8px;margin-right:-8px}.node-info .component__actions{display:none}.node-rubric{border:2px solid #1c8ca8;margin:8px 16px 24px;padding:16px;background-color:#fff}.node__label--vertical-alignment{vertical-align:middle;display:inline-block}@media only screen and (min-width:600px){.notebook-launcher.md-button.md-fab{z-index:61}}.notebook-report{border-radius:4px 4px 0 0;margin:0;height:100%}.notebook-report .note-toolbar{margin:-8px -8px 8px;padding:4px;border-bottom:0;border-top:0;border-color:#ddd currentcolor;border-style:solid none;border-width:1px 0;border-radius:0}.notebook-report .note-toolbar .btn-group{margin-top:0}.notebook-report .note-btn{border:0;border-top-left-radius:0;border-top-right-radius:0;border-bottom-left-radius:0;border-bottom-right-radius:0}.notebook-report-container{height:550px;width:450px;max-height:90%;bottom:0;right:96px;position:absolute;z-index:3}@media only screen and (min-width:960px){.notes-visible .notebook-report-container{right:516px;transition:right .25s}}.notebook-report-container__full{top:16px;bottom:16px;left:16px;right:16px;max-height:none;max-width:none;height:auto;width:auto}.notebook-report-container__full .notebook-report{height:100%;width:100%;margin:0 auto;max-height:none;border-radius:4px}.notebook-report-container__collapsed{width:300px;height:auto}.notebook-report-container__collapsed .notebook-report__actions,.notebook-report-container__collapsed .notebook-report__content,.notebook-report-container__collapsed .notebook-report__content__header{display:none}.notebook-report__toolbar{background-color:#333!important;border-radius:4px 4px 0 0}.notebook-report__toolbar__title{max-width:150px}.notebook-report__content{background-color:#fff}.notebook-report__content h1,.notebook-report__content h2,.notebook-report__content h3,.notebook-report__content h4{font-size:22px}.notebook-report__content .note-editor.note-frame{border:0;border-radius:0;padding:0;box-shadow:none}.notebook-report__content .note-resizebar{display:none}.notebook-report__content__header{padding:8px;background-color:#fff;font-size:16px}@media only screen and (max-width:599px){.notebook-report-container:not(.notebook-report-container__collapsed){top:0;bottom:0;left:0;right:0;max-height:none;max-width:none;height:auto;width:auto}.notebook-report-container:not(.notebook-report-container__collapsed) .notebook-report{border-radius:0}.notebook-tools--full{display:none}}.notebook-report-backdrop{position:fixed;top:0;left:0;width:100%;height:100%;z-index:3;background-color:#212121;opacity:.48}.notebook-menu{transition:opacity .5s}.notebook-menu.ng-enter{opacity:0}.notebook-menu .ng-enter-active{opacity:1;transition-delay:.25s}.notebook-menu.ng-hide,.notebook-menu.ng-hide-add,.notebook-menu.ng-hide-add-active,.notebook-menu.ng-hide-remove,.notebook-menu.ng-hide-remove-active,.notebook-menu.ng-leave-active{opacity:0}.notebook-item{transition:box-shadow .25s;margin:0 16px 16px;display:block}.notebook-item__content{height:250px;min-width:230px;position:relative;padding:0;background-color:#ccc;border-top-left-radius:4px;border-top-right-radius:4px}.notebook-item__content__attachment,.notebook-item__content__text{position:absolute;left:0;right:0}.notebook-item__content__attachment{background-repeat:no-repeat!important;border-top-left-radius:4px;border-top-right-radius:4px;background-position:top!important;background-size:cover!important;top:0;bottom:0}.notebook-item__content__text{bottom:0;padding:8px;font-weight:500;overflow:hidden;max-height:120px;min-height:56px;background-color:hsla(0,0%,100%,.95);border-top:1px solid #eee}.notebook-item__content__text:after{content:"";text-align:right;position:absolute;bottom:0;right:0;width:100%;height:.8em;background:linear-gradient(180deg,hsla(0,0%,100%,0),hsla(0,0%,100%,.95) 100%)}.notebook-item__content--text-only:after{content:"note";font-family:Material Icons;font-weight:400;font-style:normal;display:inline-block;line-height:1;text-transform:none;letter-spacing:normal;word-wrap:normal;white-space:nowrap;direction:ltr;-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;-moz-osx-font-smoothing:grayscale;font-feature-settings:"liga";font-size:80px;color:rgba(0,0,0,.26)}.notebook-item--question__content--text-only{content:"live_help"}.notebook-item__content__location{opacity:.9;padding:8px 0}.notebook-item__content__location md-icon{font-size:22px}.notebook-item__edit{cursor:pointer}.notebook-item__actions{margin:0;padding:0 8px;color:#fff;background-color:#333;border-bottom-left-radius:4px;border-bottom-right-radius:4px}.notebook-item__actions md-icon{color:#fff}.notebook-item__text-input{margin:0}.notebook-item__text-input__textarea{padding-left:0;padding-right:0}.notebook-item__attachment{background-color:#ddd;padding:16px;margin-bottom:16px;text-align:center;position:relative}.notebook-item__attachment__content{max-width:100%;height:auto}.notebook-item__attachment__delete{position:absolute;top:4px;right:-2px;width:34px!important;height:34px!important;min-height:0}.notebook-item__attachment__delete md-icon{margin-left:-2px;font-size:22px}.notebook-item__info{font-style:italic;opacity:.8;color:#8a6942}.notebook-item__info a,.notebook-item__info md-icon{color:#8a6942}.notebook-item__info md-icon{font-size:1.5em;min-width:0;width:auto}.notebook-item__upload{text-align:center;padding:24px;background-color:#eee;margin-bottom:16px;color:rgba(0,0,0,.54);border-radius:4px;cursor:pointer;border:1px dashed transparent;transition:all .25s}.notebook-item__upload md-icon,.notebook-item__upload span{transition:color .25s}.notebook-item__upload.dragover,.notebook-item__upload:focus,.notebook-item__upload:hover{border-color:#f05843;background-color:#fdebe8;color:#f05843}.notebook-item__upload.dragover md-icon,.notebook-item__upload.dragover span,.notebook-item__upload:focus md-icon,.notebook-item__upload:focus span,.notebook-item__upload:hover md-icon,.notebook-item__upload:hover span{color:#f05843}.view-notebook-item{width:600px}.notebook-item--report{background-color:#fff}.notebook-item--report .note-editor{margin-bottom:16px;border-color:#ccc}.notebook-item--report__container.ui-scrollpoint .notebook-item--report__toolbar{position:fixed;top:94px;left:0;right:0;z-index:1}@media only screen and (min-width:600px){.notebook-item--report__container.ui-scrollpoint .notebook-item--report__toolbar{left:54px;padding:0 24px}}@media only screen and (min-width:960px){.notebook-item--report__container.ui-scrollpoint .notebook-item--report__toolbar{padding:0 32px}}.notebook-item--report__container.ui-scrollpoint .notebook-item--report__toolbar .note-toolbar{margin:0 16px}@media only screen and (min-width:600px){.notebook-item--report__container.ui-scrollpoint .notebook-item--report__toolbar .note-toolbar{margin:0 8px}}@media only screen and (min-width:960px){.notebook-item--report__container.ui-scrollpoint .notebook-item--report__toolbar .note-toolbar{margin:0 16px}}.notebook-item--report__container.ui-scrollpoint .note-editor{padding-top:40px}.notebook-item--report__toolbar .note-toolbar{background-color:#ddd;border:1px solid #ccc;margin-bottom:-2px}.notebook-item--report__heading{text-align:center;margin-bottom:32px}.notebook-item--report__add-note{font-weight:700}.notebook-item--report__note-img{max-width:100%;height:auto!important}@media only screen and (min-width:600px){.notebook-sidebar{width:400px;max-width:none}}@media only screen and (min-width:960px){.notebook-sidebar{width:500px;max-width:none}}.notebook-items{width:100%;overflow:auto;margin-top:16px;margin-bottom:76px}.notebook-items .notebook-item{width:100%}.notebook-items .notebook-item__content{height:200px;min-width:0}.notebook-items--grading{margin-bottom:0}@media only screen and (max-width:599px){.notebook-enabled .md-fab-bottom-left,.notebook-enabled .md-fab-bottom-right{bottom:50px!important}}.notebook-grading{background-color:#fff;display:block}.notification-btn{width:60px!important}.notification-btn md-icon{margin-left:20px}.notification-count{border-radius:50%;position:absolute;background-color:#f05843;width:22px;left:4px;height:22px;line-height:18px;font-size:12px;font-weight:700;border:2px solid}.notification-count:before{content:"";position:absolute;right:-7px;top:5px;border-left:6px solid hsla(0,0%,100%,.87);border-top:4px solid transparent;border-bottom:4px solid transparent}.notification-list{padding:8px 0}.notification-dismiss{width:500px}.notification-dismiss__input{margin-bottom:0}md-list md-list-item.md-2-line .md-list-item-text h4.notification-list-item__source,md-list md-list-item.md-3-line .md-list-item-text h4.notification-list-item__source,md-list md-list-item .md-list-item-text h4.notification-list-item__source{color:rgba(0,0,0,.54);font-size:12px}md-list md-list-item.md-2-line .md-list-item-text h4.notification-list-item__source md-icon,md-list md-list-item.md-3-line .md-list-item-text h4.notification-list-item__source md-icon,md-list md-list-item .md-list-item-text h4.notification-list-item__source md-icon{font-size:18px;min-width:0;width:auto;margin-left:-4px;line-height:20px}.account-menu{border-radius:4px;padding:0;font-size:15px;max-width:380px}@media (min-width:1280px){.account-menu{min-width:380px!important}}.account-menu h3{margin:0;font-weight:300}.account-menu--fixed-height{height:304px}.account-menu--fixed-width{width:320px}@media (min-width:960px){.account-menu--fixed-width{width:380px}}.account-menu__icon{background-color:#fff;border-radius:50%}.account-menu__caret{position:absolute;right:28px;top:-8px;outline:none}.account-menu__caret:before{content:"";position:absolute;border-bottom:8px solid #fff;border-left:8px solid transparent;border-right:8px solid transparent}.account-menu__caret--notification,.account-menu__caret--pause{right:80px}.account-menu__caret--notification--with-pause{right:132px}[dir=rtl] .account-menu__caret{right:auto;left:28px}[dir=rtl] .account-menu__caret--notification,[dir=rtl] .account-menu__caret--pause{left:80px;right:auto}[dir=rtl] .account-menu__caret--notification--with-pause{left:132px;right:auto}.account-menu__info{padding:8px 12px}.account-menu__info__title{font-weight:500}.account-menu__info__team{font-weight:400;color:rgba(0,0,0,.54)}.account-menu__users,.account-menu__users md-list-item{padding:0}.account-menu__users md-list-item .md-avatar{margin:0 8px 0 0;height:48px;width:48px}.account-menu__progress md-progress-linear{transform:rotate(270deg);width:26px}.account-menu__grade{position:relative;margin-right:4px}.account-menu__grade md-icon{color:#ffc107}.account-menu__grade__overlay{position:absolute;top:0;width:100%;background-color:hsla(0,0%,100%,.6)}.account-menu__actions{background-color:#f7f7f7}.account-menu__control{padding:16px}.annotations{margin:16px 4px 16px 62px;position:relative;font-size:15px}.annotations hr{margin:10px 0 8px;border-color:rgba(0,0,0,.12)}.annotations:after{content:"";position:absolute;width:0;height:0;left:-16px;right:auto;top:0;bottom:auto;border-top:20px solid transparent;border-bottom:20px solid transparent;border-right:16px solid #757575}.annotations-container--student--report{border-top:1px solid #ddd}.annotations--report{margin-top:0;margin-bottom:0}.annotations__header{position:relative;border-top-right-radius:4px;padding:10px 12px;font-weight:700;transition:all 1s;color:#fff;background-color:#757575}.annotations__avatar{background-color:#f05843;padding:2px;position:absolute;top:0;left:-62px}.annotations__icon{transition:all 1s;color:#fff}.annotations__body{padding:12px;background-color:#fff;border-bottom-left-radius:4px;border-bottom-right-radius:4px;overflow:auto}.annotations__status{background-color:#fff;color:#ef6c00;display:inline-block;margin-left:8px;font-size:12px}.annotations__status.ng-enter,.annotations__status.ng-leave{transition:all 1s}.annotations__status.ng-enter,.annotations__status.ng-leave.ng-leave-active{opacity:0}.annotations__status.ng-enter.ng-enter-active,.annotations__status.ng-leave{opacity:1}.annotations__score{font-weight:700}.annotations__info{font-style:italic;opacity:.8;border-bottom:1px dotted;font-size:13px}.annotations--inside .annotations{margin-left:72px}.annotations--info{margin-bottom:32px;margin-right:8px;margin-left:72px}@media only screen and (min-width:600px){.annotations--info{margin:16px 16px 32px 76px}}.annotations--info:after{border-right:16px solid #ef6c00}.annotations--info .annotations__avatar{background-color:#fff}.annotations--info .annotations__header{background-color:#ef6c00}.component{position:relative}.component__wrapper{padding:0 16px;margin:24px 0}.component__content{overflow-x:auto;font-size:15px;overflow-y:hidden}@media only screen and (min-width:600px){.component__content{padding:0 8px}}.component-header,h3.component__header{padding:8px 12px;margin:0;font-size:14px}.component__rubric{position:absolute;left:-20px;top:12px}.notebook-enabled .component_content img{transition:all .25s;cursor:pointer;cursor:copy}.notebook-enabled .component_content img:focus,.notebook-enabled .component_content img:hover{box-shadow:0 0 5px 1px #f05843}.component__actions .md-button:first-child{margin-left:0}.component__actions .md-button:last-child{margin-right:0}.component__actions__info{font-style:italic;margin-left:8px;color:rgba(0,0,0,.54)}.component__actions__more{border-bottom:1px dotted}.component__prompt{margin-bottom:8px;font-weight:500}.component__prompt__content{display:inline}.component__attachment{position:relative;margin:0 8px;padding-bottom:8px}@media only screen and (min-width:600px){.component__attachment{padding-top:8px}}@media only screen and (max-width:599px){.component__add-attachment{width:100%}}.component__attachment__content{max-height:100px;width:auto}.component__attachment__delete{position:absolute;top:0;right:0;min-width:0;background-color:hsla(0,0%,100%,.75)!important;border-radius:0;padding:4px;margin:0}.component__attachment__delete>md-icon{margin-top:0}.component__revision{margin:8px 0;padding:8px}.component__revision:nth-child(odd){background-color:#f7f7f7}.component__revision__content{padding:4px 0 8px;border-bottom:1px solid #ddd}.component__revision__actions{color:#757575;padding-top:4px}.component__content--Discussion{overflow:hidden}.discussion-content{background-color:#eee;box-shadow:inset 0 0 3px #aaa}.discussion-posts{padding:12px 12px 8px}@media only screen and (min-width:1280px){.discussion-posts{padding:16px 16px 0}}.discussion-post{margin:0 auto 16px;max-width:600px}@media only screen and (min-width:600px){.discussion-post{margin-bottom:24px}}@media only screen and (min-width:1280px){.discussion-post{margin-bottom:32px}}.discussion-post md-divider{position:relative;width:auto}.discussion-post__contents{padding:16px}.discussion-post__avatar,md-list-item>.md-avatar.discussion-post__avatar{margin-right:8px}.discussion-post__avatar--reply,md-list-item>.md-avatar.discussion-post__avatar--reply{margin-top:8px}.discussion-post__user,md-list-item .md-list-item-text h3.discussion-post__user{padding-bottom:4px;font-weight:700;line-height:1.3;overflow:visible;white-space:normal}.discussion-post__date{color:#aaa}.discussion-post__date--reply{margin-left:8px;font-weight:400}.discussion-post__content{margin-top:16px;white-space:pre-wrap}.discussion-post__attachment{max-width:100%;height:auto!important;margin-top:16px}.discussion-new{background-color:#fff;max-width:570px;margin-left:auto;margin-right:auto;padding:8px;transition:all .25s;transform:scale(.95)}.discussion-new--focused{transform:scale(1)}md-input-container.discussion-new__input-container{margin:0;padding:0}md-input-container.discussion-new__input-container>textarea.md-input{min-height:68px}.discussion-new__input--textarea,.input-container textarea.discussion-new__input--textarea{padding:8px;border:0}.discussion-new__actions{padding:0 8px}.discussion-new__actions .md-button:first-of-type{margin-left:0}.discussion-new__actions .md-button:last-of-type{margin-right:0}.discussion-new__attachment{padding:0;margin:0 0 8px}.discussion-new__attachment__content{margin-top:0;margin-bottom:16px}.discussion-comments{padding:0}.discussion-comments__contents{background-color:#f7f7f7}.discussion-comments__header{background-color:transparent;padding:0}.discussion-comments__header .md-subheader-inner{padding-bottom:8px}.discussion-comments__list{padding:0;max-height:9999px;overflow-y:auto}@media only screen and (min-width:600px){.discussion-comments__list{max-height:400px}}.input--textarea.discussion-reply__input,.input-container textarea.input--textarea.discussion-reply__input{background-color:#fff;padding:4px;font-size:14px;border:0;margin-left:-1px;margin-bottom:0;resize:none}.discussion-reply,md-list-item.discussion-reply{margin:0 12px 8px;padding:0;min-height:56px}.discusstion-reply__details,md-list-item .md-list-item-text.discusstion-reply__details{margin:8px 0}.discussion-post__user--reply,md-list-item .md-list-item-text h3.discussion-post__user--reply{font-size:14px;padding:0;margin:0}.discusstion-reply__content{margin-top:2px}.discusstion-reply__content p{font-weight:400!important;color:rgba(0,0,0,.87)!important}.discussion-new-reply{padding:8px}.discussion-new-reply__input-container{padding-top:0;margin:0}.discussion-new-reply__input-container .md-errors-spacer{display:none}.discussion-new-reply__actions{margin-left:8px}.discussion-new-reply__actions .md-button{margin-top:0;margin-bottom:0}.embedded-content__iframe{border:0}.graph-select{min-width:150px;max-width:200px}.graph-controls{margin:8px 0;padding:8px 0;border-color:#eee;border-style:solid;border-width:1px 0}.match-content{background-color:#eee;margin-bottom:16px;padding:8px;box-shadow:inset 0 0 3px #aaa}.match-divider{margin:16px 8px 8px}@media only screen and (min-width:960px){.match-divider--horizontal{display:none}}.match-bucket__header{padding:12px;font-weight:500;color:#1c8ca8}.match-bucket__content{padding:0}.match-bucket--choices .match-bucket__header{color:#795c3a}.match-bucket__contents{min-height:120px;padding:0 8px 8px;background-color:#ddd;transition:background-color .25s;border-bottom-left-radius:4px;border-bottom-right-radius:4px;-moz-column-gap:8px;column-gap:8px}@media only screen and (max-width:599px){.match-bucket__contents{-moz-column-count:1!important;column-count:1!important}}.match-bucket__contents img{max-width:100%;height:auto}.match-bucket__contents--over{background-color:#1c8ca8}.match-bucket__item{list-style-type:none;cursor:move;padding-top:8px;-moz-column-break-inside:avoid;break-inside:avoid}.match-bucket__item__contents{background-color:#fff;padding:8px!important;border:1px solid #ccc}.match-bucket__item__contents .md-list-item-text{width:100%}.match-bucket__item__contents__text{margin-right:4px}.match-feedback{transition:opacity .25s;margin:8px -8px -8px;color:#fff;padding:4px 8px}.match-feedback.ng-hide{opacity:0;transition:opacity 1ms}.match-feedback md-icon{color:#fff}.outside-content iframe{border:1px solid #eee}.outside-content__source{margin-top:4px;text-align:end}.outside-content__source a{max-width:240px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;display:inline-block}.notebook-toolbar md-divider{margin:8px 0}@media only screen and (max-width:959px){.notebook-toolbar{border-top:1px solid #ddd}}.notebook-toolbar__add-menu{position:absolute;bottom:40px}.notebook-toolbar__add-menu .md-fab-action-item{background-color:#fff}.notebook-toolbar__add-icon{border-radius:50%}#closeNotebookSettingsButton{float:right}[dir=rtl] #closeNotebookSettingsButton{float:left}highchart{display:block} +body{background:#eee}body.vle{overflow:hidden}a:focus,a:hover{color:#1c8ca8}blockquote{background-color:#e7f7fb;padding:8px;margin:16px 0;border:solid #1c8ca8;border-width:0 0 0 3px}.has-indicator:after{content:"";position:absolute;border-radius:50%;padding:5px;background-color:#f05843}.has-indicator--icon-button:after{top:25px;left:5px}.badge{border-radius:4px;padding:2px 6px;font-size:12px;font-weight:500;font-style:normal;background-color:#aaa}.badge.md-button{min-width:0;min-height:0;line-height:inherit}.badge.md-button:focus,.badge.md-button:hover{background-color:#aaa}.badge.md-button:focus{outline:1px dotted #aaa}.badge--info{background-color:#ef6c00;color:#fff}.badge--warn{background-color:#c62828;color:#fff}.badge--success{background-color:#00c853;color:#fff}.divider--withmargin{margin:16px 0}.divider--dashed{border-top-style:dashed}a{color:#1c8ca8;cursor:pointer}.active{background-color:hsla(0,0%,62%,.2);color:rgba(0,0,0,.87)}.avatar{border-radius:50%;box-sizing:content-box}.avatar--square{border-radius:4px}.avatar.md-18{height:30px;width:30px}.avatar.md-24{height:36px;width:36px}.avatar.md-36{height:48px;width:48px}.avatar.md-48{height:60px;width:60px}.avatar--icon{background-color:#ddd;white-space:normal!important}.avatar--icon:not(.md-avatar){padding:6px}.avatar--icon.md-18{height:18px;width:18px}.avatar--icon.md-24{height:24px;width:24px}.avatar--icon.md-36{height:36px;width:36px}.avatar--icon.md-48{height:48px;width:48px}md-toolbar.md-light-theme:not(.md-menu-toolbar) md-icon{color:rgba(0,0,0,.54)}md-toolbar.md-light-theme:not(.md-menu-toolbar) .md-button:disabled md-icon{color:rgba(0,0,0,.26)}.md-button:not([disabled]).primary,md-icon.primary{color:#1c8ca8!important}.md-button:not([disabled]).success,md-icon.success{color:#00c853!important}.md-button:not([disabled]).warn,md-icon.warn{color:#c62828!important}.md-button:not([disabled]).info,md-icon.info{color:#ef6c00!important}.md-button:not([disabled]).accent,md-icon.accent{color:#f05843!important}.md-button:not([disabled]).accent-1,md-icon.accent-1{color:#795c3a!important}.md-button:not([disabled]).accent-2,md-icon.accent-2{color:#cad266!important}md-input-container.md-wise-theme label{color:rgba(0,0,0,.87)}md-select-menu.md-default-theme md-option[selected],md-select-menu md-option[selected]{background-color:#f4fbfd}.md-autocomplete-suggestions-container.md-default-theme li .highlight,.md-autocomplete-suggestions-container li .highlight{color:#1c8ca8;background-color:#f4fbfd}.primary{color:#1c8ca8}.accent{color:#f05843}.accent-1{color:#795c3a}.accent-2{color:#cad266}.warn{color:#c62828}.info{color:#ef6c00}.success{color:#00c853}.divider{color:rgba(0,0,0,.12)}.gray-lightest{color:#f7f7f7}.gray-lighter{color:#eee}.gray-light{color:#ddd}.gray{color:#ccc}.gray-dark{color:#aaa}.gray-darker{color:#757575}.gray-darkest{color:#333}.text{color:rgba(0,0,0,.87)}.text-secondary{color:rgba(0,0,0,.54)}.text-disabled{color:rgba(0,0,0,.26)}.text-light{color:#fff}.text-light-secondary{color:hsla(0,0%,100%,.7)}.text-light-disabled{color:hsla(0,0%,100%,.5)}.selected-bg{color:#f4fbfd}.score{color:#ffc107}.body{color:rgba(0,0,0,.87)}.body-bg{color:#eee}.primary-bg{background-color:#1c8ca8}.accent-bg{background-color:#f05843}.accent-1-bg{background-color:#795c3a}.accent-2-bg{background-color:#cad266}.warn-bg{background-color:#c62828}.info-bg{background-color:#ef6c00}.success-bg{background-color:#00c853}.divider-bg{background-color:rgba(0,0,0,.12)}.gray-lightest-bg{background-color:#f7f7f7}.gray-lighter-bg{background-color:#eee}.gray-light-bg{background-color:#ddd}.gray-bg{background-color:#ccc}.gray-dark-bg{background-color:#aaa}.gray-darker-bg{background-color:#757575}.gray-darkest-bg{background-color:#333}.text-bg{background-color:rgba(0,0,0,.87)}.text-secondary-bg{background-color:rgba(0,0,0,.54)}.text-disabled-bg{background-color:rgba(0,0,0,.26)}.text-light-bg{background-color:#fff}.text-light-secondary-bg{background-color:hsla(0,0%,100%,.7)}.text-light-disabled-bg{background-color:hsla(0,0%,100%,.5)}.selected-bg-bg{background-color:#f4fbfd}.score-bg{background-color:#ffc107}.body-bg{background-color:rgba(0,0,0,.87)}.body-bg-bg{background-color:#eee}md-progress-circular.primary path{stroke:#1c8ca8}md-progress-circular.accent path{stroke:#f05843}md-progress-circular.accent-1 path{stroke:#795c3a}md-progress-circular.accent-2 path{stroke:#cad266}md-progress-circular.warn path{stroke:#c62828}md-progress-circular.info path{stroke:#ef6c00}md-progress-circular.success path{stroke:#00c853}md-progress-circular.divider path{stroke:rgba(0,0,0,.12)}md-progress-circular.gray-lightest path{stroke:#f7f7f7}md-progress-circular.gray-lighter path{stroke:#eee}md-progress-circular.gray-light path{stroke:#ddd}md-progress-circular.gray path{stroke:#ccc}md-progress-circular.gray-dark path{stroke:#aaa}md-progress-circular.gray-darker path{stroke:#757575}md-progress-circular.gray-darkest path{stroke:#333}md-progress-circular.text path{stroke:rgba(0,0,0,.87)}md-progress-circular.text-secondary path{stroke:rgba(0,0,0,.54)}md-progress-circular.text-disabled path{stroke:rgba(0,0,0,.26)}md-progress-circular.text-light path{stroke:#fff}md-progress-circular.text-light-secondary path{stroke:hsla(0,0%,100%,.7)}md-progress-circular.text-light-disabled path{stroke:hsla(0,0%,100%,.5)}md-progress-circular.selected-bg path{stroke:#f4fbfd}md-progress-circular.score path{stroke:#ffc107}md-progress-circular.body path{stroke:rgba(0,0,0,.87)}md-progress-circular.body-bg path{stroke:#eee}.l-constrained{margin-left:auto;margin-right:auto;max-width:100%;position:relative}@media (min-width:600px){.l-constrained{width:1280px}}.l-constrained-md{width:960px;max-width:100%}.l-footer{position:fixed;bottom:0;left:0;right:0;z-index:1;background-color:#fff;border-top:1px solid #eee}.button--footer{margin:0;padding-top:0;padding-bottom:0;min-width:0;display:flex}.button--footer__element{padding-left:8px}.l-header{z-index:3}.l-header .logo{margin-left:0!important;height:36px;width:36px;vertical-align:middle}.l-header .logo-link{min-width:auto;display:none;padding:0 4px;margin-right:12px}@media only screen and (min-width:600px){.l-header .logo-link{display:block}}.l-header .logo-link:focus,.l-header .logo-link:hover{border:0}@media only screen and (max-width:599px){.l-header .md-toolbar-tools h1,.l-header .md-toolbar-tools h2,.l-header .md-toolbar-tools h3{font-size:15px}}.l-main{background-color:#eee}.l-main--with-toolbar{margin-top:42px}#content{transition:margin-top .5s}.view-content{margin:0 auto;padding:8px;position:absolute;left:0;right:0;transition:opacity .5s}@media only screen and (min-width:960px){.view-content{padding:16px}}.view-content.ng-enter{opacity:0}.view-content .ng-enter-active{opacity:1;transition-delay:.25s}.view-content.ng-hide,.view-content.ng-hide-add,.view-content.ng-hide-add-active,.view-content.ng-hide-remove,.view-content.ng-hide-remove-active,.view-content.ng-leave-active{opacity:0}.view-content--with-sidemenu{padding:8px}@media only screen and (min-width:600px){.view-content--with-sidemenu{margin-left:54px;padding:16px}}@media only screen and (min-width:600px){[dir=rtl] .view-content--with-sidemenu{margin-left:auto;margin-right:54px}}.content-head{margin:8px 0}.content-head h1,.content-head h2,.content-head h3{font-weight:300;margin-top:0;margin-bottom:0;font-size:36px}@media only screen and (max-width:959px){.content-head h1,.content-head h2,.content-head h3{font-size:32px;text-align:center}}@media only screen and (max-width:959px){.content-head__more{margin-top:8px}}.content-head__item,h2.content-head__item{margin:0 8px}.content-head__item .md-subhead,h2.content-head__item .md-subhead{padding-left:4px}@media only screen and (max-width:959px){.content-head__item .md-subhead,h2.content-head__item .md-subhead{display:block;padding-left:0}}.content-head__item md-icon,h2.content-head__item md-icon{vertical-align:text-bottom}.stepSelectMenuContainer md-select-menu,.stepSelectMenuContainer md-select-menu md-content{max-height:500px}.l-nav{background-color:#eee!important}.l-node{margin-top:42px;padding:0}@media only screen and (min-width:600px){.l-node{padding:0 0 16px;background-color:#eee!important}}@media only screen and (min-width:960px){.l-node{padding:0 0 32px}}.l-notebook{margin-top:42px;background-color:#eee!important}.l-sidebar__header{background-color:#fff!important;color:#795c3a!important}.l-sidebar__header md-select{color:rgba(0,0,0,.87)}.status-icon{margin:0 4px;z-index:1;vertical-align:bottom}.md-button.status-icon{height:auto;width:auto;min-height:0;line-height:inherit;margin:0 4px;padding:0}.avatar--icon--alert{background-color:#fff}.avatar--icon--alert__icon{font-size:48px;margin:-4px 0 0 -4px}.gu-mirror{position:fixed!important;margin:0!important;z-index:9999!important;opacity:.8;transition:opacity .25s ease-in-out}.gu-hide{display:none!important}.gu-unselectable{-webkit-user-select:none!important;-moz-user-select:none!important;-ms-user-select:none!important;user-select:none!important}.gu-transit{opacity:.2}md-dialog{width:600px}.dialog--wide{width:960px}.dialog--wider{width:1280px}.help-bubble{border-radius:4px;max-width:320px}@media (min-width:600px){.help-bubble{max-width:552px}}@media (min-width:960px){.help-bubble{max-width:912px}}@media (min-width:1280px){.help-bubble{max-width:1232px}}.help-bubble___title__content,.help-bubble__title{border-top-left-radius:4px;border-top-right-radius:4px}.help-bubble___title__content{padding:0 0 0 12px;background-color:#ef6c00}.help-bubble___title__content .md-icon-button{margin-right:0;padding-top:0;padding-bottom:0}.help-bubble__content{overflow:auto;padding:8px 12px;max-height:480px}.help-bubble__actions{border-bottom-left-radius:4px;border-bottom-right-radius:4px}div.hopscotch-bubble{border-radius:4px;border:0;font-family:Roboto,Helvetica Neue,sans-serif;font-size:14px;z-index:6}div.hopscotch-bubble .hopscotch-bubble-arrow-container{position:absolute;width:20px;height:20px}div.hopscotch-bubble .hopscotch-bubble-arrow-container.up{top:0;left:12px}div.hopscotch-bubble .hopscotch-bubble-arrow-container.up .hopscotch-bubble-arrow{border-bottom:10px solid #ef6c00;border-left:10px solid transparent;border-right:10px solid transparent;position:relative;top:-10px}div.hopscotch-bubble .hopscotch-bubble-arrow-container.up .hopscotch-bubble-arrow-border{border:0}div.hopscotch-bubble .hopscotch-bubble-arrow-container.down{bottom:-34px;left:12px}div.hopscotch-bubble .hopscotch-bubble-arrow-container.down .hopscotch-bubble-arrow{border-top:10px solid #ef6c00;border-left:10px solid transparent;border-right:10px solid transparent;position:relative;top:-10px}div.hopscotch-bubble .hopscotch-bubble-arrow-container.down .hopscotch-bubble-arrow-border{border:0}div.hopscotch-bubble .hopscotch-bubble-arrow-container.left{top:12px;left:-10px}div.hopscotch-bubble .hopscotch-bubble-arrow-container.left .hopscotch-bubble-arrow{border-right:10px solid #ef6c00;border-bottom:10px solid transparent;border-top:10px solid transparent;position:relative;left:0;top:-10px}div.hopscotch-bubble .hopscotch-bubble-arrow-container.left .hopscotch-bubble-arrow-border{border:0}div.hopscotch-bubble .hopscotch-bubble-arrow-container.right{top:12px;right:-30px}div.hopscotch-bubble .hopscotch-bubble-arrow-container.right .hopscotch-bubble-arrow{border-left:10px solid #ef6c00;border-bottom:10px solid transparent;border-top:10px solid transparent;position:relative;right:0;top:-10px}div.hopscotch-bubble .hopscotch-bubble-arrow-container.right .hopscotch-bubble-arrow-border{border:0}.input-container{padding-top:12px}.input-container--component{margin-bottom:0}.input-container--open-response.md-has-icon{padding-left:0}.input-container--open-response .md-errors-spacer{display:none}.input-wrapper{position:relative}.input-wrapper--focused .input--textarea__action md-icon{color:#1c8ca8}.input--textarea,.input-container textarea.input--textarea{padding:8px;background-color:#f7f7f7;border:1px solid #ccc;margin-bottom:8px}.input--textarea:focus,.input-container textarea.input--textarea:focus{background-color:#fff}.input--textarea[disabled],.input-container textarea.input--textarea[disabled]{color:rgba(0,0,0,.54)}.input-container textarea.input--textarea{width:100%}.input--textarea--disabled{color:rgba(0,0,0,.54)}.input--textarea__action{position:absolute;right:-4px}.input--textarea__action[disabled] md-icon{color:rgba(0,0,0,.26)!important}.input--textarea__action--notebook{top:6px}.input-wrapper--richtext .input--textarea__action--notebook{top:-7px}.input--textarea__action--revision{bottom:6px}.input-wrapper--richtext .input--textarea__action--revision{bottom:-5px}.input-label,md-input-container:not(.md-input-invalid):not(.md-input-focused).md-input-has-value label.input-label{line-height:1.2;color:rgba(0,0,0,.87)}.input-label.input-label--focused,md-input-container:not(.md-input-invalid):not(.md-input-focused).md-input-has-value label.input-label.input-label--focused{color:#1c8ca8}.autocomplete input{text-overflow:ellipsis;overflow:hidden;word-wrap:none;font-weight:500;color:rgba(0,0,0,.54)}@media only screen and (min-width:600px){.autocomplete--minwidth{min-width:300px}}@media only screen and (min-width:960px){.autocomplete--minwidth{min-width:300px}}.autocomplete--flat md-autocomplete-wrap{background-color:#fff}.autocomplete--flat md-autocomplete-wrap:not(.md-menu-showing){box-shadow:none;background-color:#eee}.select__header{height:48px}.select__header input{height:100%;width:100%;padding:0 8px;outline:none;border:0;font-size:14px;font-weight:500}.table{max-width:100%;width:auto;min-width:100px;margin:8px 0}.table tbody>tr>td,.table tbody>tr>th,.table tfoot>tr>td,.table tfoot>tr>th,.table thead>tr>td,.table thead>tr>th{border:1px solid #ccc;padding:6px;font-size:15px;min-height:32px;height:32px;min-width:32px;vertical-align:top}.table td.inactive,.table th{background-color:#f7f7f7;opacity:1;visibility:visible}.table md-input-container{margin:0}.table .md-errors-spacer{display:none}.table--student td.inactive{padding:8px 10px}.table--full-width{width:100%}.table--list{border:0;border-collapse:collapse;background-color:#fff;max-width:100%;overflow:auto}.table--list td,.table--list th{padding:0 4px;border:0}.table--list td{min-height:56px;height:56px}.table--list tr.md-button{display:table-row;text-align:left;width:auto;text-transform:none;font-size:inherit;font-weight:400}.table--list__wrap{min-width:600px}@media only screen and (max-width:959px){.table-wrap-sticky{overflow-x:auto}}.table--list__thead{font-size:14px;font-weight:700}.table--list__thead__tr{height:100%;margin:0}.table--list__thead__th{background-color:#757575;color:#fff;min-height:42px;height:42px}.table--list__thead__link{color:#fff;text-transform:none;margin:0;min-width:0;white-space:normal;line-height:1.4;width:100%}.table--list__thead__sort{margin:0}.table--list__thead__sort--reverse{transform:rotate(180deg)}.td--wrap{min-width:180px;white-space:normal;line-height:1.2}@media only screen and (max-width:959px){.td--max-width{max-width:180px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}}.md-toolbar-tools{font-size:18px}.md-toolbar-tools .autocomplete,.md-toolbar-tools .autocomplete input,.md-toolbar-tools .autocomplete md-autocomplete-wrap{height:36px}.md-toolbar--wise{min-height:42px}.md-toolbar--wise .md-toolbar-tools{height:42px;max-height:42px}.md-toolbar--wise .md-button.md-icon-button{height:42px;line-height:42px;width:42px}.md-toolbar--wise--sm .md-toolbar-tools{padding:0 8px;font-size:15px}.md-toolbar--sidenav{background-color:#333!important}.md-toolbar--sidenav .md-toolbar-tools{font-size:16px;font-weight:500}.toolbar{position:fixed;left:0;right:0;top:52px;z-index:3}.toolbar__title{margin-left:8px;font-size:16px;font-weight:500}.toolbar__tools{padding-right:8px}[dir=rtl] .toolbar__tools{padding-right:16px;padding-left:8px}.md-button.toolbar__nav,.toolbar__nav{margin:0}.md-button.toolbar__select,.toolbar__select{margin:0 4px;min-height:32px;background-color:#f7f7f7}.md-button.toolbar__select .md-select-value,.toolbar__select .md-select-value{height:32px;text-align:left}[dir=rtl] .md-button.toolbar__select .md-select-value,[dir=rtl] .toolbar__select .md-select-value{text-align:right}.toolbar__select--fixedwidth{width:168px}@media only screen and (min-width:600px){.toolbar__select--fixedwidth{width:264px}}@media only screen and (min-width:960px){.toolbar__select--fixedwidth{width:432px}}.list-item{background-color:#fff;border-bottom:1px solid #eee}.list-item.md-subheader,.list-item .md-subheader{color:rgba(0,0,0,.87);background-color:#fff}.list-item.md-subheader md-icon,.list-item .md-subheader md-icon{vertical-align:middle}.list-item.md-subheader .md-subheader-inner,.list-item .md-subheader .md-subheader-inner{padding:0}.list-item.md-subheader .md-avatar,.list-item .md-subheader .md-avatar{margin-right:8px}.list-item .autocomplete{margin:8px 0}.list-item--info._md-button-wrap>div.md-button:first-child,.list-item--info .md-subheader-content{border-left:4px solid #ef6c00!important;margin-left:-4px}.list-item--warn._md-button-wrap>div.md-button:first-child,.list-item--warn .md-subheader-content{border-left:4px solid #c62828!important;margin-left:-4px}.list-item--expanded{border-bottom-width:0}.list-item--noclick,.list-item--noclick.md-button{cursor:default;background-color:#f7f7f7}.list-item--actions{padding:0 8px!important}.list-item__subheader-button{text-transform:none;width:100%;padding:8px 16px;margin:0;white-space:normal;text-align:left;line-height:1.4}.user-list{font-size:15px}#nav{position:relative}.nav{margin-bottom:16px}.nav-mask{position:absolute;top:0;bottom:0;left:0;right:0;background-color:rgba(0,0,0,.25);z-index:1}.nav-mask.ng-hide{opacity:0}.nav-head{color:rgba(0,0,0,.54);font-weight:500}.nav-head md-icon{line-height:20px}.nav-contents--root{padding:6px 6px 12px}.nav-contents--group{background-color:#ddd;padding:8px}.nav-contents--root,.nav-contents__list{padding:0}@media (min-width:600px){.nav-contents__list{padding:8px}}.nav-item{transition:opacity .25s ease-in-out}.nav-item.prev md-list-item{background-color:#f4fbfd}.nav-item--card__content{border-top-right-radius:4px;border-top-left-radius:4px}.nav-item--card__content:focus{outline:none}.nav-item--card__content:focus .nav-item__title>span{border-bottom:1px dashed #ccc}.nav-item--root{transition:margin .25s,box-shadow .5s}.nav-item--root.expanded{flex-basis:100%;max-width:100%;max-height:none!important;margin:8px auto;padding-left:4px}.nav-item--root.expanded:first-of-type{margin-top:0}.nav-item--list__info-item{padding:0 16px 0 4px;display:inline-block}.nav-item--list__reorder{margin-left:8px;color:rgba(0,0,0,.26)}.nav-item--card--group:not(.expanded){box-shadow:0 3px 1px -2px rgba(0,0,0,.14),0 2px 2px 0 rgba(0,0,0,.098),0 1px 5px 0 rgba(0,0,0,.084),3px 3px 0 1px #d5d5d5,6px 6px 0 1px #aaa}.nav-item__collapse{margin:0}.nav-item__more{border-top:1px solid #ddd;border-bottom-right-radius:4px;border-bottom-left-radius:4px;padding:8px 16px;min-height:40px}.nav-item__users{height:auto;cursor:pointer;color:#fff;margin:0;padding:1px 6px}.nav-item__users>md-icon{padding-right:4px;color:#fff}.nav-item__users:focus.success-bg,.nav-item__users:hover.success-bg{background-color:#00c853}.nav-item__title{padding-left:16px;line-height:1.2;font-weight:400}[dir=rtl] .nav-item__title{padding-left:auto;padding-right:16px}.nav-item__info{padding:0 8px}.nav-item__progress{width:48px}.nav-item__progress>.md-container{top:0}.nav-item__progress-value{margin-left:8px;width:36px}.progress-wrapper{padding:2px 0;cursor:pointer}.menu-progress{position:absolute;top:10px;right:12px}.menu-progress path{stroke:#cad266!important;stroke-width:2px}[dir=rtl] .menu-progress{right:auto;left:12px}.menu-sidenav__item{font-weight:700;font-size:14px}.menu-sidenav__icon{margin-top:12px!important;margin-right:12px!important;margin-left:12px}.active .menu-sidenav__icon,.active .menu-sidenav__item{color:#1c8ca8}.menu-sidebar{position:absolute;top:94px;bottom:0;left:0;background-color:#fff;width:56px;overflow:hidden;padding:8px 0;text-align:center;border-right:1px solid #ccc}@media only screen and (max-width:599px){.menu-sidebar{display:none}}[dir=rtl] .menu-sidebar{right:0;left:auto}.md-button.md-icon-button.menu-sidebar__link{margin-top:6px;margin-bottom:6px}.notice{text-align:center;padding:8px;background-color:rgba(0,0,0,.04);width:100%}@media (min-width:600px){.notice{max-width:80%;border-radius:3px;margin:24px auto}}#node{margin:0 auto;position:absolute;left:0;right:0}@media only screen and (min-width:600px){#node{padding:24px 16px;margin-bottom:32px}}@media only screen and (min-width:960px){#node{padding:32px}}#node.ng-enter{transition:opacity .5s;opacity:0}#node.ng-enter-active{opacity:1}@media only screen and (min-width:600px){.node-notice{margin-top:-8px;margin-bottom:16px}}@media only screen and (min-width:960px){.node-notice{margin-top:-16px}}.node-content{padding:0 0 48px;background-color:#fff;border-radius:3px;overflow:visible}@media only screen and (max-width:599px){.node-content{box-shadow:none}}@media only screen and (min-width:600px){.node-content{padding:0;border-top:2px solid;border-bottom:2px solid}}md-content.node-content{background-color:#fff}.node-content__rubric{position:absolute;top:-22px;left:0;right:0;z-index:1}.node-content__rubric .avatar--icon{transform:scale(.94)}@media only screen and (max-width:599px){.node-content__rubric .avatar--icon{transform:scale(.8)}}.node-icon{color:#fff;vertical-align:inherit}.node-select{margin:0 8px;min-width:0;font-weight:500;font-size:15px}.node-select .md-select-value :first-child{transform:translateZ(0);flex:1 0 0}.node-select .md-select-value .node-select__icon,.node-select .md-select-value .node-select__status{display:none}.node-select .md-select-icon{margin-left:0;color:rgba(0,0,0,.87)}.node-select-option--group{background-color:#f7f7f7;border-bottom:1px solid #eee;border-top:1px solid #eee}.node-select-option--node{padding-left:20px}.node-select__icon{margin-right:8px}.node-select__status{margin-left:8px}.node-select__text{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}@media only screen and (min-width:600px){.node-select__text{margin-top:2px}}.node-title{line-height:1.2;text-transform:none;margin-top:3px}@media only screen and (max-width:599px){.node-title{font-size:15px}}.node-content__actions{padding:0 16px 16px}@media only screen and (min-width:960px){.node-content__actions{padding:0 24px 24px}}@media only screen and (min-width:1280px){.node-content__actions{padding:0 32px 32px}}.node-content__actions .md-button:first-child{margin-left:0}.node-content__actions .md-button:last-child{margin-right:0}.node-content__actions__info{font-style:italic;margin-left:8px;color:rgba(0,0,0,.54)}.node-content__actions__more{border-bottom:1px dotted}.md-button.md-icon-button.node-nav:first-of-type{margin-right:0}@media only screen and (min-width:600px){.node-sidebar-active{margin-right:68px}}@media only screen and (max-width:599px){.node-sidebar-visible{margin-bottom:42px}}.node-sidebar{position:absolute;right:0;top:0;width:52px}.node-sidebar__toolbar{position:fixed;width:52px;background-color:#fff;padding:8px 0;border-radius:3px}@media only screen and (max-width:599px){.node-sidebar__toolbar{right:0;bottom:0;left:0;width:100%;border-radius:0;padding:0;min-height:0;height:42px}}.node-info{margin:0}@media only screen and (max-width:599px){.node-info{margin:-16px;border-radius:0}}.node-info .divider{margin-left:-8px;margin-right:-8px}.node-info .component:first-child{margin-top:-16px}.node-info .component,.node-info .component__content,.node-info .component__header{margin-left:-8px;margin-right:-8px}.node-info .component__actions{display:none}.node-rubric{border:2px solid #1c8ca8;margin:8px 16px 24px;padding:16px;background-color:#fff}.node__label--vertical-alignment{vertical-align:middle;display:inline-block}@media only screen and (min-width:600px){.notebook-launcher.md-button.md-fab{z-index:61}}.notebook-report{border-radius:4px 4px 0 0;margin:0;height:100%}.notebook-report .note-toolbar{margin:-8px -8px 8px;padding:4px;border-bottom:0;border-top:0;border-color:#ddd currentcolor;border-style:solid none;border-width:1px 0;border-radius:0}.notebook-report .note-toolbar .btn-group{margin-top:0}.notebook-report .note-btn{border:0;border-top-left-radius:0;border-top-right-radius:0;border-bottom-left-radius:0;border-bottom-right-radius:0}.notebook-report-container{height:550px;width:450px;max-height:90%;bottom:0;right:96px;position:absolute;z-index:3}@media only screen and (min-width:960px){.notes-visible .notebook-report-container{right:516px;transition:right .25s}}.notebook-report-container__full{top:16px;bottom:16px;left:16px;right:16px;max-height:none;max-width:none;height:auto;width:auto}.notebook-report-container__full .notebook-report{height:100%;width:100%;margin:0 auto;max-height:none;border-radius:4px}.notebook-report-container__collapsed{width:300px;height:auto}.notebook-report-container__collapsed .notebook-report__actions,.notebook-report-container__collapsed .notebook-report__content,.notebook-report-container__collapsed .notebook-report__content__header{display:none}.notebook-report__toolbar{background-color:#333!important;border-radius:4px 4px 0 0}.notebook-report__toolbar__title{max-width:150px}.notebook-report__content{background-color:#fff}.notebook-report__content h1,.notebook-report__content h2,.notebook-report__content h3,.notebook-report__content h4{font-size:22px}.notebook-report__content .note-editor.note-frame{border:0;border-radius:0;padding:0;box-shadow:none}.notebook-report__content .note-resizebar{display:none}.notebook-report__content__header{padding:8px;background-color:#fff;font-size:16px}@media only screen and (max-width:599px){.notebook-report-container:not(.notebook-report-container__collapsed){top:0;bottom:0;left:0;right:0;max-height:none;max-width:none;height:auto;width:auto}.notebook-report-container:not(.notebook-report-container__collapsed) .notebook-report{border-radius:0}.notebook-tools--full{display:none}}.notebook-report-backdrop{position:fixed;top:0;left:0;width:100%;height:100%;z-index:3;background-color:#212121;opacity:.48}.notebook-menu{transition:opacity .5s}.notebook-menu.ng-enter{opacity:0}.notebook-menu .ng-enter-active{opacity:1;transition-delay:.25s}.notebook-menu.ng-hide,.notebook-menu.ng-hide-add,.notebook-menu.ng-hide-add-active,.notebook-menu.ng-hide-remove,.notebook-menu.ng-hide-remove-active,.notebook-menu.ng-leave-active{opacity:0}.notebook-item{transition:box-shadow .25s;margin:0 16px 16px;display:block}.notebook-item__content{height:250px;min-width:230px;position:relative;padding:0;background-color:#ccc;border-top-left-radius:4px;border-top-right-radius:4px}.notebook-item__content__attachment,.notebook-item__content__text{position:absolute;left:0;right:0}.notebook-item__content__attachment{background-repeat:no-repeat!important;border-top-left-radius:4px;border-top-right-radius:4px;background-position:top!important;background-size:cover!important;top:0;bottom:0}.notebook-item__content__text{bottom:0;padding:8px;font-weight:500;overflow:hidden;max-height:120px;min-height:56px;background-color:hsla(0,0%,100%,.95);border-top:1px solid #eee}.notebook-item__content__text:after{content:"";text-align:right;position:absolute;bottom:0;right:0;width:100%;height:.8em;background:linear-gradient(180deg,hsla(0,0%,100%,0),hsla(0,0%,100%,.95) 100%)}.notebook-item__content--text-only:after{content:"note";font-family:Material Icons;font-weight:400;font-style:normal;display:inline-block;line-height:1;text-transform:none;letter-spacing:normal;word-wrap:normal;white-space:nowrap;direction:ltr;-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;-moz-osx-font-smoothing:grayscale;font-feature-settings:"liga";font-size:80px;color:rgba(0,0,0,.26)}.notebook-item--question__content--text-only{content:"live_help"}.notebook-item__content__location{opacity:.9;padding:8px 0}.notebook-item__content__location md-icon{font-size:22px}.notebook-item__edit{cursor:pointer}.notebook-item__actions{margin:0;padding:0 8px;color:#fff;background-color:#333;border-bottom-left-radius:4px;border-bottom-right-radius:4px}.notebook-item__actions md-icon{color:#fff}.notebook-item__text-input{margin:0}.notebook-item__text-input__textarea{padding-left:0;padding-right:0}.notebook-item__attachment{background-color:#ddd;padding:16px;margin-bottom:16px;text-align:center;position:relative}.notebook-item__attachment__content{max-width:100%;height:auto}.notebook-item__attachment__delete{position:absolute;top:4px;right:-2px;width:34px!important;height:34px!important;min-height:0}.notebook-item__attachment__delete md-icon{margin-left:-2px;font-size:22px}.notebook-item__info{font-style:italic;opacity:.8;color:#8a6942}.notebook-item__info a,.notebook-item__info md-icon{color:#8a6942}.notebook-item__info md-icon{font-size:1.5em;min-width:0;width:auto}.notebook-item__upload{text-align:center;padding:24px;background-color:#eee;margin-bottom:16px;color:rgba(0,0,0,.54);border-radius:4px;cursor:pointer;border:1px dashed transparent;transition:all .25s}.notebook-item__upload md-icon,.notebook-item__upload span{transition:color .25s}.notebook-item__upload.dragover,.notebook-item__upload:focus,.notebook-item__upload:hover{border-color:#f05843;background-color:#fdebe8;color:#f05843}.notebook-item__upload.dragover md-icon,.notebook-item__upload.dragover span,.notebook-item__upload:focus md-icon,.notebook-item__upload:focus span,.notebook-item__upload:hover md-icon,.notebook-item__upload:hover span{color:#f05843}.view-notebook-item{width:600px}.notebook-item--report{background-color:#fff}.notebook-item--report .note-editor{margin-bottom:16px;border-color:#ccc}.notebook-item--report__container.ui-scrollpoint .notebook-item--report__toolbar{position:fixed;top:94px;left:0;right:0;z-index:1}@media only screen and (min-width:600px){.notebook-item--report__container.ui-scrollpoint .notebook-item--report__toolbar{left:54px;padding:0 24px}}@media only screen and (min-width:960px){.notebook-item--report__container.ui-scrollpoint .notebook-item--report__toolbar{padding:0 32px}}.notebook-item--report__container.ui-scrollpoint .notebook-item--report__toolbar .note-toolbar{margin:0 16px}@media only screen and (min-width:600px){.notebook-item--report__container.ui-scrollpoint .notebook-item--report__toolbar .note-toolbar{margin:0 8px}}@media only screen and (min-width:960px){.notebook-item--report__container.ui-scrollpoint .notebook-item--report__toolbar .note-toolbar{margin:0 16px}}.notebook-item--report__container.ui-scrollpoint .note-editor{padding-top:40px}.notebook-item--report__toolbar .note-toolbar{background-color:#ddd;border:1px solid #ccc;margin-bottom:-2px}.notebook-item--report__heading{text-align:center;margin-bottom:32px}.notebook-item--report__add-note{font-weight:700}.notebook-item--report__note-img{max-width:100%;height:auto!important}@media only screen and (min-width:600px){.notebook-sidebar{width:400px;max-width:none}}@media only screen and (min-width:960px){.notebook-sidebar{width:500px;max-width:none}}.notebook-items{width:100%;overflow:auto;margin-top:16px;margin-bottom:76px}.notebook-items .notebook-item{width:100%}.notebook-items .notebook-item__content{height:200px;min-width:0}.notebook-items--grading{margin-bottom:0}@media only screen and (max-width:599px){.notebook-enabled .md-fab-bottom-left,.notebook-enabled .md-fab-bottom-right{bottom:50px!important}}.notebook-grading{background-color:#fff;display:block}.notification-btn{width:60px!important}.notification-btn md-icon{margin-left:20px}.notification-count{border-radius:50%;position:absolute;background-color:#f05843;width:22px;left:4px;height:22px;line-height:18px;font-size:12px;font-weight:700;border:2px solid}.notification-count:before{content:"";position:absolute;right:-7px;top:5px;border-left:6px solid hsla(0,0%,100%,.87);border-top:4px solid transparent;border-bottom:4px solid transparent}.notification-list{padding:8px 0}.notification-dismiss{width:500px}.notification-dismiss__input{margin-bottom:0}md-list md-list-item.md-2-line .md-list-item-text h4.notification-list-item__source,md-list md-list-item.md-3-line .md-list-item-text h4.notification-list-item__source,md-list md-list-item .md-list-item-text h4.notification-list-item__source{color:rgba(0,0,0,.54);font-size:12px}md-list md-list-item.md-2-line .md-list-item-text h4.notification-list-item__source md-icon,md-list md-list-item.md-3-line .md-list-item-text h4.notification-list-item__source md-icon,md-list md-list-item .md-list-item-text h4.notification-list-item__source md-icon{font-size:18px;min-width:0;width:auto;margin-left:-4px;line-height:20px}.account-menu{border-radius:4px;padding:0;font-size:15px;max-width:380px}@media (min-width:1280px){.account-menu{min-width:380px!important}}.account-menu h3{margin:0;font-weight:300}.account-menu--fixed-height{height:304px}.account-menu--fixed-width{width:320px}@media (min-width:960px){.account-menu--fixed-width{width:380px}}.account-menu__icon{background-color:#fff;border-radius:50%}.account-menu__caret{position:absolute;right:28px;top:-8px;outline:none}.account-menu__caret:before{content:"";position:absolute;border-bottom:8px solid #fff;border-left:8px solid transparent;border-right:8px solid transparent}.account-menu__caret--notification,.account-menu__caret--pause{right:80px}.account-menu__caret--notification--with-pause{right:132px}[dir=rtl] .account-menu__caret{right:auto;left:28px}[dir=rtl] .account-menu__caret--notification,[dir=rtl] .account-menu__caret--pause{left:80px;right:auto}[dir=rtl] .account-menu__caret--notification--with-pause{left:132px;right:auto}.account-menu__info{padding:8px 12px}.account-menu__info__title{font-weight:500}.account-menu__info__team{font-weight:400;color:rgba(0,0,0,.54)}.account-menu__users,.account-menu__users md-list-item{padding:0}.account-menu__users md-list-item .md-avatar{margin:0 8px 0 0;height:48px;width:48px}.account-menu__progress md-progress-linear{transform:rotate(270deg);width:26px}.account-menu__grade{position:relative;margin-right:4px}.account-menu__grade md-icon{color:#ffc107}.account-menu__grade__overlay{position:absolute;top:0;width:100%;background-color:hsla(0,0%,100%,.6)}.account-menu__actions{background-color:#f7f7f7}.account-menu__control{padding:16px}.annotations{margin:16px 4px 16px 62px;position:relative;font-size:15px}.annotations hr{margin:10px 0 8px;border-color:rgba(0,0,0,.12)}.annotations:after{content:"";position:absolute;width:0;height:0;left:-16px;right:auto;top:0;bottom:auto;border-top:20px solid transparent;border-bottom:20px solid transparent;border-right:16px solid #757575}.annotations-container--student--report{border-top:1px solid #ddd}.annotations--report{margin-top:0;margin-bottom:0}.annotations__header{position:relative;border-top-right-radius:4px;padding:10px 12px;font-weight:700;transition:all 1s;color:#fff;background-color:#757575}.annotations__avatar{background-color:#f05843;padding:2px;position:absolute;top:0;left:-62px}.annotations__icon{transition:all 1s;color:#fff}.annotations__body{padding:12px;background-color:#fff;border-bottom-left-radius:4px;border-bottom-right-radius:4px;overflow:auto}.annotations__status{background-color:#fff;color:#ef6c00;display:inline-block;margin-left:8px;font-size:12px}.annotations__status.ng-enter,.annotations__status.ng-leave{transition:all 1s}.annotations__status.ng-enter,.annotations__status.ng-leave.ng-leave-active{opacity:0}.annotations__status.ng-enter.ng-enter-active,.annotations__status.ng-leave{opacity:1}.annotations__score{font-weight:700}.annotations__info{font-style:italic;opacity:.8;border-bottom:1px dotted;font-size:13px}.annotations--inside .annotations{margin-left:72px}.annotations--info{margin-bottom:32px;margin-right:8px;margin-left:72px}@media only screen and (min-width:600px){.annotations--info{margin:16px 16px 32px 76px}}.annotations--info:after{border-right:16px solid #ef6c00}.annotations--info .annotations__avatar{background-color:#fff}.annotations--info .annotations__header{background-color:#ef6c00}.component{position:relative}.component__wrapper{padding:0 16px;margin:24px 0}.component__content{overflow-x:auto;font-size:15px;overflow-y:hidden}@media only screen and (min-width:600px){.component__content{padding:0 8px}}.component-header,h3.component__header{padding:8px 12px;margin:0;font-size:14px}.component__rubric{position:absolute;left:-20px;top:12px}.notebook-enabled .component_content img{transition:all .25s;cursor:pointer;cursor:copy}.notebook-enabled .component_content img:focus,.notebook-enabled .component_content img:hover{box-shadow:0 0 5px 1px #f05843}.component__actions .md-button:first-child{margin-left:0}.component__actions .md-button:last-child{margin-right:0}.component__actions__info{font-style:italic;margin-left:8px;color:rgba(0,0,0,.54)}.component__actions__more{border-bottom:1px dotted}.component__prompt{margin-bottom:8px;font-weight:500}.component__prompt__content{display:inline}.component__attachment{position:relative;margin:0 8px;padding-bottom:8px}@media only screen and (min-width:600px){.component__attachment{padding-top:8px}}@media only screen and (max-width:599px){.component__add-attachment{width:100%}}.component__attachment__content{max-height:100px;width:auto}.component__attachment__delete{position:absolute;top:0;right:0;min-width:0;background-color:hsla(0,0%,100%,.75)!important;border-radius:0;padding:4px;margin:0}.component__attachment__delete>md-icon{margin-top:0}.component__revision{margin:8px 0;padding:8px}.component__revision:nth-child(odd){background-color:#f7f7f7}.component__revision__content{padding:4px 0 8px;border-bottom:1px solid #ddd}.component__revision__actions{color:#757575;padding-top:4px}.component__content--Discussion{overflow:hidden}.discussion-content{background-color:#eee;box-shadow:inset 0 0 3px #aaa}.discussion-posts{padding:12px 12px 8px}@media only screen and (min-width:1280px){.discussion-posts{padding:16px 16px 0}}.discussion-post{margin:0 auto 16px;max-width:600px}@media only screen and (min-width:600px){.discussion-post{margin-bottom:24px}}@media only screen and (min-width:1280px){.discussion-post{margin-bottom:32px}}.discussion-post md-divider{position:relative;width:auto}.discussion-post__contents{padding:16px}.discussion-post__avatar,md-list-item>.md-avatar.discussion-post__avatar{margin-right:8px}.discussion-post__avatar--reply,md-list-item>.md-avatar.discussion-post__avatar--reply{margin-top:8px}.discussion-post__user,md-list-item .md-list-item-text h3.discussion-post__user{padding-bottom:4px;font-weight:700;line-height:1.3;overflow:visible;white-space:normal}.discussion-post__date{color:#aaa}.discussion-post__date--reply{margin-left:8px;font-weight:400}.discussion-post__content{margin-top:16px;white-space:pre-wrap}.discussion-post__attachment{max-width:100%;height:auto!important;margin-top:16px}.discussion-new{background-color:#fff;max-width:570px;margin-left:auto;margin-right:auto;padding:8px;transition:all .25s;transform:scale(.95)}.discussion-new--focused{transform:scale(1)}md-input-container.discussion-new__input-container{margin:0;padding:0}md-input-container.discussion-new__input-container>textarea.md-input{min-height:68px}.discussion-new__input--textarea,.input-container textarea.discussion-new__input--textarea{padding:8px;border:0}.discussion-new__actions{padding:0 8px}.discussion-new__actions .md-button:first-of-type{margin-left:0}.discussion-new__actions .md-button:last-of-type{margin-right:0}.discussion-new__attachment{padding:0;margin:0 0 8px}.discussion-new__attachment__content{margin-top:0;margin-bottom:16px}.discussion-comments{padding:0}.discussion-comments__contents{background-color:#f7f7f7}.discussion-comments__header{background-color:transparent;padding:0}.discussion-comments__header .md-subheader-inner{padding-bottom:8px}.discussion-comments__list{padding:0;max-height:9999px;overflow-y:auto}@media only screen and (min-width:600px){.discussion-comments__list{max-height:400px}}.input--textarea.discussion-reply__input,.input-container textarea.input--textarea.discussion-reply__input{background-color:#fff;padding:4px;font-size:14px;border:0;margin-left:-1px;margin-bottom:0;resize:none}.discussion-reply,md-list-item.discussion-reply{margin:0 12px 8px;padding:0;min-height:56px}.discusstion-reply__details,md-list-item .md-list-item-text.discusstion-reply__details{margin:8px 0}.discussion-post__user--reply,md-list-item .md-list-item-text h3.discussion-post__user--reply{font-size:14px;padding:0;margin:0}.discusstion-reply__content{margin-top:2px}.discusstion-reply__content p{font-weight:400!important;color:rgba(0,0,0,.87)!important}.discussion-new-reply{padding:8px}.discussion-new-reply__input-container{padding-top:0;margin:0}.discussion-new-reply__input-container .md-errors-spacer{display:none}.discussion-new-reply__actions{margin-left:8px}.discussion-new-reply__actions .md-button{margin-top:0;margin-bottom:0}.embedded-content__iframe{border:0}.graph-select{min-width:150px;max-width:200px}.graph-controls{margin:8px 0;padding:8px 0;border-color:#eee;border-style:solid;border-width:1px 0}.match-content{background-color:#eee;margin-bottom:16px;padding:8px;box-shadow:inset 0 0 3px #aaa}.match-divider{margin:16px 8px 8px}@media only screen and (min-width:960px){.match-divider--horizontal{display:none}}.match-bucket__header{padding:12px;font-weight:500;color:#1c8ca8}.match-bucket__content{padding:0}.match-bucket--choices .match-bucket__header{color:#795c3a}.match-bucket__contents{min-height:120px;padding:0 8px 8px;background-color:#ddd;transition:background-color .25s;border-bottom-left-radius:4px;border-bottom-right-radius:4px;-moz-column-gap:8px;column-gap:8px}@media only screen and (max-width:599px){.match-bucket__contents{-moz-column-count:1!important;column-count:1!important}}.match-bucket__contents img{max-width:100%;height:auto}.match-bucket__contents--over{background-color:#1c8ca8}.match-bucket__item{list-style-type:none;cursor:move;padding-top:8px;-moz-column-break-inside:avoid;break-inside:avoid}.match-bucket__item__contents{background-color:#fff;padding:8px!important;border:1px solid #ccc}.match-bucket__item__contents .md-list-item-text{width:100%}.match-bucket__item__contents__text{margin-right:4px}.match-feedback{transition:opacity .25s;margin:8px -8px -8px;color:#fff;padding:4px 8px}.match-feedback.ng-hide{opacity:0;transition:opacity 1ms}.match-feedback md-icon{color:#fff}.outside-content iframe{border:1px solid #eee}.outside-content__source{margin-top:4px;text-align:end}.outside-content__source a{max-width:240px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;display:inline-block}.notebook-toolbar md-divider{margin:8px 0}@media only screen and (max-width:959px){.notebook-toolbar{border-top:1px solid #ddd}}.notebook-toolbar__add-menu{position:absolute;bottom:40px}.notebook-toolbar__add-menu .md-fab-action-item{background-color:#fff}.notebook-toolbar__add-icon{border-radius:50%}#closeNotebookSettingsButton{float:right}[dir=rtl] #closeNotebookSettingsButton{float:left}highchart{display:block} /*# sourceMappingURL=vle.css.map */ diff --git a/src/main/webapp/wise5/themes/default/style/vle.css.map b/src/main/webapp/wise5/themes/default/style/vle.css.map index 0b411e4041..2146a66748 100644 --- a/src/main/webapp/wise5/themes/default/style/vle.css.map +++ b/src/main/webapp/wise5/themes/default/style/vle.css.map @@ -1 +1 @@ -{"version":3,"sources":["src/main/webapp/wise5/themes/default/style/base/_presets.scss","src/main/webapp/wise5/themes/default/style/base/_config.scss","src/main/webapp/wise5/themes/default/style/base/_helpers.scss","src/main/webapp/wise5/themes/default/style/layouts/_l-default.scss","src/main/webapp/wise5/themes/default/style/material/_config.scss","src/main/webapp/wise5/themes/default/style/layouts/_l-footer.scss","src/main/webapp/wise5/themes/default/style/layouts/_l-header.scss","src/main/webapp/wise5/themes/default/style/layouts/_l-main.scss","src/main/webapp/wise5/themes/default/style/vle.css","src/main/webapp/wise5/themes/default/style/layouts/_l-nav.scss","src/main/webapp/wise5/themes/default/style/layouts/_l-node.scss","src/main/webapp/wise5/themes/default/style/layouts/_l-notebook.scss","src/main/webapp/wise5/themes/default/style/layouts/_l-sidebar.scss","src/main/webapp/wise5/themes/default/style/modules/_alerts.scss","src/main/webapp/wise5/themes/default/style/modules/_dragula.scss","src/main/webapp/wise5/themes/default/style/modules/_dialog.scss","src/main/webapp/wise5/themes/default/style/modules/_help.scss","src/main/webapp/wise5/themes/default/style/modules/_inputs.scss","src/main/webapp/wise5/themes/default/style/modules/_table.scss","src/main/webapp/wise5/themes/default/style/modules/_toolbar.scss","src/main/webapp/wise5/themes/default/style/modules/_list.scss","src/main/webapp/wise5/themes/default/style/modules/_nav.scss","src/main/webapp/wise5/themes/default/style/modules/_menu.scss","src/main/webapp/wise5/themes/default/style/modules/_notice.scss","src/main/webapp/wise5/themes/default/style/modules/_node.scss","src/main/webapp/wise5/themes/default/style/modules/_notebook.scss","src/main/webapp/wise5/themes/default/style/modules/_notifications.scss","src/main/webapp/wise5/themes/default/style/modules/_account-menu.scss","src/main/webapp/wise5/themes/default/style/modules/_annotations.scss","src/main/webapp/wise5/themes/default/style/modules/_component.scss","src/main/webapp/wise5/themes/default/style/modules/_component--discussion.scss","src/main/webapp/wise5/themes/default/style/modules/_component--embedded.scss","src/main/webapp/wise5/themes/default/style/modules/_component--graph.scss","src/main/webapp/wise5/themes/default/style/modules/_component--match.scss","src/main/webapp/wise5/themes/default/style/modules/_component--outside.scss","src/main/webapp/wise5/themes/default/style/modules/_notebook-toolbar.scss","src/main/webapp/wise5/themes/default/style/modules/_highcharts.scss"],"names":[],"mappings":"AAIA,KACE,eCSuB,CDVzB,SAIM,eAAgB,CAItB,gBAEQ,aCbgB,CDiBxB,WACE,wBAAgD,CAChD,WAAY,CACZ,aAAc,CAGd,oBAAuB,CAAvB,sBAAuB,CAGzB,qBAEQ,UAAW,CACX,iBAAkB,CAClB,iBAAkB,CAClB,WAAY,CACZ,wBC3BW,CD+BnB,kCAEQ,QAAS,CACT,QAAS,CAKjB,OACI,iBCQoB,CDPpB,eAAgB,CAChB,cElC8B,CFmC9B,eAAgB,CAChB,iBAAkB,CAClB,qBClCkB,CD4BtB,iBASQ,WAAY,CACZ,YAAa,CACb,mBAAoB,CAX5B,8CAcY,qBC1CU,CD4BtB,uBAkBY,uBC9CU,CDmDtB,aACI,wBC3Da,CD4Db,UAAc,CAGlB,aACI,wBCjEa,CDkEb,UAAc,CAGlB,gBACI,wBCpEgB,CDqEhB,UAAc,CAIlB,qBACI,aAAc,CAGlB,iBACI,uBAAwB,CAI5B,EACE,aC7FsB,CD8FtB,cAAe,CAGjB,QACI,kCAAuC,CACvC,qBChFyB,CDoF7B,QACE,iBAAkB,CAClB,sBAAuB,CAGzB,gBACE,iBCxDsB,CD4DxB,cAEI,WAAqC,CACrC,UAAoC,CAHxC,cAMI,WAAqC,CACrC,UAAoC,CAPxC,cAUI,WAAqC,CACrC,UAAoC,CAXxC,cAcI,WAAqC,CACrC,UAAoC,CAKxC,cACE,qBCxHqB,CDyHrB,4BAA8B,CAFhC,8BAKM,WA1ImB,CAqIzB,oBASI,WAAY,CACZ,UAAW,CAVf,oBAaI,WAAY,CACZ,UAAW,CAdf,oBAiBI,WAAY,CACZ,UAAW,CAlBf,oBAqBI,WAAY,CACZ,UAAW,CAIf,wDAEI,qBC7ImC,CD2IvC,4EAOM,qBCjJgC,CDuJtC,mDAEI,uBAAkC,CAFtC,mDAKI,uBAAkC,CALtC,6CAQI,uBAA+B,CARnC,6CAWI,uBAA+B,CAXnC,iDAcI,uBAAiC,CAdrC,qDAiBI,uBAAmC,CAjBvC,qDAoBI,uBAAmC,CAKvC,uCACE,qBCnL2B,CDsL7B,uFACE,wBCzM0C,CD4M5C,2HAEE,aC/MsB,CDgNtB,wBC/M0C,CDqNxC,SACE,aCvNkB,CDsNpB,QACE,aClNa,CDiNf,UACE,aCjNe,CDgNjB,UACE,aChNe,CD+MjB,MACE,aC/MW,CD8Mb,MACE,aC9MW,CD6Mb,SACE,aC7Mc,CD4MhB,SACE,qBC5M0B,CD2M5B,eACE,aC3MoB,CD0MtB,cACE,UC1MmB,CDyMrB,YACE,UCzMiB,CDwMnB,MACE,UCxMW,CDuMb,WACE,UCvMgB,CDsMlB,aACE,aCtMkB,CDqMpB,cACE,UCrMmB,CDoMrB,MACE,qBCpMuB,CDmMzB,gBACE,qBCnMiC,CDkMnC,eACE,qBClMgC,CDiMlC,YACE,UCjMgC,CDgMlC,sBACE,wBChM6C,CD+L/C,qBACE,wBC/L4C,CD8L9C,aACE,aCtNsC,CDqNxC,OACE,aC7LY,CD4Ld,MACE,qBCpMuB,CDmMzB,SACE,UC1MmB,CDgNrB,YACE,wBC9NkB,CD6NpB,WACE,wBCzNa,CDwNf,aACE,wBCxNe,CDuNjB,aACE,wBCvNe,CDsNjB,SACE,wBCtNW,CDqNb,SACE,wBCrNW,CDoNb,YACE,wBCpNc,CDmNhB,YACE,gCCnN0B,CDkN5B,kBACE,wBClNoB,CDiNtB,iBACE,qBCjNmB,CDgNrB,eACE,qBChNiB,CD+MnB,SACE,qBC/MW,CD8Mb,cACE,qBC9MgB,CD6MlB,gBACE,wBC7MkB,CD4MpB,iBACE,qBC5MmB,CD2MrB,SACE,gCC3MuB,CD0MzB,mBACE,gCC1MiC,CDyMnC,kBACE,gCCzMgC,CDwMlC,eACE,qBCxMgC,CDuMlC,yBACE,mCCvM6C,CDsM/C,wBACE,mCCtM4C,CDqM9C,gBACE,wBC7NsC,CD4NxC,UACE,wBCpMY,CDmMd,SACE,gCC3MuB,CD0MzB,YACE,qBCjNmB,CDuNrB,kCAEQ,cCtOY,CDoOpB,iCAEQ,cCjOO,CD+Nf,mCAEQ,cChOS,CD8NjB,mCAEQ,cC/NS,CD6NjB,+BAEQ,cC9NK,CD4Nb,+BAEQ,cC7NK,CD2Nb,kCAEQ,cC5NQ,CD0NhB,kCAEQ,sBC3NoB,CDyN5B,wCAEQ,cC1Nc,CDwNtB,uCAEQ,WCzNa,CDuNrB,qCAEQ,WCxNW,CDsNnB,+BAEQ,WCvNK,CDqNb,oCAEQ,WCtNU,CDoNlB,sCAEQ,cCrNY,CDmNpB,uCAEQ,WCpNa,CDkNrB,+BAEQ,sBCnNiB,CDiNzB,yCAEQ,sBClN2B,CDgNnC,wCAEQ,sBCjN0B,CD+MlC,qCAEQ,WChN0B,CD8MlC,+CAEQ,yBC/MuC,CD6M/C,8CAEQ,yBC9MsC,CD4M9C,sCAEQ,cCrOgC,CDmOxC,gCAEQ,cC5MM,CD0Md,+BAEQ,sBCnNiB,CDiNzB,kCAEQ,WCzNa,CEXzB,eACE,gBAAiB,CACjB,iBAAkB,CAClB,cAAe,CACf,iBAAkB,CAElB,yBANF,eAOM,YCW2B,CDThC,CAED,kBACE,WCK8B,CDJ9B,cAAe,CEbjB,UACE,cAAe,CACf,QAAS,CACT,MAAO,CACP,OAAQ,CACR,SAAU,CACV,qBAAyB,CACzB,yBJIuB,CIAzB,gBACE,QAAS,CACT,aAAc,CACd,gBAAiB,CACjB,WAAY,CACZ,YAAa,CAGf,yBACE,gBAAiB,CCpBnB,UACI,SAAU,CADd,gBAIQ,uBAAyB,CACzB,WAAY,CACZ,UAAW,CACX,qBAAsB,CAP9B,qBAWQ,cAAe,CACf,YAAa,CACb,aAAc,CACd,iBAAkB,CAElB,yCAhBR,qBAiBY,aAAc,CAMrB,CAvBL,sDAqBY,QAAc,CAKtB,yCA1BJ,6FA6BgB,cJlBkB,CImBrB,CC9Bb,QACE,qBNUuB,CMPzB,sBACE,eNmCwB,CMhC1B,SACE,yBAA2B,CAG7B,cACE,aAAc,CACd,WAAY,CACZ,iBAAkB,CAClB,MAAO,CACP,OAAQ,CACR,sBAAyB,CAEzB,yCARF,cASI,YAAa,CAuBhB,CAhCD,uBAaI,SAAU,CAbd,+BAiBI,SAAU,CACV,qBAAuB,CAlB3B,gLA8BI,SAAU,CAId,6BACE,WAAY,CAEZ,yCAHF,6BAII,gBAAiB,CACjB,YAAa,CAEhB,CAEC,yCCwZA,uCDvZE,gBAAiB,CACjB,iBAAkB,CAErB,CAED,cACE,YAAa,CADf,mDAMI,eAAgB,CAChB,YAAa,CACb,eAAgB,CAChB,cL3D8B,CK6D9B,yCAXJ,mDAYM,cL9D4B,CK+D5B,iBAAkB,CAErB,CAID,yCADF,oBAEI,cAAe,CAElB,CAED,0CAEE,YAAa,CAFf,kEAKI,gBAAiB,CAEjB,yCAPJ,kEAQM,aAAc,CACd,cAAe,CAElB,CAXH,0DAcI,0BAA2B,CAI/B,2FAEE,gBAAiB,CEzGnB,OACI,+BAA6C,CCDjD,QACE,eTuCwB,CStCxB,SAAU,CAEV,yCAJF,QAKI,gBAAiB,CACjB,+BAA6C,CAMhD,CAHC,yCATF,QAUI,gBAAiB,CAEpB,CCZD,YACI,eVuCsB,CUtCtB,+BAA6C,CCEjD,mBACE,+BAAoC,CACpC,uBAAmC,CAFrC,6BAKI,qBXQyB,CYpB7B,aACI,YAAa,CACb,SAAU,CACV,qBAAsB,CAG1B,uBACI,WAAY,CACZ,UAAW,CACX,YAAa,CACb,mBAAoB,CACpB,YAAa,CACb,SAAU,CAGd,uBACI,iBAAkB,CAClB,SAAU,CACV,eAAgB,CAGpB,eACI,OAAQ,CACR,QAAS,CACT,gCZFkC,CYGlC,mCZHkC,CYIlC,qBZJkC,CYDtC,qBAQQ,WAAY,CACZ,UAAc,CACd,OAAQ,CACR,UAAW,CACX,iBAAkB,CAClB,eAAgB,CAIxB,qBACI,kCAA0C,CAC1C,qCAA6C,CAGjD,yBACI,KAAM,CACN,OAAQ,CACR,2BZQoB,CYXxB,wCAMQ,qBAAsB,CACtB,kCAAmC,CAI3C,wBACI,KAAM,CACN,MAAO,CACP,0BZHoB,CYAxB,uCAMQ,qBAAsB,CACtB,mCAAoC,CAI5C,4BACI,QAAS,CACT,OAAQ,CACR,8BZdoB,CYWxB,2CAMQ,wBAAyB,CACzB,kCAAmC,CAI3C,2BACI,QAAS,CACT,MAAO,CACP,6BZzBoB,CYsBxB,0CAMQ,wBAAyB,CACzB,mCAAoC,CAI5C,qBACI,qBAAyB,CAG7B,2BACI,cAAe,CACf,oBAAqB,CC7FzB,WACE,wBAA0B,CAC1B,kBAAoB,CACpB,sBAAwB,CACxB,UAAY,CACZ,mCAAqC,CAEvC,SACE,sBAAwB,CAE1B,iBACE,kCAAoC,CACpC,+BAAiC,CACjC,8BAAgC,CAChC,0BAA4B,CAE9B,YACE,UAAY,CCjBd,UACI,WXkB4B,CWfhC,cACI,WXe4B,CWZhC,eACI,YXY6B,CYrBjC,aACI,iBfqDoB,CepDpB,eAAgB,CAEhB,yBAJJ,aAKQ,eAAuC,CAU9C,CAPG,yBARJ,aASQ,eAAuC,CAM9C,CAHG,0BAZJ,aAaQ,gBAAuC,CAE9C,CAOD,kDAJI,0BfoCoB,CenCpB,2BfTa,CeYjB,8BAGI,kBAAqB,CACrB,wBfhBa,CeYjB,8CAOQ,cAAe,CACf,aAAc,CACd,gBAAiB,CAIzB,sBACI,aAAc,CACd,gBAAiB,CACjB,gBAAiB,CAGrB,sBACI,6BfYoB,CeXpB,8BfWoB,CeRxB,qBACI,iBfOoB,CeLpB,QAAc,CACd,4CAAiD,CACjD,cdrC8B,CcsC9B,SAAU,CANd,uDASQ,iBAAkB,CAClB,UAAW,CACX,WAAY,CAXpB,0DAgBY,KAAM,CACN,SAAU,CAjBtB,kFAoBgB,gCfxDC,CeyDD,kCAAmC,CACnC,mCAAoC,CACpC,iBAAkB,CAClB,SAAU,CAxB1B,yFA4BgB,QAGuC,CA/BvD,4DAoCY,YAAa,CACb,SAAU,CArCtB,oFAwCgB,6Bf5EC,Ce6ED,kCAAmC,CACnC,mCAAoC,CACpC,iBAAkB,CAClB,SAAU,CA5C1B,2FAgDgB,QAGuC,CAnDvD,4DAwDY,QAAS,CACT,UAAW,CAzDvB,oFA4DgB,+BfhGC,CeiGD,oCAAqC,CACrC,iCAAkC,CAClC,iBAAkB,CAClB,MAAO,CACP,SAAU,CAjE1B,2FAqEgB,QAGqC,CAxErD,6DA6EY,QAAS,CACT,WAAY,CA9ExB,qFAiFgB,8BfrHC,CesHD,oCAAqC,CACrC,iCAAkC,CAClC,iBAAkB,CAClB,OAAQ,CACR,SAAU,CAtF1B,4FA0FgB,QAGqC,CCrIrD,iBACI,gBAAiB,CAGrB,4BACI,eAAgB,CAGpB,4CAEQ,cAAe,CAFvB,kDAMQ,YAAa,CAIrB,eACI,iBAAkB,CAGtB,yDAEQ,ahB7BgB,CgBiCxB,2DACI,WAAY,CACZ,wBhBvBsB,CgBwBtB,qBhBrBa,CgBsBb,iBAAkB,CAJtB,uEAOQ,qBAAyB,CAPjC,+EAWQ,qBhBxB+B,CgB4BvC,0CACI,UAAW,CAGf,2BACI,qBhBjCmC,CgBoCvC,yBACI,iBAAkB,CAClB,UAAW,CAFf,2CAKQ,+BAAwC,CAIhD,mCACI,OAjE8B,CAmE9B,4DACI,QAnEoC,CAuE5C,mCACI,UAzE8B,CA2E9B,4DACI,WAAsD,CAK9D,mHACI,eAAgB,CAChB,qBhBjEyB,CgB+D7B,6JAKQ,ahBvFgB,CgB2FxB,oBAEQ,sBAAuB,CACvB,eAAgB,CAChB,cAAe,CACf,eAAgB,CAChB,qBhB7E+B,CgBkFnC,yCADJ,wBAEQ,eAAgB,CAMvB,CAHG,yCALJ,wBAMQ,eAAgB,CAEvB,CAED,yCAEQ,qBAAyB,CAFjC,+DAKY,eAAgB,CAChB,qBhBxGa,CgB6GzB,gBACI,WhB5EiC,CgB2ErC,sBAIQ,WAAY,CACZ,UAAW,CACX,aAAc,CACd,YAAa,CACb,QAAc,CACd,cftH0B,CeuH1B,eAAgB,CCrIxB,OACE,cAAe,CACf,UAAW,CACX,eAAgB,CAChB,YAAa,CAJf,kHAYM,qBjBIW,CiBHX,WAAY,CACZ,chBA4B,CgBC5B,eAAgB,CAChB,WAAY,CACZ,cAAe,CACf,kBAAmB,CAlBzB,6BAwBI,wBjBXsB,CiBYtB,SAAU,CACV,kBAAmB,CA1BvB,0BA8BI,QAAS,CA9Bb,yBAkCI,YAAa,CAIjB,4BAGM,gBAAiB,CAKvB,mBACE,UAAW,CAGb,aACE,QAAc,CACd,wBAAyB,CACzB,qBAAyB,CACzB,cAAe,CACf,aAAc,CALhB,gCASI,aAAc,CACd,QAAc,CAVlB,gBAcI,eAAgB,CAChB,WAAY,CAfhB,0BAoBM,iBAAkB,CAClB,eAAgB,CAChB,UAAW,CACX,mBAAoB,CACpB,iBAAkB,CAClB,eAAmB,CAKzB,mBACE,ed9D8B,CckE9B,yCADF,mBAEI,eAAgB,CAEnB,CAED,oBACE,chB7EgC,CgB8EhC,eAAgB,CAGlB,wBACE,WAAY,CACZ,QAAS,CAGX,wBACE,wBjBnFsB,CiBoFtB,UjB/EoC,CiBgFpC,ejB5DwB,CiB6DxB,WjB7DwB,CiBgE1B,0BACE,UAAc,CACd,mBAAoB,CACpB,QAAS,CACT,WAAY,CACZ,kBAAmB,CACnB,eAAgB,CAChB,UAAW,CAGb,0BACE,QAAS,CAGX,mCACE,wBAAyB,CAG3B,UACE,eAAgB,CAChB,kBAAmB,CACnB,eAAgB,CAIhB,yCADF,eAEI,eAAgB,CAChB,eAAgB,CAChB,sBAAuB,CACvB,kBAAmB,CAEtB,CC1ID,kBACI,cAAe,CADnB,2HAWY,WAAY,CAKxB,kBACI,elB0BsB,CkB3B1B,oCAIQ,WlBuBkB,CkBtBlB,elBsBkB,CkB3B1B,4CASQ,WlBkBkB,CkBjBlB,gBlBiBkB,CkBhBlB,UlBgBkB,CkBZ1B,wCAEQ,aAAc,CACd,cjBpB0B,CiBwBlC,qBACI,+BAAkD,CADtD,uCAIQ,cjB5B0B,CiB6B1B,eAAgB,CAIxB,SACI,cAAe,CACf,MAAO,CACP,OAAQ,CACR,Qf5CoB,Ce6CpB,SAAU,CAGd,gBACI,eAAgB,CAChB,cjB3C8B,CiB4C9B,eAAgB,CAGpB,gBACI,iBAAkB,CXk5BtB,0BW/4BI,kBAAmB,CACnB,gBAAiB,CAGrB,sCACI,QAAS,CAGb,4CACI,YAAa,CACb,eAAgB,CAChB,wBlB/DsB,CkB4D1B,8EAMQ,WAAY,CACZ,eAAgB,CX+4BxB,kGWz4BU,gBAAiB,CAK3B,6BACI,WAAY,CAEZ,yCAHJ,6BAIQ,WAAY,CAMnB,CAHG,yCAPJ,6BAQQ,WAAY,CAEnB,CCrGD,WACI,qBAAyB,CACzB,4BnBYqB,CmBdzB,iDAKQ,qBnBeqB,CmBdrB,qBAAyB,CANjC,iEASY,qBAAsB,CATlC,yFAaY,SAAU,CAbtB,uEAiBY,gBAAiB,CAjB7B,yBAsBQ,YAAa,CAIrB,kGAEQ,uCAA+C,CAC/C,gBAAiB,CAIzB,kGAIQ,uCAA+C,CAC/C,gBAAiB,CAIzB,qBACI,qBAAsB,CAG1B,kDACI,cAAe,CACf,wBnBnCsB,CmBsC1B,oBACI,uBAAyB,CAG7B,6BACI,mBAAoB,CACpB,UAAW,CACX,gBAAiB,CACjB,QAAS,CACT,kBAAmB,CACnB,eAAgB,CAChB,eAAgB,CAGpB,WACI,clBpD8B,CmBdlC,KACI,iBAAkB,CAGtB,KACI,kBAAmB,CAGvB,UACI,iBAAkB,CAClB,KAAM,CACN,QAAS,CACT,MAAO,CACP,OAAQ,CACR,gCAAkC,CAClC,SAAU,CAPd,kBAUQ,SAAU,CAIlB,UACI,qBpBFmC,CoBGnC,eAAgB,CAFpB,kBAKQ,gBAAiB,CAIzB,oBACI,oBAAqB,CAGzB,qBACI,qBpBrBmB,CoBsBnB,WAAY,CAOhB,wCACI,SAAU,CAEV,yBAHJ,oBAIQ,WAAY,CAEnB,CAED,UACI,mCAAqC,CADzC,4BAKY,wBAKG,CAKf,yBACI,2BpBdoB,CoBepB,0BpBfoB,CoBaxB,+BAKQ,YAAa,CALrB,qDAQY,6BpB3DK,CoBgEjB,gBACI,qCAA0C,CAD9C,yBAIQ,eAAgB,CAEhB,cAAe,CACf,yBAA2B,CAC3B,eAAgB,CAChB,gBAAiB,CATzB,uCAYY,YAAa,CAYzB,2BACI,oBAAqB,CACrB,oBAAqB,CAGzB,yBACI,eAAgB,CAChB,qBpBzFkC,CoBgGtC,sCAEQ,4IACsF,CAI9F,oBACI,QAAS,CAGb,gBACI,yBpBnHmB,CoBoHnB,8BpB7EoB,CoB8EpB,6BpB9EoB,CoB+EpB,gBAAiB,CACjB,eAAgB,CAGpB,iBACI,WAAY,CACZ,cAAe,CACf,UAAc,CACd,QAAS,CACT,eAAgB,CALpB,yBAQQ,iBAAkB,CAClB,UAAc,CATtB,oEAcY,wBpB5IQ,CoBiJpB,iBACI,iBAAkB,CAClB,eAAgB,CAChB,eAAgB,Cbk+BpB,2Ba/9BI,iBAAkB,CAClB,kBAAmB,CAGvB,gBACI,aAAc,CAGlB,oBACI,UAAW,CADf,kCAIQ,KAAM,CAId,0BACI,eAAgB,CAChB,UAAW,CAGf,kBACI,aAAc,CACd,cAAe,CCrLnB,eACI,iBAAkB,CAClB,QAAS,CACT,UAAW,CAHf,oBAMQ,wBAAoC,CACpC,gBAAiB,CdkpCzB,yBc9oCE,UAAU,CACV,SAAS,CAOX,oBACI,eAAgB,CAEhB,cpBZ8B,CoBelC,oBACI,yBAA2B,CAC3B,2BAA6B,CAC7B,gBAAiB,CAGrB,wDAEQ,arBpCgB,CqBwCxB,cACI,iBAAkB,CAClB,QAA8C,CAC9C,QAAS,CACT,MAAO,CACP,qBAAsB,CACtB,UA9CqB,CA+CrB,eAAgB,CAChB,aAAc,CACd,iBAAkB,CAClB,2BrBnCa,CqBqCb,yCAZJ,cAaQ,YAAa,CAEpB,CdooCD,wBcloCE,OAAO,CACP,SAAS,CAGX,6CACI,cAAe,CACf,iBAAkB,CChEtB,QACE,iBAAkB,CAClB,WAAY,CACZ,gCAAkC,CAClC,UAAW,CAEX,yBANF,QAOI,aAAc,CACd,iBtBiDsB,CsBhDtB,gBAAiB,CAEpB,CCLD,MACI,aAAc,CACd,iBAAkB,CAClB,MAAO,CACP,OAAQ,CAER,yCANJ,MAQQ,iBAAkB,CAClB,kBAAmB,CAe1B,CAZG,yCAZJ,MAaQ,YAAa,CAWpB,CAxBD,eAiBQ,sBAAuB,CACvB,SAAU,CAlBlB,sBAsBQ,SAAU,CAOd,yCADJ,aAEQ,eAAgB,CAChB,kBAAmB,CAM1B,CAHG,yCANJ,aAOQ,gBAAiB,CAExB,CAED,cACI,gBAAiB,CACjB,qBAAyB,CACzB,iBvBSsB,CuBRtB,gBAAiB,CAEjB,yCANJ,cAOQ,eAAgB,CAQvB,CALG,yCAVJ,cAWQ,SAAU,CACV,oBAAqB,CACrB,uBAAwB,CAE/B,CAED,wBAEQ,qBAAyB,CAIjC,sBACI,iBAAkB,CAClB,SAAU,CACV,MAAO,CACP,OAAQ,CACR,SAAU,CALd,oCAQQ,oBAAsB,CAEtB,yCAVR,oCAWY,mBAAqB,CAE5B,CAGL,WACI,UAAc,CACd,sBAAuB,CAG3B,aACI,YAAa,CACb,WAAY,CACZ,eAAgB,CAChB,ctB/E8B,CsB2ElC,2CAQY,uBAA6B,CAC7B,UAAW,CATvB,oGAiBY,YAAa,CAjBzB,6BAsBQ,aAAc,CACd,qBvB5FqB,CuBgG7B,2BAEI,wBvBzGsB,CuB0GtB,4BvBzGqB,CuB0GrB,yBvB1GqB,CuB6GzB,0BACI,iBAAkB,CAGtB,mBACI,gBAAiB,CAGrB,qBACI,eAAgB,CAGpB,mBACI,sBAAuB,CACvB,kBAAmB,CACnB,eAAgB,CAEhB,yCALJ,mBAMQ,cAAe,CAEtB,CAED,YACI,eAAgB,CAChB,mBAAoB,CACpB,cAAe,CAEf,yCALJ,YAMQ,ctBzI0B,CsB2IjC,CAED,uBACI,mBAAoB,CAEpB,yCAHJ,uBAIQ,mBAAoB,CAc3B,CAXG,0CAPJ,uBAQQ,mBAAoB,CAU3B,CAlBD,8CAYQ,aAAc,CAZtB,6CAgBQ,cAAe,CAIvB,6BACI,iBAAkB,CAClB,eAAgB,CAChB,qBvB7JmC,CuBgKvC,6BACI,wBAAyB,CAG7B,iDAKQ,cAAe,CAKnB,yCADJ,qBAEQ,iBAAuC,CAE9C,CAGG,yCADJ,sBAEQ,kBvB/JkB,CuBiKzB,CAED,cACI,iBAAkB,CAClB,OAAQ,CACR,KAAM,CACN,UpB3MoB,CoB8MxB,uBACI,cAAe,CACf,UpBhNoB,CoBiNpB,qBAAyB,CACzB,aAAc,CACd,iBvBjKsB,CuBmKtB,yCAPJ,uBAQQ,OAAQ,CACR,QAAS,CACT,MAAO,CACP,UAAW,CACX,eAAgB,CAChB,SAAU,CACV,YAAa,CACb,WvBzLkB,CuB2LzB,CAID,WACI,QAAS,CAET,yCAHJ,WAIQ,YAAa,CACb,eAAgB,CAsBvB,CA3BD,oBASQ,gBAAiB,CACjB,iBAAkB,CAV1B,kCAeY,gBAAiB,CAf7B,mFAoBQ,gBAAiB,CACjB,iBAAkB,CArB1B,+BAyBQ,YAAa,CAIrB,aACI,wBvBvQoB,CuBwQpB,oBAAqB,CACrB,YAAa,CACb,qBAAyB,CAO7B,iCACI,qBAAsB,CACtB,oBAAqB,CC/QrB,yCADJ,oCAGY,UAAW,CACd,CAIT,iBACI,yBAA0B,CAC1B,QAAS,CACT,WAAY,CAHhB,+BAMQ,oBAAqB,CACrB,WAAY,CACZ,eAAc,CAAd,YAAc,CAEd,8BxBPe,CwBOf,uBxBPe,CwBOf,kBxBPe,CwBQf,eAAgB,CAXxB,0CAcY,YAAa,CAdzB,2BAmBQ,QAAc,CACd,wBAAyB,CACzB,yBAA0B,CAC1B,2BAA4B,CAC5B,4BAA6B,CAIrC,2BACI,YAAa,CACb,WAAY,CACZ,cAAe,CACf,QAAS,CACT,UAAW,CACX,iBAAkB,CAClB,SAAU,CAIV,yCADJ,0CAGY,WAAY,CACZ,qBAAuB,CAC1B,CAIT,iCACI,QAAS,CACT,WAAY,CACZ,SAAU,CACV,UAAW,CACX,eAAgB,CAChB,cAAe,CACf,WAAY,CACZ,UAAW,CARf,kDAWQ,WAAY,CACZ,UAAW,CACX,aAAc,CACd,eAAgB,CAChB,iBxBnBgB,CwBuBxB,sCACI,WAAY,CACZ,WAAY,CAFhB,wMAKQ,YAAa,CAIrB,0BACI,+BAAkD,CAClD,yBAA0D,CAG9D,iCACI,eAAgB,CAGpB,0BAKI,qBAAyB,CAL7B,oHAEQ,cvBnF0B,CuBiFlC,kDAQQ,QAAc,CACd,eAAgB,CAChB,SAAU,CACV,eAAgB,CAXxB,0CAeQ,YAAa,CAIrB,kCACI,WAAY,CACZ,qBAAyB,CACzB,cvBvG8B,CuB0GlC,yCACI,sEAEQ,KAAM,CACN,QAAS,CACT,MAAO,CACP,OAAQ,CACR,eAAgB,CAChB,cAAe,CACf,WAAY,CACZ,UAAW,CATnB,uFAYY,eAAgB,CAK5B,sBACI,YAAa,CAChB,CAGL,0BACI,cAAe,CACf,KAAM,CACN,MAAO,CACP,UAAW,CACX,WAAY,CACZ,SAAU,CACV,wBAAoC,CACpC,WAAY,CAGhB,eACI,sBAAyB,CAD7B,wBAIQ,SAAU,CAJlB,gCAQQ,SAAU,CACV,qBAAuB,CAT/B,sLAkBQ,SAAU,CAIlB,eACI,0BAA4B,CAC5B,kBAAmB,CACnB,aAAc,CAGlB,wBACI,YAAa,CACb,eAAgB,CAChB,iBAAkB,CAClB,SAAU,CACV,qBxB3Ka,CwB4Kb,0BxBtIoB,CwBuIpB,2BxBvIoB,CwB0IxB,kEACI,iBAAkB,CAClB,MAAO,CACP,OAAQ,CAGZ,oCACI,qCAAuC,CACvC,0BxBlJoB,CwBmJpB,2BxBnJoB,CwBoJpB,iCAA0C,CAC1C,+BAAiC,CACjC,KAAM,CACN,QAAS,CAGb,8BACI,QAAS,CACT,WAAY,CACZ,eAAgB,CAChB,eAAgB,CAChB,gBAAiB,CACjB,eAAgB,CAChB,oCAAwC,CACxC,yBxB1MqB,CwBkMzB,oCAWQ,UAAW,CACX,gBAAiB,CACjB,iBAAkB,CAClB,QAAS,CACT,OAAQ,CACR,UAAW,CACX,WAAa,CACb,6EAAiF,CAIzF,yCAEQ,cAAe,CACf,0BAA6B,CAC7B,eAAmB,CACnB,iBAAkB,CAClB,oBAAqB,CACrB,aAAc,CACd,mBAAoB,CACpB,qBAAsB,CACtB,gBAAiB,CACjB,kBAAmB,CACnB,aAAc,CAGd,kCAAmC,CAEnC,iCAAkC,CAGlC,iCAAkC,CAGlC,4BAA6B,CAE7B,cAAe,CACf,qBxB1O8B,CwB8OtC,6CACI,mBAAoB,CAGxB,kCACI,UAAY,CACZ,aAAc,CAFlB,0CAKQ,cAAe,CAIvB,qBACI,cAAe,CAGnB,wBACI,QAAS,CACT,aAAc,CACd,UAAc,CACd,qBxBtQqB,CwBuQrB,6BxBpOoB,CwBqOpB,8BxBrOoB,CwB+NxB,gCASQ,UAAc,CAItB,2BACI,QAAS,CAGb,qCACI,cAAe,CACf,eAAgB,CAGpB,2BACI,qBxB7RmB,CwB8RnB,YAAa,CACb,kBAAmB,CACnB,iBAAkB,CAClB,iBAAkB,CAGtB,oCACI,cAAe,CACf,WAAY,CAGhB,mCACI,iBAAkB,CAClB,OAAQ,CACR,UAAW,CAEX,oBAAsB,CACtB,qBAAuB,CACvB,YAAa,CAPjB,2CAUQ,gBAAiB,CACjB,cAAe,CAIvB,qBACI,iBAAkB,CAClB,UAAW,CACX,aAAqC,CAHzC,oDAMQ,aAAqC,CAN7C,6BAUQ,eAAgB,CAChB,WAAY,CACZ,UAAW,CAInB,uBACI,iBAAkB,CAClB,YAAa,CACb,qBxB5UqB,CwB6UrB,kBAAmB,CACnB,qBxBvUmC,CwBwUnC,iBAAkB,CAClB,cAAe,CACf,6BAA8B,CAC9B,mBAAqB,CATzB,2DAYQ,qBAAuB,CAZ/B,0FAgBQ,oBxBjWW,CwBkWX,wBAA+C,CAC/C,axBnWW,CwBiVnB,2NAqBY,axBtWO,CwB2WnB,oBACI,WrB/V4B,CqBqWhC,uBACI,qBAAyB,CAD7B,oCAIQ,kBAAmB,CACnB,iBxB7WS,CwBiXjB,iFAGY,cAAe,CACf,QAAgD,CAChD,MAAO,CACP,OAAQ,CACR,SAAU,CAEV,yCATZ,iFAUgB,SAAmC,CAInC,cAJmC,CAsB1C,CAfG,yCAjBZ,iFAkBgB,cAAe,CActB,CAhCT,+FAsBgB,aAAc,CAEd,yCAxBhB,+FAyBoB,YAAa,CAMpB,CAHG,yCA5BhB,+FA6BoB,aAAc,CAErB,CA/Bb,8DAmCY,gBAAiB,CAK7B,8CAGQ,qBxB7Ze,CwB8Zf,qBxB7ZS,CwB8ZT,kBAAmB,CAI3B,gCACI,iBAAkB,CAClB,kBAAmB,CAMvB,iCACI,eAAgB,CAGpB,iCACI,cAAe,CACf,qBAAuB,CAIvB,yCADJ,kBAEQ,WAAY,CACZ,cAAe,CAOtB,CAJG,yCANJ,kBAOQ,WAAY,CACZ,cAAe,CAEtB,CAED,gBACI,UAAW,CACX,aAAc,CACd,eAAgB,CAChB,kBAAmB,CAJvB,+BAOQ,UAAW,CAPnB,wCAWQ,YAAa,CACb,WAAY,CAIpB,yBACI,eAAgB,CAGpB,yCACI,6EAEQ,qBAA6C,CAChD,CAIT,kBACI,qBAAyB,CACzB,aAAc,CC7elB,kBACI,oBAAsB,CAD1B,0BAIQ,gBAAiB,CAIzB,oBACI,iBAAkB,CAClB,iBAAkB,CAClB,wBzBLe,CyBMf,UAAW,CACX,QAAS,CACT,WAAY,CACZ,gBAAiB,CACjB,cAAe,CACf,eAAgB,CAChB,gBAAiB,CAVrB,2BAaQ,UAAW,CACX,iBAAkB,CAClB,UAAW,CACX,OAAQ,CACR,yCAA6C,CAC7C,gCAAiC,CACjC,mCAAoC,CAI5C,mBACI,aAAc,CAGlB,sBACI,WAAY,CAGhB,6BACI,eAAgB,CAGpB,kPAIQ,qBzB1B+B,CyB2B/B,cxBlC0B,CwB6BlC,0QAQY,cxBrCsB,CwBsCtB,WAAY,CACZ,UAAW,CACX,gBAAiB,CACjB,gBxBzCsB,CyBdlC,cACI,iB1BqDoB,C0BpDpB,SAAU,CACV,czBW8B,CyBV9B,eAAgB,CAEhB,0BANJ,cAOQ,yBAA2B,CAOlC,CAdD,iBAWQ,QAAS,CACT,eAAgB,CAIxB,4BACI,Y1BiCyE,C0B9B7E,2BACI,WAAY,CAEZ,yBAHJ,2BAIQ,WAAY,CAEnB,CAED,oBACI,qB1BNkC,C0BOlC,iBAAkB,CAGtB,qBACI,iBAAkB,CAClB,UAAW,CACX,QAAS,CACT,YAAa,CAJjB,4BAOQ,UAAW,CACX,iBAAkB,CAClB,4BAA6B,CAC7B,iCAAkC,CAClC,kCAAmC,CAI3C,+DACI,UAAW,CAGf,+CACI,WAAY,CnBsxDhB,+BmBjxDI,UAAW,CACX,SAAU,CnBoxDd,mFmBjxDI,SAAS,CACT,UAAU,CnBoxDd,yDmBjxDM,UAAW,CACX,UAAU,CAIhB,oBACI,gBAAiB,CAGrB,2BACI,eAAgB,CAGpB,0BACI,eAAgB,CAChB,qB1B5DmC,C0B+DvC,uDAIQ,SAAU,CAJlB,6CAOY,gBAAiB,CACjB,WAAY,CACZ,UAAW,CAKvB,2CAEQ,wBAAyB,CACzB,UAAW,CAInB,qBACI,iBAAkB,CAClB,gBAAiB,CAFrB,6BAKQ,a1BnFU,C0BuFlB,8BACI,iBAAkB,CAClB,KAAM,CACN,UAAW,CACX,mCAA0C,CAG9C,uBACI,wB1B7GsB,C0BgH1B,uBACI,YAAa,CC9HjB,aACI,yBAA0B,CAC1B,iBAAkB,CAClB,c1BW8B,C0BdlC,gBAMQ,iBAAkB,CAClB,4BAA6B,CAPrC,mBAWQ,UAAW,CACX,iBAAkB,CAClB,OAAQ,CACR,QAAS,CACT,UAAW,CACX,UAAW,CACX,KAAQ,CACR,WAAY,CACZ,iCAAkC,CAClC,oCAAqC,CACrC,+B3BHgB,C2BOxB,wCACI,yB3BXmB,C2BcvB,qBACI,YAAa,CACb,eAAgB,CAGpB,qBACI,iBAAkB,CAElB,2BAA4B,CAC5B,iBAAkB,CAClB,eAAgB,CAChB,iBAAkB,CAClB,UAAc,CACd,wB3BxBoB,C2B2BxB,qBACI,wB3BxCe,C2ByCf,WAAY,CACZ,iBAAkB,CAClB,KAAM,CACN,UAAW,CAGf,mBACI,iBAAkB,CAClB,UAAc,CAGlB,mBACI,YAAa,CACb,qBAAyB,CACzB,6B3BPoB,C2BQpB,8B3BRoB,C2BSpB,aAAc,CAGlB,qBACI,qBAAyB,CACzB,a3B1Da,C2B2Db,oBAAqB,CACrB,eAAgB,CAChB,c1BzD8B,C0BoDlC,4DAQQ,iBAAkB,CAR1B,4EAYQ,SAAS,CAZjB,4EAgBQ,SAAS,CAIjB,oBACI,eAAgB,CAGpB,mBACI,iBAAkB,CAClB,UAAY,CACZ,wBAAyB,CACzB,c1BhF8B,C0BmFlC,kCAEQ,gBAAiB,CAKzB,mBACI,kBAAmB,CACnB,gBAAiB,CACjB,gBAAiB,CAEjB,yCALJ,mBAMQ,0BAA2B,CAclC,CApBD,yBAUQ,+B3BxGS,C2B8FjB,wCAcQ,qBAAyB,CAdjC,wCAkBQ,wB3BhHS,C4BVjB,WACI,iBAAkB,CAGtB,oBACI,cAAe,CACf,aAAc,CAGlB,oBACI,eAAgB,CAChB,c3BG8B,C2BF9B,iBAAkB,CAElB,yCALJ,oBAMQ,aAAc,CAErB,CAED,uCACI,gBAAiB,CACjB,QAAS,CACT,c3BR8B,C2BWlC,mBACI,iBAAkB,CAClB,UAAW,CACX,QAAS,CAGb,yCAGY,mBAAqB,CACrB,cAAe,CACf,WAAY,CALxB,8FAUgB,8B5BnCG,C4B0CnB,2CAEQ,aAAc,CAFtB,0CAMQ,cAAe,CAIvB,0BACI,iBAAkB,CAClB,eAAgB,CAEhB,qB5BzCmC,C4B4CvC,0BACI,wBAAyB,CAG7B,mBACI,iBAAkB,CAClB,eAAgB,CAGpB,4BACI,cAAe,CAGnB,uBACI,iBAAkB,CAClB,YAAa,CACb,kBAAmB,CAEnB,yCALJ,uBAMQ,eAAgB,CAEvB,CAGG,yCADJ,2BAEQ,UAAW,CAElB,CAED,gCACI,gBAAiB,CACjB,UAAW,CAGf,+BACI,iBAAkB,CAClB,KAAM,CACN,OAAQ,CACR,WAAY,CACZ,8CAAsD,CACtD,eAAgB,CAChB,WAAY,CACZ,QAAS,CARb,uCAeQ,YAAa,CAIrB,qBACI,YAAa,CACb,WAAY,CAFhB,oCAKQ,wB5B9GkB,C4BkH1B,8BACI,iBAAoB,CACpB,4B5BlHmB,C4BqHvB,8BACI,a5BnHoB,C4BoHpB,eAAgB,CCnIpB,gCACI,eAAgB,CAGpB,oBACI,qB7BMqB,C6BJrB,6B7BOkB,C6BJtB,kBACI,qBAAsB,CAEtB,0CAHJ,kBAIQ,mBAAoB,CAE3B,CAED,iBACI,kBAAmB,CACnB,e1BJ4B,C0BM5B,yCAJJ,iBAKQ,kBAAmB,CAY1B,CATG,0CARJ,iBASQ,kBAAmB,CAQ1B,CAjBD,4BAcQ,iBAAkB,CAClB,UAAW,CAInB,2BACI,YAAa,CAGjB,yEACI,gBAAiB,CAGrB,uFACI,cAAe,CAInB,gFACI,kBAAmB,CACnB,eAAgB,CAChB,eAAgB,CAChB,gBAAiB,CACjB,kBAAmB,CAGvB,uBACI,U7B7CkB,C6BgDtB,8BACI,eAAgB,CAChB,eAAgB,CAGpB,0BACI,eAAgB,CAChB,oBAAqB,CAGzB,6BACI,cAAe,CACf,qBAAuB,CACvB,eAAgB,CAGpB,gBACI,qBAAyB,CACzB,eAAgB,CAChB,gBAAiB,CACjB,iBAAkB,CAClB,WAAY,CACZ,mBAAqB,CACrB,oBAAsB,CAG1B,yBACI,kBAAmB,CAGvB,mDACI,QAAS,CACT,SAAU,CAFd,qEAKQ,eAAgB,CAIxB,2FACI,WAAY,CACZ,QAAc,CAGlB,yBACI,aAAc,CADlB,kDAKY,aAAc,CAL1B,iDASY,cAAe,CAK3B,4BACI,SAAU,CACV,cAAe,CAGnB,qCACI,YAAa,CACb,kBAAmB,CAGvB,qBACI,SAAU,CAGd,+BACI,wB7B7HsB,C6BgI1B,6BACI,4BAA6B,CAC7B,SAAU,CAFd,iDAKQ,kBAAmB,CAI3B,2BACI,SAAU,CACV,iBAAkB,CAClB,eAAgB,CAEhB,yCALJ,2BAMQ,gBAAiB,CAExB,CAED,2GACI,qBAAyB,CACzB,WAAY,CACZ,cAAqC,CACrC,QAAc,CACd,gBAAiB,CACjB,eAAgB,CAChB,WAAY,CAGhB,gDACI,iBAAkB,CAClB,SAAU,CACV,eAAgB,CAGpB,uFACI,YAAa,CAGjB,8FACI,cAAqC,CACrC,SAAU,CACV,QAAS,CAGb,4BACI,cAAe,CADnB,8BAIQ,yBAA2B,CAC3B,+BAA+B,CAIvC,sBACI,WAAY,CAGhB,uCACI,aAAc,CACd,QAAS,CAFb,yDAKQ,YAAa,CAIrB,+BACI,eAAgB,CADpB,0CAIQ,YAAa,CACb,eAAgB,CCjNxB,0BACI,QAAc,CCLlB,cACI,eAAgB,CAChB,eAAgB,CAGpB,gBACI,YAAa,CACb,aAAc,CAGd,iBAAqB,CAArB,kBAAqB,CAArB,kBAAqB,CCPzB,eACI,qBhCUqB,CgCTrB,kBAAmB,CACnB,WAAY,CACZ,6BhCUkB,CgCPtB,eACI,mBAAoB,CAIpB,yCADJ,2BAEQ,YAAa,CAEpB,CAED,sBACI,YAAa,CACb,eAAgB,CAChB,ahCtBoB,CgCyBxB,uBACI,SAAU,CAGd,6CAEQ,ahCzBa,CgC6BrB,wBACI,gBAAiB,CAEjB,iBAAkB,CAClB,qBhCzBmB,CgC0BnB,gCAAkC,CAClC,6BhCYoB,CgCXpB,8BhCWoB,CgCTpB,mBAAe,CAAf,cAAe,CAEf,yCAXJ,wBAYQ,6BAA0B,CAA1B,wBAA0B,CAOjC,CAnBD,4BAgBQ,cAAe,CACf,WAAY,CAIpB,8BACI,wBhCzDoB,CgC4DxB,oBACI,oBAAqB,CACrB,WAAY,CACZ,eAAgB,CAChB,8BAAmB,CAAnB,kBAAmB,CAOvB,8BACI,qBAAyB,CACzB,qBAAuB,CACvB,qBhC3Da,CgCwDjB,iDAMQ,UAAW,CAInB,oCACI,gBAAiB,CAGrB,gBACI,uBAAyB,CAGzB,oBAAqB,CACrB,UAAc,CACd,eAAgB,CANpB,wBASQ,SAAU,CACV,sBAAuB,CAV/B,wBAcQ,UAAc,CCpGtB,wBAEI,qBjCYqB,CiCRzB,yBACE,cAAe,CACf,cAAe,CAFjB,2BAKI,eAAgB,CAChB,kBAAmB,CACnB,eAAgB,CAChB,sBAAuB,CACvB,oBAAqB,CCfzB,6BAEQ,YAAa,CAGjB,yCALJ,kBAMQ,yBlCSe,CkCPtB,CAED,4BACI,iBAAkB,CAClB,WAAY,CAFhB,gDAKQ,qBAAyB,CAIjC,4BACI,iBAAkB,CAGtB,6BACE,WAAW,C3BuzEb,uC2BnzEE,UAAU,CC5BZ,UACE,aAAc","file":"vle.css","sourcesContent":["// Config\n$avatar-icon-padding: 6px !default;\n$avatar-icon-padding-lg: 8px !default;\n\nbody {\n background: color('body-bg');\n\n &.vle {\n overflow: hidden;\n }\n}\n\na {\n &:hover, &:focus { // TODO: remove when bootstrap css dependency is removed\n color: color('primary');\n }\n}\n\nblockquote {\n background-color: lighten(color('primary'), 56%);\n padding: 8px;\n margin: 16px 0;\n border-style: solid;\n border-color: color('primary');\n border-width: 0 0 0 3px;\n}\n\n.has-indicator {\n &:after {\n content: '';\n position: absolute;\n border-radius: 50%;\n padding: 5px;\n background-color: color('accent');\n }\n}\n\n.has-indicator--icon-button {\n &:after {\n top: 25px;\n left: 5px;\n }\n}\n\n// Badges\n.badge {\n border-radius: $card-border-radius;\n padding: 2px 6px;\n font-size: rem(1.2);\n font-weight: 500;\n font-style: normal;\n background-color: color('gray-dark');\n\n &.md-button {\n min-width: 0;\n min-height: 0;\n line-height: inherit;\n\n &:hover, &:focus {\n background-color: color('gray-dark');\n }\n\n &:focus {\n outline: 1px dotted color('gray-dark');\n }\n }\n}\n\n.badge--info {\n background-color: color('info');\n color: #ffffff;\n}\n\n.badge--warn {\n background-color: color('warn');\n color: #ffffff;\n}\n\n.badge--success {\n background-color: color('success');\n color: #ffffff;\n}\n\n// Dividers\n.divider--withmargin {\n margin: 16px 0;\n}\n\n.divider--dashed {\n border-top-style: dashed;\n}\n\n// Links\na {\n color: color('primary');\n cursor: pointer;\n}\n\n.active {\n background-color: rgba(158,158,158,0.2);\n color: color('text');\n}\n\n// Images & Icons\n.avatar {\n border-radius: 50%;\n box-sizing: content-box;\n}\n\n.avatar--square {\n border-radius: $card-border-radius;\n}\n\n// Rules for sizing avatars (matches material icons plus avatar-icon padding)\n.avatar {\n &.md-18 {\n height: 18px + $avatar-icon-padding*2;\n width: 18px + $avatar-icon-padding*2;\n }\n &.md-24 {\n height: 24px + $avatar-icon-padding*2;\n width: 24px + $avatar-icon-padding*2;\n }\n &.md-36 {\n height: 36px + $avatar-icon-padding*2;\n width: 36px + $avatar-icon-padding*2;\n }\n &.md-48 {\n height: 48px + $avatar-icon-padding*2;\n width: 48px + $avatar-icon-padding*2;\n }\n}\n\n// Rules for sizing avatar backgrounds (when using a child md-icon)\n.avatar--icon {\n background-color: color('gray-light');\n white-space: normal !important;\n\n &:not(.md-avatar) {\n padding: $avatar-icon-padding;\n }\n\n &.md-18 {\n height: 18px;\n width: 18px;\n }\n &.md-24 {\n height: 24px;\n width: 24px;\n }\n &.md-36 {\n height: 36px;\n width: 36px;\n }\n &.md-48 {\n height: 48px;\n width: 48px;\n }\n}\n\nmd-toolbar.md-light-theme:not(.md-menu-toolbar) {\n md-icon {\n color: color('text-secondary');\n }\n\n .md-button:disabled {\n md-icon {\n color: color('text-disabled');\n }\n }\n}\n\n// hacks for now\nmd-icon, .md-button:not([disabled]) {\n &.primary {\n color: color('primary') !important;\n }\n &.success {\n color: color('success') !important;\n }\n &.warn {\n color: color('warn') !important;\n }\n &.info {\n color: color('info') !important;\n }\n &.accent {\n color: color('accent') !important;\n }\n &.accent-1 {\n color: color('accent-1') !important;\n }\n &.accent-2 {\n color: color('accent-2') !important;\n }\n}\n\n// Theme overrides\nmd-input-container.md-wise-theme label {\n color: color('text');\n}\n\nmd-select-menu.md-default-theme md-option[selected], md-select-menu md-option[selected] {\n background-color: color('selected-bg');\n}\n\n.md-autocomplete-suggestions-container.md-default-theme li .highlight,\n.md-autocomplete-suggestions-container li .highlight {\n color: color('primary');\n background-color: color('selected-bg');\n}\n\n// Color\n// Set classes for each named color in the $colors map\n@each $key, $value in $colors {\n .#{$key} {\n color: $value;\n }\n}\n\n// Set classes for each named color in the $colors map\n@each $key, $value in $colors {\n .#{$key}-bg {\n background-color: $value;\n }\n}\n\n// Set theme colors for angular-ui elements\n@each $key, $value in $colors {\n md-progress-circular.#{$key} {\n path {\n stroke: $value;\n }\n }\n}\n","// Colors\n$_primary-color: #1C8CA8; // should match the default color of your ng-material default theme's 'primary' palette\n$_selected-bg: lighten($_primary-color, 59%);\n\n$color: (\n 'primary': $_primary-color,\n 'accent': #F05843, // should match the default color of your ng-material default theme's 'accent' palette\n 'accent-1': #795C3A,\n 'accent-2': #CAD266,\n 'warn': #c62828, // should match the default color of your ng-material default theme's 'warn' palette\n 'info': #ef6c00,\n 'success': #00C853,\n 'divider': rgba(0, 0, 0, 0.12),\n 'gray-lightest': #f7f7f7,\n 'gray-lighter': #eeeeee,\n 'gray-light': #dddddd,\n 'gray': #cccccc,\n 'gray-dark': #aaaaaa,\n 'gray-darker': #757575,\n 'gray-darkest': #333333,\n 'text': rgba(0, 0, 0, 0.87),\n 'text-secondary': rgba(0, 0, 0, 0.54),\n 'text-disabled': rgba(0, 0, 0, 0.26),\n 'text-light': rgba(255, 255, 255, 1),\n 'text-light-secondary': rgba(255, 255, 255, 0.70),\n 'text-light-disabled': rgba(255, 255, 255, 0.50),\n 'selected-bg': $_selected-bg,\n 'score': #FFC107\n);\n\n$body-color: (\n 'body': map-get($color, 'text'),\n 'body-bg': map-get($color, 'gray-lighter')\n);\n\n$colors: (map-merge($color, $body-color));\n\n// Typography\n$baseline-grid: 8px;\n$body-font-size-base: rem(1.500);\n$caption-font-size-base: rem(1.300);\n\n// Layout\n$wise-toolbar-height: 42px;\n\n// Menus\n$menu-border-radius: 2px;\n$max-visible-items: 6;\n$menu-item-height: 6 * $baseline-grid !default;\n$dense-menu-item-height: 4 * $baseline-grid !default;\n$max-menu-height: 2 * $baseline-grid + $max-visible-items * $menu-item-height !default;\n$max-dense-menu-height: 2 * $baseline-grid + $max-visible-items * $dense-menu-item-height !default;\n\n// Cards\n$card-border-radius: 4px;\n\n// Buttons\n$button-border-radius: 3px;\n","// Helper functions and mixins\n\n// Get colors from $colors map\n@function color($key) {\n @if map-has-key($colors, $key) {\n @return map-get($colors, $key);\n }\n @warn \"Unknown `#{$key}` in $colors.\";\n @return null;\n}\n\n// set size in pixels given rem\n@function rem($multiplier) {\n $font-size: 10px;\n @return $multiplier * $font-size;\n}\n","// 1. Config\n\n// 2. Base\n.l-constrained {\n margin-left: auto;\n margin-right: auto;\n max-width: 100%;\n position: relative;\n\n @media (min-width: $layout-breakpoint-xs) {\n width: $layout-breakpoint-md;\n }\n}\n\n.l-constrained-md {\n width: $layout-breakpoint-sm;\n max-width: 100%;\n}\n","// Helpers\n@function rem($multiplier) {\n $font-size: 10px;\n @return $multiplier * $font-size;\n}\n\n// Angular Material variables for use and !default overrides\n$md-toolbar-height: 52px;\n$md-toolbar-medium-tall-height: 74px;\n$md-toolbar-tall-height: 104px;\n$md-toolbar-height-mobile-portrait: 52px;\n$md-toolbar-height-mobile-landscape: 52px;\n\n$caption-font-size-base: rem(1.300);\n\n//$list-item-height: 56px;\n\n$card-border-radius: 4px;\n\n$layout-breakpoint-xs: 600px;\n$layout-breakpoint-sm: 960px;\n$layout-breakpoint-md: 1280px;\n$layout-breakpoint-lg: 1920px;\n\n$button-border-radius: 3px;\n\n$tooltip-fontsize-lg: rem(1.1);\n$tooltip-fontsize-sm: rem(1.1);\n//$tooltip-height-lg: rem(2.2);\n$tooltip-height-sm: rem(2.2);\n//$tooltip-top-margin-lg: rem(1.4);\n$tooltip-top-margin-sm: rem(1.4);\n//$tooltip-lr-padding-lg: rem(0.8);\n$tooltip-lr-padding-sm: rem(0.8);\n//$tooltip-max-width: rem(3.20);\n\n$progress-linear-bar-height: 7px;\n","// 1. Config\n\n// 2. Base\n.l-footer {\n position: fixed;\n bottom: 0;\n left: 0;\n right: 0;\n z-index: 1;\n background-color: #ffffff;\n border-top: 1px solid color('gray-lighter');\n}\n\n// Buttons\n.button--footer {\n margin: 0;\n padding-top: 0;\n padding-bottom: 0;\n min-width: 0;\n display: flex;\n}\n\n.button--footer__element {\n padding-left: 8px;\n}\n","// 1. Config\n\n// 2. Base\n.l-header {\n z-index: 3;\n\n .logo {\n margin-left: 0 !important;\n height: 36px;\n width: 36px;\n vertical-align: middle;\n }\n\n .logo-link {\n min-width: auto;\n display: none;\n padding: 0 4px;\n margin-right: 12px;\n\n @media only screen and (min-width: ($layout-breakpoint-xs)) {\n display: block;\n }\n\n &:hover, &:focus {\n border: 0 none;\n }\n }\n\n // Handle mobile portrait\n @media only screen and (max-width: ($layout-breakpoint-xs - 1)) {\n .md-toolbar-tools {\n h1, h2, h3 {\n font-size: $body-font-size-base;\n }\n }\n }\n}\n","// 1. Config\n\n// 2. Base\n.l-main {\n background-color: color('body-bg');\n}\n\n.l-main--with-toolbar {\n margin-top: $wise-toolbar-height;\n}\n\n#content {\n transition: margin-top 0.5s;\n}\n\n.view-content {\n margin: 0 auto;\n padding: 8px;\n position: absolute;\n left: 0;\n right: 0;\n transition: opacity 500ms;\n\n @media only screen and (min-width: $layout-breakpoint-sm) {\n padding: 16px;\n }\n\n &.ng-enter {\n opacity: 0;\n }\n\n .ng-enter-active {\n opacity: 1;\n transition-delay: 250ms;\n }\n\n &.ng-leave-active,\n &.ng-hide {\n opacity: 0;\n }\n\n &.ng-hide-add,\n &.ng-hide-add-active,\n &.ng-hide-remove,\n &.ng-hide-remove-active {\n opacity: 0;\n }\n}\n\n.view-content--with-sidemenu {\n padding: 8px;\n\n @media only screen and (min-width: $layout-breakpoint-xs) {\n margin-left: 54px;\n padding: 16px;\n }\n}\n[dir='rtl'] .view-content--with-sidemenu {\n @media only screen and (min-width: $layout-breakpoint-xs) {\n margin-left: auto;\n margin-right: 54px;\n }\n}\n\n.content-head {\n margin: 8px 0;\n\n h1,\n h2,\n h3 {\n font-weight: 300;\n margin-top: 0;\n margin-bottom: 0;\n font-size: rem(3.6);\n\n @media only screen and (max-width: $layout-breakpoint-sm - 1) {\n font-size: rem(3.2);\n text-align: center;\n }\n }\n}\n\n.content-head__more {\n @media only screen and (max-width: $layout-breakpoint-sm - 1) {\n margin-top: 8px;\n }\n}\n\n.content-head__item,\nh2.content-head__item {\n margin: 0 8px;\n\n .md-subhead {\n padding-left: 4px;\n\n @media only screen and (max-width: $layout-breakpoint-sm - 1) {\n display: block;\n padding-left: 0;\n }\n }\n\n md-icon {\n vertical-align: text-bottom;\n }\n}\n\n.stepSelectMenuContainer md-select-menu,\n.stepSelectMenuContainer md-select-menu md-content {\n max-height: 500px;\n}\n","/*\n WISE Project Styles\n */\nbody {\n background: #eeeeee; }\n body.vle {\n overflow: hidden; }\n\na:hover, a:focus {\n color: #1C8CA8; }\n\nblockquote {\n background-color: #e7f7fb;\n padding: 8px;\n margin: 16px 0;\n border-style: solid;\n border-color: #1C8CA8;\n border-width: 0 0 0 3px; }\n\n.has-indicator:after {\n content: '';\n position: absolute;\n border-radius: 50%;\n padding: 5px;\n background-color: #F05843; }\n\n.has-indicator--icon-button:after {\n top: 25px;\n left: 5px; }\n\n.badge {\n border-radius: 4px;\n padding: 2px 6px;\n font-size: 12px;\n font-weight: 500;\n font-style: normal;\n background-color: #aaaaaa; }\n .badge.md-button {\n min-width: 0;\n min-height: 0;\n line-height: inherit; }\n .badge.md-button:hover, .badge.md-button:focus {\n background-color: #aaaaaa; }\n .badge.md-button:focus {\n outline: 1px dotted #aaaaaa; }\n\n.badge--info {\n background-color: #ef6c00;\n color: #ffffff; }\n\n.badge--warn {\n background-color: #c62828;\n color: #ffffff; }\n\n.badge--success {\n background-color: #00C853;\n color: #ffffff; }\n\n.divider--withmargin {\n margin: 16px 0; }\n\n.divider--dashed {\n border-top-style: dashed; }\n\na {\n color: #1C8CA8;\n cursor: pointer; }\n\n.active {\n background-color: rgba(158, 158, 158, 0.2);\n color: rgba(0, 0, 0, 0.87); }\n\n.avatar {\n border-radius: 50%;\n box-sizing: content-box; }\n\n.avatar--square {\n border-radius: 4px; }\n\n.avatar.md-18 {\n height: 30px;\n width: 30px; }\n\n.avatar.md-24 {\n height: 36px;\n width: 36px; }\n\n.avatar.md-36 {\n height: 48px;\n width: 48px; }\n\n.avatar.md-48 {\n height: 60px;\n width: 60px; }\n\n.avatar--icon {\n background-color: #dddddd;\n white-space: normal !important; }\n .avatar--icon:not(.md-avatar) {\n padding: 6px; }\n .avatar--icon.md-18 {\n height: 18px;\n width: 18px; }\n .avatar--icon.md-24 {\n height: 24px;\n width: 24px; }\n .avatar--icon.md-36 {\n height: 36px;\n width: 36px; }\n .avatar--icon.md-48 {\n height: 48px;\n width: 48px; }\n\nmd-toolbar.md-light-theme:not(.md-menu-toolbar) md-icon {\n color: rgba(0, 0, 0, 0.54); }\n\nmd-toolbar.md-light-theme:not(.md-menu-toolbar) .md-button:disabled md-icon {\n color: rgba(0, 0, 0, 0.26); }\n\nmd-icon.primary, .md-button:not([disabled]).primary {\n color: #1C8CA8 !important; }\n\nmd-icon.success, .md-button:not([disabled]).success {\n color: #00C853 !important; }\n\nmd-icon.warn, .md-button:not([disabled]).warn {\n color: #c62828 !important; }\n\nmd-icon.info, .md-button:not([disabled]).info {\n color: #ef6c00 !important; }\n\nmd-icon.accent, .md-button:not([disabled]).accent {\n color: #F05843 !important; }\n\nmd-icon.accent-1, .md-button:not([disabled]).accent-1 {\n color: #795C3A !important; }\n\nmd-icon.accent-2, .md-button:not([disabled]).accent-2 {\n color: #CAD266 !important; }\n\nmd-input-container.md-wise-theme label {\n color: rgba(0, 0, 0, 0.87); }\n\nmd-select-menu.md-default-theme md-option[selected], md-select-menu md-option[selected] {\n background-color: #f4fbfd; }\n\n.md-autocomplete-suggestions-container.md-default-theme li .highlight,\n.md-autocomplete-suggestions-container li .highlight {\n color: #1C8CA8;\n background-color: #f4fbfd; }\n\n.primary {\n color: #1C8CA8; }\n\n.accent {\n color: #F05843; }\n\n.accent-1 {\n color: #795C3A; }\n\n.accent-2 {\n color: #CAD266; }\n\n.warn {\n color: #c62828; }\n\n.info {\n color: #ef6c00; }\n\n.success {\n color: #00C853; }\n\n.divider {\n color: rgba(0, 0, 0, 0.12); }\n\n.gray-lightest {\n color: #f7f7f7; }\n\n.gray-lighter {\n color: #eeeeee; }\n\n.gray-light {\n color: #dddddd; }\n\n.gray {\n color: #cccccc; }\n\n.gray-dark {\n color: #aaaaaa; }\n\n.gray-darker {\n color: #757575; }\n\n.gray-darkest {\n color: #333333; }\n\n.text {\n color: rgba(0, 0, 0, 0.87); }\n\n.text-secondary {\n color: rgba(0, 0, 0, 0.54); }\n\n.text-disabled {\n color: rgba(0, 0, 0, 0.26); }\n\n.text-light {\n color: white; }\n\n.text-light-secondary {\n color: rgba(255, 255, 255, 0.7); }\n\n.text-light-disabled {\n color: rgba(255, 255, 255, 0.5); }\n\n.selected-bg {\n color: #f4fbfd; }\n\n.score {\n color: #FFC107; }\n\n.body {\n color: rgba(0, 0, 0, 0.87); }\n\n.body-bg {\n color: #eeeeee; }\n\n.primary-bg {\n background-color: #1C8CA8; }\n\n.accent-bg {\n background-color: #F05843; }\n\n.accent-1-bg {\n background-color: #795C3A; }\n\n.accent-2-bg {\n background-color: #CAD266; }\n\n.warn-bg {\n background-color: #c62828; }\n\n.info-bg {\n background-color: #ef6c00; }\n\n.success-bg {\n background-color: #00C853; }\n\n.divider-bg {\n background-color: rgba(0, 0, 0, 0.12); }\n\n.gray-lightest-bg {\n background-color: #f7f7f7; }\n\n.gray-lighter-bg {\n background-color: #eeeeee; }\n\n.gray-light-bg {\n background-color: #dddddd; }\n\n.gray-bg {\n background-color: #cccccc; }\n\n.gray-dark-bg {\n background-color: #aaaaaa; }\n\n.gray-darker-bg {\n background-color: #757575; }\n\n.gray-darkest-bg {\n background-color: #333333; }\n\n.text-bg {\n background-color: rgba(0, 0, 0, 0.87); }\n\n.text-secondary-bg {\n background-color: rgba(0, 0, 0, 0.54); }\n\n.text-disabled-bg {\n background-color: rgba(0, 0, 0, 0.26); }\n\n.text-light-bg {\n background-color: white; }\n\n.text-light-secondary-bg {\n background-color: rgba(255, 255, 255, 0.7); }\n\n.text-light-disabled-bg {\n background-color: rgba(255, 255, 255, 0.5); }\n\n.selected-bg-bg {\n background-color: #f4fbfd; }\n\n.score-bg {\n background-color: #FFC107; }\n\n.body-bg {\n background-color: rgba(0, 0, 0, 0.87); }\n\n.body-bg-bg {\n background-color: #eeeeee; }\n\nmd-progress-circular.primary path {\n stroke: #1C8CA8; }\n\nmd-progress-circular.accent path {\n stroke: #F05843; }\n\nmd-progress-circular.accent-1 path {\n stroke: #795C3A; }\n\nmd-progress-circular.accent-2 path {\n stroke: #CAD266; }\n\nmd-progress-circular.warn path {\n stroke: #c62828; }\n\nmd-progress-circular.info path {\n stroke: #ef6c00; }\n\nmd-progress-circular.success path {\n stroke: #00C853; }\n\nmd-progress-circular.divider path {\n stroke: rgba(0, 0, 0, 0.12); }\n\nmd-progress-circular.gray-lightest path {\n stroke: #f7f7f7; }\n\nmd-progress-circular.gray-lighter path {\n stroke: #eeeeee; }\n\nmd-progress-circular.gray-light path {\n stroke: #dddddd; }\n\nmd-progress-circular.gray path {\n stroke: #cccccc; }\n\nmd-progress-circular.gray-dark path {\n stroke: #aaaaaa; }\n\nmd-progress-circular.gray-darker path {\n stroke: #757575; }\n\nmd-progress-circular.gray-darkest path {\n stroke: #333333; }\n\nmd-progress-circular.text path {\n stroke: rgba(0, 0, 0, 0.87); }\n\nmd-progress-circular.text-secondary path {\n stroke: rgba(0, 0, 0, 0.54); }\n\nmd-progress-circular.text-disabled path {\n stroke: rgba(0, 0, 0, 0.26); }\n\nmd-progress-circular.text-light path {\n stroke: white; }\n\nmd-progress-circular.text-light-secondary path {\n stroke: rgba(255, 255, 255, 0.7); }\n\nmd-progress-circular.text-light-disabled path {\n stroke: rgba(255, 255, 255, 0.5); }\n\nmd-progress-circular.selected-bg path {\n stroke: #f4fbfd; }\n\nmd-progress-circular.score path {\n stroke: #FFC107; }\n\nmd-progress-circular.body path {\n stroke: rgba(0, 0, 0, 0.87); }\n\nmd-progress-circular.body-bg path {\n stroke: #eeeeee; }\n\n.l-constrained {\n margin-left: auto;\n margin-right: auto;\n max-width: 100%;\n position: relative; }\n @media (min-width: 600px) {\n .l-constrained {\n width: 1280px; } }\n\n.l-constrained-md {\n width: 960px;\n max-width: 100%; }\n\n.l-footer {\n position: fixed;\n bottom: 0;\n left: 0;\n right: 0;\n z-index: 1;\n background-color: #ffffff;\n border-top: 1px solid #eeeeee; }\n\n.button--footer {\n margin: 0;\n padding-top: 0;\n padding-bottom: 0;\n min-width: 0;\n display: flex; }\n\n.button--footer__element {\n padding-left: 8px; }\n\n.l-header {\n z-index: 3; }\n .l-header .logo {\n margin-left: 0 !important;\n height: 36px;\n width: 36px;\n vertical-align: middle; }\n .l-header .logo-link {\n min-width: auto;\n display: none;\n padding: 0 4px;\n margin-right: 12px; }\n @media only screen and (min-width: 600px) {\n .l-header .logo-link {\n display: block; } }\n .l-header .logo-link:hover, .l-header .logo-link:focus {\n border: 0 none; }\n @media only screen and (max-width: 599px) {\n .l-header .md-toolbar-tools h1, .l-header .md-toolbar-tools h2, .l-header .md-toolbar-tools h3 {\n font-size: 15px; } }\n\n.l-main {\n background-color: #eeeeee; }\n\n.l-main--with-toolbar {\n margin-top: 42px; }\n\n#content {\n transition: margin-top 0.5s; }\n\n.view-content {\n margin: 0 auto;\n padding: 8px;\n position: absolute;\n left: 0;\n right: 0;\n transition: opacity 500ms; }\n @media only screen and (min-width: 960px) {\n .view-content {\n padding: 16px; } }\n .view-content.ng-enter {\n opacity: 0; }\n .view-content .ng-enter-active {\n opacity: 1;\n transition-delay: 250ms; }\n .view-content.ng-leave-active, .view-content.ng-hide {\n opacity: 0; }\n .view-content.ng-hide-add, .view-content.ng-hide-add-active, .view-content.ng-hide-remove, .view-content.ng-hide-remove-active {\n opacity: 0; }\n\n.view-content--with-sidemenu {\n padding: 8px; }\n @media only screen and (min-width: 600px) {\n .view-content--with-sidemenu {\n margin-left: 54px;\n padding: 16px; } }\n\n@media only screen and (min-width: 600px) {\n [dir='rtl'] .view-content--with-sidemenu {\n margin-left: auto;\n margin-right: 54px; } }\n\n.content-head {\n margin: 8px 0; }\n .content-head h1,\n .content-head h2,\n .content-head h3 {\n font-weight: 300;\n margin-top: 0;\n margin-bottom: 0;\n font-size: 36px; }\n @media only screen and (max-width: 959px) {\n .content-head h1,\n .content-head h2,\n .content-head h3 {\n font-size: 32px;\n text-align: center; } }\n\n@media only screen and (max-width: 959px) {\n .content-head__more {\n margin-top: 8px; } }\n\n.content-head__item,\nh2.content-head__item {\n margin: 0 8px; }\n .content-head__item .md-subhead,\n h2.content-head__item .md-subhead {\n padding-left: 4px; }\n @media only screen and (max-width: 959px) {\n .content-head__item .md-subhead,\n h2.content-head__item .md-subhead {\n display: block;\n padding-left: 0; } }\n .content-head__item md-icon,\n h2.content-head__item md-icon {\n vertical-align: text-bottom; }\n\n.stepSelectMenuContainer md-select-menu,\n.stepSelectMenuContainer md-select-menu md-content {\n max-height: 500px; }\n\n.l-nav {\n background-color: #eeeeee !important; }\n\n.l-node {\n margin-top: 42px;\n padding: 0; }\n @media only screen and (min-width: 600px) {\n .l-node {\n padding: 0 0 16px;\n background-color: #eeeeee !important; } }\n @media only screen and (min-width: 960px) {\n .l-node {\n padding: 0 0 32px; } }\n\n.l-notebook {\n margin-top: 42px;\n background-color: #eeeeee !important; }\n\n.l-sidebar__header {\n background-color: #ffffff !important;\n color: #795C3A !important; }\n .l-sidebar__header md-select {\n color: rgba(0, 0, 0, 0.87); }\n\n.status-icon {\n margin: 0 4px;\n z-index: 1;\n vertical-align: bottom; }\n\n.md-button.status-icon {\n height: auto;\n width: auto;\n min-height: 0;\n line-height: inherit;\n margin: 0 4px;\n padding: 0; }\n\n.status-corner-wrapper {\n position: absolute;\n z-index: 1;\n overflow: hidden; }\n\n.status-corner {\n width: 0;\n height: 0;\n border-top-color: rgba(0, 0, 0, 0.26);\n border-bottom-color: rgba(0, 0, 0, 0.26);\n color: rgba(0, 0, 0, 0.26); }\n .status-corner:after {\n content: '!';\n color: #ffffff;\n top: 2px;\n right: 10px;\n position: absolute;\n font-weight: 700; }\n\n.status-corner--warn {\n border-top-color: #c62828 !important;\n border-bottom-color: #c62828 !important; }\n\n.status-corner-top-right {\n top: 0;\n right: 0;\n border-top-right-radius: 4px; }\n .status-corner-top-right .status-corner {\n border-top: 36px solid;\n border-left: 36px solid transparent; }\n\n.status-corner-top-left {\n top: 0;\n left: 0;\n border-top-left-radius: 4px; }\n .status-corner-top-left .status-corner {\n border-top: 36px solid;\n border-right: 36px solid transparent; }\n\n.status-corner-bottom-right {\n bottom: 0;\n right: 0;\n border-bottom-right-radius: 4px; }\n .status-corner-bottom-right .status-corner {\n border-bottom: 36px solid;\n border-left: 36px solid transparent; }\n\n.status-corner-bottom-left {\n bottom: 0;\n left: 0;\n border-bottom-left-radius: 4px; }\n .status-corner-bottom-left .status-corner {\n border-bottom: 36px solid;\n border-right: 36px solid transparent; }\n\n.avatar--icon--alert {\n background-color: #ffffff; }\n\n.avatar--icon--alert__icon {\n font-size: 48px;\n margin: -4px 0 0 -4px; }\n\n.gu-mirror {\n position: fixed !important;\n margin: 0 !important;\n z-index: 9999 !important;\n opacity: 0.8;\n transition: opacity 250ms ease-in-out; }\n\n.gu-hide {\n display: none !important; }\n\n.gu-unselectable {\n -webkit-user-select: none !important;\n -moz-user-select: none !important;\n -ms-user-select: none !important;\n user-select: none !important; }\n\n.gu-transit {\n opacity: 0.2; }\n\nmd-dialog {\n width: 600px; }\n\n.dialog--wide {\n width: 960px; }\n\n.dialog--wider {\n width: 1280px; }\n\n.help-bubble {\n border-radius: 4px;\n max-width: 320px; }\n @media (min-width: 600px) {\n .help-bubble {\n max-width: 552px; } }\n @media (min-width: 960px) {\n .help-bubble {\n max-width: 912px; } }\n @media (min-width: 1280px) {\n .help-bubble {\n max-width: 1232px; } }\n\n.help-bubble__title {\n border-top-left-radius: 4px;\n border-top-right-radius: 4px; }\n\n.help-bubble___title__content {\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n padding: 0px 0 0 12px;\n background-color: #ef6c00; }\n .help-bubble___title__content .md-icon-button {\n margin-right: 0;\n padding-top: 0;\n padding-bottom: 0; }\n\n.help-bubble__content {\n overflow: auto;\n padding: 8px 12px;\n max-height: 480px; }\n\n.help-bubble__actions {\n border-bottom-left-radius: 4px;\n border-bottom-right-radius: 4px; }\n\ndiv.hopscotch-bubble {\n border-radius: 4px;\n border: 0 none;\n font-family: Roboto, \"Helvetica Neue\", sans-serif;\n font-size: 14px;\n z-index: 6; }\n div.hopscotch-bubble .hopscotch-bubble-arrow-container {\n position: absolute;\n width: 20px;\n height: 20px; }\n div.hopscotch-bubble .hopscotch-bubble-arrow-container.up {\n top: 0;\n left: 12px; }\n div.hopscotch-bubble .hopscotch-bubble-arrow-container.up .hopscotch-bubble-arrow {\n border-bottom: 10px solid #ef6c00;\n border-left: 10px solid transparent;\n border-right: 10px solid transparent;\n position: relative;\n top: -10px; }\n div.hopscotch-bubble .hopscotch-bubble-arrow-container.up .hopscotch-bubble-arrow-border {\n border: 0 none;\n /*border-bottom: 12px solid color('gray-darker');\n border-left: 12px solid transparent;\n border-right: 12px solid transparent;*/ }\n div.hopscotch-bubble .hopscotch-bubble-arrow-container.down {\n bottom: -34px;\n left: 12px; }\n div.hopscotch-bubble .hopscotch-bubble-arrow-container.down .hopscotch-bubble-arrow {\n border-top: 10px solid #ef6c00;\n border-left: 10px solid transparent;\n border-right: 10px solid transparent;\n position: relative;\n top: -10px; }\n div.hopscotch-bubble .hopscotch-bubble-arrow-container.down .hopscotch-bubble-arrow-border {\n border: 0 none;\n /*border-top: 12px solid color('gray-darker');\n border-left: 12px solid transparent;\n border-right: 12px solid transparent;*/ }\n div.hopscotch-bubble .hopscotch-bubble-arrow-container.left {\n top: 12px;\n left: -10px; }\n div.hopscotch-bubble .hopscotch-bubble-arrow-container.left .hopscotch-bubble-arrow {\n border-right: 10px solid #ef6c00;\n border-bottom: 10px solid transparent;\n border-top: 10px solid transparent;\n position: relative;\n left: 0;\n top: -10px; }\n div.hopscotch-bubble .hopscotch-bubble-arrow-container.left .hopscotch-bubble-arrow-border {\n border: 0 none;\n /*border-right: 12px solid color('gray-darker');\n border-bottom: 12px solid transparent;\n border-top: 12px solid transparent;*/ }\n div.hopscotch-bubble .hopscotch-bubble-arrow-container.right {\n top: 12px;\n right: -30px; }\n div.hopscotch-bubble .hopscotch-bubble-arrow-container.right .hopscotch-bubble-arrow {\n border-left: 10px solid #ef6c00;\n border-bottom: 10px solid transparent;\n border-top: 10px solid transparent;\n position: relative;\n right: 0;\n top: -10px; }\n div.hopscotch-bubble .hopscotch-bubble-arrow-container.right .hopscotch-bubble-arrow-border {\n border: 0 none;\n /*border-right: 12px solid color('gray-darker');\n border-bottom: 12px solid transparent;\n border-top: 12px solid transparent;*/ }\n\n.input-container {\n padding-top: 12px; }\n\n.input-container--component {\n margin-bottom: 0; }\n\n.input-container--open-response.md-has-icon {\n padding-left: 0; }\n\n.input-container--open-response .md-errors-spacer {\n display: none; }\n\n.input-wrapper {\n position: relative; }\n\n.input-wrapper--focused .input--textarea__action md-icon {\n color: #1C8CA8; }\n\n.input--textarea, .input-container textarea.input--textarea {\n padding: 8px;\n background-color: #f7f7f7;\n border: 1px solid #cccccc;\n margin-bottom: 8px; }\n .input--textarea:focus, .input-container textarea.input--textarea:focus {\n background-color: #ffffff; }\n .input--textarea[disabled], .input-container textarea.input--textarea[disabled] {\n color: rgba(0, 0, 0, 0.54); }\n\n.input-container textarea.input--textarea {\n width: 100%; }\n\n.input--textarea--disabled {\n color: rgba(0, 0, 0, 0.54); }\n\n.input--textarea__action {\n position: absolute;\n right: -4px; }\n .input--textarea__action[disabled] md-icon {\n color: rgba(0, 0, 0, 0.26) !important; }\n\n.input--textarea__action--notebook {\n top: 6px; }\n .input-wrapper--richtext .input--textarea__action--notebook {\n top: -7px; }\n\n.input--textarea__action--revision {\n bottom: 6px; }\n .input-wrapper--richtext .input--textarea__action--revision {\n bottom: -5px; }\n\n.input-label, md-input-container:not(.md-input-invalid):not(.md-input-focused).md-input-has-value label.input-label {\n line-height: 1.2;\n color: rgba(0, 0, 0, 0.87); }\n .input-label.input-label--focused, md-input-container:not(.md-input-invalid):not(.md-input-focused).md-input-has-value label.input-label.input-label--focused {\n color: #1C8CA8; }\n\n.autocomplete input {\n text-overflow: ellipsis;\n overflow: hidden;\n word-wrap: none;\n font-weight: 500;\n color: rgba(0, 0, 0, 0.54); }\n\n@media only screen and (min-width: 600px) {\n .autocomplete--minwidth {\n min-width: 300px; } }\n\n@media only screen and (min-width: 960px) {\n .autocomplete--minwidth {\n min-width: 300px; } }\n\n.autocomplete--flat md-autocomplete-wrap {\n background-color: #ffffff; }\n .autocomplete--flat md-autocomplete-wrap:not(.md-menu-showing) {\n box-shadow: none;\n background-color: #eeeeee; }\n\n.select__header {\n height: 48px; }\n .select__header input {\n height: 100%;\n width: 100%;\n padding: 0 8px;\n outline: none;\n border: 0 none;\n font-size: 14px;\n font-weight: 500; }\n\n.table {\n max-width: 100%;\n width: auto;\n min-width: 100px;\n margin: 8px 0; }\n .table thead > tr > th,\n .table thead > tr > td,\n .table tbody > tr > th,\n .table tbody > tr > td,\n .table tfoot > tr > th,\n .table tfoot > tr > td {\n border: 1px solid #cccccc;\n padding: 6px;\n font-size: 15px;\n min-height: 32px;\n height: 32px;\n min-width: 32px;\n vertical-align: top; }\n .table td.inactive,\n .table th {\n background-color: #f7f7f7;\n opacity: 1;\n visibility: visible; }\n .table md-input-container {\n margin: 0; }\n .table .md-errors-spacer {\n display: none; }\n\n.table--student td.inactive {\n padding: 8px 10px; }\n\n.table--full-width {\n width: 100%; }\n\n.table--list {\n border: 0 none;\n border-collapse: collapse;\n background-color: #ffffff;\n max-width: 100%;\n overflow: auto; }\n .table--list th,\n .table--list td {\n padding: 0 4px;\n border: 0 none; }\n .table--list td {\n min-height: 56px;\n height: 56px; }\n .table--list tr.md-button {\n display: table-row;\n text-align: left;\n width: auto;\n text-transform: none;\n font-size: inherit;\n font-weight: normal; }\n\n.table--list__wrap {\n min-width: 600px; }\n\n@media only screen and (max-width: 959px) {\n .table-wrap-sticky {\n overflow-x: auto; } }\n\n.table--list__thead {\n font-size: 14px;\n font-weight: 700; }\n\n.table--list__thead__tr {\n height: 100%;\n margin: 0; }\n\n.table--list__thead__th {\n background-color: #757575;\n color: white;\n min-height: 42px;\n height: 42px; }\n\n.table--list__thead__link {\n color: #ffffff;\n text-transform: none;\n margin: 0;\n min-width: 0;\n white-space: normal;\n line-height: 1.4;\n width: 100%; }\n\n.table--list__thead__sort {\n margin: 0; }\n\n.table--list__thead__sort--reverse {\n transform: rotate(180deg); }\n\n.td--wrap {\n min-width: 180px;\n white-space: normal;\n line-height: 1.2; }\n\n@media only screen and (max-width: 959px) {\n .td--max-width {\n max-width: 180px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap; } }\n\n.md-toolbar-tools {\n font-size: 18px; }\n .md-toolbar-tools .autocomplete {\n height: 36px; }\n .md-toolbar-tools .autocomplete md-autocomplete-wrap {\n height: 36px; }\n .md-toolbar-tools .autocomplete input {\n height: 36px; }\n\n.md-toolbar--wise {\n min-height: 42px; }\n .md-toolbar--wise .md-toolbar-tools {\n height: 42px;\n max-height: 42px; }\n .md-toolbar--wise .md-button.md-icon-button {\n height: 42px;\n line-height: 42px;\n width: 42px; }\n\n.md-toolbar--wise--sm .md-toolbar-tools {\n padding: 0 8px;\n font-size: 15px; }\n\n.md-toolbar--sidenav {\n background-color: #333333 !important; }\n .md-toolbar--sidenav .md-toolbar-tools {\n font-size: 16px;\n font-weight: 500; }\n\n.toolbar {\n position: fixed;\n left: 0;\n right: 0;\n top: 52px;\n z-index: 3; }\n\n.toolbar__title {\n margin-left: 8px;\n font-size: 16px;\n font-weight: 500; }\n\n.toolbar__tools {\n padding-right: 8px; }\n\n[dir=rtl] .toolbar__tools {\n padding-right: 16px;\n padding-left: 8px; }\n\n.toolbar__nav, .md-button.toolbar__nav {\n margin: 0; }\n\n.toolbar__select, .md-button.toolbar__select {\n margin: 0 4px;\n min-height: 32px;\n background-color: #f7f7f7; }\n .toolbar__select .md-select-value, .md-button.toolbar__select .md-select-value {\n height: 32px;\n text-align: left; }\n\n[dir=rtl] .toolbar__select .md-select-value, [dir=rtl] .md-button.toolbar__select .md-select-value {\n text-align: right; }\n\n.toolbar__select--fixedwidth {\n width: 168px; }\n @media only screen and (min-width: 600px) {\n .toolbar__select--fixedwidth {\n width: 264px; } }\n @media only screen and (min-width: 960px) {\n .toolbar__select--fixedwidth {\n width: 432px; } }\n\n.list-item {\n background-color: #ffffff;\n border-bottom: 1px solid #eeeeee; }\n .list-item .md-subheader, .list-item.md-subheader {\n color: rgba(0, 0, 0, 0.87);\n background-color: #ffffff; }\n .list-item .md-subheader md-icon, .list-item.md-subheader md-icon {\n vertical-align: middle; }\n .list-item .md-subheader .md-subheader-inner, .list-item.md-subheader .md-subheader-inner {\n padding: 0; }\n .list-item .md-subheader .md-avatar, .list-item.md-subheader .md-avatar {\n margin-right: 8px; }\n .list-item .autocomplete {\n margin: 8px 0; }\n\n.list-item--info._md-button-wrap > div.md-button:first-child, .list-item--info .md-subheader-content {\n border-left: 4px solid #ef6c00 !important;\n margin-left: -4px; }\n\n.list-item--warn._md-button-wrap > div.md-button:first-child, .list-item--warn .md-subheader-content {\n border-left: 4px solid #c62828 !important;\n margin-left: -4px; }\n\n.list-item--expanded {\n border-bottom-width: 0; }\n\n.list-item--noclick, .list-item--noclick.md-button {\n cursor: default;\n background-color: #f7f7f7; }\n\n.list-item--actions {\n padding: 0 8px !important; }\n\n.list-item__subheader-button {\n text-transform: none;\n width: 100%;\n padding: 8px 16px;\n margin: 0;\n white-space: normal;\n text-align: left;\n line-height: 1.4; }\n\n.user-list {\n font-size: 15px; }\n\n#nav {\n position: relative; }\n\n.nav {\n margin-bottom: 16px; }\n\n.nav-mask {\n position: absolute;\n top: 0;\n bottom: 0;\n left: 0;\n right: 0;\n background-color: rgba(0, 0, 0, 0.25);\n z-index: 1; }\n .nav-mask.ng-hide {\n opacity: 0; }\n\n.nav-head {\n color: rgba(0, 0, 0, 0.54);\n font-weight: 500; }\n .nav-head md-icon {\n line-height: 20px; }\n\n.nav-contents--root {\n padding: 6px 6px 12px; }\n\n.nav-contents--group {\n background-color: #dddddd;\n padding: 8px; }\n\n.nav-contents--root {\n padding: 0; }\n\n.nav-contents__list {\n padding: 0; }\n @media (min-width: 600px) {\n .nav-contents__list {\n padding: 8px; } }\n\n.nav-item {\n transition: opacity 250ms ease-in-out; }\n .nav-item.prev md-list-item {\n background-color: #f4fbfd;\n /*&._md-button-wrap>div.md-button:first-child {\n border-left: 4px solid color('primary');\n margin-left: -4px;\n }*/ }\n\n.nav-item--card__content {\n border-top-right-radius: 4px;\n border-top-left-radius: 4px; }\n .nav-item--card__content:focus {\n outline: none; }\n .nav-item--card__content:focus .nav-item__title > span {\n border-bottom: 1px dashed #cccccc; }\n\n.nav-item--root {\n transition: margin 250ms, box-shadow 500ms; }\n .nav-item--root.expanded {\n flex-basis: 100%;\n max-width: 100%;\n max-height: none !important;\n margin: 8px auto;\n padding-left: 4px; }\n .nav-item--root.expanded:first-of-type {\n margin-top: 0; }\n\n.nav-item--list__info-item {\n padding: 0 16px 0 4px;\n display: inline-block; }\n\n.nav-item--list__reorder {\n margin-left: 8px;\n color: rgba(0, 0, 0, 0.26); }\n\n.nav-item--card--group:not(.expanded) {\n box-shadow: 0px 3px 1px -2px rgba(0, 0, 0, 0.14), 0px 2px 2px 0px rgba(0, 0, 0, 0.098), 0px 1px 5px 0px rgba(0, 0, 0, 0.084), 3px 3px 0px 1px #d5d5d5, 6px 6px 0px 1px #aaaaaa; }\n\n.nav-item__collapse {\n margin: 0; }\n\n.nav-item__more {\n border-top: 1px solid #dddddd;\n border-bottom-right-radius: 4px;\n border-bottom-left-radius: 4px;\n padding: 8px 16px;\n min-height: 40px; }\n\n.nav-item__users {\n height: auto;\n cursor: pointer;\n color: #ffffff;\n margin: 0;\n padding: 1px 6px; }\n .nav-item__users > md-icon {\n padding-right: 4px;\n color: #ffffff; }\n .nav-item__users:hover.success-bg, .nav-item__users:focus.success-bg {\n background-color: #00C853; }\n\n.nav-item__title {\n padding-left: 16px;\n line-height: 1.2;\n font-weight: 400; }\n\n[dir=rtl] .nav-item__title {\n padding-left: auto;\n padding-right: 16px; }\n\n.nav-item__info {\n padding: 0 8px; }\n\n.nav-item__progress {\n width: 48px; }\n .nav-item__progress > .md-container {\n top: 0; }\n\n.nav-item__progress-value {\n margin-left: 8px;\n width: 36px; }\n\n.progress-wrapper {\n padding: 2px 0;\n cursor: pointer; }\n\n.menu-progress {\n position: absolute;\n top: 10px;\n right: 12px; }\n .menu-progress path {\n stroke: #CAD266 !important;\n stroke-width: 2px; }\n\n[dir=rtl] .menu-progress {\n right: auto;\n left: 12px; }\n\n.menu-sidenav__item {\n font-weight: 700;\n font-size: 14px; }\n\n.menu-sidenav__icon {\n margin-top: 12px !important;\n margin-right: 12px !important;\n margin-left: 12px; }\n\n.active .menu-sidenav__icon, .active .menu-sidenav__item {\n color: #1C8CA8; }\n\n.menu-sidebar {\n position: absolute;\n top: 94px;\n bottom: 0;\n left: 0;\n background-color: #fff;\n width: 56px;\n overflow: hidden;\n padding: 8px 0;\n text-align: center;\n border-right: 1px solid #cccccc; }\n @media only screen and (max-width: 599px) {\n .menu-sidebar {\n display: none; } }\n\n[dir=rtl] .menu-sidebar {\n right: 0;\n left: auto; }\n\n.md-button.md-icon-button.menu-sidebar__link {\n margin-top: 6px;\n margin-bottom: 6px; }\n\n.notice {\n text-align: center;\n padding: 8px;\n background-color: rgba(0, 0, 0, 0.04);\n width: 100%; }\n @media (min-width: 600px) {\n .notice {\n max-width: 80%;\n border-radius: 3px;\n margin: 24px auto; } }\n\n#node {\n margin: 0 auto;\n position: absolute;\n left: 0;\n right: 0; }\n @media only screen and (min-width: 600px) {\n #node {\n padding: 8px;\n padding: 24px 16px;\n margin-bottom: 32px; } }\n @media only screen and (min-width: 960px) {\n #node {\n padding: 32px; } }\n #node.ng-enter {\n transition: opacity .5s;\n opacity: 0; }\n #node.ng-enter-active {\n opacity: 1; }\n\n@media only screen and (min-width: 600px) {\n .node-notice {\n margin-top: -8px;\n margin-bottom: 16px; } }\n\n@media only screen and (min-width: 960px) {\n .node-notice {\n margin-top: -16px; } }\n\n.node-content {\n padding: 0 0 48px;\n background-color: #ffffff;\n border-radius: 3px;\n overflow: visible; }\n @media only screen and (max-width: 599px) {\n .node-content {\n box-shadow: none; } }\n @media only screen and (min-width: 600px) {\n .node-content {\n padding: 0;\n border-top: 2px solid;\n border-bottom: 2px solid; } }\n\nmd-content.node-content {\n background-color: #ffffff; }\n\n.node-content__rubric {\n position: absolute;\n top: -22px;\n left: 0;\n right: 0;\n z-index: 1; }\n .node-content__rubric .avatar--icon {\n transform: scale(0.94); }\n @media only screen and (max-width: 599px) {\n .node-content__rubric .avatar--icon {\n transform: scale(0.8); } }\n\n.node-icon {\n color: #ffffff;\n vertical-align: inherit; }\n\n.node-select {\n margin: 0 8px;\n min-width: 0;\n font-weight: 500;\n font-size: 15px; }\n .node-select .md-select-value *:first-child {\n transform: translate3d(0, 0, 0);\n flex: 1 0 0; }\n .node-select .md-select-value .node-select__icon {\n display: none; }\n .node-select .md-select-value .node-select__status {\n display: none; }\n .node-select .md-select-icon {\n margin-left: 0;\n color: rgba(0, 0, 0, 0.87); }\n\n.node-select-option--group {\n background-color: #f7f7f7;\n border-bottom: 1px solid #eeeeee;\n border-top: 1px solid #eeeeee; }\n\n.node-select-option--node {\n padding-left: 20px; }\n\n.node-select__icon {\n margin-right: 8px; }\n\n.node-select__status {\n margin-left: 8px; }\n\n.node-select__text {\n text-overflow: ellipsis;\n white-space: nowrap;\n overflow: hidden; }\n @media only screen and (min-width: 600px) {\n .node-select__text {\n margin-top: 2px; } }\n\n.node-title {\n line-height: 1.2;\n text-transform: none;\n margin-top: 3px; }\n @media only screen and (max-width: 599px) {\n .node-title {\n font-size: 15px; } }\n\n.node-content__actions {\n padding: 0 16px 16px; }\n @media only screen and (min-width: 960px) {\n .node-content__actions {\n padding: 0 24px 24px; } }\n @media only screen and (min-width: 1280px) {\n .node-content__actions {\n padding: 0 32px 32px; } }\n .node-content__actions .md-button:first-child {\n margin-left: 0; }\n .node-content__actions .md-button:last-child {\n margin-right: 0; }\n\n.node-content__actions__info {\n font-style: italic;\n margin-left: 8px;\n color: rgba(0, 0, 0, 0.54); }\n\n.node-content__actions__more {\n border-bottom: 1px dotted; }\n\n.md-button.md-icon-button.node-nav:first-of-type {\n margin-right: 0; }\n\n@media only screen and (min-width: 600px) {\n .node-sidebar-active {\n margin-right: 68px; } }\n\n@media only screen and (max-width: 599px) {\n .node-sidebar-visible {\n margin-bottom: 42px; } }\n\n.node-sidebar {\n position: absolute;\n right: 0;\n top: 0;\n width: 52px; }\n\n.node-sidebar__toolbar {\n position: fixed;\n width: 52px;\n background-color: #ffffff;\n padding: 8px 0;\n border-radius: 3px; }\n @media only screen and (max-width: 599px) {\n .node-sidebar__toolbar {\n right: 0;\n bottom: 0;\n left: 0;\n width: 100%;\n border-radius: 0;\n padding: 0;\n min-height: 0;\n height: 42px; } }\n\n.node-info {\n margin: 0; }\n @media only screen and (max-width: 599px) {\n .node-info {\n margin: -16px;\n border-radius: 0; } }\n .node-info .divider {\n margin-left: -8px;\n margin-right: -8px; }\n .node-info .component:first-child {\n margin-top: -16px; }\n .node-info .component, .node-info .component__content, .node-info .component__header {\n margin-left: -8px;\n margin-right: -8px; }\n .node-info .component__actions {\n display: none; }\n\n.node-rubric {\n border: 2px solid #1C8CA8;\n margin: 8px 16px 24px;\n padding: 16px;\n background-color: #ffffff; }\n\n.node__label--vertical-alignment {\n vertical-align: middle;\n display: inline-block; }\n\n@media only screen and (min-width: 600px) {\n .notebook-launcher.md-button.md-fab {\n z-index: 61; } }\n\n.notebook-report {\n border-radius: 4px 4px 0 0;\n margin: 0;\n height: 100%; }\n .notebook-report .note-toolbar {\n margin: -8px -8px 8px;\n padding: 4px;\n border: 0 none;\n border-top: 1px solid #dddddd;\n border-bottom: 1px solid #dddddd;\n border-radius: 0; }\n .notebook-report .note-toolbar .btn-group {\n margin-top: 0; }\n .notebook-report .note-btn {\n border: 0 none;\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n border-bottom-left-radius: 0;\n border-bottom-right-radius: 0; }\n\n.notebook-report-container {\n height: 550px;\n width: 450px;\n max-height: 90%;\n bottom: 0;\n right: 96px;\n position: absolute;\n z-index: 3; }\n\n@media only screen and (min-width: 960px) {\n .notes-visible .notebook-report-container {\n right: 516px;\n transition: right 250ms; } }\n\n.notebook-report-container__full {\n top: 16px;\n bottom: 16px;\n left: 16px;\n right: 16px;\n max-height: none;\n max-width: none;\n height: auto;\n width: auto; }\n .notebook-report-container__full .notebook-report {\n height: 100%;\n width: 100%;\n margin: 0 auto;\n max-height: none;\n border-radius: 4px; }\n\n.notebook-report-container__collapsed {\n width: 300px;\n height: auto; }\n .notebook-report-container__collapsed .notebook-report__content, .notebook-report-container__collapsed .notebook-report__actions, .notebook-report-container__collapsed .notebook-report__content__header {\n display: none; }\n\n.notebook-report__toolbar {\n background-color: #333333 !important;\n border-radius: 4px 4px 0 0; }\n\n.notebook-report__toolbar__title {\n max-width: 150px; }\n\n.notebook-report__content {\n background-color: #ffffff; }\n .notebook-report__content h1, .notebook-report__content h2, .notebook-report__content h3, .notebook-report__content h4 {\n font-size: 22px; }\n .notebook-report__content .note-editor.note-frame {\n border: 0 none;\n border-radius: 0;\n padding: 0;\n box-shadow: none; }\n .notebook-report__content .note-resizebar {\n display: none; }\n\n.notebook-report__content__header {\n padding: 8px;\n background-color: #ffffff;\n font-size: 16px; }\n\n@media only screen and (max-width: 599px) {\n .notebook-report-container:not(.notebook-report-container__collapsed) {\n top: 0;\n bottom: 0;\n left: 0;\n right: 0;\n max-height: none;\n max-width: none;\n height: auto;\n width: auto; }\n .notebook-report-container:not(.notebook-report-container__collapsed) .notebook-report {\n border-radius: 0; }\n .notebook-tools--full {\n display: none; } }\n\n.notebook-report-backdrop {\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n z-index: 3;\n background-color: #212121;\n opacity: .48; }\n\n.notebook-menu {\n transition: opacity 500ms; }\n .notebook-menu.ng-enter {\n opacity: 0; }\n .notebook-menu .ng-enter-active {\n opacity: 1;\n transition-delay: 250ms; }\n .notebook-menu.ng-leave-active, .notebook-menu.ng-hide {\n opacity: 0; }\n .notebook-menu.ng-hide-add, .notebook-menu.ng-hide-add-active, .notebook-menu.ng-hide-remove, .notebook-menu.ng-hide-remove-active {\n opacity: 0; }\n\n.notebook-item {\n transition: box-shadow 250ms;\n margin: 0 16px 16px;\n display: block; }\n\n.notebook-item__content {\n height: 250px;\n min-width: 230px;\n position: relative;\n padding: 0;\n background-color: #cccccc;\n border-top-left-radius: 4px;\n border-top-right-radius: 4px; }\n\n.notebook-item__content__attachment, .notebook-item__content__text {\n position: absolute;\n left: 0;\n right: 0; }\n\n.notebook-item__content__attachment {\n background-repeat: no-repeat !important;\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n background-position: center top !important;\n background-size: cover !important;\n top: 0;\n bottom: 0; }\n\n.notebook-item__content__text {\n bottom: 0;\n padding: 8px;\n font-weight: 500;\n overflow: hidden;\n max-height: 120px;\n min-height: 56px;\n background-color: rgba(255, 255, 255, 0.95);\n border-top: 1px solid #eeeeee; }\n .notebook-item__content__text:after {\n content: \"\";\n text-align: right;\n position: absolute;\n bottom: 0;\n right: 0;\n width: 100%;\n height: 0.8em;\n background: linear-gradient(180deg, rgba(255, 255, 255, 0), rgba(255, 255, 255, 0.95) 100%); }\n\n.notebook-item__content--text-only:after {\n content: \"note\";\n font-family: 'Material Icons';\n font-weight: normal;\n font-style: normal;\n display: inline-block;\n line-height: 1;\n text-transform: none;\n letter-spacing: normal;\n word-wrap: normal;\n white-space: nowrap;\n direction: ltr;\n /* Support for all WebKit browsers. */\n -webkit-font-smoothing: antialiased;\n /* Support for Safari and Chrome. */\n text-rendering: optimizeLegibility;\n /* Support for Firefox. */\n -moz-osx-font-smoothing: grayscale;\n /* Support for IE. */\n font-feature-settings: 'liga';\n font-size: 80px;\n color: rgba(0, 0, 0, 0.26); }\n\n.notebook-item--question__content--text-only {\n content: \"live_help\"; }\n\n.notebook-item__content__location {\n opacity: 0.9;\n padding: 8px 0; }\n .notebook-item__content__location md-icon {\n font-size: 22px; }\n\n.notebook-item__edit {\n cursor: pointer; }\n\n.notebook-item__actions {\n margin: 0;\n padding: 0 8px;\n color: #ffffff;\n background-color: #333333;\n border-bottom-left-radius: 4px;\n border-bottom-right-radius: 4px; }\n .notebook-item__actions md-icon {\n color: #ffffff; }\n\n.notebook-item__text-input {\n margin: 0; }\n\n.notebook-item__text-input__textarea {\n padding-left: 0;\n padding-right: 0; }\n\n.notebook-item__attachment {\n background-color: #dddddd;\n padding: 16px;\n margin-bottom: 16px;\n text-align: center;\n position: relative; }\n\n.notebook-item__attachment__content {\n max-width: 100%;\n height: auto; }\n\n.notebook-item__attachment__delete {\n position: absolute;\n top: 4px;\n right: -2px;\n width: 34px !important;\n height: 34px !important;\n min-height: 0; }\n .notebook-item__attachment__delete md-icon {\n margin-left: -2px;\n font-size: 22px; }\n\n.notebook-item__info {\n font-style: italic;\n opacity: .8;\n color: #8a6942; }\n .notebook-item__info a, .notebook-item__info md-icon {\n color: #8a6942; }\n .notebook-item__info md-icon {\n font-size: 1.5em;\n min-width: 0;\n width: auto; }\n\n.notebook-item__upload {\n text-align: center;\n padding: 24px;\n background-color: #eeeeee;\n margin-bottom: 16px;\n color: rgba(0, 0, 0, 0.54);\n border-radius: 4px;\n cursor: pointer;\n border: 1px dashed transparent;\n transition: all 250ms; }\n .notebook-item__upload md-icon, .notebook-item__upload span {\n transition: color 250ms; }\n .notebook-item__upload:hover, .notebook-item__upload:focus, .notebook-item__upload.dragover {\n border-color: #F05843;\n background-color: #fdebe8;\n color: #F05843; }\n .notebook-item__upload:hover md-icon, .notebook-item__upload:hover span, .notebook-item__upload:focus md-icon, .notebook-item__upload:focus span, .notebook-item__upload.dragover md-icon, .notebook-item__upload.dragover span {\n color: #F05843; }\n\n.view-notebook-item {\n width: 600px; }\n\n.notebook-item--report {\n background-color: #ffffff; }\n .notebook-item--report .note-editor {\n margin-bottom: 16px;\n border-color: #cccccc; }\n\n.notebook-item--report__container.ui-scrollpoint .notebook-item--report__toolbar {\n position: fixed;\n top: 94px;\n left: 0;\n right: 0;\n z-index: 1; }\n @media only screen and (min-width: 600px) {\n .notebook-item--report__container.ui-scrollpoint .notebook-item--report__toolbar {\n left: 54px; } }\n @media only screen and (min-width: 600px) {\n .notebook-item--report__container.ui-scrollpoint .notebook-item--report__toolbar {\n padding: 0 24px; } }\n @media only screen and (min-width: 960px) {\n .notebook-item--report__container.ui-scrollpoint .notebook-item--report__toolbar {\n padding: 0 32px; } }\n .notebook-item--report__container.ui-scrollpoint .notebook-item--report__toolbar .note-toolbar {\n margin: 0 16px; }\n @media only screen and (min-width: 600px) {\n .notebook-item--report__container.ui-scrollpoint .notebook-item--report__toolbar .note-toolbar {\n margin: 0 8px; } }\n @media only screen and (min-width: 960px) {\n .notebook-item--report__container.ui-scrollpoint .notebook-item--report__toolbar .note-toolbar {\n margin: 0 16px; } }\n\n.notebook-item--report__container.ui-scrollpoint .note-editor {\n padding-top: 40px; }\n\n.notebook-item--report__toolbar .note-toolbar {\n background-color: #dddddd;\n border: 1px solid #cccccc;\n margin-bottom: -2px; }\n\n.notebook-item--report__heading {\n text-align: center;\n margin-bottom: 32px; }\n\n.notebook-item--report__add-note {\n font-weight: 700; }\n\n.notebook-item--report__note-img {\n max-width: 100%;\n height: auto !important; }\n\n@media only screen and (min-width: 600px) {\n .notebook-sidebar {\n width: 400px;\n max-width: none; } }\n\n@media only screen and (min-width: 960px) {\n .notebook-sidebar {\n width: 500px;\n max-width: none; } }\n\n.notebook-items {\n width: 100%;\n overflow: auto;\n margin-top: 16px;\n margin-bottom: 76px; }\n .notebook-items .notebook-item {\n width: 100%; }\n .notebook-items .notebook-item__content {\n height: 200px;\n min-width: 0; }\n\n.notebook-items--grading {\n margin-bottom: 0; }\n\n@media only screen and (max-width: 599px) {\n .notebook-enabled .md-fab-bottom-right, .notebook-enabled .md-fab-bottom-left {\n bottom: 50px !important; } }\n\n.notebook-grading {\n background-color: #ffffff;\n display: block; }\n\n.notification-btn {\n width: 60px !important; }\n .notification-btn md-icon {\n margin-left: 20px; }\n\n.notification-count {\n border-radius: 50%;\n position: absolute;\n background-color: #F05843;\n width: 22px;\n left: 4px;\n height: 22px;\n line-height: 18px;\n font-size: 12px;\n font-weight: 700;\n border: 2px solid; }\n .notification-count:before {\n content: \"\";\n position: absolute;\n right: -7px;\n top: 5px;\n border-left: 6px solid rgba(255, 255, 255, 0.87);\n border-top: 4px solid transparent;\n border-bottom: 4px solid transparent; }\n\n.notification-list {\n padding: 8px 0; }\n\n.notification-dismiss {\n width: 500px; }\n\n.notification-dismiss__input {\n margin-bottom: 0; }\n\nmd-list md-list-item .md-list-item-text h4.notification-list-item__source,\nmd-list md-list-item.md-2-line .md-list-item-text h4.notification-list-item__source,\nmd-list md-list-item.md-3-line .md-list-item-text h4.notification-list-item__source {\n color: rgba(0, 0, 0, 0.54);\n font-size: 12px; }\n md-list md-list-item .md-list-item-text h4.notification-list-item__source md-icon,\n md-list md-list-item.md-2-line .md-list-item-text h4.notification-list-item__source md-icon,\n md-list md-list-item.md-3-line .md-list-item-text h4.notification-list-item__source md-icon {\n font-size: 18px;\n min-width: 0;\n width: auto;\n margin-left: -4px;\n line-height: 20px; }\n\n.account-menu {\n border-radius: 4px;\n padding: 0;\n font-size: 15px;\n max-width: 380px; }\n @media (min-width: 1280px) {\n .account-menu {\n min-width: 380px !important; } }\n .account-menu h3 {\n margin: 0;\n font-weight: 300; }\n\n.account-menu--fixed-height {\n height: 304px; }\n\n.account-menu--fixed-width {\n width: 320px; }\n @media (min-width: 960px) {\n .account-menu--fixed-width {\n width: 380px; } }\n\n.account-menu__icon {\n background-color: white;\n border-radius: 50%; }\n\n.account-menu__caret {\n position: absolute;\n right: 28px;\n top: -8px;\n outline: none; }\n .account-menu__caret:before {\n content: '';\n position: absolute;\n border-bottom: 8px solid #fff;\n border-left: 8px solid transparent;\n border-right: 8px solid transparent; }\n\n.account-menu__caret--pause, .account-menu__caret--notification {\n right: 80px; }\n\n.account-menu__caret--notification--with-pause {\n right: 132px; }\n\n[dir=rtl] .account-menu__caret {\n right: auto;\n left: 28px; }\n\n[dir=rtl] .account-menu__caret--pause, [dir=rtl] .account-menu__caret--notification {\n left: 80px;\n right: auto; }\n\n[dir=rtl] .account-menu__caret--notification--with-pause {\n left: 132px;\n right: auto; }\n\n.account-menu__info {\n padding: 8px 12px; }\n\n.account-menu__info__title {\n font-weight: 500; }\n\n.account-menu__info__team {\n font-weight: 400;\n color: rgba(0, 0, 0, 0.54); }\n\n.account-menu__users {\n padding: 0; }\n .account-menu__users md-list-item {\n padding: 0; }\n .account-menu__users md-list-item .md-avatar {\n margin: 0 8px 0 0;\n height: 48px;\n width: 48px; }\n\n.account-menu__progress md-progress-linear {\n transform: rotate(270deg);\n width: 26px; }\n\n.account-menu__grade {\n position: relative;\n margin-right: 4px; }\n .account-menu__grade md-icon {\n color: #FFC107; }\n\n.account-menu__grade__overlay {\n position: absolute;\n top: 0;\n width: 100%;\n background-color: rgba(255, 255, 255, 0.6); }\n\n.account-menu__actions {\n background-color: #f7f7f7; }\n\n.account-menu__control {\n padding: 16px; }\n\n.annotations {\n margin: 16px 4px 16px 62px;\n position: relative;\n font-size: 15px; }\n .annotations hr {\n margin: 10px 0 8px;\n border-color: rgba(0, 0, 0, 0.12); }\n .annotations:after {\n content: \"\";\n position: absolute;\n width: 0;\n height: 0;\n left: -16px;\n right: auto;\n top: 0px;\n bottom: auto;\n border-top: 20px solid transparent;\n border-bottom: 20px solid transparent;\n border-right: 16px solid #757575; }\n\n.annotations-container--student--report {\n border-top: 1px solid #dddddd; }\n\n.annotations--report {\n margin-top: 0;\n margin-bottom: 0; }\n\n.annotations__header {\n position: relative;\n border-top-right-radius: 4px;\n padding: 10px 12px;\n font-weight: 700;\n transition: all 1s;\n color: #ffffff;\n background-color: #757575; }\n\n.annotations__avatar {\n background-color: #F05843;\n padding: 2px;\n position: absolute;\n top: 0;\n left: -62px; }\n\n.annotations__icon {\n transition: all 1s;\n color: #ffffff; }\n\n.annotations__body {\n padding: 12px;\n background-color: #ffffff;\n border-bottom-left-radius: 4px;\n border-bottom-right-radius: 4px;\n overflow: auto; }\n\n.annotations__status {\n background-color: #ffffff;\n color: #ef6c00;\n display: inline-block;\n margin-left: 8px;\n font-size: 12px; }\n .annotations__status.ng-enter, .annotations__status.ng-leave {\n transition: all 1s; }\n .annotations__status.ng-enter, .annotations__status.ng-leave.ng-leave-active {\n opacity: 0; }\n .annotations__status.ng-leave, .annotations__status.ng-enter.ng-enter-active {\n opacity: 1; }\n\n.annotations__score {\n font-weight: 700; }\n\n.annotations__info {\n font-style: italic;\n opacity: 0.8;\n border-bottom: 1px dotted;\n font-size: 13px; }\n\n.annotations--inside .annotations {\n margin-left: 72px; }\n\n.annotations--info {\n margin-bottom: 32px;\n margin-right: 8px;\n margin-left: 72px; }\n @media only screen and (min-width: 600px) {\n .annotations--info {\n margin: 16px 16px 32px 76px; } }\n .annotations--info:after {\n border-right: 16px solid #ef6c00; }\n .annotations--info .annotations__avatar {\n background-color: #ffffff; }\n .annotations--info .annotations__header {\n background-color: #ef6c00; }\n\n.component {\n position: relative; }\n\n.component__wrapper {\n padding: 0 16px;\n margin: 24px 0; }\n\n.component__content {\n overflow-x: auto;\n font-size: 15px;\n overflow-y: hidden; }\n @media only screen and (min-width: 600px) {\n .component__content {\n padding: 0 8px; } }\n\nh3.component__header, .component-header {\n padding: 8px 12px;\n margin: 0;\n font-size: 14px; }\n\n.component__rubric {\n position: absolute;\n left: -20px;\n top: 12px; }\n\n.notebook-enabled .component_content img {\n transition: all 250ms;\n cursor: pointer;\n cursor: copy; }\n .notebook-enabled .component_content img:hover, .notebook-enabled .component_content img:focus {\n box-shadow: 0 0 5px 1px #F05843; }\n\n.component__actions .md-button:first-child {\n margin-left: 0; }\n\n.component__actions .md-button:last-child {\n margin-right: 0; }\n\n.component__actions__info {\n font-style: italic;\n margin-left: 8px;\n color: rgba(0, 0, 0, 0.54); }\n\n.component__actions__more {\n border-bottom: 1px dotted; }\n\n.component__prompt {\n margin-bottom: 8px;\n font-weight: 500; }\n\n.component__prompt__content {\n display: inline; }\n\n.component__attachment {\n position: relative;\n margin: 0 8px;\n padding-bottom: 8px; }\n @media only screen and (min-width: 600px) {\n .component__attachment {\n padding-top: 8px; } }\n\n@media only screen and (max-width: 599px) {\n .component__add-attachment {\n width: 100%; } }\n\n.component__attachment__content {\n max-height: 100px;\n width: auto; }\n\n.component__attachment__delete {\n position: absolute;\n top: 0;\n right: 0;\n min-width: 0;\n background-color: rgba(255, 255, 255, 0.75) !important;\n border-radius: 0;\n padding: 4px;\n margin: 0; }\n .component__attachment__delete > md-icon {\n margin-top: 0; }\n\n.component__revision {\n margin: 8px 0;\n padding: 8px; }\n .component__revision:nth-child(odd) {\n background-color: #f7f7f7; }\n\n.component__revision__content {\n padding: 4px 0 8px 0;\n border-bottom: 1px solid #dddddd; }\n\n.component__revision__actions {\n color: #757575;\n padding-top: 4px; }\n\n.component__content--Discussion {\n overflow: hidden; }\n\n.discussion-content {\n background-color: #eeeeee;\n box-shadow: inset 0 0 3px #aaaaaa; }\n\n.discussion-posts {\n padding: 12px 12px 8px; }\n @media only screen and (min-width: 1280px) {\n .discussion-posts {\n padding: 16px 16px 0; } }\n\n.discussion-post {\n margin: 0 auto 16px;\n max-width: 600px; }\n @media only screen and (min-width: 600px) {\n .discussion-post {\n margin-bottom: 24px; } }\n @media only screen and (min-width: 1280px) {\n .discussion-post {\n margin-bottom: 32px; } }\n .discussion-post md-divider {\n position: relative;\n width: auto; }\n\n.discussion-post__contents {\n padding: 16px; }\n\n.discussion-post__avatar, md-list-item > .md-avatar.discussion-post__avatar {\n margin-right: 8px; }\n\n.discussion-post__avatar--reply, md-list-item > .md-avatar.discussion-post__avatar--reply {\n margin-top: 8px; }\n\n.discussion-post__user, md-list-item .md-list-item-text h3.discussion-post__user {\n padding-bottom: 4px;\n font-weight: 700;\n line-height: 1.3;\n overflow: visible;\n white-space: normal; }\n\n.discussion-post__date {\n color: #aaaaaa; }\n\n.discussion-post__date--reply {\n margin-left: 8px;\n font-weight: 400; }\n\n.discussion-post__content {\n margin-top: 16px;\n white-space: pre-wrap; }\n\n.discussion-post__attachment {\n max-width: 100%;\n height: auto !important;\n margin-top: 16px; }\n\n.discussion-new {\n background-color: #ffffff;\n max-width: 570px;\n margin-left: auto;\n margin-right: auto;\n padding: 8px;\n transition: all 250ms;\n transform: scale(0.95); }\n\n.discussion-new--focused {\n transform: scale(1); }\n\nmd-input-container.discussion-new__input-container {\n margin: 0;\n padding: 0; }\n md-input-container.discussion-new__input-container > textarea.md-input {\n min-height: 68px; }\n\n.discussion-new__input--textarea, .input-container textarea.discussion-new__input--textarea {\n padding: 8px;\n border: 0 none; }\n\n.discussion-new__actions {\n padding: 0 8px; }\n .discussion-new__actions .md-button:first-of-type {\n margin-left: 0; }\n .discussion-new__actions .md-button:last-of-type {\n margin-right: 0; }\n\n.discussion-new__attachment {\n padding: 0;\n margin: 0 0 8px; }\n\n.discussion-new__attachment__content {\n margin-top: 0;\n margin-bottom: 16px; }\n\n.discussion-comments {\n padding: 0; }\n\n.discussion-comments__contents {\n background-color: #f7f7f7; }\n\n.discussion-comments__header {\n background-color: transparent;\n padding: 0; }\n .discussion-comments__header .md-subheader-inner {\n padding-bottom: 8px; }\n\n.discussion-comments__list {\n padding: 0;\n max-height: 9999px;\n overflow-y: auto; }\n @media only screen and (min-width: 600px) {\n .discussion-comments__list {\n max-height: 400px; } }\n\n.input--textarea.discussion-reply__input, .input-container textarea.input--textarea.discussion-reply__input {\n background-color: #ffffff;\n padding: 4px;\n font-size: 14px;\n border: 0 none;\n margin-left: -1px;\n margin-bottom: 0;\n resize: none; }\n\n.discussion-reply, md-list-item.discussion-reply {\n margin: 0 12px 8px;\n padding: 0;\n min-height: 56px; }\n\n.discusstion-reply__details, md-list-item .md-list-item-text.discusstion-reply__details {\n margin: 8px 0; }\n\n.discussion-post__user--reply, md-list-item .md-list-item-text h3.discussion-post__user--reply {\n font-size: 14px;\n padding: 0;\n margin: 0; }\n\n.discusstion-reply__content {\n margin-top: 2px; }\n .discusstion-reply__content p {\n font-weight: 400 !important;\n color: rgba(0, 0, 0, 0.87) !important; }\n\n.discussion-new-reply {\n padding: 8px; }\n\n.discussion-new-reply__input-container {\n padding-top: 0;\n margin: 0; }\n .discussion-new-reply__input-container .md-errors-spacer {\n display: none; }\n\n.discussion-new-reply__actions {\n margin-left: 8px; }\n .discussion-new-reply__actions .md-button {\n margin-top: 0;\n margin-bottom: 0; }\n\n.embedded-content__iframe {\n border: 0 none; }\n\n.graph-select {\n min-width: 150px;\n max-width: 200px; }\n\n.graph-controls {\n margin: 8px 0;\n padding: 8px 0;\n border: 1px solid #eeeeee;\n border-left-width: 0;\n border-right-width: 0; }\n\n.match-content {\n background-color: #eeeeee;\n margin-bottom: 16px;\n padding: 8px;\n box-shadow: inset 0 0 3px #aaaaaa; }\n\n.match-divider {\n margin: 16px 8px 8px; }\n\n@media only screen and (min-width: 960px) {\n .match-divider--horizontal {\n display: none; } }\n\n.match-bucket__header {\n padding: 12px;\n font-weight: 500;\n color: #1C8CA8; }\n\n.match-bucket__content {\n padding: 0; }\n\n.match-bucket--choices .match-bucket__header {\n color: #795C3A; }\n\n.match-bucket__contents {\n min-height: 120px;\n padding: 0 8px 8px;\n background-color: #dddddd;\n transition: background-color 250ms;\n border-bottom-left-radius: 4px;\n border-bottom-right-radius: 4px;\n column-gap: 8px; }\n @media only screen and (max-width: 599px) {\n .match-bucket__contents {\n column-count: 1 !important; } }\n .match-bucket__contents img {\n max-width: 100%;\n height: auto; }\n\n.match-bucket__contents--over {\n background-color: #1C8CA8; }\n\n.match-bucket__item {\n list-style-type: none;\n cursor: move;\n padding-top: 8px;\n break-inside: avoid; }\n\n.match-bucket__item__contents {\n background-color: #ffffff;\n padding: 8px !important;\n border: 1px solid #cccccc; }\n .match-bucket__item__contents .md-list-item-text {\n width: 100%; }\n\n.match-bucket__item__contents__text {\n margin-right: 4px; }\n\n.match-feedback {\n transition: opacity 250ms;\n /*padding-left: 8px;\n border-left: 4px solid;*/\n margin: 8px -8px -8px;\n color: #ffffff;\n padding: 4px 8px; }\n .match-feedback.ng-hide {\n opacity: 0;\n transition: opacity 1ms; }\n .match-feedback md-icon {\n color: #ffffff; }\n\n.outside-content iframe {\n border: 1px solid #eeeeee; }\n\n.outside-content__source {\n margin-top: 4px;\n text-align: end; }\n .outside-content__source a {\n max-width: 240px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n display: inline-block; }\n\n.notebook-toolbar md-divider {\n margin: 8px 0; }\n\n@media only screen and (max-width: 959px) {\n .notebook-toolbar {\n border-top: 1px solid #dddddd; } }\n\n.notebook-toolbar__add-menu {\n position: absolute;\n bottom: 40px; }\n .notebook-toolbar__add-menu .md-fab-action-item {\n background-color: #ffffff; }\n\n.notebook-toolbar__add-icon {\n border-radius: 50%; }\n\n#closeNotebookSettingsButton {\n float: right; }\n\n[dir=rtl] #closeNotebookSettingsButton {\n float: left; }\n\nhighchart {\n display: block; }\n","// 1. Config\n\n// 2. Base\n.l-nav {\n background-color: color('body-bg') !important;\n}\n","// 1. Config\n\n// 2. Base\n.l-node {\n margin-top: $wise-toolbar-height;\n padding: 0;\n\n @media only screen and (min-width: $layout-breakpoint-xs) {\n padding: 0 0 16px;\n background-color: color('body-bg') !important;\n }\n\n @media only screen and (min-width: $layout-breakpoint-sm) {\n padding: 0 0 32px;\n }\n}\n","// 1. Config\n\n// 2. Base\n.l-notebook {\n margin-top: $wise-toolbar-height;\n background-color: color('body-bg') !important;\n}\n","// 1. Config\n\n// 2. Base\n.l-sidebar {\n\n}\n\n.l-sidebar__header {\n background-color: #ffffff !important;\n color: color('accent-1') !important;\n\n md-select {\n color: color('body');\n }\n}",".status-icon {\n margin: 0 4px;\n z-index: 1;\n vertical-align: bottom;\n}\n\n.md-button.status-icon {\n height: auto;\n width: auto;\n min-height: 0;\n line-height: inherit;\n margin: 0 4px;\n padding: 0;\n}\n\n.status-corner-wrapper {\n position: absolute;\n z-index: 1;\n overflow: hidden;\n}\n\n.status-corner {\n width: 0;\n height: 0;\n border-top-color: color('text-disabled');\n border-bottom-color: color('text-disabled');\n color: color('text-disabled');\n\n &:after {\n content: '!';\n color: #ffffff;\n top: 2px;\n right: 10px;\n position: absolute;\n font-weight: 700;\n }\n}\n\n.status-corner--warn {\n border-top-color: color('warn') !important;\n border-bottom-color: color('warn') !important;\n}\n\n.status-corner-top-right {\n top: 0;\n right: 0;\n border-top-right-radius: $card-border-radius;\n\n .status-corner {\n border-top: 36px solid;\n border-left: 36px solid transparent;\n }\n}\n\n.status-corner-top-left {\n top: 0;\n left: 0;\n border-top-left-radius: $card-border-radius;\n\n .status-corner {\n border-top: 36px solid;\n border-right: 36px solid transparent;\n }\n}\n\n.status-corner-bottom-right {\n bottom: 0;\n right: 0;\n border-bottom-right-radius: $card-border-radius;\n\n .status-corner {\n border-bottom: 36px solid;\n border-left: 36px solid transparent;\n }\n}\n\n.status-corner-bottom-left {\n bottom: 0;\n left: 0;\n border-bottom-left-radius: $card-border-radius;\n\n .status-corner {\n border-bottom: 36px solid;\n border-right: 36px solid transparent;\n }\n}\n\n.avatar--icon--alert {\n background-color: #ffffff;\n}\n\n.avatar--icon--alert__icon {\n font-size: 48px;\n margin: -4px 0 0 -4px;\n}\n",".gu-mirror {\n position: fixed !important;\n margin: 0 !important;\n z-index: 9999 !important;\n opacity: 0.8;\n transition: opacity 250ms ease-in-out;\n}\n.gu-hide {\n display: none !important;\n}\n.gu-unselectable {\n -webkit-user-select: none !important;\n -moz-user-select: none !important;\n -ms-user-select: none !important;\n user-select: none !important;\n}\n.gu-transit {\n opacity: 0.2;\n}\n","md-dialog {\n width: $layout-breakpoint-xs;\n}\n\n.dialog--wide {\n width: $layout-breakpoint-sm;\n}\n\n.dialog--wider {\n width: $layout-breakpoint-md;\n}\n",".help-bubble {\n border-radius: $card-border-radius;\n max-width: 320px;\n\n @media (min-width: $layout-breakpoint-xs) {\n max-width: ($layout-breakpoint-xs - 48);\n }\n\n @media (min-width: $layout-breakpoint-sm) {\n max-width: ($layout-breakpoint-sm - 48);\n }\n\n @media (min-width: $layout-breakpoint-md) {\n max-width: ($layout-breakpoint-md - 48);\n }\n}\n\n.help-bubble__title {\n border-top-left-radius: $card-border-radius;\n border-top-right-radius: $card-border-radius;\n}\n\n.help-bubble___title__content {\n border-top-left-radius: $card-border-radius;\n border-top-right-radius: $card-border-radius;\n padding: 0px 0 0 12px;\n background-color: color('info');\n\n .md-icon-button {\n margin-right: 0;\n padding-top: 0;\n padding-bottom: 0;\n }\n}\n\n.help-bubble__content {\n overflow: auto;\n padding: 8px 12px;\n max-height: 480px;\n}\n\n.help-bubble__actions {\n border-bottom-left-radius: $card-border-radius;\n border-bottom-right-radius: $card-border-radius;\n}\n\ndiv.hopscotch-bubble {\n border-radius: $card-border-radius;\n //border: 2px solid color('gray-darker');\n border: 0 none;\n font-family: Roboto, \"Helvetica Neue\", sans-serif;\n font-size: rem(1.4);\n z-index: 6;\n\n .hopscotch-bubble-arrow-container {\n position: absolute;\n width: 20px;\n height: 20px;\n }\n\n .hopscotch-bubble-arrow-container {\n &.up {\n top: 0;\n left: 12px;\n\n .hopscotch-bubble-arrow {\n border-bottom: 10px solid color('info');\n border-left: 10px solid transparent;\n border-right: 10px solid transparent;\n position: relative;\n top: -10px;\n }\n\n .hopscotch-bubble-arrow-border {\n border: 0 none;\n /*border-bottom: 12px solid color('gray-darker');\n border-left: 12px solid transparent;\n border-right: 12px solid transparent;*/\n }\n }\n\n &.down {\n bottom: -34px;\n left: 12px;\n\n .hopscotch-bubble-arrow {\n border-top: 10px solid color('info');\n border-left: 10px solid transparent;\n border-right: 10px solid transparent;\n position: relative;\n top: -10px;\n }\n\n .hopscotch-bubble-arrow-border {\n border: 0 none;\n /*border-top: 12px solid color('gray-darker');\n border-left: 12px solid transparent;\n border-right: 12px solid transparent;*/\n }\n }\n\n &.left {\n top: 12px;\n left: -10px;\n\n .hopscotch-bubble-arrow {\n border-right: 10px solid color('info');\n border-bottom: 10px solid transparent;\n border-top: 10px solid transparent;\n position: relative;\n left: 0;\n top: -10px;\n }\n\n .hopscotch-bubble-arrow-border {\n border: 0 none;\n /*border-right: 12px solid color('gray-darker');\n border-bottom: 12px solid transparent;\n border-top: 12px solid transparent;*/\n }\n }\n\n &.right {\n top: 12px;\n right: -30px;\n\n .hopscotch-bubble-arrow {\n border-left: 10px solid color('info');\n border-bottom: 10px solid transparent;\n border-top: 10px solid transparent;\n position: relative;\n right: 0;\n top: -10px;\n }\n\n .hopscotch-bubble-arrow-border {\n border: 0 none;\n /*border-right: 12px solid color('gray-darker');\n border-bottom: 12px solid transparent;\n border-top: 12px solid transparent;*/\n }\n }\n }\n}\n","// Variables\n$input-action-width: 44px;\n$input-action-vertical-offset: 6px;\n$input-action-vertical-offset-richtext: -7px;\n\n// Base\n.input-container {\n padding-top: 12px;\n}\n\n.input-container--component {\n margin-bottom: 0;\n}\n\n.input-container--open-response {\n &.md-has-icon {\n padding-left: 0;\n }\n\n .md-errors-spacer {\n display: none;\n }\n}\n\n.input-wrapper {\n position: relative;\n}\n\n.input-wrapper--focused {\n .input--textarea__action md-icon {\n color: color('primary');\n }\n}\n\n.input--textarea, .input-container textarea.input--textarea {\n padding: 8px;\n background-color: color('gray-lightest');\n border: 1px solid color('gray');\n margin-bottom: 8px;\n\n &:focus {\n background-color: #ffffff;\n }\n\n &[disabled] {\n color: color('text-secondary');\n }\n}\n\n.input-container textarea.input--textarea {\n width: 100%;\n}\n\n.input--textarea--disabled {\n color: color('text-secondary');\n}\n\n.input--textarea__action {\n position: absolute;\n right: -4px;\n\n &[disabled] md-icon {\n color: color('text-disabled') !important;\n }\n}\n\n.input--textarea__action--notebook {\n top: $input-action-vertical-offset;\n\n .input-wrapper--richtext & {\n top: $input-action-vertical-offset-richtext\n }\n}\n\n.input--textarea__action--revision {\n bottom: $input-action-vertical-offset;\n\n .input-wrapper--richtext & {\n bottom: ($input-action-vertical-offset-richtext + 2px);\n }\n\n}\n\n.input-label, md-input-container:not(.md-input-invalid):not(.md-input-focused).md-input-has-value label.input-label {\n line-height: 1.2;\n color: color('text');\n\n &.input-label--focused {\n color: color('primary');\n }\n}\n\n.autocomplete {\n input {\n text-overflow: ellipsis;\n overflow: hidden;\n word-wrap: none;\n font-weight: 500;\n color: color('text-secondary');\n }\n}\n\n.autocomplete--minwidth {\n @media only screen and (min-width: $layout-breakpoint-xs) {\n min-width: 300px;\n }\n\n @media only screen and (min-width: $layout-breakpoint-sm) {\n min-width: 300px;\n }\n}\n\n.autocomplete--flat {\n md-autocomplete-wrap {\n background-color: #ffffff;\n\n &:not(.md-menu-showing) {\n box-shadow: none;\n background-color: color('gray-lighter');\n }\n }\n}\n\n.select__header {\n height: $menu-item-height;\n\n input {\n height: 100%;\n width: 100%;\n padding: 0 8px;\n outline: none;\n border: 0 none;\n font-size: rem(1.4);\n font-weight: 500;\n }\n}\n",".table {\n max-width: 100%;\n width: auto;\n min-width: 100px;\n margin: 8px 0;\n\n thead,\n tbody,\n tfoot {\n // TODO: remove chaining when bootstrap dependency is removed\n > tr > th,\n > tr > td {\n border: 1px solid color('gray');\n padding: 6px;\n font-size: rem(1.5);\n min-height: 32px;\n height: 32px;\n min-width: 32px;\n vertical-align: top;\n }\n }\n\n td.inactive,\n th {\n background-color: color('gray-lightest');\n opacity: 1;\n visibility: visible;\n }\n\n md-input-container {\n margin: 0;\n }\n\n .md-errors-spacer {\n display: none;\n }\n}\n\n.table--student {\n td {\n &.inactive {\n padding: 8px 10px;\n }\n }\n}\n\n.table--full-width {\n width: 100%;\n}\n\n.table--list {\n border: 0 none;\n border-collapse: collapse;\n background-color: #ffffff;\n max-width: 100%;\n overflow: auto;\n\n th,\n td {\n padding: 0 4px;\n border: 0 none;\n }\n\n td {\n min-height: 56px;\n height: 56px;\n }\n\n tr {\n &.md-button {\n display: table-row;\n text-align: left;\n width: auto;\n text-transform: none;\n font-size: inherit;\n font-weight: normal;\n }\n }\n}\n\n.table--list__wrap {\n min-width: $layout-breakpoint-xs;\n}\n\n.table-wrap-sticky {\n @media only screen and (max-width: $layout-breakpoint-sm - 1) {\n overflow-x: auto;\n }\n}\n\n.table--list__thead {\n font-size: rem(1.4);\n font-weight: 700;\n}\n\n.table--list__thead__tr {\n height: 100%;\n margin: 0;\n}\n\n.table--list__thead__th {\n background-color: color('gray-darker');\n color: color('text-light');\n min-height: $wise-toolbar-height;\n height: $wise-toolbar-height;\n}\n\n.table--list__thead__link {\n color: #ffffff;\n text-transform: none;\n margin: 0;\n min-width: 0;\n white-space: normal;\n line-height: 1.4;\n width: 100%;\n}\n\n.table--list__thead__sort {\n margin: 0;\n}\n\n.table--list__thead__sort--reverse {\n transform: rotate(180deg);\n}\n\n.td--wrap {\n min-width: 180px;\n white-space: normal;\n line-height: 1.2;\n}\n\n.td--max-width {\n @media only screen and (max-width: $layout-breakpoint-sm - 1) {\n max-width: 180px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n}\n",".md-toolbar-tools {\n font-size: 18px;\n\n .autocomplete {\n height: 36px;\n\n md-autocomplete-wrap {\n height: 36px;\n }\n\n input {\n height: 36px;\n }\n }\n}\n\n.md-toolbar--wise {\n min-height: $wise-toolbar-height;\n\n .md-toolbar-tools {\n height: $wise-toolbar-height;\n max-height: $wise-toolbar-height;\n }\n\n .md-button.md-icon-button {\n height: $wise-toolbar-height;\n line-height: $wise-toolbar-height;\n width: $wise-toolbar-height;\n }\n}\n\n.md-toolbar--wise--sm {\n .md-toolbar-tools {\n padding: 0 8px;\n font-size: $body-font-size-base;\n }\n}\n\n.md-toolbar--sidenav {\n background-color: color('gray-darkest') !important;\n\n .md-toolbar-tools {\n font-size: rem(1.6);\n font-weight: 500;\n }\n}\n\n.toolbar {\n position: fixed;\n left: 0;\n right: 0;\n top: $md-toolbar-height;\n z-index: 3;\n}\n\n.toolbar__title {\n margin-left: 8px;\n font-size: rem(1.6);\n font-weight: 500;\n}\n\n.toolbar__tools {\n padding-right: 8px;\n}\n[dir=rtl] .toolbar__tools {\n padding-right: 16px;\n padding-left: 8px;\n}\n\n.toolbar__nav, .md-button.toolbar__nav {\n margin: 0;\n}\n\n.toolbar__select, .md-button.toolbar__select {\n margin: 0 4px;\n min-height: 32px;\n background-color: color('gray-lightest');\n\n .md-select-value {\n height: 32px;\n text-align: left;\n }\n}\n[dir=rtl] {\n .toolbar__select, .md-button.toolbar__select {\n .md-select-value {\n text-align: right;\n }\n }\n}\n\n.toolbar__select--fixedwidth {\n width: 168px;\n\n @media only screen and (min-width: $layout-breakpoint-xs) {\n width: 264px;\n }\n\n @media only screen and (min-width: $layout-breakpoint-sm) {\n width: 432px;\n }\n}\n",".list-item {\n background-color: #ffffff;\n border-bottom: 1px solid color('gray-lighter');\n\n .md-subheader, &.md-subheader {\n color: color('text');\n background-color: #ffffff;\n\n md-icon {\n vertical-align: middle;\n }\n\n .md-subheader-inner {\n padding: 0;\n }\n\n .md-avatar {\n margin-right: 8px;\n }\n }\n\n .autocomplete {\n margin: 8px 0;\n }\n}\n\n.list-item--info {\n &._md-button-wrap>div.md-button:first-child, .md-subheader-content {\n border-left: 4px solid color('info') !important;\n margin-left: -4px;\n }\n}\n\n.list-item--warn {\n //background-color: lighten(color('warn'), 56%);\n\n &._md-button-wrap>div.md-button:first-child, .md-subheader-content {\n border-left: 4px solid color('warn') !important;\n margin-left: -4px;\n }\n}\n\n.list-item--expanded {\n border-bottom-width: 0;\n}\n\n.list-item--noclick, .list-item--noclick.md-button {\n cursor: default;\n background-color: color('gray-lightest');\n}\n\n.list-item--actions {\n padding: 0 8px !important;\n}\n\n.list-item__subheader-button {\n text-transform: none;\n width: 100%;\n padding: 8px 16px;\n margin: 0;\n white-space: normal;\n text-align: left;\n line-height: 1.4;\n}\n\n.user-list {\n font-size: rem(1.5);\n}\n","#nav {\n position: relative;\n}\n\n.nav {\n margin-bottom: 16px;\n}\n\n.nav-mask {\n position: absolute;\n top: 0;\n bottom: 0;\n left: 0;\n right: 0;\n background-color: rgba(0,0,0,0.25);\n z-index: 1;\n\n &.ng-hide {\n opacity: 0;\n }\n}\n\n.nav-head {\n color: color('text-secondary');\n font-weight: 500;\n\n md-icon {\n line-height: 20px;\n }\n}\n\n.nav-contents--root {\n padding: 6px 6px 12px;\n}\n\n.nav-contents--group {\n background-color: color('gray-light');\n padding: 8px;\n}\n\n.nav-contents--root {\n padding: 0;\n}\n\n.nav-contents__list {\n padding: 0;\n\n @media (min-width: $layout-breakpoint-xs) {\n padding: 8px;\n }\n}\n\n.nav-item {\n transition: opacity 250ms ease-in-out;\n\n &.prev {\n md-list-item {\n background-color: color('selected-bg');\n\n /*&._md-button-wrap>div.md-button:first-child {\n border-left: 4px solid color('primary');\n margin-left: -4px;\n }*/\n }\n }\n}\n\n.nav-item--card__content {\n border-top-right-radius: $card-border-radius;\n border-top-left-radius: $card-border-radius;\n\n &:focus {\n outline: none;\n\n .nav-item__title > span {\n border-bottom: 1px dashed color('gray');\n }\n }\n}\n\n.nav-item--root {\n transition: margin 250ms, box-shadow 500ms;\n\n &.expanded {\n flex-basis: 100%;\n //max-width: ($layout-breakpoint-md - 100) !important;\n max-width: 100%;\n max-height: none !important;\n margin: 8px auto;\n padding-left: 4px;\n\n &:first-of-type {\n margin-top: 0;\n }\n\n //@media only screen and (min-width: $layout-breakpoint-sm) {\n //margin: 8px auto;\n //}\n }\n}\n\n//.nav-item--list {\n//}\n\n.nav-item--list__info-item {\n padding: 0 16px 0 4px;\n display: inline-block;\n}\n\n.nav-item--list__reorder {\n margin-left: 8px;\n color: color('text-disabled');\n}\n\n.nav-item--card {\n\n}\n\n.nav-item--card--group {\n &:not(.expanded) {\n box-shadow: 0px 3px 1px -2px rgba(0, 0, 0, 0.14), 0px 2px 2px 0px rgba(0, 0, 0, 0.098),\n 0px 1px 5px 0px rgba(0, 0, 0, 0.084), 3px 3px 0px 1px #d5d5d5, 6px 6px 0px 1px #aaaaaa;\n }\n}\n\n.nav-item__collapse {\n margin: 0;\n}\n\n.nav-item__more {\n border-top: 1px solid color('gray-light');\n border-bottom-right-radius: $card-border-radius;\n border-bottom-left-radius: $card-border-radius;\n padding: 8px 16px;\n min-height: 40px;\n}\n\n.nav-item__users {\n height: auto;\n cursor: pointer;\n color: #ffffff;\n margin: 0;\n padding: 1px 6px;\n\n > md-icon {\n padding-right: 4px;\n color: #ffffff;\n }\n\n &:hover, &:focus {\n &.success-bg {\n background-color: color('success');\n }\n }\n}\n\n.nav-item__title {\n padding-left: 16px;\n line-height: 1.2;\n font-weight: 400;\n}\n[dir=rtl] .nav-item__title {\n padding-left: auto;\n padding-right: 16px;\n}\n\n.nav-item__info {\n padding: 0 8px;\n}\n\n.nav-item__progress {\n width: 48px;\n\n > .md-container {\n top: 0;\n }\n}\n\n.nav-item__progress-value {\n margin-left: 8px;\n width: 36px;\n}\n\n.progress-wrapper {\n padding: 2px 0;\n cursor: pointer;\n}\n","// 1. Variables\n$menu-sidebar-width: 56px;\n\n// 2. Base\n.menu-progress {\n position: absolute;\n top: 10px;\n right: 12px;\n\n path {\n stroke: color('accent-2') !important;\n stroke-width: 2px;\n }\n}\n[dir=rtl] .menu-progress {\n right:auto;\n left:12px;\n}\n\n.menu-sidenav {\n\n}\n\n.menu-sidenav__item {\n font-weight: 700;\n //color: color('text-secondary');\n font-size: rem(1.4);\n}\n\n.menu-sidenav__icon {\n margin-top: 12px !important;\n margin-right: 12px !important;\n margin-left: 12px;\n}\n\n.active {\n .menu-sidenav__icon, .menu-sidenav__item {\n color: color('primary');\n }\n}\n\n.menu-sidebar {\n position: absolute;\n top: $wise-toolbar-height + $md-toolbar-height;\n bottom: 0;\n left: 0;\n background-color: #fff;\n width: $menu-sidebar-width;\n overflow: hidden;\n padding: 8px 0;\n text-align: center;\n border-right: 1px solid color('gray');\n\n @media only screen and (max-width: ($layout-breakpoint-xs - 1)) {\n display: none;\n }\n}\n[dir=rtl] .menu-sidebar {\n right:0;\n left:auto;\n}\n\n.md-button.md-icon-button.menu-sidebar__link {\n margin-top: 6px;\n margin-bottom: 6px;\n}\n",".notice {\n text-align: center;\n padding: 8px;\n background-color: rgba(0,0,0,0.04);\n width: 100%;\n\n @media (min-width: $layout-breakpoint-xs) {\n max-width: 80%;\n border-radius: $button-border-radius;\n margin: 24px auto;\n }\n}","// Variables\n$input-action-width: 48px;\n$input-action-vertical-offset: 0;\n$input-action-vertical-offset-richtext: -7px;\n\n// Base\n#node {\n margin: 0 auto;\n position: absolute;\n left: 0;\n right: 0;\n\n @media only screen and (min-width: $layout-breakpoint-xs) {\n padding: 8px;\n padding: 24px 16px;\n margin-bottom: 32px;\n }\n\n @media only screen and (min-width: $layout-breakpoint-sm) {\n padding: 32px;\n }\n\n &.ng-enter {\n transition: opacity .5s;\n opacity: 0;\n }\n\n &.ng-enter-active {\n opacity: 1;\n }\n}\n\n// TODO: use BEM conventions\n\n.node-notice {\n @media only screen and (min-width: $layout-breakpoint-xs) {\n margin-top: -8px;\n margin-bottom: 16px;\n }\n\n @media only screen and (min-width: $layout-breakpoint-sm) {\n margin-top: -16px;\n }\n}\n\n.node-content {\n padding: 0 0 48px;\n background-color: #ffffff;\n border-radius: $button-border-radius;\n overflow: visible;\n\n @media only screen and (max-width: $layout-breakpoint-xs - 1) {\n box-shadow: none;\n }\n\n @media only screen and (min-width: $layout-breakpoint-xs) {\n padding: 0;\n border-top: 2px solid;\n border-bottom: 2px solid;\n }\n}\n\nmd-content {\n &.node-content {\n background-color: #ffffff;\n }\n}\n\n.node-content__rubric {\n position: absolute;\n top: -22px;\n left: 0;\n right: 0;\n z-index: 1;\n\n .avatar--icon {\n transform: scale(0.94);\n\n @media only screen and (max-width: $layout-breakpoint-xs - 1) {\n transform: scale(0.8);\n }\n }\n}\n\n.node-icon {\n color: #ffffff;\n vertical-align: inherit;\n}\n\n.node-select {\n margin: 0 8px;\n min-width: 0;\n font-weight: 500;\n font-size: $body-font-size-base;\n\n .md-select-value {\n *:first-child {\n transform: translate3d(0,0,0);\n flex: 1 0 0;\n }\n\n .node-select__icon {\n display: none;\n }\n\n .node-select__status {\n display: none;\n }\n }\n\n .md-select-icon {\n margin-left: 0;\n color: color('text');\n }\n}\n\n.node-select-option--group {\n //color: rgba(0,0,0,0.54);\n background-color: color('gray-lightest');\n border-bottom: 1px solid color('gray-lighter');\n border-top: 1px solid color('gray-lighter');\n}\n\n.node-select-option--node {\n padding-left: 20px;\n}\n\n.node-select__icon {\n margin-right: 8px;\n}\n\n.node-select__status {\n margin-left: 8px;\n}\n\n.node-select__text {\n text-overflow: ellipsis;\n white-space: nowrap;\n overflow: hidden;\n\n @media only screen and (min-width: $layout-breakpoint-xs) {\n margin-top: 2px;\n }\n}\n\n.node-title {\n line-height: 1.2;\n text-transform: none;\n margin-top: 3px;\n\n @media only screen and (max-width: $layout-breakpoint-xs - 1) {\n font-size: $body-font-size-base;\n }\n}\n\n.node-content__actions {\n padding: 0 16px 16px;\n\n @media only screen and (min-width: $layout-breakpoint-sm) {\n padding: 0 24px 24px;\n }\n\n @media only screen and (min-width: $layout-breakpoint-md) {\n padding: 0 32px 32px;\n }\n\n .md-button:first-child {\n margin-left: 0;\n }\n\n .md-button:last-child {\n margin-right: 0;\n }\n}\n\n.node-content__actions__info {\n font-style: italic;\n margin-left: 8px;\n color: color('text-secondary');\n}\n\n.node-content__actions__more {\n border-bottom: 1px dotted;\n}\n\n.md-button.md-icon-button.node-nav {\n &:not(:first-of-type) {\n }\n\n &:first-of-type {\n margin-right: 0;\n }\n}\n\n.node-sidebar-active {\n @media only screen and (min-width: $layout-breakpoint-xs) {\n margin-right: ($md-toolbar-height + 16);\n }\n}\n\n.node-sidebar-visible {\n @media only screen and (max-width: $layout-breakpoint-xs - 1) {\n margin-bottom: $wise-toolbar-height;\n }\n}\n\n.node-sidebar {\n position: absolute;\n right: 0;\n top: 0;\n width: $md-toolbar-height;\n}\n\n.node-sidebar__toolbar {\n position: fixed;\n width: $md-toolbar-height;\n background-color: #ffffff;\n padding: 8px 0;\n border-radius: $button-border-radius;\n\n @media only screen and (max-width: $layout-breakpoint-xs - 1) {\n right: 0;\n bottom: 0;\n left: 0;\n width: 100%;\n border-radius: 0;\n padding: 0;\n min-height: 0;\n height: $wise-toolbar-height;\n }\n}\n\n// TODO: move to own sass module file (only gets used by teacher)\n// TODO: use BEM (.node--info)\n.node-info {\n margin: 0;\n\n @media only screen and (max-width: $layout-breakpoint-xs - 1) {\n margin: -16px;\n border-radius: 0;\n }\n\n .divider {\n margin-left: -8px;\n margin-right: -8px;\n }\n\n .component {\n &:first-child {\n margin-top: -16px;\n }\n }\n\n .component, .component__content, .component__header {\n margin-left: -8px;\n margin-right: -8px;\n }\n\n .component__actions {\n display: none;\n }\n}\n\n.node-rubric {\n border: 2px solid color('primary');\n margin: 8px 16px 24px;\n padding: 16px;\n background-color: #ffffff;\n}\n\n.node-rubric--component {\n\n}\n\n.node__label--vertical-alignment {\n vertical-align: middle;\n display: inline-block;\n}\n","// Variables\n$notebook-sidebar-width: 56px;\n\n// Base\n.notebook-launcher {\n @media only screen and (min-width: $layout-breakpoint-xs) {\n &.md-button.md-fab {\n z-index: 61;\n }\n }\n}\n\n.notebook-report {\n border-radius: 4px 4px 0 0;\n margin: 0;\n height: 100%;\n\n .note-toolbar {\n margin: -8px -8px 8px;\n padding: 4px;\n border: 0 none;\n border-top: 1px solid color('gray-light');\n border-bottom: 1px solid color('gray-light');\n border-radius: 0;\n\n .btn-group {\n margin-top: 0;\n }\n }\n\n .note-btn {\n border: 0 none;\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n border-bottom-left-radius: 0;\n border-bottom-right-radius: 0;\n }\n}\n\n.notebook-report-container {\n height: 550px;\n width: 450px;\n max-height: 90%;\n bottom: 0;\n right: 96px;\n position: absolute;\n z-index: 3;\n}\n\n.notes-visible {\n @media only screen and (min-width: $layout-breakpoint-sm) {\n .notebook-report-container {\n right: 516px;\n transition: right 250ms;\n }\n }\n}\n\n.notebook-report-container__full {\n top: 16px;\n bottom: 16px;\n left: 16px;\n right: 16px;\n max-height: none;\n max-width: none;\n height: auto;\n width: auto;\n\n .notebook-report {\n height: 100%;\n width: 100%;\n margin: 0 auto;\n max-height: none;\n border-radius: $card-border-radius;\n }\n}\n\n.notebook-report-container__collapsed {\n width: 300px;\n height: auto;\n\n .notebook-report__content, .notebook-report__actions, .notebook-report__content__header {\n display: none;\n }\n}\n\n.notebook-report__toolbar {\n background-color: color('gray-darkest') !important;\n border-radius: $card-border-radius $card-border-radius 0 0;\n}\n\n.notebook-report__toolbar__title {\n max-width: 150px;\n}\n\n.notebook-report__content {\n h1, h2, h3, h4 {\n font-size: rem(2.2);\n }\n\n background-color: #ffffff;\n\n .note-editor.note-frame {\n border: 0 none;\n border-radius: 0;\n padding: 0;\n box-shadow: none;\n }\n\n .note-resizebar {\n display: none;\n }\n}\n\n.notebook-report__content__header {\n padding: 8px;\n background-color: #ffffff;\n font-size: rem(1.6);\n}\n\n@media only screen and (max-width: $layout-breakpoint-xs - 1) {\n .notebook-report-container {\n &:not(.notebook-report-container__collapsed) {\n top: 0;\n bottom: 0;\n left: 0;\n right: 0;\n max-height: none;\n max-width: none;\n height: auto;\n width: auto;\n\n .notebook-report {\n border-radius: 0;\n }\n }\n }\n\n .notebook-tools--full {\n display: none;\n }\n}\n\n.notebook-report-backdrop {\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n z-index: 3;\n background-color: rgba(33,33,33,1.0);\n opacity: .48;\n}\n\n.notebook-menu {\n transition: opacity 500ms;\n\n &.ng-enter {\n opacity: 0;\n }\n\n .ng-enter-active {\n opacity: 1;\n transition-delay: 250ms;\n }\n\n &.ng-leave-active, &.ng-hide {\n opacity: 0;\n }\n\n &.ng-hide-add, &.ng-hide-add-active,\n &.ng-hide-remove, &.ng-hide-remove-active {\n opacity: 0;\n }\n}\n\n.notebook-item {\n transition: box-shadow 250ms;\n margin: 0 16px 16px;\n display: block;\n}\n\n.notebook-item__content {\n height: 250px;\n min-width: 230px;\n position: relative;\n padding: 0;\n background-color: color('gray');\n border-top-left-radius: $card-border-radius;\n border-top-right-radius: $card-border-radius;\n}\n\n.notebook-item__content__attachment, .notebook-item__content__text {\n position: absolute;\n left: 0;\n right: 0;\n}\n\n.notebook-item__content__attachment {\n background-repeat: no-repeat !important;\n border-top-left-radius: $card-border-radius;\n border-top-right-radius: $card-border-radius;\n background-position: center top !important;\n background-size: cover !important;\n top: 0;\n bottom: 0;\n}\n\n.notebook-item__content__text {\n bottom: 0;\n padding: 8px;\n font-weight: 500;\n overflow: hidden;\n max-height: 120px;\n min-height: 56px;\n background-color: rgba(255,255,255,0.95);\n border-top: 1px solid color('gray-lighter');\n\n &:after {\n content: \"\";\n text-align: right;\n position: absolute;\n bottom: 0;\n right: 0;\n width: 100%;\n height: 0.8em;\n background: linear-gradient(180deg,hsla(0,0%,100%,0),rgba(255,255,255,0.95) 100%);\n }\n}\n\n.notebook-item__content--text-only {\n &:after {\n content: \"note\";\n font-family: 'Material Icons';\n font-weight: normal;\n font-style: normal;\n display: inline-block;\n line-height: 1;\n text-transform: none;\n letter-spacing: normal;\n word-wrap: normal;\n white-space: nowrap;\n direction: ltr;\n\n /* Support for all WebKit browsers. */\n -webkit-font-smoothing: antialiased;\n /* Support for Safari and Chrome. */\n text-rendering: optimizeLegibility;\n\n /* Support for Firefox. */\n -moz-osx-font-smoothing: grayscale;\n\n /* Support for IE. */\n font-feature-settings: 'liga';\n //position: absolute;\n font-size: 80px;\n color: color('text-disabled');\n }\n}\n\n.notebook-item--question__content--text-only {\n content: \"live_help\";\n}\n\n.notebook-item__content__location {\n opacity: 0.9;\n padding: 8px 0;\n\n md-icon {\n font-size: 22px;\n }\n}\n\n.notebook-item__edit {\n cursor: pointer;\n}\n\n.notebook-item__actions {\n margin: 0;\n padding: 0 8px;\n color: #ffffff;\n background-color: color('gray-darkest');\n border-bottom-left-radius: $card-border-radius;\n border-bottom-right-radius: $card-border-radius;\n\n md-icon {\n color: #ffffff;\n }\n}\n\n.notebook-item__text-input {\n margin: 0;\n}\n\n.notebook-item__text-input__textarea {\n padding-left: 0;\n padding-right: 0;\n}\n\n.notebook-item__attachment {\n background-color: color('gray-light');\n padding: 16px;\n margin-bottom: 16px;\n text-align: center;\n position: relative;\n}\n\n.notebook-item__attachment__content {\n max-width: 100%;\n height: auto;\n}\n\n.notebook-item__attachment__delete {\n position: absolute;\n top: 4px;\n right: -2px;\n // TODO: generalize for on item buttons like this (delete attachment, etc)\n width: 34px !important;\n height: 34px !important;\n min-height: 0;\n\n md-icon {\n margin-left: -2px;\n font-size: 22px;\n }\n}\n\n.notebook-item__info {\n font-style: italic;\n opacity: .8;\n color: lighten(color('accent-1'), 5%);\n\n a, md-icon {\n color: lighten(color('accent-1'), 5%);\n }\n\n md-icon {\n font-size: 1.5em;\n min-width: 0;\n width: auto;\n }\n}\n\n.notebook-item__upload {\n text-align: center;\n padding: 24px;\n background-color: color('gray-lighter');\n margin-bottom: 16px;\n color: color('text-secondary');\n border-radius: 4px;\n cursor: pointer;\n border: 1px dashed transparent;\n transition: all 250ms;\n\n md-icon, span {\n transition: color 250ms;\n }\n\n &:hover, &:focus, &.dragover {\n border-color: color('accent');\n background-color: lighten(color('accent'), 35%);\n color: color('accent');\n\n md-icon, span {\n color: color('accent');\n }\n }\n}\n\n.view-notebook-item {\n width: $layout-breakpoint-xs;\n}\n\n.view-notebook-item__content {\n}\n\n.notebook-item--report {\n background-color: #ffffff;\n\n .note-editor {\n margin-bottom: 16px;\n border-color: color('gray');\n }\n}\n\n.notebook-item--report__container {\n &.ui-scrollpoint {\n .notebook-item--report__toolbar {\n position: fixed;\n top: ($wise-toolbar-height + $md-toolbar-height);\n left: 0;\n right: 0;\n z-index: 1;\n\n @media only screen and (min-width: $layout-breakpoint-xs) {\n left: ($notebook-sidebar-width - 2);\n }\n\n @media only screen and (min-width: $layout-breakpoint-xs) {\n padding: 0 24px;\n }\n\n @media only screen and (min-width: $layout-breakpoint-sm) {\n padding: 0 32px;\n }\n\n .note-toolbar {\n margin: 0 16px;\n\n @media only screen and (min-width: $layout-breakpoint-xs) {\n margin: 0 8px;\n }\n\n @media only screen and (min-width: $layout-breakpoint-sm) {\n margin: 0 16px;\n }\n }\n }\n\n .note-editor {\n padding-top: 40px;\n }\n }\n}\n\n.notebook-item--report__toolbar {\n\n .note-toolbar {\n background-color: color('gray-light');\n border: 1px solid color('gray');\n margin-bottom: -2px;\n }\n}\n\n.notebook-item--report__heading {\n text-align: center;\n margin-bottom: 32px;\n}\n\n.notebook-item--report__content {\n}\n\n.notebook-item--report__add-note {\n font-weight: 700;\n}\n\n.notebook-item--report__note-img {\n max-width: 100%;\n height: auto !important;\n}\n\n.notebook-sidebar {\n @media only screen and (min-width: $layout-breakpoint-xs) {\n width: 400px;\n max-width: none;\n }\n\n @media only screen and (min-width: $layout-breakpoint-sm) {\n width: 500px;\n max-width: none;\n }\n}\n\n.notebook-items {\n width: 100%;\n overflow: auto;\n margin-top: 16px;\n margin-bottom: 76px;\n\n .notebook-item {\n width: 100%;\n }\n\n .notebook-item__content {\n height: 200px;\n min-width: 0;\n }\n}\n\n.notebook-items--grading {\n margin-bottom: 0;\n}\n\n@media only screen and (max-width: ($layout-breakpoint-xs - 1)) {\n .notebook-enabled {\n .md-fab-bottom-right, .md-fab-bottom-left {\n bottom: ($wise-toolbar-height + 8) !important;\n }\n }\n}\n\n.notebook-grading {\n background-color: #ffffff;\n display: block;\n}\n",".notification-btn {\n width: 60px !important;\n\n md-icon {\n margin-left: 20px;\n }\n}\n\n.notification-count {\n border-radius: 50%;\n position: absolute;\n background-color: color('accent');\n width: 22px;\n left: 4px;\n height: 22px;\n line-height: 18px;\n font-size: 12px;\n font-weight: 700;\n border: 2px solid;\n\n &:before {\n content: \"\";\n position: absolute;\n right: -7px;\n top: 5px;\n border-left: 6px solid rgba(255,255,255,0.87);\n border-top: 4px solid transparent;\n border-bottom: 4px solid transparent;\n }\n}\n\n.notification-list {\n padding: 8px 0;\n}\n\n.notification-dismiss {\n width: 500px;\n}\n\n.notification-dismiss__input {\n margin-bottom: 0;\n}\n\nmd-list md-list-item .md-list-item-text h4,\nmd-list md-list-item.md-2-line .md-list-item-text h4,\nmd-list md-list-item.md-3-line .md-list-item-text h4 {\n &.notification-list-item__source {\n color: color('text-secondary');\n font-size: rem(1.2);\n\n md-icon {\n font-size: rem(1.8);\n min-width: 0;\n width: auto;\n margin-left: -4px;\n line-height: rem(2);\n }\n }\n}\n",".account-menu {\n border-radius: $card-border-radius;\n padding: 0;\n font-size: $body-font-size-base;\n max-width: 380px;\n\n @media (min-width: $layout-breakpoint-md) {\n min-width: 380px !important;\n }\n\n h3 {\n margin: 0;\n font-weight: 300;\n }\n}\n\n.account-menu--fixed-height {\n height: $max-menu-height;\n}\n\n.account-menu--fixed-width {\n width: 320px;\n\n @media (min-width: $layout-breakpoint-sm) {\n width: 380px;\n }\n}\n\n.account-menu__icon {\n background-color: color('text-light');\n border-radius: 50%;\n}\n\n.account-menu__caret {\n position: absolute;\n right: 28px;\n top: -8px;\n outline: none;\n\n &:before {\n content: '';\n position: absolute;\n border-bottom: 8px solid #fff;\n border-left: 8px solid transparent;\n border-right: 8px solid transparent;\n }\n}\n\n.account-menu__caret--pause, .account-menu__caret--notification {\n right: 80px;\n}\n\n.account-menu__caret--notification--with-pause {\n right: 132px;\n}\n\n[dir=rtl] {\n .account-menu__caret {\n right: auto;\n left: 28px;\n }\n .account-menu__caret--pause, .account-menu__caret--notification {\n left:80px;\n right:auto;\n }\n .account-menu__caret--notification--with-pause {\n left: 132px;\n right:auto;\n }\n}\n\n.account-menu__info {\n padding: 8px 12px;\n}\n\n.account-menu__info__title {\n font-weight: 500;\n}\n\n.account-menu__info__team {\n font-weight: 400;\n color: color('text-secondary');\n}\n\n.account-menu__users {\n padding: 0;\n\n md-list-item {\n padding: 0;\n\n .md-avatar {\n margin: 0 8px 0 0;\n height: 48px;\n width: 48px;\n }\n }\n}\n\n.account-menu__progress {\n md-progress-linear {\n transform: rotate(270deg);\n width: 26px;\n }\n}\n\n.account-menu__grade {\n position: relative;\n margin-right: 4px;\n\n md-icon {\n color: color('score');\n }\n}\n\n.account-menu__grade__overlay {\n position: absolute;\n top: 0;\n width: 100%;\n background-color: rgba(255, 255, 255, 0.6);\n}\n\n.account-menu__actions {\n background-color: color('gray-lightest');\n}\n\n.account-menu__control {\n padding: 16px;\n}\n",".annotations {\n margin: 16px 4px 16px 62px;\n position: relative;\n font-size: rem(1.5);\n\n hr {\n margin: 10px 0 8px;\n border-color: rgba(0,0,0,.12);\n }\n\n &:after {\n content: \"\";\n position: absolute;\n width: 0;\n height: 0;\n left: -16px;\n right: auto;\n top: 0px;\n bottom: auto;\n border-top: 20px solid transparent;\n border-bottom: 20px solid transparent;\n border-right: 16px solid color('gray-darker');\n }\n}\n\n.annotations-container--student--report {\n border-top: 1px solid color('gray-light');\n}\n\n.annotations--report {\n margin-top: 0;\n margin-bottom: 0;\n}\n\n.annotations__header {\n position: relative;\n //border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n padding: 10px 12px;\n font-weight: 700;\n transition: all 1s;\n color: #ffffff;\n background-color: color('gray-darker');\n}\n\n.annotations__avatar {\n background-color: color('accent');\n padding: 2px;\n position: absolute;\n top: 0;\n left: -62px;\n}\n\n.annotations__icon {\n transition: all 1s;\n color: #ffffff;\n}\n\n.annotations__body {\n padding: 12px;\n background-color: #ffffff;\n border-bottom-left-radius: $card-border-radius;\n border-bottom-right-radius: $card-border-radius;\n overflow: auto;\n}\n\n.annotations__status {\n background-color: #ffffff;\n color: color('info');\n display: inline-block;\n margin-left: 8px;\n font-size: rem(1.2);\n\n &.ng-enter, &.ng-leave {\n transition: all 1s;\n }\n\n &.ng-enter, &.ng-leave.ng-leave-active {\n opacity:0;\n }\n\n &.ng-leave, &.ng-enter.ng-enter-active {\n opacity:1;\n }\n}\n\n.annotations__score {\n font-weight: 700;\n}\n\n.annotations__info {\n font-style: italic;\n opacity: 0.8;\n border-bottom: 1px dotted;\n font-size: rem(1.3);\n}\n\n.annotations--inside {\n .annotations {\n margin-left: 72px;\n }\n}\n\n// TODO: move to own file\n.annotations--info {\n margin-bottom: 32px;\n margin-right: 8px;\n margin-left: 72px;\n\n @media only screen and (min-width: ($layout-breakpoint-xs)) {\n margin: 16px 16px 32px 76px;\n }\n\n &:after {\n border-right: 16px solid color('info');\n }\n\n .annotations__avatar {\n background-color: #ffffff;\n }\n\n .annotations__header {\n background-color: color('info');\n }\n}\n",".component {\n position: relative;\n}\n\n.component__wrapper {\n padding: 0 16px;\n margin: 24px 0;\n}\n\n.component__content {\n overflow-x: auto;\n font-size: rem(1.5);\n overflow-y: hidden; // TODO: figure out why this is needed after update to ng-material 1.1.1\n\n @media only screen and (min-width: $layout-breakpoint-xs) {\n padding: 0 8px;\n }\n}\n\nh3.component__header, .component-header {\n padding: 8px 12px;\n margin: 0;\n font-size: rem(1.4);\n}\n\n.component__rubric {\n position: absolute;\n left: -20px;\n top: 12px;\n}\n\n.notebook-enabled {\n .component_content {\n img {\n transition: all 250ms;\n cursor: pointer;\n cursor: copy;\n //position: relative;\n //border: 2px solid transparent;\n\n &:hover, &:focus {\n box-shadow: 0 0 5px 1px color('accent');\n //border: 2px solid #ffffff;\n }\n }\n }\n}\n\n.component__actions {\n .md-button:first-child {\n margin-left: 0;\n }\n\n .md-button:last-child {\n margin-right: 0;\n }\n}\n\n.component__actions__info {\n font-style: italic;\n margin-left: 8px;\n //color: color('accent-1');\n color: color('text-secondary');\n}\n\n.component__actions__more {\n border-bottom: 1px dotted;\n}\n\n.component__prompt {\n margin-bottom: 8px;\n font-weight: 500;\n}\n\n.component__prompt__content {\n display: inline;\n}\n\n.component__attachment {\n position: relative;\n margin: 0 8px;\n padding-bottom: 8px;\n\n @media only screen and (min-width: $layout-breakpoint-xs) {\n padding-top: 8px;\n }\n}\n\n.component__add-attachment {\n @media only screen and (max-width: ($layout-breakpoint-xs - 1)) {\n width: 100%;\n }\n}\n\n.component__attachment__content {\n max-height: 100px;\n width: auto;\n}\n\n.component__attachment__delete {\n position: absolute;\n top: 0;\n right: 0;\n min-width: 0;\n background-color: rgba(255, 255, 255, 0.75) !important;\n border-radius: 0;\n padding: 4px;\n margin: 0;\n\n //@media only screen and (min-width: $layout-breakpoint-sm) {\n //margin-top: 8px;\n //}\n\n > md-icon {\n margin-top: 0;\n }\n}\n\n.component__revision {\n margin: 8px 0;\n padding: 8px;\n\n &:nth-child(odd) {\n background-color: color('gray-lightest');\n }\n}\n\n.component__revision__content {\n padding: 4px 0 8px 0;\n border-bottom: 1px solid color('gray-light');\n}\n\n.component__revision__actions {\n color: color('gray-darker');\n padding-top: 4px;\n}\n","// Variables\n\n// Base\n.component__content--Discussion {\n overflow: hidden;\n}\n\n.discussion-content {\n background-color: color('gray-lighter');\n //margin: 0 0 -16px;\n box-shadow: inset 0 0 3px color('gray-dark');\n}\n\n.discussion-posts {\n padding: 12px 12px 8px;\n\n @media only screen and (min-width: $layout-breakpoint-md) {\n padding: 16px 16px 0;\n }\n}\n\n.discussion-post {\n margin: 0 auto 16px;\n max-width: $layout-breakpoint-xs;\n\n @media only screen and (min-width: $layout-breakpoint-xs) {\n margin-bottom: 24px;\n }\n\n @media only screen and (min-width: $layout-breakpoint-md) {\n margin-bottom: 32px;\n }\n\n // angular-material fix for when discussion posts are shown inside an md-list-item (e.g. in the grading tool)\n md-divider {\n position: relative;\n width: auto;\n }\n}\n\n.discussion-post__contents {\n padding: 16px;\n}\n\n.discussion-post__avatar, md-list-item > .md-avatar.discussion-post__avatar {\n margin-right: 8px;\n}\n\n.discussion-post__avatar--reply, md-list-item > .md-avatar.discussion-post__avatar--reply {\n margin-top: 8px;\n}\n\n\n.discussion-post__user, md-list-item .md-list-item-text h3.discussion-post__user {\n padding-bottom: 4px;\n font-weight: 700;\n line-height: 1.3;\n overflow: visible;\n white-space: normal;\n}\n\n.discussion-post__date {\n color: color('gray-dark');\n}\n\n.discussion-post__date--reply {\n margin-left: 8px;\n font-weight: 400;\n}\n\n.discussion-post__content {\n margin-top: 16px;\n white-space: pre-wrap;\n}\n\n.discussion-post__attachment {\n max-width: 100%;\n height: auto !important;\n margin-top: 16px;\n}\n\n.discussion-new {\n background-color: #ffffff;\n max-width: 570px;\n margin-left: auto;\n margin-right: auto;\n padding: 8px;\n transition: all 250ms;\n transform: scale(0.95);\n}\n\n.discussion-new--focused {\n transform: scale(1);\n}\n\nmd-input-container.discussion-new__input-container {\n margin: 0;\n padding: 0;\n\n > textarea.md-input {\n min-height: 68px;\n }\n}\n\n.discussion-new__input--textarea, .input-container textarea.discussion-new__input--textarea {\n padding: 8px;\n border: 0 none;\n}\n\n.discussion-new__actions {\n padding: 0 8px;\n\n .md-button {\n &:first-of-type {\n margin-left: 0;\n }\n\n &:last-of-type {\n margin-right: 0;\n }\n }\n}\n\n.discussion-new__attachment {\n padding: 0;\n margin: 0 0 8px;\n}\n\n.discussion-new__attachment__content {\n margin-top: 0;\n margin-bottom: 16px;\n}\n\n.discussion-comments {\n padding: 0;\n}\n\n.discussion-comments__contents {\n background-color: color('gray-lightest');\n}\n\n.discussion-comments__header {\n background-color: transparent;\n padding: 0;\n\n .md-subheader-inner {\n padding-bottom: 8px;\n }\n}\n\n.discussion-comments__list {\n padding: 0;\n max-height: 9999px;\n overflow-y: auto;\n\n @media only screen and (min-width: $layout-breakpoint-xs) {\n max-height: 400px;\n }\n}\n\n.input--textarea.discussion-reply__input, .input-container textarea.input--textarea.discussion-reply__input {\n background-color: #ffffff;\n padding: 4px;\n font-size: ($body-font-size-base) - 1;\n border: 0 none;\n margin-left: -1px;\n margin-bottom: 0;\n resize: none;\n}\n\n.discussion-reply, md-list-item.discussion-reply {\n margin: 0 12px 8px;\n padding: 0;\n min-height: 56px;\n}\n\n.discusstion-reply__details, md-list-item .md-list-item-text.discusstion-reply__details {\n margin: 8px 0;\n}\n\n.discussion-post__user--reply, md-list-item .md-list-item-text h3.discussion-post__user--reply {\n font-size: ($body-font-size-base) - 1;\n padding: 0;\n margin: 0;\n}\n\n.discusstion-reply__content {\n margin-top: 2px;\n\n p {\n font-weight: 400 !important;\n color: color('body') !important;\n }\n}\n\n.discussion-new-reply {\n padding: 8px;\n}\n\n.discussion-new-reply__input-container {\n padding-top: 0;\n margin: 0;\n\n .md-errors-spacer {\n display: none;\n }\n}\n\n.discussion-new-reply__actions {\n margin-left: 8px;\n\n .md-button {\n margin-top: 0;\n margin-bottom: 0;\n }\n}\n",".embedded-content {\n\n}\n\n.embedded-content__iframe {\n border: 0 none;\n}\n",".graph-select {\n min-width: 150px;\n max-width: 200px;\n}\n\n.graph-controls {\n margin: 8px 0;\n padding: 8px 0;\n border: 1px solid color('gray-lighter');\n border-left-width: 0;\n border-right-width: 0;\n}\n","// Variables\n\n// Base\n.match-content {\n background-color: color('gray-lighter');\n margin-bottom: 16px;\n padding: 8px;\n box-shadow: inset 0 0 3px color('gray-dark');\n}\n\n.match-divider {\n margin: 16px 8px 8px;\n}\n\n.match-divider--horizontal {\n @media only screen and (min-width: $layout-breakpoint-sm) {\n display: none;\n }\n}\n\n.match-bucket__header {\n padding: 12px;\n font-weight: 500;\n color: color('primary');\n}\n\n.match-bucket__content {\n padding: 0;\n}\n\n.match-bucket--choices {\n .match-bucket__header {\n color: color('accent-1');\n }\n}\n\n.match-bucket__contents {\n min-height: 120px;\n //margin: 0;\n padding: 0 8px 8px;\n background-color: color('gray-light');\n transition: background-color 250ms;\n border-bottom-left-radius: $card-border-radius;\n border-bottom-right-radius: $card-border-radius;\n\n column-gap: 8px;\n\n @media only screen and (max-width: ($layout-breakpoint-xs - 1)) {\n column-count: 1 !important;\n }\n\n img {\n max-width: 100%;\n height: auto;\n }\n}\n\n.match-bucket__contents--over {\n background-color: color('primary');\n}\n\n.match-bucket__item {\n list-style-type: none;\n cursor: move;\n padding-top: 8px;\n break-inside: avoid;\n\n &:hover, &:focus {\n //background-color: rgba(0,0,0,0.2);\n }\n}\n\n.match-bucket__item__contents {\n background-color: #ffffff;\n padding: 8px !important;\n border: 1px solid color('gray');\n\n .md-list-item-text {\n width: 100%;\n }\n}\n\n.match-bucket__item__contents__text {\n margin-right: 4px;\n}\n\n.match-feedback {\n transition: opacity 250ms;\n /*padding-left: 8px;\n border-left: 4px solid;*/\n margin: 8px -8px -8px;\n color: #ffffff;\n padding: 4px 8px;\n\n &.ng-hide {\n opacity: 0;\n transition: opacity 1ms;\n }\n\n md-icon {\n color: #ffffff;\n }\n}\n",".outside-content {\n iframe {\n border: 1px solid color('gray-lighter');\n }\n}\n\n.outside-content__source {\n margin-top: 4px;\n text-align: end;\n\n a {\n max-width: 240px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n display: inline-block;\n }\n}",".notebook-toolbar {\n md-divider {\n margin: 8px 0;\n }\n\n @media only screen and (max-width: ($layout-breakpoint-sm - 1)) {\n border-top: 1px solid color('gray-light');\n }\n}\n\n.notebook-toolbar__add-menu {\n position: absolute;\n bottom: 40px;\n\n .md-fab-action-item {\n background-color: #ffffff;\n }\n}\n\n.notebook-toolbar__add-icon {\n border-radius: 50%;\n}\n\n#closeNotebookSettingsButton {\n float:right;\n}\n\n[dir=rtl] #closeNotebookSettingsButton {\n float:left;\n}","highchart {\n display: block;\n}\n"]} \ No newline at end of file +{"version":3,"sources":["src/main/webapp/wise5/themes/default/style/base/_presets.scss","src/main/webapp/wise5/themes/default/style/base/_config.scss","src/main/webapp/wise5/themes/default/style/base/_helpers.scss","src/main/webapp/wise5/themes/default/style/layouts/_l-default.scss","src/main/webapp/wise5/themes/default/style/material/_config.scss","src/main/webapp/wise5/themes/default/style/layouts/_l-footer.scss","src/main/webapp/wise5/themes/default/style/layouts/_l-header.scss","src/main/webapp/wise5/themes/default/style/layouts/_l-main.scss","src/main/webapp/wise5/themes/default/style/vle.css","src/main/webapp/wise5/themes/default/style/layouts/_l-nav.scss","src/main/webapp/wise5/themes/default/style/layouts/_l-node.scss","src/main/webapp/wise5/themes/default/style/layouts/_l-notebook.scss","src/main/webapp/wise5/themes/default/style/layouts/_l-sidebar.scss","src/main/webapp/wise5/themes/default/style/modules/_alerts.scss","src/main/webapp/wise5/themes/default/style/modules/_dragula.scss","src/main/webapp/wise5/themes/default/style/modules/_dialog.scss","src/main/webapp/wise5/themes/default/style/modules/_help.scss","src/main/webapp/wise5/themes/default/style/modules/_inputs.scss","src/main/webapp/wise5/themes/default/style/modules/_table.scss","src/main/webapp/wise5/themes/default/style/modules/_toolbar.scss","src/main/webapp/wise5/themes/default/style/modules/_list.scss","src/main/webapp/wise5/themes/default/style/modules/_nav.scss","src/main/webapp/wise5/themes/default/style/modules/_menu.scss","src/main/webapp/wise5/themes/default/style/modules/_notice.scss","src/main/webapp/wise5/themes/default/style/modules/_node.scss","src/main/webapp/wise5/themes/default/style/modules/_notebook.scss","src/main/webapp/wise5/themes/default/style/modules/_notifications.scss","src/main/webapp/wise5/themes/default/style/modules/_account-menu.scss","src/main/webapp/wise5/themes/default/style/modules/_annotations.scss","src/main/webapp/wise5/themes/default/style/modules/_component.scss","src/main/webapp/wise5/themes/default/style/modules/_component--discussion.scss","src/main/webapp/wise5/themes/default/style/modules/_component--embedded.scss","src/main/webapp/wise5/themes/default/style/modules/_component--graph.scss","src/main/webapp/wise5/themes/default/style/modules/_component--match.scss","src/main/webapp/wise5/themes/default/style/modules/_component--outside.scss","src/main/webapp/wise5/themes/default/style/modules/_notebook-toolbar.scss","src/main/webapp/wise5/themes/default/style/modules/_highcharts.scss"],"names":[],"mappings":"AAIA,KACE,eCSuB,CDVzB,SAIM,eAAgB,CAItB,gBAEQ,aCbgB,CDiBxB,WACE,wBAAgD,CAChD,WAAY,CACZ,aAAc,CAGd,oBAAuB,CAAvB,sBAAuB,CAGzB,qBAEQ,UAAW,CACX,iBAAkB,CAClB,iBAAkB,CAClB,WAAY,CACZ,wBC3BW,CD+BnB,kCAEQ,QAAS,CACT,QAAS,CAKjB,OACI,iBCQoB,CDPpB,eAAgB,CAChB,cElC8B,CFmC9B,eAAgB,CAChB,iBAAkB,CAClB,qBClCkB,CD4BtB,iBASQ,WAAY,CACZ,YAAa,CACb,mBAAoB,CAX5B,8CAcY,qBC1CU,CD4BtB,uBAkBY,uBC9CU,CDmDtB,aACI,wBC3Da,CD4Db,UAAc,CAGlB,aACI,wBCjEa,CDkEb,UAAc,CAGlB,gBACI,wBCpEgB,CDqEhB,UAAc,CAIlB,qBACI,aAAc,CAGlB,iBACI,uBAAwB,CAI5B,EACE,aC7FsB,CD8FtB,cAAe,CAGjB,QACI,kCAAuC,CACvC,qBChFyB,CDoF7B,QACE,iBAAkB,CAClB,sBAAuB,CAGzB,gBACE,iBCxDsB,CD4DxB,cAEI,WAAqC,CACrC,UAAoC,CAHxC,cAMI,WAAqC,CACrC,UAAoC,CAPxC,cAUI,WAAqC,CACrC,UAAoC,CAXxC,cAcI,WAAqC,CACrC,UAAoC,CAKxC,cACE,qBCxHqB,CDyHrB,4BAA8B,CAFhC,8BAKM,WA1ImB,CAqIzB,oBASI,WAAY,CACZ,UAAW,CAVf,oBAaI,WAAY,CACZ,UAAW,CAdf,oBAiBI,WAAY,CACZ,UAAW,CAlBf,oBAqBI,WAAY,CACZ,UAAW,CAIf,wDAEI,qBC7ImC,CD2IvC,4EAOM,qBCjJgC,CDuJtC,mDAEI,uBAAkC,CAFtC,mDAKI,uBAAkC,CALtC,6CAQI,uBAA+B,CARnC,6CAWI,uBAA+B,CAXnC,iDAcI,uBAAiC,CAdrC,qDAiBI,uBAAmC,CAjBvC,qDAoBI,uBAAmC,CAKvC,uCACE,qBCnL2B,CDsL7B,uFACE,wBCzM0C,CD4M5C,2HAEE,aC/MsB,CDgNtB,wBC/M0C,CDqNxC,SACE,aCvNkB,CDsNpB,QACE,aClNa,CDiNf,UACE,aCjNe,CDgNjB,UACE,aChNe,CD+MjB,MACE,aC/MW,CD8Mb,MACE,aC9MW,CD6Mb,SACE,aC7Mc,CD4MhB,SACE,qBC5M0B,CD2M5B,eACE,aC3MoB,CD0MtB,cACE,UC1MmB,CDyMrB,YACE,UCzMiB,CDwMnB,MACE,UCxMW,CDuMb,WACE,UCvMgB,CDsMlB,aACE,aCtMkB,CDqMpB,cACE,UCrMmB,CDoMrB,MACE,qBCpMuB,CDmMzB,gBACE,qBCnMiC,CDkMnC,eACE,qBClMgC,CDiMlC,YACE,UCjMgC,CDgMlC,sBACE,wBChM6C,CD+L/C,qBACE,wBC/L4C,CD8L9C,aACE,aCtNsC,CDqNxC,OACE,aC7LY,CD4Ld,MACE,qBCpMuB,CDmMzB,SACE,UC1MmB,CDgNrB,YACE,wBC9NkB,CD6NpB,WACE,wBCzNa,CDwNf,aACE,wBCxNe,CDuNjB,aACE,wBCvNe,CDsNjB,SACE,wBCtNW,CDqNb,SACE,wBCrNW,CDoNb,YACE,wBCpNc,CDmNhB,YACE,gCCnN0B,CDkN5B,kBACE,wBClNoB,CDiNtB,iBACE,qBCjNmB,CDgNrB,eACE,qBChNiB,CD+MnB,SACE,qBC/MW,CD8Mb,cACE,qBC9MgB,CD6MlB,gBACE,wBC7MkB,CD4MpB,iBACE,qBC5MmB,CD2MrB,SACE,gCC3MuB,CD0MzB,mBACE,gCC1MiC,CDyMnC,kBACE,gCCzMgC,CDwMlC,eACE,qBCxMgC,CDuMlC,yBACE,mCCvM6C,CDsM/C,wBACE,mCCtM4C,CDqM9C,gBACE,wBC7NsC,CD4NxC,UACE,wBCpMY,CDmMd,SACE,gCC3MuB,CD0MzB,YACE,qBCjNmB,CDuNrB,kCAEQ,cCtOY,CDoOpB,iCAEQ,cCjOO,CD+Nf,mCAEQ,cChOS,CD8NjB,mCAEQ,cC/NS,CD6NjB,+BAEQ,cC9NK,CD4Nb,+BAEQ,cC7NK,CD2Nb,kCAEQ,cC5NQ,CD0NhB,kCAEQ,sBC3NoB,CDyN5B,wCAEQ,cC1Nc,CDwNtB,uCAEQ,WCzNa,CDuNrB,qCAEQ,WCxNW,CDsNnB,+BAEQ,WCvNK,CDqNb,oCAEQ,WCtNU,CDoNlB,sCAEQ,cCrNY,CDmNpB,uCAEQ,WCpNa,CDkNrB,+BAEQ,sBCnNiB,CDiNzB,yCAEQ,sBClN2B,CDgNnC,wCAEQ,sBCjN0B,CD+MlC,qCAEQ,WChN0B,CD8MlC,+CAEQ,yBC/MuC,CD6M/C,8CAEQ,yBC9MsC,CD4M9C,sCAEQ,cCrOgC,CDmOxC,gCAEQ,cC5MM,CD0Md,+BAEQ,sBCnNiB,CDiNzB,kCAEQ,WCzNa,CEXzB,eACE,gBAAiB,CACjB,iBAAkB,CAClB,cAAe,CACf,iBAAkB,CAElB,yBANF,eAOM,YCW2B,CDThC,CAED,kBACE,WCK8B,CDJ9B,cAAe,CEbjB,UACE,cAAe,CACf,QAAS,CACT,MAAO,CACP,OAAQ,CACR,SAAU,CACV,qBAAyB,CACzB,yBJIuB,CIAzB,gBACE,QAAS,CACT,aAAc,CACd,gBAAiB,CACjB,WAAY,CACZ,YAAa,CAGf,yBACE,gBAAiB,CCpBnB,UACI,SAAU,CADd,gBAIQ,uBAAyB,CACzB,WAAY,CACZ,UAAW,CACX,qBAAsB,CAP9B,qBAWQ,cAAe,CACf,YAAa,CACb,aAAc,CACd,iBAAkB,CAElB,yCAhBR,qBAiBY,aAAc,CAMrB,CAvBL,sDAqBY,QAAc,CAKtB,yCA1BJ,6FA6BgB,cJlBkB,CImBrB,CC9Bb,QACE,qBNUuB,CMPzB,sBACE,eNmCwB,CMhC1B,SACE,yBAA2B,CAG7B,cACE,aAAc,CACd,WAAY,CACZ,iBAAkB,CAClB,MAAO,CACP,OAAQ,CACR,sBAAyB,CAEzB,yCARF,cASI,YAAa,CAuBhB,CAhCD,uBAaI,SAAU,CAbd,+BAiBI,SAAU,CACV,qBAAuB,CAlB3B,gLA8BI,SAAU,CAId,6BACE,WAAY,CAEZ,yCAHF,6BAII,gBAAiB,CACjB,YAAa,CAEhB,CAEC,yCCwZA,uCDvZE,gBAAiB,CACjB,iBAAkB,CAErB,CAED,cACE,YAAa,CADf,mDAMI,eAAgB,CAChB,YAAa,CACb,eAAgB,CAChB,cL3D8B,CK6D9B,yCAXJ,mDAYM,cL9D4B,CK+D5B,iBAAkB,CAErB,CAID,yCADF,oBAEI,cAAe,CAElB,CAED,0CAEE,YAAa,CAFf,kEAKI,gBAAiB,CAEjB,yCAPJ,kEAQM,aAAc,CACd,cAAe,CAElB,CAXH,0DAcI,0BAA2B,CAI/B,2FAEE,gBAAiB,CEzGnB,OACI,+BAA6C,CCDjD,QACE,eTuCwB,CStCxB,SAAU,CAEV,yCAJF,QAKI,gBAAiB,CACjB,+BAA6C,CAMhD,CAHC,yCATF,QAUI,gBAAiB,CAEpB,CCZD,YACI,eVuCsB,CUtCtB,+BAA6C,CCEjD,mBACE,+BAAoC,CACpC,uBAAmC,CAFrC,6BAKI,qBXQyB,CYpB7B,aACI,YAAa,CACb,SAAU,CACV,qBAAsB,CAG1B,uBACI,WAAY,CACZ,UAAW,CACX,YAAa,CACb,mBAAoB,CACpB,YAAa,CACb,SAAU,CAGd,qBACI,qBAAyB,CAG7B,2BACI,cAAe,CACf,oBAAqB,CCrBzB,WACE,wBAA0B,CAC1B,kBAAoB,CACpB,sBAAwB,CACxB,UAAY,CACZ,mCAAqC,CAEvC,SACE,sBAAwB,CAE1B,iBACE,kCAAoC,CACpC,+BAAiC,CACjC,8BAAgC,CAChC,0BAA4B,CAE9B,YACE,UAAY,CCjBd,UACI,WXkB4B,CWfhC,cACI,WXe4B,CWZhC,eACI,YXY6B,CYrBjC,aACI,iBfqDoB,CepDpB,eAAgB,CAEhB,yBAJJ,aAKQ,eAAuC,CAU9C,CAPG,yBARJ,aASQ,eAAuC,CAM9C,CAHG,0BAZJ,aAaQ,gBAAuC,CAE9C,CAOD,kDAJI,0BfoCoB,CenCpB,2BfTa,CeYjB,8BAGI,kBAAqB,CACrB,wBfhBa,CeYjB,8CAOQ,cAAe,CACf,aAAc,CACd,gBAAiB,CAIzB,sBACI,aAAc,CACd,gBAAiB,CACjB,gBAAiB,CAGrB,sBACI,6BfYoB,CeXpB,8BfWoB,CeRxB,qBACI,iBfOoB,CeLpB,QAAc,CACd,4CAAiD,CACjD,cdrC8B,CcsC9B,SAAU,CANd,uDASQ,iBAAkB,CAClB,UAAW,CACX,WAAY,CAXpB,0DAgBY,KAAM,CACN,SAAU,CAjBtB,kFAoBgB,gCfxDC,CeyDD,kCAAmC,CACnC,mCAAoC,CACpC,iBAAkB,CAClB,SAAU,CAxB1B,yFA4BgB,QAGuC,CA/BvD,4DAoCY,YAAa,CACb,SAAU,CArCtB,oFAwCgB,6Bf5EC,Ce6ED,kCAAmC,CACnC,mCAAoC,CACpC,iBAAkB,CAClB,SAAU,CA5C1B,2FAgDgB,QAGuC,CAnDvD,4DAwDY,QAAS,CACT,UAAW,CAzDvB,oFA4DgB,+BfhGC,CeiGD,oCAAqC,CACrC,iCAAkC,CAClC,iBAAkB,CAClB,MAAO,CACP,SAAU,CAjE1B,2FAqEgB,QAGqC,CAxErD,6DA6EY,QAAS,CACT,WAAY,CA9ExB,qFAiFgB,8BfrHC,CesHD,oCAAqC,CACrC,iCAAkC,CAClC,iBAAkB,CAClB,OAAQ,CACR,SAAU,CAtF1B,4FA0FgB,QAGqC,CCrIrD,iBACI,gBAAiB,CAGrB,4BACI,eAAgB,CAGpB,4CAEQ,cAAe,CAFvB,kDAMQ,YAAa,CAIrB,eACI,iBAAkB,CAGtB,yDAEQ,ahB7BgB,CgBiCxB,2DACI,WAAY,CACZ,wBhBvBsB,CgBwBtB,qBhBrBa,CgBsBb,iBAAkB,CAJtB,uEAOQ,qBAAyB,CAPjC,+EAWQ,qBhBxB+B,CgB4BvC,0CACI,UAAW,CAGf,2BACI,qBhBjCmC,CgBoCvC,yBACI,iBAAkB,CAClB,UAAW,CAFf,2CAKQ,+BAAwC,CAIhD,mCACI,OAjE8B,CAmE9B,4DACI,QAnEoC,CAuE5C,mCACI,UAzE8B,CA2E9B,4DACI,WAAsD,CAK9D,mHACI,eAAgB,CAChB,qBhBjEyB,CgB+D7B,6JAKQ,ahBvFgB,CgB2FxB,oBAEQ,sBAAuB,CACvB,eAAgB,CAChB,cAAe,CACf,eAAgB,CAChB,qBhB7E+B,CgBkFnC,yCADJ,wBAEQ,eAAgB,CAMvB,CAHG,yCALJ,wBAMQ,eAAgB,CAEvB,CAED,yCAEQ,qBAAyB,CAFjC,+DAKY,eAAgB,CAChB,qBhBxGa,CgB6GzB,gBACI,WhB5EiC,CgB2ErC,sBAIQ,WAAY,CACZ,UAAW,CACX,aAAc,CACd,YAAa,CACb,QAAc,CACd,cftH0B,CeuH1B,eAAgB,CCrIxB,OACE,cAAe,CACf,UAAW,CACX,eAAgB,CAChB,YAAa,CAJf,kHAYM,qBjBIW,CiBHX,WAAY,CACZ,chBA4B,CgBC5B,eAAgB,CAChB,WAAY,CACZ,cAAe,CACf,kBAAmB,CAlBzB,6BAwBI,wBjBXsB,CiBYtB,SAAU,CACV,kBAAmB,CA1BvB,0BA8BI,QAAS,CA9Bb,yBAkCI,YAAa,CAIjB,4BAGM,gBAAiB,CAKvB,mBACE,UAAW,CAGb,aACE,QAAc,CACd,wBAAyB,CACzB,qBAAyB,CACzB,cAAe,CACf,aAAc,CALhB,gCASI,aAAc,CACd,QAAc,CAVlB,gBAcI,eAAgB,CAChB,WAAY,CAfhB,0BAoBM,iBAAkB,CAClB,eAAgB,CAChB,UAAW,CACX,mBAAoB,CACpB,iBAAkB,CAClB,eAAmB,CAKzB,mBACE,ed9D8B,CckE9B,yCADF,mBAEI,eAAgB,CAEnB,CAED,oBACE,chB7EgC,CgB8EhC,eAAgB,CAGlB,wBACE,WAAY,CACZ,QAAS,CAGX,wBACE,wBjBnFsB,CiBoFtB,UjB/EoC,CiBgFpC,ejB5DwB,CiB6DxB,WjB7DwB,CiBgE1B,0BACE,UAAc,CACd,mBAAoB,CACpB,QAAS,CACT,WAAY,CACZ,kBAAmB,CACnB,eAAgB,CAChB,UAAW,CAGb,0BACE,QAAS,CAGX,mCACE,wBAAyB,CAG3B,UACE,eAAgB,CAChB,kBAAmB,CACnB,eAAgB,CAIhB,yCADF,eAEI,eAAgB,CAChB,eAAgB,CAChB,sBAAuB,CACvB,kBAAmB,CAEtB,CC1ID,kBACI,cAAe,CADnB,2HAWY,WAAY,CAKxB,kBACI,elB0BsB,CkB3B1B,oCAIQ,WlBuBkB,CkBtBlB,elBsBkB,CkB3B1B,4CASQ,WlBkBkB,CkBjBlB,gBlBiBkB,CkBhBlB,UlBgBkB,CkBZ1B,wCAEQ,aAAc,CACd,cjBpB0B,CiBwBlC,qBACI,+BAAkD,CADtD,uCAIQ,cjB5B0B,CiB6B1B,eAAgB,CAIxB,SACI,cAAe,CACf,MAAO,CACP,OAAQ,CACR,Qf5CoB,Ce6CpB,SAAU,CAGd,gBACI,eAAgB,CAChB,cjB3C8B,CiB4C9B,eAAgB,CAGpB,gBACI,iBAAkB,CX21BtB,0BWx1BI,kBAAmB,CACnB,gBAAiB,CAGrB,sCACI,QAAS,CAGb,4CACI,YAAa,CACb,eAAgB,CAChB,wBlB/DsB,CkB4D1B,8EAMQ,WAAY,CACZ,eAAgB,CXw1BxB,kGWl1BU,gBAAiB,CAK3B,6BACI,WAAY,CAEZ,yCAHJ,6BAIQ,WAAY,CAMnB,CAHG,yCAPJ,6BAQQ,WAAY,CAEnB,CCrGD,WACI,qBAAyB,CACzB,4BnBYqB,CmBdzB,iDAKQ,qBnBeqB,CmBdrB,qBAAyB,CANjC,iEASY,qBAAsB,CATlC,yFAaY,SAAU,CAbtB,uEAiBY,gBAAiB,CAjB7B,yBAsBQ,YAAa,CAIrB,kGAEQ,uCAA+C,CAC/C,gBAAiB,CAIzB,kGAIQ,uCAA+C,CAC/C,gBAAiB,CAIzB,qBACI,qBAAsB,CAG1B,kDACI,cAAe,CACf,wBnBnCsB,CmBsC1B,oBACI,uBAAyB,CAG7B,6BACI,mBAAoB,CACpB,UAAW,CACX,gBAAiB,CACjB,QAAS,CACT,kBAAmB,CACnB,eAAgB,CAChB,eAAgB,CAGpB,WACI,clBpD8B,CmBdlC,KACI,iBAAkB,CAGtB,KACI,kBAAmB,CAGvB,UACI,iBAAkB,CAClB,KAAM,CACN,QAAS,CACT,MAAO,CACP,OAAQ,CACR,gCAAkC,CAClC,SAAU,CAPd,kBAUQ,SAAU,CAIlB,UACI,qBpBFmC,CoBGnC,eAAgB,CAFpB,kBAKQ,gBAAiB,CAIzB,oBACI,oBAAqB,CAGzB,qBACI,qBpBrBmB,CoBsBnB,WAAY,CAOhB,wCACI,SAAU,CAEV,yBAHJ,oBAIQ,WAAY,CAEnB,CAED,UACI,mCAAqC,CADzC,4BAKY,wBAKG,CAKf,yBACI,2BpBdoB,CoBepB,0BpBfoB,CoBaxB,+BAKQ,YAAa,CALrB,qDAQY,6BpB3DK,CoBgEjB,gBACI,qCAA0C,CAD9C,yBAIQ,eAAgB,CAEhB,cAAe,CACf,yBAA2B,CAC3B,eAAgB,CAChB,gBAAiB,CATzB,uCAYY,YAAa,CAYzB,2BACI,oBAAqB,CACrB,oBAAqB,CAGzB,yBACI,eAAgB,CAChB,qBpBzFkC,CoBgGtC,sCAEQ,4IACsF,CAI9F,oBACI,QAAS,CAGb,gBACI,yBpBnHmB,CoBoHnB,8BpB7EoB,CoB8EpB,6BpB9EoB,CoB+EpB,gBAAiB,CACjB,eAAgB,CAGpB,iBACI,WAAY,CACZ,cAAe,CACf,UAAc,CACd,QAAS,CACT,eAAgB,CALpB,yBAQQ,iBAAkB,CAClB,UAAc,CATtB,oEAcY,wBpB5IQ,CoBiJpB,iBACI,iBAAkB,CAClB,eAAgB,CAChB,eAAgB,Cb26BpB,2Bax6BI,iBAAkB,CAClB,kBAAmB,CAGvB,gBACI,aAAc,CAGlB,oBACI,UAAW,CADf,kCAIQ,KAAM,CAId,0BACI,eAAgB,CAChB,UAAW,CAGf,kBACI,aAAc,CACd,cAAe,CCrLnB,eACI,iBAAkB,CAClB,QAAS,CACT,UAAW,CAHf,oBAMQ,wBAAoC,CACpC,gBAAiB,Cd2lCzB,yBcvlCE,UAAU,CACV,SAAS,CAOX,oBACI,eAAgB,CAEhB,cpBZ8B,CoBelC,oBACI,yBAA2B,CAC3B,2BAA6B,CAC7B,gBAAiB,CAGrB,wDAEQ,arBpCgB,CqBwCxB,cACI,iBAAkB,CAClB,QAA8C,CAC9C,QAAS,CACT,MAAO,CACP,qBAAsB,CACtB,UA9CqB,CA+CrB,eAAgB,CAChB,aAAc,CACd,iBAAkB,CAClB,2BrBnCa,CqBqCb,yCAZJ,cAaQ,YAAa,CAEpB,Cd6kCD,wBc3kCE,OAAO,CACP,SAAS,CAGX,6CACI,cAAe,CACf,iBAAkB,CChEtB,QACE,iBAAkB,CAClB,WAAY,CACZ,gCAAkC,CAClC,UAAW,CAEX,yBANF,QAOI,aAAc,CACd,iBtBiDsB,CsBhDtB,gBAAiB,CAEpB,CCLD,MACI,aAAc,CACd,iBAAkB,CAClB,MAAO,CACP,OAAQ,CAER,yCANJ,MAQQ,iBAAkB,CAClB,kBAAmB,CAe1B,CAZG,yCAZJ,MAaQ,YAAa,CAWpB,CAxBD,eAiBQ,sBAAuB,CACvB,SAAU,CAlBlB,sBAsBQ,SAAU,CAOd,yCADJ,aAEQ,eAAgB,CAChB,kBAAmB,CAM1B,CAHG,yCANJ,aAOQ,gBAAiB,CAExB,CAED,cACI,gBAAiB,CACjB,qBAAyB,CACzB,iBvBSsB,CuBRtB,gBAAiB,CAEjB,yCANJ,cAOQ,eAAgB,CAQvB,CALG,yCAVJ,cAWQ,SAAU,CACV,oBAAqB,CACrB,uBAAwB,CAE/B,CAED,wBAEQ,qBAAyB,CAIjC,sBACI,iBAAkB,CAClB,SAAU,CACV,MAAO,CACP,OAAQ,CACR,SAAU,CALd,oCAQQ,oBAAsB,CAEtB,yCAVR,oCAWY,mBAAqB,CAE5B,CAGL,WACI,UAAc,CACd,sBAAuB,CAG3B,aACI,YAAa,CACb,WAAY,CACZ,eAAgB,CAChB,ctB/E8B,CsB2ElC,2CAQY,uBAA6B,CAC7B,UAAW,CATvB,oGAiBY,YAAa,CAjBzB,6BAsBQ,aAAc,CACd,qBvB5FqB,CuBgG7B,2BAEI,wBvBzGsB,CuB0GtB,4BvBzGqB,CuB0GrB,yBvB1GqB,CuB6GzB,0BACI,iBAAkB,CAGtB,mBACI,gBAAiB,CAGrB,qBACI,eAAgB,CAGpB,mBACI,sBAAuB,CACvB,kBAAmB,CACnB,eAAgB,CAEhB,yCALJ,mBAMQ,cAAe,CAEtB,CAED,YACI,eAAgB,CAChB,mBAAoB,CACpB,cAAe,CAEf,yCALJ,YAMQ,ctBzI0B,CsB2IjC,CAED,uBACI,mBAAoB,CAEpB,yCAHJ,uBAIQ,mBAAoB,CAc3B,CAXG,0CAPJ,uBAQQ,mBAAoB,CAU3B,CAlBD,8CAYQ,aAAc,CAZtB,6CAgBQ,cAAe,CAIvB,6BACI,iBAAkB,CAClB,eAAgB,CAChB,qBvB7JmC,CuBgKvC,6BACI,wBAAyB,CAG7B,iDAKQ,cAAe,CAKnB,yCADJ,qBAEQ,iBAAuC,CAE9C,CAGG,yCADJ,sBAEQ,kBvB/JkB,CuBiKzB,CAED,cACI,iBAAkB,CAClB,OAAQ,CACR,KAAM,CACN,UpB3MoB,CoB8MxB,uBACI,cAAe,CACf,UpBhNoB,CoBiNpB,qBAAyB,CACzB,aAAc,CACd,iBvBjKsB,CuBmKtB,yCAPJ,uBAQQ,OAAQ,CACR,QAAS,CACT,MAAO,CACP,UAAW,CACX,eAAgB,CAChB,SAAU,CACV,YAAa,CACb,WvBzLkB,CuB2LzB,CAID,WACI,QAAS,CAET,yCAHJ,WAIQ,YAAa,CACb,eAAgB,CAsBvB,CA3BD,oBASQ,gBAAiB,CACjB,iBAAkB,CAV1B,kCAeY,gBAAiB,CAf7B,mFAoBQ,gBAAiB,CACjB,iBAAkB,CArB1B,+BAyBQ,YAAa,CAIrB,aACI,wBvBvQoB,CuBwQpB,oBAAqB,CACrB,YAAa,CACb,qBAAyB,CAO7B,iCACI,qBAAsB,CACtB,oBAAqB,CC/QrB,yCADJ,oCAGY,UAAW,CACd,CAIT,iBACI,yBAA0B,CAC1B,QAAS,CACT,WAAY,CAHhB,+BAMQ,oBAAqB,CACrB,WAAY,CACZ,eAAc,CAAd,YAAc,CAEd,8BxBPe,CwBOf,uBxBPe,CwBOf,kBxBPe,CwBQf,eAAgB,CAXxB,0CAcY,YAAa,CAdzB,2BAmBQ,QAAc,CACd,wBAAyB,CACzB,yBAA0B,CAC1B,2BAA4B,CAC5B,4BAA6B,CAIrC,2BACI,YAAa,CACb,WAAY,CACZ,cAAe,CACf,QAAS,CACT,UAAW,CACX,iBAAkB,CAClB,SAAU,CAIV,yCADJ,0CAGY,WAAY,CACZ,qBAAuB,CAC1B,CAIT,iCACI,QAAS,CACT,WAAY,CACZ,SAAU,CACV,UAAW,CACX,eAAgB,CAChB,cAAe,CACf,WAAY,CACZ,UAAW,CARf,kDAWQ,WAAY,CACZ,UAAW,CACX,aAAc,CACd,eAAgB,CAChB,iBxBnBgB,CwBuBxB,sCACI,WAAY,CACZ,WAAY,CAFhB,wMAKQ,YAAa,CAIrB,0BACI,+BAAkD,CAClD,yBAA0D,CAG9D,iCACI,eAAgB,CAGpB,0BAKI,qBAAyB,CAL7B,oHAEQ,cvBnF0B,CuBiFlC,kDAQQ,QAAc,CACd,eAAgB,CAChB,SAAU,CACV,eAAgB,CAXxB,0CAeQ,YAAa,CAIrB,kCACI,WAAY,CACZ,qBAAyB,CACzB,cvBvG8B,CuB0GlC,yCACI,sEAEQ,KAAM,CACN,QAAS,CACT,MAAO,CACP,OAAQ,CACR,eAAgB,CAChB,cAAe,CACf,WAAY,CACZ,UAAW,CATnB,uFAYY,eAAgB,CAK5B,sBACI,YAAa,CAChB,CAGL,0BACI,cAAe,CACf,KAAM,CACN,MAAO,CACP,UAAW,CACX,WAAY,CACZ,SAAU,CACV,wBAAoC,CACpC,WAAY,CAGhB,eACI,sBAAyB,CAD7B,wBAIQ,SAAU,CAJlB,gCAQQ,SAAU,CACV,qBAAuB,CAT/B,sLAkBQ,SAAU,CAIlB,eACI,0BAA4B,CAC5B,kBAAmB,CACnB,aAAc,CAGlB,wBACI,YAAa,CACb,eAAgB,CAChB,iBAAkB,CAClB,SAAU,CACV,qBxB3Ka,CwB4Kb,0BxBtIoB,CwBuIpB,2BxBvIoB,CwB0IxB,kEACI,iBAAkB,CAClB,MAAO,CACP,OAAQ,CAGZ,oCACI,qCAAuC,CACvC,0BxBlJoB,CwBmJpB,2BxBnJoB,CwBoJpB,iCAA0C,CAC1C,+BAAiC,CACjC,KAAM,CACN,QAAS,CAGb,8BACI,QAAS,CACT,WAAY,CACZ,eAAgB,CAChB,eAAgB,CAChB,gBAAiB,CACjB,eAAgB,CAChB,oCAAwC,CACxC,yBxB1MqB,CwBkMzB,oCAWQ,UAAW,CACX,gBAAiB,CACjB,iBAAkB,CAClB,QAAS,CACT,OAAQ,CACR,UAAW,CACX,WAAa,CACb,6EAAiF,CAIzF,yCAEQ,cAAe,CACf,0BAA6B,CAC7B,eAAmB,CACnB,iBAAkB,CAClB,oBAAqB,CACrB,aAAc,CACd,mBAAoB,CACpB,qBAAsB,CACtB,gBAAiB,CACjB,kBAAmB,CACnB,aAAc,CAGd,kCAAmC,CAEnC,iCAAkC,CAGlC,iCAAkC,CAGlC,4BAA6B,CAE7B,cAAe,CACf,qBxB1O8B,CwB8OtC,6CACI,mBAAoB,CAGxB,kCACI,UAAY,CACZ,aAAc,CAFlB,0CAKQ,cAAe,CAIvB,qBACI,cAAe,CAGnB,wBACI,QAAS,CACT,aAAc,CACd,UAAc,CACd,qBxBtQqB,CwBuQrB,6BxBpOoB,CwBqOpB,8BxBrOoB,CwB+NxB,gCASQ,UAAc,CAItB,2BACI,QAAS,CAGb,qCACI,cAAe,CACf,eAAgB,CAGpB,2BACI,qBxB7RmB,CwB8RnB,YAAa,CACb,kBAAmB,CACnB,iBAAkB,CAClB,iBAAkB,CAGtB,oCACI,cAAe,CACf,WAAY,CAGhB,mCACI,iBAAkB,CAClB,OAAQ,CACR,UAAW,CAEX,oBAAsB,CACtB,qBAAuB,CACvB,YAAa,CAPjB,2CAUQ,gBAAiB,CACjB,cAAe,CAIvB,qBACI,iBAAkB,CAClB,UAAW,CACX,aAAqC,CAHzC,oDAMQ,aAAqC,CAN7C,6BAUQ,eAAgB,CAChB,WAAY,CACZ,UAAW,CAInB,uBACI,iBAAkB,CAClB,YAAa,CACb,qBxB5UqB,CwB6UrB,kBAAmB,CACnB,qBxBvUmC,CwBwUnC,iBAAkB,CAClB,cAAe,CACf,6BAA8B,CAC9B,mBAAqB,CATzB,2DAYQ,qBAAuB,CAZ/B,0FAgBQ,oBxBjWW,CwBkWX,wBAA+C,CAC/C,axBnWW,CwBiVnB,2NAqBY,axBtWO,CwB2WnB,oBACI,WrB/V4B,CqBqWhC,uBACI,qBAAyB,CAD7B,oCAIQ,kBAAmB,CACnB,iBxB7WS,CwBiXjB,iFAGY,cAAe,CACf,QAAgD,CAChD,MAAO,CACP,OAAQ,CACR,SAAU,CAEV,yCATZ,iFAUgB,SAAmC,CAInC,cAJmC,CAsB1C,CAfG,yCAjBZ,iFAkBgB,cAAe,CActB,CAhCT,+FAsBgB,aAAc,CAEd,yCAxBhB,+FAyBoB,YAAa,CAMpB,CAHG,yCA5BhB,+FA6BoB,aAAc,CAErB,CA/Bb,8DAmCY,gBAAiB,CAK7B,8CAGQ,qBxB7Ze,CwB8Zf,qBxB7ZS,CwB8ZT,kBAAmB,CAI3B,gCACI,iBAAkB,CAClB,kBAAmB,CAMvB,iCACI,eAAgB,CAGpB,iCACI,cAAe,CACf,qBAAuB,CAIvB,yCADJ,kBAEQ,WAAY,CACZ,cAAe,CAOtB,CAJG,yCANJ,kBAOQ,WAAY,CACZ,cAAe,CAEtB,CAED,gBACI,UAAW,CACX,aAAc,CACd,eAAgB,CAChB,kBAAmB,CAJvB,+BAOQ,UAAW,CAPnB,wCAWQ,YAAa,CACb,WAAY,CAIpB,yBACI,eAAgB,CAGpB,yCACI,6EAEQ,qBAA6C,CAChD,CAIT,kBACI,qBAAyB,CACzB,aAAc,CC7elB,kBACI,oBAAsB,CAD1B,0BAIQ,gBAAiB,CAIzB,oBACI,iBAAkB,CAClB,iBAAkB,CAClB,wBzBLe,CyBMf,UAAW,CACX,QAAS,CACT,WAAY,CACZ,gBAAiB,CACjB,cAAe,CACf,eAAgB,CAChB,gBAAiB,CAVrB,2BAaQ,UAAW,CACX,iBAAkB,CAClB,UAAW,CACX,OAAQ,CACR,yCAA6C,CAC7C,gCAAiC,CACjC,mCAAoC,CAI5C,mBACI,aAAc,CAGlB,sBACI,WAAY,CAGhB,6BACI,eAAgB,CAGpB,kPAIQ,qBzB1B+B,CyB2B/B,cxBlC0B,CwB6BlC,0QAQY,cxBrCsB,CwBsCtB,WAAY,CACZ,UAAW,CACX,gBAAiB,CACjB,gBxBzCsB,CyBdlC,cACI,iB1BqDoB,C0BpDpB,SAAU,CACV,czBW8B,CyBV9B,eAAgB,CAEhB,0BANJ,cAOQ,yBAA2B,CAOlC,CAdD,iBAWQ,QAAS,CACT,eAAgB,CAIxB,4BACI,Y1BiCyE,C0B9B7E,2BACI,WAAY,CAEZ,yBAHJ,2BAIQ,WAAY,CAEnB,CAED,oBACI,qB1BNkC,C0BOlC,iBAAkB,CAGtB,qBACI,iBAAkB,CAClB,UAAW,CACX,QAAS,CACT,YAAa,CAJjB,4BAOQ,UAAW,CACX,iBAAkB,CAClB,4BAA6B,CAC7B,iCAAkC,CAClC,kCAAmC,CAI3C,+DACI,UAAW,CAGf,+CACI,WAAY,CnB+tDhB,+BmB1tDI,UAAW,CACX,SAAU,CnB6tDd,mFmB1tDI,SAAS,CACT,UAAU,CnB6tDd,yDmB1tDM,UAAW,CACX,UAAU,CAIhB,oBACI,gBAAiB,CAGrB,2BACI,eAAgB,CAGpB,0BACI,eAAgB,CAChB,qB1B5DmC,C0B+DvC,uDAIQ,SAAU,CAJlB,6CAOY,gBAAiB,CACjB,WAAY,CACZ,UAAW,CAKvB,2CAEQ,wBAAyB,CACzB,UAAW,CAInB,qBACI,iBAAkB,CAClB,gBAAiB,CAFrB,6BAKQ,a1BnFU,C0BuFlB,8BACI,iBAAkB,CAClB,KAAM,CACN,UAAW,CACX,mCAA0C,CAG9C,uBACI,wB1B7GsB,C0BgH1B,uBACI,YAAa,CC9HjB,aACI,yBAA0B,CAC1B,iBAAkB,CAClB,c1BW8B,C0BdlC,gBAMQ,iBAAkB,CAClB,4BAA6B,CAPrC,mBAWQ,UAAW,CACX,iBAAkB,CAClB,OAAQ,CACR,QAAS,CACT,UAAW,CACX,UAAW,CACX,KAAQ,CACR,WAAY,CACZ,iCAAkC,CAClC,oCAAqC,CACrC,+B3BHgB,C2BOxB,wCACI,yB3BXmB,C2BcvB,qBACI,YAAa,CACb,eAAgB,CAGpB,qBACI,iBAAkB,CAElB,2BAA4B,CAC5B,iBAAkB,CAClB,eAAgB,CAChB,iBAAkB,CAClB,UAAc,CACd,wB3BxBoB,C2B2BxB,qBACI,wB3BxCe,C2ByCf,WAAY,CACZ,iBAAkB,CAClB,KAAM,CACN,UAAW,CAGf,mBACI,iBAAkB,CAClB,UAAc,CAGlB,mBACI,YAAa,CACb,qBAAyB,CACzB,6B3BPoB,C2BQpB,8B3BRoB,C2BSpB,aAAc,CAGlB,qBACI,qBAAyB,CACzB,a3B1Da,C2B2Db,oBAAqB,CACrB,eAAgB,CAChB,c1BzD8B,C0BoDlC,4DAQQ,iBAAkB,CAR1B,4EAYQ,SAAS,CAZjB,4EAgBQ,SAAS,CAIjB,oBACI,eAAgB,CAGpB,mBACI,iBAAkB,CAClB,UAAY,CACZ,wBAAyB,CACzB,c1BhF8B,C0BmFlC,kCAEQ,gBAAiB,CAKzB,mBACI,kBAAmB,CACnB,gBAAiB,CACjB,gBAAiB,CAEjB,yCALJ,mBAMQ,0BAA2B,CAclC,CApBD,yBAUQ,+B3BxGS,C2B8FjB,wCAcQ,qBAAyB,CAdjC,wCAkBQ,wB3BhHS,C4BVjB,WACI,iBAAkB,CAGtB,oBACI,cAAe,CACf,aAAc,CAGlB,oBACI,eAAgB,CAChB,c3BG8B,C2BF9B,iBAAkB,CAElB,yCALJ,oBAMQ,aAAc,CAErB,CAED,uCACI,gBAAiB,CACjB,QAAS,CACT,c3BR8B,C2BWlC,mBACI,iBAAkB,CAClB,UAAW,CACX,QAAS,CAGb,yCAGY,mBAAqB,CACrB,cAAe,CACf,WAAY,CALxB,8FAUgB,8B5BnCG,C4B0CnB,2CAEQ,aAAc,CAFtB,0CAMQ,cAAe,CAIvB,0BACI,iBAAkB,CAClB,eAAgB,CAEhB,qB5BzCmC,C4B4CvC,0BACI,wBAAyB,CAG7B,mBACI,iBAAkB,CAClB,eAAgB,CAGpB,4BACI,cAAe,CAGnB,uBACI,iBAAkB,CAClB,YAAa,CACb,kBAAmB,CAEnB,yCALJ,uBAMQ,eAAgB,CAEvB,CAGG,yCADJ,2BAEQ,UAAW,CAElB,CAED,gCACI,gBAAiB,CACjB,UAAW,CAGf,+BACI,iBAAkB,CAClB,KAAM,CACN,OAAQ,CACR,WAAY,CACZ,8CAAsD,CACtD,eAAgB,CAChB,WAAY,CACZ,QAAS,CARb,uCAeQ,YAAa,CAIrB,qBACI,YAAa,CACb,WAAY,CAFhB,oCAKQ,wB5B9GkB,C4BkH1B,8BACI,iBAAoB,CACpB,4B5BlHmB,C4BqHvB,8BACI,a5BnHoB,C4BoHpB,eAAgB,CCnIpB,gCACI,eAAgB,CAGpB,oBACI,qB7BMqB,C6BJrB,6B7BOkB,C6BJtB,kBACI,qBAAsB,CAEtB,0CAHJ,kBAIQ,mBAAoB,CAE3B,CAED,iBACI,kBAAmB,CACnB,e1BJ4B,C0BM5B,yCAJJ,iBAKQ,kBAAmB,CAY1B,CATG,0CARJ,iBASQ,kBAAmB,CAQ1B,CAjBD,4BAcQ,iBAAkB,CAClB,UAAW,CAInB,2BACI,YAAa,CAGjB,yEACI,gBAAiB,CAGrB,uFACI,cAAe,CAInB,gFACI,kBAAmB,CACnB,eAAgB,CAChB,eAAgB,CAChB,gBAAiB,CACjB,kBAAmB,CAGvB,uBACI,U7B7CkB,C6BgDtB,8BACI,eAAgB,CAChB,eAAgB,CAGpB,0BACI,eAAgB,CAChB,oBAAqB,CAGzB,6BACI,cAAe,CACf,qBAAuB,CACvB,eAAgB,CAGpB,gBACI,qBAAyB,CACzB,eAAgB,CAChB,gBAAiB,CACjB,iBAAkB,CAClB,WAAY,CACZ,mBAAqB,CACrB,oBAAsB,CAG1B,yBACI,kBAAmB,CAGvB,mDACI,QAAS,CACT,SAAU,CAFd,qEAKQ,eAAgB,CAIxB,2FACI,WAAY,CACZ,QAAc,CAGlB,yBACI,aAAc,CADlB,kDAKY,aAAc,CAL1B,iDASY,cAAe,CAK3B,4BACI,SAAU,CACV,cAAe,CAGnB,qCACI,YAAa,CACb,kBAAmB,CAGvB,qBACI,SAAU,CAGd,+BACI,wB7B7HsB,C6BgI1B,6BACI,4BAA6B,CAC7B,SAAU,CAFd,iDAKQ,kBAAmB,CAI3B,2BACI,SAAU,CACV,iBAAkB,CAClB,eAAgB,CAEhB,yCALJ,2BAMQ,gBAAiB,CAExB,CAED,2GACI,qBAAyB,CACzB,WAAY,CACZ,cAAqC,CACrC,QAAc,CACd,gBAAiB,CACjB,eAAgB,CAChB,WAAY,CAGhB,gDACI,iBAAkB,CAClB,SAAU,CACV,eAAgB,CAGpB,uFACI,YAAa,CAGjB,8FACI,cAAqC,CACrC,SAAU,CACV,QAAS,CAGb,4BACI,cAAe,CADnB,8BAIQ,yBAA2B,CAC3B,+BAA+B,CAIvC,sBACI,WAAY,CAGhB,uCACI,aAAc,CACd,QAAS,CAFb,yDAKQ,YAAa,CAIrB,+BACI,eAAgB,CADpB,0CAIQ,YAAa,CACb,eAAgB,CCjNxB,0BACI,QAAc,CCLlB,cACI,eAAgB,CAChB,eAAgB,CAGpB,gBACI,YAAa,CACb,aAAc,CAGd,iBAAqB,CAArB,kBAAqB,CAArB,kBAAqB,CCPzB,eACI,qBhCUqB,CgCTrB,kBAAmB,CACnB,WAAY,CACZ,6BhCUkB,CgCPtB,eACI,mBAAoB,CAIpB,yCADJ,2BAEQ,YAAa,CAEpB,CAED,sBACI,YAAa,CACb,eAAgB,CAChB,ahCtBoB,CgCyBxB,uBACI,SAAU,CAGd,6CAEQ,ahCzBa,CgC6BrB,wBACI,gBAAiB,CAEjB,iBAAkB,CAClB,qBhCzBmB,CgC0BnB,gCAAkC,CAClC,6BhCYoB,CgCXpB,8BhCWoB,CgCTpB,mBAAe,CAAf,cAAe,CAEf,yCAXJ,wBAYQ,6BAA0B,CAA1B,wBAA0B,CAOjC,CAnBD,4BAgBQ,cAAe,CACf,WAAY,CAIpB,8BACI,wBhCzDoB,CgC4DxB,oBACI,oBAAqB,CACrB,WAAY,CACZ,eAAgB,CAChB,8BAAmB,CAAnB,kBAAmB,CAOvB,8BACI,qBAAyB,CACzB,qBAAuB,CACvB,qBhC3Da,CgCwDjB,iDAMQ,UAAW,CAInB,oCACI,gBAAiB,CAGrB,gBACI,uBAAyB,CAGzB,oBAAqB,CACrB,UAAc,CACd,eAAgB,CANpB,wBASQ,SAAU,CACV,sBAAuB,CAV/B,wBAcQ,UAAc,CCpGtB,wBAEI,qBjCYqB,CiCRzB,yBACE,cAAe,CACf,cAAe,CAFjB,2BAKI,eAAgB,CAChB,kBAAmB,CACnB,eAAgB,CAChB,sBAAuB,CACvB,oBAAqB,CCfzB,6BAEQ,YAAa,CAGjB,yCALJ,kBAMQ,yBlCSe,CkCPtB,CAED,4BACI,iBAAkB,CAClB,WAAY,CAFhB,gDAKQ,qBAAyB,CAIjC,4BACI,iBAAkB,CAGtB,6BACE,WAAW,C3BgwEb,uC2B5vEE,UAAU,CC5BZ,UACE,aAAc","file":"vle.css","sourcesContent":["// Config\n$avatar-icon-padding: 6px !default;\n$avatar-icon-padding-lg: 8px !default;\n\nbody {\n background: color('body-bg');\n\n &.vle {\n overflow: hidden;\n }\n}\n\na {\n &:hover, &:focus { // TODO: remove when bootstrap css dependency is removed\n color: color('primary');\n }\n}\n\nblockquote {\n background-color: lighten(color('primary'), 56%);\n padding: 8px;\n margin: 16px 0;\n border-style: solid;\n border-color: color('primary');\n border-width: 0 0 0 3px;\n}\n\n.has-indicator {\n &:after {\n content: '';\n position: absolute;\n border-radius: 50%;\n padding: 5px;\n background-color: color('accent');\n }\n}\n\n.has-indicator--icon-button {\n &:after {\n top: 25px;\n left: 5px;\n }\n}\n\n// Badges\n.badge {\n border-radius: $card-border-radius;\n padding: 2px 6px;\n font-size: rem(1.2);\n font-weight: 500;\n font-style: normal;\n background-color: color('gray-dark');\n\n &.md-button {\n min-width: 0;\n min-height: 0;\n line-height: inherit;\n\n &:hover, &:focus {\n background-color: color('gray-dark');\n }\n\n &:focus {\n outline: 1px dotted color('gray-dark');\n }\n }\n}\n\n.badge--info {\n background-color: color('info');\n color: #ffffff;\n}\n\n.badge--warn {\n background-color: color('warn');\n color: #ffffff;\n}\n\n.badge--success {\n background-color: color('success');\n color: #ffffff;\n}\n\n// Dividers\n.divider--withmargin {\n margin: 16px 0;\n}\n\n.divider--dashed {\n border-top-style: dashed;\n}\n\n// Links\na {\n color: color('primary');\n cursor: pointer;\n}\n\n.active {\n background-color: rgba(158,158,158,0.2);\n color: color('text');\n}\n\n// Images & Icons\n.avatar {\n border-radius: 50%;\n box-sizing: content-box;\n}\n\n.avatar--square {\n border-radius: $card-border-radius;\n}\n\n// Rules for sizing avatars (matches material icons plus avatar-icon padding)\n.avatar {\n &.md-18 {\n height: 18px + $avatar-icon-padding*2;\n width: 18px + $avatar-icon-padding*2;\n }\n &.md-24 {\n height: 24px + $avatar-icon-padding*2;\n width: 24px + $avatar-icon-padding*2;\n }\n &.md-36 {\n height: 36px + $avatar-icon-padding*2;\n width: 36px + $avatar-icon-padding*2;\n }\n &.md-48 {\n height: 48px + $avatar-icon-padding*2;\n width: 48px + $avatar-icon-padding*2;\n }\n}\n\n// Rules for sizing avatar backgrounds (when using a child md-icon)\n.avatar--icon {\n background-color: color('gray-light');\n white-space: normal !important;\n\n &:not(.md-avatar) {\n padding: $avatar-icon-padding;\n }\n\n &.md-18 {\n height: 18px;\n width: 18px;\n }\n &.md-24 {\n height: 24px;\n width: 24px;\n }\n &.md-36 {\n height: 36px;\n width: 36px;\n }\n &.md-48 {\n height: 48px;\n width: 48px;\n }\n}\n\nmd-toolbar.md-light-theme:not(.md-menu-toolbar) {\n md-icon {\n color: color('text-secondary');\n }\n\n .md-button:disabled {\n md-icon {\n color: color('text-disabled');\n }\n }\n}\n\n// hacks for now\nmd-icon, .md-button:not([disabled]) {\n &.primary {\n color: color('primary') !important;\n }\n &.success {\n color: color('success') !important;\n }\n &.warn {\n color: color('warn') !important;\n }\n &.info {\n color: color('info') !important;\n }\n &.accent {\n color: color('accent') !important;\n }\n &.accent-1 {\n color: color('accent-1') !important;\n }\n &.accent-2 {\n color: color('accent-2') !important;\n }\n}\n\n// Theme overrides\nmd-input-container.md-wise-theme label {\n color: color('text');\n}\n\nmd-select-menu.md-default-theme md-option[selected], md-select-menu md-option[selected] {\n background-color: color('selected-bg');\n}\n\n.md-autocomplete-suggestions-container.md-default-theme li .highlight,\n.md-autocomplete-suggestions-container li .highlight {\n color: color('primary');\n background-color: color('selected-bg');\n}\n\n// Color\n// Set classes for each named color in the $colors map\n@each $key, $value in $colors {\n .#{$key} {\n color: $value;\n }\n}\n\n// Set classes for each named color in the $colors map\n@each $key, $value in $colors {\n .#{$key}-bg {\n background-color: $value;\n }\n}\n\n// Set theme colors for angular-ui elements\n@each $key, $value in $colors {\n md-progress-circular.#{$key} {\n path {\n stroke: $value;\n }\n }\n}\n","// Colors\n$_primary-color: #1C8CA8; // should match the default color of your ng-material default theme's 'primary' palette\n$_selected-bg: lighten($_primary-color, 59%);\n\n$color: (\n 'primary': $_primary-color,\n 'accent': #F05843, // should match the default color of your ng-material default theme's 'accent' palette\n 'accent-1': #795C3A,\n 'accent-2': #CAD266,\n 'warn': #c62828, // should match the default color of your ng-material default theme's 'warn' palette\n 'info': #ef6c00,\n 'success': #00C853,\n 'divider': rgba(0, 0, 0, 0.12),\n 'gray-lightest': #f7f7f7,\n 'gray-lighter': #eeeeee,\n 'gray-light': #dddddd,\n 'gray': #cccccc,\n 'gray-dark': #aaaaaa,\n 'gray-darker': #757575,\n 'gray-darkest': #333333,\n 'text': rgba(0, 0, 0, 0.87),\n 'text-secondary': rgba(0, 0, 0, 0.54),\n 'text-disabled': rgba(0, 0, 0, 0.26),\n 'text-light': rgba(255, 255, 255, 1),\n 'text-light-secondary': rgba(255, 255, 255, 0.70),\n 'text-light-disabled': rgba(255, 255, 255, 0.50),\n 'selected-bg': $_selected-bg,\n 'score': #FFC107\n);\n\n$body-color: (\n 'body': map-get($color, 'text'),\n 'body-bg': map-get($color, 'gray-lighter')\n);\n\n$colors: (map-merge($color, $body-color));\n\n// Typography\n$baseline-grid: 8px;\n$body-font-size-base: rem(1.500);\n$caption-font-size-base: rem(1.300);\n\n// Layout\n$wise-toolbar-height: 42px;\n\n// Menus\n$menu-border-radius: 2px;\n$max-visible-items: 6;\n$menu-item-height: 6 * $baseline-grid !default;\n$dense-menu-item-height: 4 * $baseline-grid !default;\n$max-menu-height: 2 * $baseline-grid + $max-visible-items * $menu-item-height !default;\n$max-dense-menu-height: 2 * $baseline-grid + $max-visible-items * $dense-menu-item-height !default;\n\n// Cards\n$card-border-radius: 4px;\n\n// Buttons\n$button-border-radius: 3px;\n","// Helper functions and mixins\n\n// Get colors from $colors map\n@function color($key) {\n @if map-has-key($colors, $key) {\n @return map-get($colors, $key);\n }\n @warn \"Unknown `#{$key}` in $colors.\";\n @return null;\n}\n\n// set size in pixels given rem\n@function rem($multiplier) {\n $font-size: 10px;\n @return $multiplier * $font-size;\n}\n","// 1. Config\n\n// 2. Base\n.l-constrained {\n margin-left: auto;\n margin-right: auto;\n max-width: 100%;\n position: relative;\n\n @media (min-width: $layout-breakpoint-xs) {\n width: $layout-breakpoint-md;\n }\n}\n\n.l-constrained-md {\n width: $layout-breakpoint-sm;\n max-width: 100%;\n}\n","// Helpers\n@function rem($multiplier) {\n $font-size: 10px;\n @return $multiplier * $font-size;\n}\n\n// Angular Material variables for use and !default overrides\n$md-toolbar-height: 52px;\n$md-toolbar-medium-tall-height: 74px;\n$md-toolbar-tall-height: 104px;\n$md-toolbar-height-mobile-portrait: 52px;\n$md-toolbar-height-mobile-landscape: 52px;\n\n$caption-font-size-base: rem(1.300);\n\n//$list-item-height: 56px;\n\n$card-border-radius: 4px;\n\n$layout-breakpoint-xs: 600px;\n$layout-breakpoint-sm: 960px;\n$layout-breakpoint-md: 1280px;\n$layout-breakpoint-lg: 1920px;\n\n$button-border-radius: 3px;\n\n$tooltip-fontsize-lg: rem(1.1);\n$tooltip-fontsize-sm: rem(1.1);\n//$tooltip-height-lg: rem(2.2);\n$tooltip-height-sm: rem(2.2);\n//$tooltip-top-margin-lg: rem(1.4);\n$tooltip-top-margin-sm: rem(1.4);\n//$tooltip-lr-padding-lg: rem(0.8);\n$tooltip-lr-padding-sm: rem(0.8);\n//$tooltip-max-width: rem(3.20);\n\n$progress-linear-bar-height: 7px;\n","// 1. Config\n\n// 2. Base\n.l-footer {\n position: fixed;\n bottom: 0;\n left: 0;\n right: 0;\n z-index: 1;\n background-color: #ffffff;\n border-top: 1px solid color('gray-lighter');\n}\n\n// Buttons\n.button--footer {\n margin: 0;\n padding-top: 0;\n padding-bottom: 0;\n min-width: 0;\n display: flex;\n}\n\n.button--footer__element {\n padding-left: 8px;\n}\n","// 1. Config\n\n// 2. Base\n.l-header {\n z-index: 3;\n\n .logo {\n margin-left: 0 !important;\n height: 36px;\n width: 36px;\n vertical-align: middle;\n }\n\n .logo-link {\n min-width: auto;\n display: none;\n padding: 0 4px;\n margin-right: 12px;\n\n @media only screen and (min-width: ($layout-breakpoint-xs)) {\n display: block;\n }\n\n &:hover, &:focus {\n border: 0 none;\n }\n }\n\n // Handle mobile portrait\n @media only screen and (max-width: ($layout-breakpoint-xs - 1)) {\n .md-toolbar-tools {\n h1, h2, h3 {\n font-size: $body-font-size-base;\n }\n }\n }\n}\n","// 1. Config\n\n// 2. Base\n.l-main {\n background-color: color('body-bg');\n}\n\n.l-main--with-toolbar {\n margin-top: $wise-toolbar-height;\n}\n\n#content {\n transition: margin-top 0.5s;\n}\n\n.view-content {\n margin: 0 auto;\n padding: 8px;\n position: absolute;\n left: 0;\n right: 0;\n transition: opacity 500ms;\n\n @media only screen and (min-width: $layout-breakpoint-sm) {\n padding: 16px;\n }\n\n &.ng-enter {\n opacity: 0;\n }\n\n .ng-enter-active {\n opacity: 1;\n transition-delay: 250ms;\n }\n\n &.ng-leave-active,\n &.ng-hide {\n opacity: 0;\n }\n\n &.ng-hide-add,\n &.ng-hide-add-active,\n &.ng-hide-remove,\n &.ng-hide-remove-active {\n opacity: 0;\n }\n}\n\n.view-content--with-sidemenu {\n padding: 8px;\n\n @media only screen and (min-width: $layout-breakpoint-xs) {\n margin-left: 54px;\n padding: 16px;\n }\n}\n[dir='rtl'] .view-content--with-sidemenu {\n @media only screen and (min-width: $layout-breakpoint-xs) {\n margin-left: auto;\n margin-right: 54px;\n }\n}\n\n.content-head {\n margin: 8px 0;\n\n h1,\n h2,\n h3 {\n font-weight: 300;\n margin-top: 0;\n margin-bottom: 0;\n font-size: rem(3.6);\n\n @media only screen and (max-width: $layout-breakpoint-sm - 1) {\n font-size: rem(3.2);\n text-align: center;\n }\n }\n}\n\n.content-head__more {\n @media only screen and (max-width: $layout-breakpoint-sm - 1) {\n margin-top: 8px;\n }\n}\n\n.content-head__item,\nh2.content-head__item {\n margin: 0 8px;\n\n .md-subhead {\n padding-left: 4px;\n\n @media only screen and (max-width: $layout-breakpoint-sm - 1) {\n display: block;\n padding-left: 0;\n }\n }\n\n md-icon {\n vertical-align: text-bottom;\n }\n}\n\n.stepSelectMenuContainer md-select-menu,\n.stepSelectMenuContainer md-select-menu md-content {\n max-height: 500px;\n}\n","/*\n WISE Project Styles\n */\nbody {\n background: #eeeeee; }\n body.vle {\n overflow: hidden; }\n\na:hover, a:focus {\n color: #1C8CA8; }\n\nblockquote {\n background-color: #e7f7fb;\n padding: 8px;\n margin: 16px 0;\n border-style: solid;\n border-color: #1C8CA8;\n border-width: 0 0 0 3px; }\n\n.has-indicator:after {\n content: '';\n position: absolute;\n border-radius: 50%;\n padding: 5px;\n background-color: #F05843; }\n\n.has-indicator--icon-button:after {\n top: 25px;\n left: 5px; }\n\n.badge {\n border-radius: 4px;\n padding: 2px 6px;\n font-size: 12px;\n font-weight: 500;\n font-style: normal;\n background-color: #aaaaaa; }\n .badge.md-button {\n min-width: 0;\n min-height: 0;\n line-height: inherit; }\n .badge.md-button:hover, .badge.md-button:focus {\n background-color: #aaaaaa; }\n .badge.md-button:focus {\n outline: 1px dotted #aaaaaa; }\n\n.badge--info {\n background-color: #ef6c00;\n color: #ffffff; }\n\n.badge--warn {\n background-color: #c62828;\n color: #ffffff; }\n\n.badge--success {\n background-color: #00C853;\n color: #ffffff; }\n\n.divider--withmargin {\n margin: 16px 0; }\n\n.divider--dashed {\n border-top-style: dashed; }\n\na {\n color: #1C8CA8;\n cursor: pointer; }\n\n.active {\n background-color: rgba(158, 158, 158, 0.2);\n color: rgba(0, 0, 0, 0.87); }\n\n.avatar {\n border-radius: 50%;\n box-sizing: content-box; }\n\n.avatar--square {\n border-radius: 4px; }\n\n.avatar.md-18 {\n height: 30px;\n width: 30px; }\n\n.avatar.md-24 {\n height: 36px;\n width: 36px; }\n\n.avatar.md-36 {\n height: 48px;\n width: 48px; }\n\n.avatar.md-48 {\n height: 60px;\n width: 60px; }\n\n.avatar--icon {\n background-color: #dddddd;\n white-space: normal !important; }\n .avatar--icon:not(.md-avatar) {\n padding: 6px; }\n .avatar--icon.md-18 {\n height: 18px;\n width: 18px; }\n .avatar--icon.md-24 {\n height: 24px;\n width: 24px; }\n .avatar--icon.md-36 {\n height: 36px;\n width: 36px; }\n .avatar--icon.md-48 {\n height: 48px;\n width: 48px; }\n\nmd-toolbar.md-light-theme:not(.md-menu-toolbar) md-icon {\n color: rgba(0, 0, 0, 0.54); }\n\nmd-toolbar.md-light-theme:not(.md-menu-toolbar) .md-button:disabled md-icon {\n color: rgba(0, 0, 0, 0.26); }\n\nmd-icon.primary, .md-button:not([disabled]).primary {\n color: #1C8CA8 !important; }\n\nmd-icon.success, .md-button:not([disabled]).success {\n color: #00C853 !important; }\n\nmd-icon.warn, .md-button:not([disabled]).warn {\n color: #c62828 !important; }\n\nmd-icon.info, .md-button:not([disabled]).info {\n color: #ef6c00 !important; }\n\nmd-icon.accent, .md-button:not([disabled]).accent {\n color: #F05843 !important; }\n\nmd-icon.accent-1, .md-button:not([disabled]).accent-1 {\n color: #795C3A !important; }\n\nmd-icon.accent-2, .md-button:not([disabled]).accent-2 {\n color: #CAD266 !important; }\n\nmd-input-container.md-wise-theme label {\n color: rgba(0, 0, 0, 0.87); }\n\nmd-select-menu.md-default-theme md-option[selected], md-select-menu md-option[selected] {\n background-color: #f4fbfd; }\n\n.md-autocomplete-suggestions-container.md-default-theme li .highlight,\n.md-autocomplete-suggestions-container li .highlight {\n color: #1C8CA8;\n background-color: #f4fbfd; }\n\n.primary {\n color: #1C8CA8; }\n\n.accent {\n color: #F05843; }\n\n.accent-1 {\n color: #795C3A; }\n\n.accent-2 {\n color: #CAD266; }\n\n.warn {\n color: #c62828; }\n\n.info {\n color: #ef6c00; }\n\n.success {\n color: #00C853; }\n\n.divider {\n color: rgba(0, 0, 0, 0.12); }\n\n.gray-lightest {\n color: #f7f7f7; }\n\n.gray-lighter {\n color: #eeeeee; }\n\n.gray-light {\n color: #dddddd; }\n\n.gray {\n color: #cccccc; }\n\n.gray-dark {\n color: #aaaaaa; }\n\n.gray-darker {\n color: #757575; }\n\n.gray-darkest {\n color: #333333; }\n\n.text {\n color: rgba(0, 0, 0, 0.87); }\n\n.text-secondary {\n color: rgba(0, 0, 0, 0.54); }\n\n.text-disabled {\n color: rgba(0, 0, 0, 0.26); }\n\n.text-light {\n color: white; }\n\n.text-light-secondary {\n color: rgba(255, 255, 255, 0.7); }\n\n.text-light-disabled {\n color: rgba(255, 255, 255, 0.5); }\n\n.selected-bg {\n color: #f4fbfd; }\n\n.score {\n color: #FFC107; }\n\n.body {\n color: rgba(0, 0, 0, 0.87); }\n\n.body-bg {\n color: #eeeeee; }\n\n.primary-bg {\n background-color: #1C8CA8; }\n\n.accent-bg {\n background-color: #F05843; }\n\n.accent-1-bg {\n background-color: #795C3A; }\n\n.accent-2-bg {\n background-color: #CAD266; }\n\n.warn-bg {\n background-color: #c62828; }\n\n.info-bg {\n background-color: #ef6c00; }\n\n.success-bg {\n background-color: #00C853; }\n\n.divider-bg {\n background-color: rgba(0, 0, 0, 0.12); }\n\n.gray-lightest-bg {\n background-color: #f7f7f7; }\n\n.gray-lighter-bg {\n background-color: #eeeeee; }\n\n.gray-light-bg {\n background-color: #dddddd; }\n\n.gray-bg {\n background-color: #cccccc; }\n\n.gray-dark-bg {\n background-color: #aaaaaa; }\n\n.gray-darker-bg {\n background-color: #757575; }\n\n.gray-darkest-bg {\n background-color: #333333; }\n\n.text-bg {\n background-color: rgba(0, 0, 0, 0.87); }\n\n.text-secondary-bg {\n background-color: rgba(0, 0, 0, 0.54); }\n\n.text-disabled-bg {\n background-color: rgba(0, 0, 0, 0.26); }\n\n.text-light-bg {\n background-color: white; }\n\n.text-light-secondary-bg {\n background-color: rgba(255, 255, 255, 0.7); }\n\n.text-light-disabled-bg {\n background-color: rgba(255, 255, 255, 0.5); }\n\n.selected-bg-bg {\n background-color: #f4fbfd; }\n\n.score-bg {\n background-color: #FFC107; }\n\n.body-bg {\n background-color: rgba(0, 0, 0, 0.87); }\n\n.body-bg-bg {\n background-color: #eeeeee; }\n\nmd-progress-circular.primary path {\n stroke: #1C8CA8; }\n\nmd-progress-circular.accent path {\n stroke: #F05843; }\n\nmd-progress-circular.accent-1 path {\n stroke: #795C3A; }\n\nmd-progress-circular.accent-2 path {\n stroke: #CAD266; }\n\nmd-progress-circular.warn path {\n stroke: #c62828; }\n\nmd-progress-circular.info path {\n stroke: #ef6c00; }\n\nmd-progress-circular.success path {\n stroke: #00C853; }\n\nmd-progress-circular.divider path {\n stroke: rgba(0, 0, 0, 0.12); }\n\nmd-progress-circular.gray-lightest path {\n stroke: #f7f7f7; }\n\nmd-progress-circular.gray-lighter path {\n stroke: #eeeeee; }\n\nmd-progress-circular.gray-light path {\n stroke: #dddddd; }\n\nmd-progress-circular.gray path {\n stroke: #cccccc; }\n\nmd-progress-circular.gray-dark path {\n stroke: #aaaaaa; }\n\nmd-progress-circular.gray-darker path {\n stroke: #757575; }\n\nmd-progress-circular.gray-darkest path {\n stroke: #333333; }\n\nmd-progress-circular.text path {\n stroke: rgba(0, 0, 0, 0.87); }\n\nmd-progress-circular.text-secondary path {\n stroke: rgba(0, 0, 0, 0.54); }\n\nmd-progress-circular.text-disabled path {\n stroke: rgba(0, 0, 0, 0.26); }\n\nmd-progress-circular.text-light path {\n stroke: white; }\n\nmd-progress-circular.text-light-secondary path {\n stroke: rgba(255, 255, 255, 0.7); }\n\nmd-progress-circular.text-light-disabled path {\n stroke: rgba(255, 255, 255, 0.5); }\n\nmd-progress-circular.selected-bg path {\n stroke: #f4fbfd; }\n\nmd-progress-circular.score path {\n stroke: #FFC107; }\n\nmd-progress-circular.body path {\n stroke: rgba(0, 0, 0, 0.87); }\n\nmd-progress-circular.body-bg path {\n stroke: #eeeeee; }\n\n.l-constrained {\n margin-left: auto;\n margin-right: auto;\n max-width: 100%;\n position: relative; }\n @media (min-width: 600px) {\n .l-constrained {\n width: 1280px; } }\n\n.l-constrained-md {\n width: 960px;\n max-width: 100%; }\n\n.l-footer {\n position: fixed;\n bottom: 0;\n left: 0;\n right: 0;\n z-index: 1;\n background-color: #ffffff;\n border-top: 1px solid #eeeeee; }\n\n.button--footer {\n margin: 0;\n padding-top: 0;\n padding-bottom: 0;\n min-width: 0;\n display: flex; }\n\n.button--footer__element {\n padding-left: 8px; }\n\n.l-header {\n z-index: 3; }\n .l-header .logo {\n margin-left: 0 !important;\n height: 36px;\n width: 36px;\n vertical-align: middle; }\n .l-header .logo-link {\n min-width: auto;\n display: none;\n padding: 0 4px;\n margin-right: 12px; }\n @media only screen and (min-width: 600px) {\n .l-header .logo-link {\n display: block; } }\n .l-header .logo-link:hover, .l-header .logo-link:focus {\n border: 0 none; }\n @media only screen and (max-width: 599px) {\n .l-header .md-toolbar-tools h1, .l-header .md-toolbar-tools h2, .l-header .md-toolbar-tools h3 {\n font-size: 15px; } }\n\n.l-main {\n background-color: #eeeeee; }\n\n.l-main--with-toolbar {\n margin-top: 42px; }\n\n#content {\n transition: margin-top 0.5s; }\n\n.view-content {\n margin: 0 auto;\n padding: 8px;\n position: absolute;\n left: 0;\n right: 0;\n transition: opacity 500ms; }\n @media only screen and (min-width: 960px) {\n .view-content {\n padding: 16px; } }\n .view-content.ng-enter {\n opacity: 0; }\n .view-content .ng-enter-active {\n opacity: 1;\n transition-delay: 250ms; }\n .view-content.ng-leave-active, .view-content.ng-hide {\n opacity: 0; }\n .view-content.ng-hide-add, .view-content.ng-hide-add-active, .view-content.ng-hide-remove, .view-content.ng-hide-remove-active {\n opacity: 0; }\n\n.view-content--with-sidemenu {\n padding: 8px; }\n @media only screen and (min-width: 600px) {\n .view-content--with-sidemenu {\n margin-left: 54px;\n padding: 16px; } }\n\n@media only screen and (min-width: 600px) {\n [dir='rtl'] .view-content--with-sidemenu {\n margin-left: auto;\n margin-right: 54px; } }\n\n.content-head {\n margin: 8px 0; }\n .content-head h1,\n .content-head h2,\n .content-head h3 {\n font-weight: 300;\n margin-top: 0;\n margin-bottom: 0;\n font-size: 36px; }\n @media only screen and (max-width: 959px) {\n .content-head h1,\n .content-head h2,\n .content-head h3 {\n font-size: 32px;\n text-align: center; } }\n\n@media only screen and (max-width: 959px) {\n .content-head__more {\n margin-top: 8px; } }\n\n.content-head__item,\nh2.content-head__item {\n margin: 0 8px; }\n .content-head__item .md-subhead,\n h2.content-head__item .md-subhead {\n padding-left: 4px; }\n @media only screen and (max-width: 959px) {\n .content-head__item .md-subhead,\n h2.content-head__item .md-subhead {\n display: block;\n padding-left: 0; } }\n .content-head__item md-icon,\n h2.content-head__item md-icon {\n vertical-align: text-bottom; }\n\n.stepSelectMenuContainer md-select-menu,\n.stepSelectMenuContainer md-select-menu md-content {\n max-height: 500px; }\n\n.l-nav {\n background-color: #eeeeee !important; }\n\n.l-node {\n margin-top: 42px;\n padding: 0; }\n @media only screen and (min-width: 600px) {\n .l-node {\n padding: 0 0 16px;\n background-color: #eeeeee !important; } }\n @media only screen and (min-width: 960px) {\n .l-node {\n padding: 0 0 32px; } }\n\n.l-notebook {\n margin-top: 42px;\n background-color: #eeeeee !important; }\n\n.l-sidebar__header {\n background-color: #ffffff !important;\n color: #795C3A !important; }\n .l-sidebar__header md-select {\n color: rgba(0, 0, 0, 0.87); }\n\n.status-icon {\n margin: 0 4px;\n z-index: 1;\n vertical-align: bottom; }\n\n.md-button.status-icon {\n height: auto;\n width: auto;\n min-height: 0;\n line-height: inherit;\n margin: 0 4px;\n padding: 0; }\n\n.avatar--icon--alert {\n background-color: #ffffff; }\n\n.avatar--icon--alert__icon {\n font-size: 48px;\n margin: -4px 0 0 -4px; }\n\n.gu-mirror {\n position: fixed !important;\n margin: 0 !important;\n z-index: 9999 !important;\n opacity: 0.8;\n transition: opacity 250ms ease-in-out; }\n\n.gu-hide {\n display: none !important; }\n\n.gu-unselectable {\n -webkit-user-select: none !important;\n -moz-user-select: none !important;\n -ms-user-select: none !important;\n user-select: none !important; }\n\n.gu-transit {\n opacity: 0.2; }\n\nmd-dialog {\n width: 600px; }\n\n.dialog--wide {\n width: 960px; }\n\n.dialog--wider {\n width: 1280px; }\n\n.help-bubble {\n border-radius: 4px;\n max-width: 320px; }\n @media (min-width: 600px) {\n .help-bubble {\n max-width: 552px; } }\n @media (min-width: 960px) {\n .help-bubble {\n max-width: 912px; } }\n @media (min-width: 1280px) {\n .help-bubble {\n max-width: 1232px; } }\n\n.help-bubble__title {\n border-top-left-radius: 4px;\n border-top-right-radius: 4px; }\n\n.help-bubble___title__content {\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n padding: 0px 0 0 12px;\n background-color: #ef6c00; }\n .help-bubble___title__content .md-icon-button {\n margin-right: 0;\n padding-top: 0;\n padding-bottom: 0; }\n\n.help-bubble__content {\n overflow: auto;\n padding: 8px 12px;\n max-height: 480px; }\n\n.help-bubble__actions {\n border-bottom-left-radius: 4px;\n border-bottom-right-radius: 4px; }\n\ndiv.hopscotch-bubble {\n border-radius: 4px;\n border: 0 none;\n font-family: Roboto, \"Helvetica Neue\", sans-serif;\n font-size: 14px;\n z-index: 6; }\n div.hopscotch-bubble .hopscotch-bubble-arrow-container {\n position: absolute;\n width: 20px;\n height: 20px; }\n div.hopscotch-bubble .hopscotch-bubble-arrow-container.up {\n top: 0;\n left: 12px; }\n div.hopscotch-bubble .hopscotch-bubble-arrow-container.up .hopscotch-bubble-arrow {\n border-bottom: 10px solid #ef6c00;\n border-left: 10px solid transparent;\n border-right: 10px solid transparent;\n position: relative;\n top: -10px; }\n div.hopscotch-bubble .hopscotch-bubble-arrow-container.up .hopscotch-bubble-arrow-border {\n border: 0 none;\n /*border-bottom: 12px solid color('gray-darker');\n border-left: 12px solid transparent;\n border-right: 12px solid transparent;*/ }\n div.hopscotch-bubble .hopscotch-bubble-arrow-container.down {\n bottom: -34px;\n left: 12px; }\n div.hopscotch-bubble .hopscotch-bubble-arrow-container.down .hopscotch-bubble-arrow {\n border-top: 10px solid #ef6c00;\n border-left: 10px solid transparent;\n border-right: 10px solid transparent;\n position: relative;\n top: -10px; }\n div.hopscotch-bubble .hopscotch-bubble-arrow-container.down .hopscotch-bubble-arrow-border {\n border: 0 none;\n /*border-top: 12px solid color('gray-darker');\n border-left: 12px solid transparent;\n border-right: 12px solid transparent;*/ }\n div.hopscotch-bubble .hopscotch-bubble-arrow-container.left {\n top: 12px;\n left: -10px; }\n div.hopscotch-bubble .hopscotch-bubble-arrow-container.left .hopscotch-bubble-arrow {\n border-right: 10px solid #ef6c00;\n border-bottom: 10px solid transparent;\n border-top: 10px solid transparent;\n position: relative;\n left: 0;\n top: -10px; }\n div.hopscotch-bubble .hopscotch-bubble-arrow-container.left .hopscotch-bubble-arrow-border {\n border: 0 none;\n /*border-right: 12px solid color('gray-darker');\n border-bottom: 12px solid transparent;\n border-top: 12px solid transparent;*/ }\n div.hopscotch-bubble .hopscotch-bubble-arrow-container.right {\n top: 12px;\n right: -30px; }\n div.hopscotch-bubble .hopscotch-bubble-arrow-container.right .hopscotch-bubble-arrow {\n border-left: 10px solid #ef6c00;\n border-bottom: 10px solid transparent;\n border-top: 10px solid transparent;\n position: relative;\n right: 0;\n top: -10px; }\n div.hopscotch-bubble .hopscotch-bubble-arrow-container.right .hopscotch-bubble-arrow-border {\n border: 0 none;\n /*border-right: 12px solid color('gray-darker');\n border-bottom: 12px solid transparent;\n border-top: 12px solid transparent;*/ }\n\n.input-container {\n padding-top: 12px; }\n\n.input-container--component {\n margin-bottom: 0; }\n\n.input-container--open-response.md-has-icon {\n padding-left: 0; }\n\n.input-container--open-response .md-errors-spacer {\n display: none; }\n\n.input-wrapper {\n position: relative; }\n\n.input-wrapper--focused .input--textarea__action md-icon {\n color: #1C8CA8; }\n\n.input--textarea, .input-container textarea.input--textarea {\n padding: 8px;\n background-color: #f7f7f7;\n border: 1px solid #cccccc;\n margin-bottom: 8px; }\n .input--textarea:focus, .input-container textarea.input--textarea:focus {\n background-color: #ffffff; }\n .input--textarea[disabled], .input-container textarea.input--textarea[disabled] {\n color: rgba(0, 0, 0, 0.54); }\n\n.input-container textarea.input--textarea {\n width: 100%; }\n\n.input--textarea--disabled {\n color: rgba(0, 0, 0, 0.54); }\n\n.input--textarea__action {\n position: absolute;\n right: -4px; }\n .input--textarea__action[disabled] md-icon {\n color: rgba(0, 0, 0, 0.26) !important; }\n\n.input--textarea__action--notebook {\n top: 6px; }\n .input-wrapper--richtext .input--textarea__action--notebook {\n top: -7px; }\n\n.input--textarea__action--revision {\n bottom: 6px; }\n .input-wrapper--richtext .input--textarea__action--revision {\n bottom: -5px; }\n\n.input-label, md-input-container:not(.md-input-invalid):not(.md-input-focused).md-input-has-value label.input-label {\n line-height: 1.2;\n color: rgba(0, 0, 0, 0.87); }\n .input-label.input-label--focused, md-input-container:not(.md-input-invalid):not(.md-input-focused).md-input-has-value label.input-label.input-label--focused {\n color: #1C8CA8; }\n\n.autocomplete input {\n text-overflow: ellipsis;\n overflow: hidden;\n word-wrap: none;\n font-weight: 500;\n color: rgba(0, 0, 0, 0.54); }\n\n@media only screen and (min-width: 600px) {\n .autocomplete--minwidth {\n min-width: 300px; } }\n\n@media only screen and (min-width: 960px) {\n .autocomplete--minwidth {\n min-width: 300px; } }\n\n.autocomplete--flat md-autocomplete-wrap {\n background-color: #ffffff; }\n .autocomplete--flat md-autocomplete-wrap:not(.md-menu-showing) {\n box-shadow: none;\n background-color: #eeeeee; }\n\n.select__header {\n height: 48px; }\n .select__header input {\n height: 100%;\n width: 100%;\n padding: 0 8px;\n outline: none;\n border: 0 none;\n font-size: 14px;\n font-weight: 500; }\n\n.table {\n max-width: 100%;\n width: auto;\n min-width: 100px;\n margin: 8px 0; }\n .table thead > tr > th,\n .table thead > tr > td,\n .table tbody > tr > th,\n .table tbody > tr > td,\n .table tfoot > tr > th,\n .table tfoot > tr > td {\n border: 1px solid #cccccc;\n padding: 6px;\n font-size: 15px;\n min-height: 32px;\n height: 32px;\n min-width: 32px;\n vertical-align: top; }\n .table td.inactive,\n .table th {\n background-color: #f7f7f7;\n opacity: 1;\n visibility: visible; }\n .table md-input-container {\n margin: 0; }\n .table .md-errors-spacer {\n display: none; }\n\n.table--student td.inactive {\n padding: 8px 10px; }\n\n.table--full-width {\n width: 100%; }\n\n.table--list {\n border: 0 none;\n border-collapse: collapse;\n background-color: #ffffff;\n max-width: 100%;\n overflow: auto; }\n .table--list th,\n .table--list td {\n padding: 0 4px;\n border: 0 none; }\n .table--list td {\n min-height: 56px;\n height: 56px; }\n .table--list tr.md-button {\n display: table-row;\n text-align: left;\n width: auto;\n text-transform: none;\n font-size: inherit;\n font-weight: normal; }\n\n.table--list__wrap {\n min-width: 600px; }\n\n@media only screen and (max-width: 959px) {\n .table-wrap-sticky {\n overflow-x: auto; } }\n\n.table--list__thead {\n font-size: 14px;\n font-weight: 700; }\n\n.table--list__thead__tr {\n height: 100%;\n margin: 0; }\n\n.table--list__thead__th {\n background-color: #757575;\n color: white;\n min-height: 42px;\n height: 42px; }\n\n.table--list__thead__link {\n color: #ffffff;\n text-transform: none;\n margin: 0;\n min-width: 0;\n white-space: normal;\n line-height: 1.4;\n width: 100%; }\n\n.table--list__thead__sort {\n margin: 0; }\n\n.table--list__thead__sort--reverse {\n transform: rotate(180deg); }\n\n.td--wrap {\n min-width: 180px;\n white-space: normal;\n line-height: 1.2; }\n\n@media only screen and (max-width: 959px) {\n .td--max-width {\n max-width: 180px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap; } }\n\n.md-toolbar-tools {\n font-size: 18px; }\n .md-toolbar-tools .autocomplete {\n height: 36px; }\n .md-toolbar-tools .autocomplete md-autocomplete-wrap {\n height: 36px; }\n .md-toolbar-tools .autocomplete input {\n height: 36px; }\n\n.md-toolbar--wise {\n min-height: 42px; }\n .md-toolbar--wise .md-toolbar-tools {\n height: 42px;\n max-height: 42px; }\n .md-toolbar--wise .md-button.md-icon-button {\n height: 42px;\n line-height: 42px;\n width: 42px; }\n\n.md-toolbar--wise--sm .md-toolbar-tools {\n padding: 0 8px;\n font-size: 15px; }\n\n.md-toolbar--sidenav {\n background-color: #333333 !important; }\n .md-toolbar--sidenav .md-toolbar-tools {\n font-size: 16px;\n font-weight: 500; }\n\n.toolbar {\n position: fixed;\n left: 0;\n right: 0;\n top: 52px;\n z-index: 3; }\n\n.toolbar__title {\n margin-left: 8px;\n font-size: 16px;\n font-weight: 500; }\n\n.toolbar__tools {\n padding-right: 8px; }\n\n[dir=rtl] .toolbar__tools {\n padding-right: 16px;\n padding-left: 8px; }\n\n.toolbar__nav, .md-button.toolbar__nav {\n margin: 0; }\n\n.toolbar__select, .md-button.toolbar__select {\n margin: 0 4px;\n min-height: 32px;\n background-color: #f7f7f7; }\n .toolbar__select .md-select-value, .md-button.toolbar__select .md-select-value {\n height: 32px;\n text-align: left; }\n\n[dir=rtl] .toolbar__select .md-select-value, [dir=rtl] .md-button.toolbar__select .md-select-value {\n text-align: right; }\n\n.toolbar__select--fixedwidth {\n width: 168px; }\n @media only screen and (min-width: 600px) {\n .toolbar__select--fixedwidth {\n width: 264px; } }\n @media only screen and (min-width: 960px) {\n .toolbar__select--fixedwidth {\n width: 432px; } }\n\n.list-item {\n background-color: #ffffff;\n border-bottom: 1px solid #eeeeee; }\n .list-item .md-subheader, .list-item.md-subheader {\n color: rgba(0, 0, 0, 0.87);\n background-color: #ffffff; }\n .list-item .md-subheader md-icon, .list-item.md-subheader md-icon {\n vertical-align: middle; }\n .list-item .md-subheader .md-subheader-inner, .list-item.md-subheader .md-subheader-inner {\n padding: 0; }\n .list-item .md-subheader .md-avatar, .list-item.md-subheader .md-avatar {\n margin-right: 8px; }\n .list-item .autocomplete {\n margin: 8px 0; }\n\n.list-item--info._md-button-wrap > div.md-button:first-child, .list-item--info .md-subheader-content {\n border-left: 4px solid #ef6c00 !important;\n margin-left: -4px; }\n\n.list-item--warn._md-button-wrap > div.md-button:first-child, .list-item--warn .md-subheader-content {\n border-left: 4px solid #c62828 !important;\n margin-left: -4px; }\n\n.list-item--expanded {\n border-bottom-width: 0; }\n\n.list-item--noclick, .list-item--noclick.md-button {\n cursor: default;\n background-color: #f7f7f7; }\n\n.list-item--actions {\n padding: 0 8px !important; }\n\n.list-item__subheader-button {\n text-transform: none;\n width: 100%;\n padding: 8px 16px;\n margin: 0;\n white-space: normal;\n text-align: left;\n line-height: 1.4; }\n\n.user-list {\n font-size: 15px; }\n\n#nav {\n position: relative; }\n\n.nav {\n margin-bottom: 16px; }\n\n.nav-mask {\n position: absolute;\n top: 0;\n bottom: 0;\n left: 0;\n right: 0;\n background-color: rgba(0, 0, 0, 0.25);\n z-index: 1; }\n .nav-mask.ng-hide {\n opacity: 0; }\n\n.nav-head {\n color: rgba(0, 0, 0, 0.54);\n font-weight: 500; }\n .nav-head md-icon {\n line-height: 20px; }\n\n.nav-contents--root {\n padding: 6px 6px 12px; }\n\n.nav-contents--group {\n background-color: #dddddd;\n padding: 8px; }\n\n.nav-contents--root {\n padding: 0; }\n\n.nav-contents__list {\n padding: 0; }\n @media (min-width: 600px) {\n .nav-contents__list {\n padding: 8px; } }\n\n.nav-item {\n transition: opacity 250ms ease-in-out; }\n .nav-item.prev md-list-item {\n background-color: #f4fbfd;\n /*&._md-button-wrap>div.md-button:first-child {\n border-left: 4px solid color('primary');\n margin-left: -4px;\n }*/ }\n\n.nav-item--card__content {\n border-top-right-radius: 4px;\n border-top-left-radius: 4px; }\n .nav-item--card__content:focus {\n outline: none; }\n .nav-item--card__content:focus .nav-item__title > span {\n border-bottom: 1px dashed #cccccc; }\n\n.nav-item--root {\n transition: margin 250ms, box-shadow 500ms; }\n .nav-item--root.expanded {\n flex-basis: 100%;\n max-width: 100%;\n max-height: none !important;\n margin: 8px auto;\n padding-left: 4px; }\n .nav-item--root.expanded:first-of-type {\n margin-top: 0; }\n\n.nav-item--list__info-item {\n padding: 0 16px 0 4px;\n display: inline-block; }\n\n.nav-item--list__reorder {\n margin-left: 8px;\n color: rgba(0, 0, 0, 0.26); }\n\n.nav-item--card--group:not(.expanded) {\n box-shadow: 0px 3px 1px -2px rgba(0, 0, 0, 0.14), 0px 2px 2px 0px rgba(0, 0, 0, 0.098), 0px 1px 5px 0px rgba(0, 0, 0, 0.084), 3px 3px 0px 1px #d5d5d5, 6px 6px 0px 1px #aaaaaa; }\n\n.nav-item__collapse {\n margin: 0; }\n\n.nav-item__more {\n border-top: 1px solid #dddddd;\n border-bottom-right-radius: 4px;\n border-bottom-left-radius: 4px;\n padding: 8px 16px;\n min-height: 40px; }\n\n.nav-item__users {\n height: auto;\n cursor: pointer;\n color: #ffffff;\n margin: 0;\n padding: 1px 6px; }\n .nav-item__users > md-icon {\n padding-right: 4px;\n color: #ffffff; }\n .nav-item__users:hover.success-bg, .nav-item__users:focus.success-bg {\n background-color: #00C853; }\n\n.nav-item__title {\n padding-left: 16px;\n line-height: 1.2;\n font-weight: 400; }\n\n[dir=rtl] .nav-item__title {\n padding-left: auto;\n padding-right: 16px; }\n\n.nav-item__info {\n padding: 0 8px; }\n\n.nav-item__progress {\n width: 48px; }\n .nav-item__progress > .md-container {\n top: 0; }\n\n.nav-item__progress-value {\n margin-left: 8px;\n width: 36px; }\n\n.progress-wrapper {\n padding: 2px 0;\n cursor: pointer; }\n\n.menu-progress {\n position: absolute;\n top: 10px;\n right: 12px; }\n .menu-progress path {\n stroke: #CAD266 !important;\n stroke-width: 2px; }\n\n[dir=rtl] .menu-progress {\n right: auto;\n left: 12px; }\n\n.menu-sidenav__item {\n font-weight: 700;\n font-size: 14px; }\n\n.menu-sidenav__icon {\n margin-top: 12px !important;\n margin-right: 12px !important;\n margin-left: 12px; }\n\n.active .menu-sidenav__icon, .active .menu-sidenav__item {\n color: #1C8CA8; }\n\n.menu-sidebar {\n position: absolute;\n top: 94px;\n bottom: 0;\n left: 0;\n background-color: #fff;\n width: 56px;\n overflow: hidden;\n padding: 8px 0;\n text-align: center;\n border-right: 1px solid #cccccc; }\n @media only screen and (max-width: 599px) {\n .menu-sidebar {\n display: none; } }\n\n[dir=rtl] .menu-sidebar {\n right: 0;\n left: auto; }\n\n.md-button.md-icon-button.menu-sidebar__link {\n margin-top: 6px;\n margin-bottom: 6px; }\n\n.notice {\n text-align: center;\n padding: 8px;\n background-color: rgba(0, 0, 0, 0.04);\n width: 100%; }\n @media (min-width: 600px) {\n .notice {\n max-width: 80%;\n border-radius: 3px;\n margin: 24px auto; } }\n\n#node {\n margin: 0 auto;\n position: absolute;\n left: 0;\n right: 0; }\n @media only screen and (min-width: 600px) {\n #node {\n padding: 8px;\n padding: 24px 16px;\n margin-bottom: 32px; } }\n @media only screen and (min-width: 960px) {\n #node {\n padding: 32px; } }\n #node.ng-enter {\n transition: opacity .5s;\n opacity: 0; }\n #node.ng-enter-active {\n opacity: 1; }\n\n@media only screen and (min-width: 600px) {\n .node-notice {\n margin-top: -8px;\n margin-bottom: 16px; } }\n\n@media only screen and (min-width: 960px) {\n .node-notice {\n margin-top: -16px; } }\n\n.node-content {\n padding: 0 0 48px;\n background-color: #ffffff;\n border-radius: 3px;\n overflow: visible; }\n @media only screen and (max-width: 599px) {\n .node-content {\n box-shadow: none; } }\n @media only screen and (min-width: 600px) {\n .node-content {\n padding: 0;\n border-top: 2px solid;\n border-bottom: 2px solid; } }\n\nmd-content.node-content {\n background-color: #ffffff; }\n\n.node-content__rubric {\n position: absolute;\n top: -22px;\n left: 0;\n right: 0;\n z-index: 1; }\n .node-content__rubric .avatar--icon {\n transform: scale(0.94); }\n @media only screen and (max-width: 599px) {\n .node-content__rubric .avatar--icon {\n transform: scale(0.8); } }\n\n.node-icon {\n color: #ffffff;\n vertical-align: inherit; }\n\n.node-select {\n margin: 0 8px;\n min-width: 0;\n font-weight: 500;\n font-size: 15px; }\n .node-select .md-select-value *:first-child {\n transform: translate3d(0, 0, 0);\n flex: 1 0 0; }\n .node-select .md-select-value .node-select__icon {\n display: none; }\n .node-select .md-select-value .node-select__status {\n display: none; }\n .node-select .md-select-icon {\n margin-left: 0;\n color: rgba(0, 0, 0, 0.87); }\n\n.node-select-option--group {\n background-color: #f7f7f7;\n border-bottom: 1px solid #eeeeee;\n border-top: 1px solid #eeeeee; }\n\n.node-select-option--node {\n padding-left: 20px; }\n\n.node-select__icon {\n margin-right: 8px; }\n\n.node-select__status {\n margin-left: 8px; }\n\n.node-select__text {\n text-overflow: ellipsis;\n white-space: nowrap;\n overflow: hidden; }\n @media only screen and (min-width: 600px) {\n .node-select__text {\n margin-top: 2px; } }\n\n.node-title {\n line-height: 1.2;\n text-transform: none;\n margin-top: 3px; }\n @media only screen and (max-width: 599px) {\n .node-title {\n font-size: 15px; } }\n\n.node-content__actions {\n padding: 0 16px 16px; }\n @media only screen and (min-width: 960px) {\n .node-content__actions {\n padding: 0 24px 24px; } }\n @media only screen and (min-width: 1280px) {\n .node-content__actions {\n padding: 0 32px 32px; } }\n .node-content__actions .md-button:first-child {\n margin-left: 0; }\n .node-content__actions .md-button:last-child {\n margin-right: 0; }\n\n.node-content__actions__info {\n font-style: italic;\n margin-left: 8px;\n color: rgba(0, 0, 0, 0.54); }\n\n.node-content__actions__more {\n border-bottom: 1px dotted; }\n\n.md-button.md-icon-button.node-nav:first-of-type {\n margin-right: 0; }\n\n@media only screen and (min-width: 600px) {\n .node-sidebar-active {\n margin-right: 68px; } }\n\n@media only screen and (max-width: 599px) {\n .node-sidebar-visible {\n margin-bottom: 42px; } }\n\n.node-sidebar {\n position: absolute;\n right: 0;\n top: 0;\n width: 52px; }\n\n.node-sidebar__toolbar {\n position: fixed;\n width: 52px;\n background-color: #ffffff;\n padding: 8px 0;\n border-radius: 3px; }\n @media only screen and (max-width: 599px) {\n .node-sidebar__toolbar {\n right: 0;\n bottom: 0;\n left: 0;\n width: 100%;\n border-radius: 0;\n padding: 0;\n min-height: 0;\n height: 42px; } }\n\n.node-info {\n margin: 0; }\n @media only screen and (max-width: 599px) {\n .node-info {\n margin: -16px;\n border-radius: 0; } }\n .node-info .divider {\n margin-left: -8px;\n margin-right: -8px; }\n .node-info .component:first-child {\n margin-top: -16px; }\n .node-info .component, .node-info .component__content, .node-info .component__header {\n margin-left: -8px;\n margin-right: -8px; }\n .node-info .component__actions {\n display: none; }\n\n.node-rubric {\n border: 2px solid #1C8CA8;\n margin: 8px 16px 24px;\n padding: 16px;\n background-color: #ffffff; }\n\n.node__label--vertical-alignment {\n vertical-align: middle;\n display: inline-block; }\n\n@media only screen and (min-width: 600px) {\n .notebook-launcher.md-button.md-fab {\n z-index: 61; } }\n\n.notebook-report {\n border-radius: 4px 4px 0 0;\n margin: 0;\n height: 100%; }\n .notebook-report .note-toolbar {\n margin: -8px -8px 8px;\n padding: 4px;\n border: 0 none;\n border-top: 1px solid #dddddd;\n border-bottom: 1px solid #dddddd;\n border-radius: 0; }\n .notebook-report .note-toolbar .btn-group {\n margin-top: 0; }\n .notebook-report .note-btn {\n border: 0 none;\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n border-bottom-left-radius: 0;\n border-bottom-right-radius: 0; }\n\n.notebook-report-container {\n height: 550px;\n width: 450px;\n max-height: 90%;\n bottom: 0;\n right: 96px;\n position: absolute;\n z-index: 3; }\n\n@media only screen and (min-width: 960px) {\n .notes-visible .notebook-report-container {\n right: 516px;\n transition: right 250ms; } }\n\n.notebook-report-container__full {\n top: 16px;\n bottom: 16px;\n left: 16px;\n right: 16px;\n max-height: none;\n max-width: none;\n height: auto;\n width: auto; }\n .notebook-report-container__full .notebook-report {\n height: 100%;\n width: 100%;\n margin: 0 auto;\n max-height: none;\n border-radius: 4px; }\n\n.notebook-report-container__collapsed {\n width: 300px;\n height: auto; }\n .notebook-report-container__collapsed .notebook-report__content, .notebook-report-container__collapsed .notebook-report__actions, .notebook-report-container__collapsed .notebook-report__content__header {\n display: none; }\n\n.notebook-report__toolbar {\n background-color: #333333 !important;\n border-radius: 4px 4px 0 0; }\n\n.notebook-report__toolbar__title {\n max-width: 150px; }\n\n.notebook-report__content {\n background-color: #ffffff; }\n .notebook-report__content h1, .notebook-report__content h2, .notebook-report__content h3, .notebook-report__content h4 {\n font-size: 22px; }\n .notebook-report__content .note-editor.note-frame {\n border: 0 none;\n border-radius: 0;\n padding: 0;\n box-shadow: none; }\n .notebook-report__content .note-resizebar {\n display: none; }\n\n.notebook-report__content__header {\n padding: 8px;\n background-color: #ffffff;\n font-size: 16px; }\n\n@media only screen and (max-width: 599px) {\n .notebook-report-container:not(.notebook-report-container__collapsed) {\n top: 0;\n bottom: 0;\n left: 0;\n right: 0;\n max-height: none;\n max-width: none;\n height: auto;\n width: auto; }\n .notebook-report-container:not(.notebook-report-container__collapsed) .notebook-report {\n border-radius: 0; }\n .notebook-tools--full {\n display: none; } }\n\n.notebook-report-backdrop {\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n z-index: 3;\n background-color: #212121;\n opacity: .48; }\n\n.notebook-menu {\n transition: opacity 500ms; }\n .notebook-menu.ng-enter {\n opacity: 0; }\n .notebook-menu .ng-enter-active {\n opacity: 1;\n transition-delay: 250ms; }\n .notebook-menu.ng-leave-active, .notebook-menu.ng-hide {\n opacity: 0; }\n .notebook-menu.ng-hide-add, .notebook-menu.ng-hide-add-active, .notebook-menu.ng-hide-remove, .notebook-menu.ng-hide-remove-active {\n opacity: 0; }\n\n.notebook-item {\n transition: box-shadow 250ms;\n margin: 0 16px 16px;\n display: block; }\n\n.notebook-item__content {\n height: 250px;\n min-width: 230px;\n position: relative;\n padding: 0;\n background-color: #cccccc;\n border-top-left-radius: 4px;\n border-top-right-radius: 4px; }\n\n.notebook-item__content__attachment, .notebook-item__content__text {\n position: absolute;\n left: 0;\n right: 0; }\n\n.notebook-item__content__attachment {\n background-repeat: no-repeat !important;\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n background-position: center top !important;\n background-size: cover !important;\n top: 0;\n bottom: 0; }\n\n.notebook-item__content__text {\n bottom: 0;\n padding: 8px;\n font-weight: 500;\n overflow: hidden;\n max-height: 120px;\n min-height: 56px;\n background-color: rgba(255, 255, 255, 0.95);\n border-top: 1px solid #eeeeee; }\n .notebook-item__content__text:after {\n content: \"\";\n text-align: right;\n position: absolute;\n bottom: 0;\n right: 0;\n width: 100%;\n height: 0.8em;\n background: linear-gradient(180deg, rgba(255, 255, 255, 0), rgba(255, 255, 255, 0.95) 100%); }\n\n.notebook-item__content--text-only:after {\n content: \"note\";\n font-family: 'Material Icons';\n font-weight: normal;\n font-style: normal;\n display: inline-block;\n line-height: 1;\n text-transform: none;\n letter-spacing: normal;\n word-wrap: normal;\n white-space: nowrap;\n direction: ltr;\n /* Support for all WebKit browsers. */\n -webkit-font-smoothing: antialiased;\n /* Support for Safari and Chrome. */\n text-rendering: optimizeLegibility;\n /* Support for Firefox. */\n -moz-osx-font-smoothing: grayscale;\n /* Support for IE. */\n font-feature-settings: 'liga';\n font-size: 80px;\n color: rgba(0, 0, 0, 0.26); }\n\n.notebook-item--question__content--text-only {\n content: \"live_help\"; }\n\n.notebook-item__content__location {\n opacity: 0.9;\n padding: 8px 0; }\n .notebook-item__content__location md-icon {\n font-size: 22px; }\n\n.notebook-item__edit {\n cursor: pointer; }\n\n.notebook-item__actions {\n margin: 0;\n padding: 0 8px;\n color: #ffffff;\n background-color: #333333;\n border-bottom-left-radius: 4px;\n border-bottom-right-radius: 4px; }\n .notebook-item__actions md-icon {\n color: #ffffff; }\n\n.notebook-item__text-input {\n margin: 0; }\n\n.notebook-item__text-input__textarea {\n padding-left: 0;\n padding-right: 0; }\n\n.notebook-item__attachment {\n background-color: #dddddd;\n padding: 16px;\n margin-bottom: 16px;\n text-align: center;\n position: relative; }\n\n.notebook-item__attachment__content {\n max-width: 100%;\n height: auto; }\n\n.notebook-item__attachment__delete {\n position: absolute;\n top: 4px;\n right: -2px;\n width: 34px !important;\n height: 34px !important;\n min-height: 0; }\n .notebook-item__attachment__delete md-icon {\n margin-left: -2px;\n font-size: 22px; }\n\n.notebook-item__info {\n font-style: italic;\n opacity: .8;\n color: #8a6942; }\n .notebook-item__info a, .notebook-item__info md-icon {\n color: #8a6942; }\n .notebook-item__info md-icon {\n font-size: 1.5em;\n min-width: 0;\n width: auto; }\n\n.notebook-item__upload {\n text-align: center;\n padding: 24px;\n background-color: #eeeeee;\n margin-bottom: 16px;\n color: rgba(0, 0, 0, 0.54);\n border-radius: 4px;\n cursor: pointer;\n border: 1px dashed transparent;\n transition: all 250ms; }\n .notebook-item__upload md-icon, .notebook-item__upload span {\n transition: color 250ms; }\n .notebook-item__upload:hover, .notebook-item__upload:focus, .notebook-item__upload.dragover {\n border-color: #F05843;\n background-color: #fdebe8;\n color: #F05843; }\n .notebook-item__upload:hover md-icon, .notebook-item__upload:hover span, .notebook-item__upload:focus md-icon, .notebook-item__upload:focus span, .notebook-item__upload.dragover md-icon, .notebook-item__upload.dragover span {\n color: #F05843; }\n\n.view-notebook-item {\n width: 600px; }\n\n.notebook-item--report {\n background-color: #ffffff; }\n .notebook-item--report .note-editor {\n margin-bottom: 16px;\n border-color: #cccccc; }\n\n.notebook-item--report__container.ui-scrollpoint .notebook-item--report__toolbar {\n position: fixed;\n top: 94px;\n left: 0;\n right: 0;\n z-index: 1; }\n @media only screen and (min-width: 600px) {\n .notebook-item--report__container.ui-scrollpoint .notebook-item--report__toolbar {\n left: 54px; } }\n @media only screen and (min-width: 600px) {\n .notebook-item--report__container.ui-scrollpoint .notebook-item--report__toolbar {\n padding: 0 24px; } }\n @media only screen and (min-width: 960px) {\n .notebook-item--report__container.ui-scrollpoint .notebook-item--report__toolbar {\n padding: 0 32px; } }\n .notebook-item--report__container.ui-scrollpoint .notebook-item--report__toolbar .note-toolbar {\n margin: 0 16px; }\n @media only screen and (min-width: 600px) {\n .notebook-item--report__container.ui-scrollpoint .notebook-item--report__toolbar .note-toolbar {\n margin: 0 8px; } }\n @media only screen and (min-width: 960px) {\n .notebook-item--report__container.ui-scrollpoint .notebook-item--report__toolbar .note-toolbar {\n margin: 0 16px; } }\n\n.notebook-item--report__container.ui-scrollpoint .note-editor {\n padding-top: 40px; }\n\n.notebook-item--report__toolbar .note-toolbar {\n background-color: #dddddd;\n border: 1px solid #cccccc;\n margin-bottom: -2px; }\n\n.notebook-item--report__heading {\n text-align: center;\n margin-bottom: 32px; }\n\n.notebook-item--report__add-note {\n font-weight: 700; }\n\n.notebook-item--report__note-img {\n max-width: 100%;\n height: auto !important; }\n\n@media only screen and (min-width: 600px) {\n .notebook-sidebar {\n width: 400px;\n max-width: none; } }\n\n@media only screen and (min-width: 960px) {\n .notebook-sidebar {\n width: 500px;\n max-width: none; } }\n\n.notebook-items {\n width: 100%;\n overflow: auto;\n margin-top: 16px;\n margin-bottom: 76px; }\n .notebook-items .notebook-item {\n width: 100%; }\n .notebook-items .notebook-item__content {\n height: 200px;\n min-width: 0; }\n\n.notebook-items--grading {\n margin-bottom: 0; }\n\n@media only screen and (max-width: 599px) {\n .notebook-enabled .md-fab-bottom-right, .notebook-enabled .md-fab-bottom-left {\n bottom: 50px !important; } }\n\n.notebook-grading {\n background-color: #ffffff;\n display: block; }\n\n.notification-btn {\n width: 60px !important; }\n .notification-btn md-icon {\n margin-left: 20px; }\n\n.notification-count {\n border-radius: 50%;\n position: absolute;\n background-color: #F05843;\n width: 22px;\n left: 4px;\n height: 22px;\n line-height: 18px;\n font-size: 12px;\n font-weight: 700;\n border: 2px solid; }\n .notification-count:before {\n content: \"\";\n position: absolute;\n right: -7px;\n top: 5px;\n border-left: 6px solid rgba(255, 255, 255, 0.87);\n border-top: 4px solid transparent;\n border-bottom: 4px solid transparent; }\n\n.notification-list {\n padding: 8px 0; }\n\n.notification-dismiss {\n width: 500px; }\n\n.notification-dismiss__input {\n margin-bottom: 0; }\n\nmd-list md-list-item .md-list-item-text h4.notification-list-item__source,\nmd-list md-list-item.md-2-line .md-list-item-text h4.notification-list-item__source,\nmd-list md-list-item.md-3-line .md-list-item-text h4.notification-list-item__source {\n color: rgba(0, 0, 0, 0.54);\n font-size: 12px; }\n md-list md-list-item .md-list-item-text h4.notification-list-item__source md-icon,\n md-list md-list-item.md-2-line .md-list-item-text h4.notification-list-item__source md-icon,\n md-list md-list-item.md-3-line .md-list-item-text h4.notification-list-item__source md-icon {\n font-size: 18px;\n min-width: 0;\n width: auto;\n margin-left: -4px;\n line-height: 20px; }\n\n.account-menu {\n border-radius: 4px;\n padding: 0;\n font-size: 15px;\n max-width: 380px; }\n @media (min-width: 1280px) {\n .account-menu {\n min-width: 380px !important; } }\n .account-menu h3 {\n margin: 0;\n font-weight: 300; }\n\n.account-menu--fixed-height {\n height: 304px; }\n\n.account-menu--fixed-width {\n width: 320px; }\n @media (min-width: 960px) {\n .account-menu--fixed-width {\n width: 380px; } }\n\n.account-menu__icon {\n background-color: white;\n border-radius: 50%; }\n\n.account-menu__caret {\n position: absolute;\n right: 28px;\n top: -8px;\n outline: none; }\n .account-menu__caret:before {\n content: '';\n position: absolute;\n border-bottom: 8px solid #fff;\n border-left: 8px solid transparent;\n border-right: 8px solid transparent; }\n\n.account-menu__caret--pause, .account-menu__caret--notification {\n right: 80px; }\n\n.account-menu__caret--notification--with-pause {\n right: 132px; }\n\n[dir=rtl] .account-menu__caret {\n right: auto;\n left: 28px; }\n\n[dir=rtl] .account-menu__caret--pause, [dir=rtl] .account-menu__caret--notification {\n left: 80px;\n right: auto; }\n\n[dir=rtl] .account-menu__caret--notification--with-pause {\n left: 132px;\n right: auto; }\n\n.account-menu__info {\n padding: 8px 12px; }\n\n.account-menu__info__title {\n font-weight: 500; }\n\n.account-menu__info__team {\n font-weight: 400;\n color: rgba(0, 0, 0, 0.54); }\n\n.account-menu__users {\n padding: 0; }\n .account-menu__users md-list-item {\n padding: 0; }\n .account-menu__users md-list-item .md-avatar {\n margin: 0 8px 0 0;\n height: 48px;\n width: 48px; }\n\n.account-menu__progress md-progress-linear {\n transform: rotate(270deg);\n width: 26px; }\n\n.account-menu__grade {\n position: relative;\n margin-right: 4px; }\n .account-menu__grade md-icon {\n color: #FFC107; }\n\n.account-menu__grade__overlay {\n position: absolute;\n top: 0;\n width: 100%;\n background-color: rgba(255, 255, 255, 0.6); }\n\n.account-menu__actions {\n background-color: #f7f7f7; }\n\n.account-menu__control {\n padding: 16px; }\n\n.annotations {\n margin: 16px 4px 16px 62px;\n position: relative;\n font-size: 15px; }\n .annotations hr {\n margin: 10px 0 8px;\n border-color: rgba(0, 0, 0, 0.12); }\n .annotations:after {\n content: \"\";\n position: absolute;\n width: 0;\n height: 0;\n left: -16px;\n right: auto;\n top: 0px;\n bottom: auto;\n border-top: 20px solid transparent;\n border-bottom: 20px solid transparent;\n border-right: 16px solid #757575; }\n\n.annotations-container--student--report {\n border-top: 1px solid #dddddd; }\n\n.annotations--report {\n margin-top: 0;\n margin-bottom: 0; }\n\n.annotations__header {\n position: relative;\n border-top-right-radius: 4px;\n padding: 10px 12px;\n font-weight: 700;\n transition: all 1s;\n color: #ffffff;\n background-color: #757575; }\n\n.annotations__avatar {\n background-color: #F05843;\n padding: 2px;\n position: absolute;\n top: 0;\n left: -62px; }\n\n.annotations__icon {\n transition: all 1s;\n color: #ffffff; }\n\n.annotations__body {\n padding: 12px;\n background-color: #ffffff;\n border-bottom-left-radius: 4px;\n border-bottom-right-radius: 4px;\n overflow: auto; }\n\n.annotations__status {\n background-color: #ffffff;\n color: #ef6c00;\n display: inline-block;\n margin-left: 8px;\n font-size: 12px; }\n .annotations__status.ng-enter, .annotations__status.ng-leave {\n transition: all 1s; }\n .annotations__status.ng-enter, .annotations__status.ng-leave.ng-leave-active {\n opacity: 0; }\n .annotations__status.ng-leave, .annotations__status.ng-enter.ng-enter-active {\n opacity: 1; }\n\n.annotations__score {\n font-weight: 700; }\n\n.annotations__info {\n font-style: italic;\n opacity: 0.8;\n border-bottom: 1px dotted;\n font-size: 13px; }\n\n.annotations--inside .annotations {\n margin-left: 72px; }\n\n.annotations--info {\n margin-bottom: 32px;\n margin-right: 8px;\n margin-left: 72px; }\n @media only screen and (min-width: 600px) {\n .annotations--info {\n margin: 16px 16px 32px 76px; } }\n .annotations--info:after {\n border-right: 16px solid #ef6c00; }\n .annotations--info .annotations__avatar {\n background-color: #ffffff; }\n .annotations--info .annotations__header {\n background-color: #ef6c00; }\n\n.component {\n position: relative; }\n\n.component__wrapper {\n padding: 0 16px;\n margin: 24px 0; }\n\n.component__content {\n overflow-x: auto;\n font-size: 15px;\n overflow-y: hidden; }\n @media only screen and (min-width: 600px) {\n .component__content {\n padding: 0 8px; } }\n\nh3.component__header, .component-header {\n padding: 8px 12px;\n margin: 0;\n font-size: 14px; }\n\n.component__rubric {\n position: absolute;\n left: -20px;\n top: 12px; }\n\n.notebook-enabled .component_content img {\n transition: all 250ms;\n cursor: pointer;\n cursor: copy; }\n .notebook-enabled .component_content img:hover, .notebook-enabled .component_content img:focus {\n box-shadow: 0 0 5px 1px #F05843; }\n\n.component__actions .md-button:first-child {\n margin-left: 0; }\n\n.component__actions .md-button:last-child {\n margin-right: 0; }\n\n.component__actions__info {\n font-style: italic;\n margin-left: 8px;\n color: rgba(0, 0, 0, 0.54); }\n\n.component__actions__more {\n border-bottom: 1px dotted; }\n\n.component__prompt {\n margin-bottom: 8px;\n font-weight: 500; }\n\n.component__prompt__content {\n display: inline; }\n\n.component__attachment {\n position: relative;\n margin: 0 8px;\n padding-bottom: 8px; }\n @media only screen and (min-width: 600px) {\n .component__attachment {\n padding-top: 8px; } }\n\n@media only screen and (max-width: 599px) {\n .component__add-attachment {\n width: 100%; } }\n\n.component__attachment__content {\n max-height: 100px;\n width: auto; }\n\n.component__attachment__delete {\n position: absolute;\n top: 0;\n right: 0;\n min-width: 0;\n background-color: rgba(255, 255, 255, 0.75) !important;\n border-radius: 0;\n padding: 4px;\n margin: 0; }\n .component__attachment__delete > md-icon {\n margin-top: 0; }\n\n.component__revision {\n margin: 8px 0;\n padding: 8px; }\n .component__revision:nth-child(odd) {\n background-color: #f7f7f7; }\n\n.component__revision__content {\n padding: 4px 0 8px 0;\n border-bottom: 1px solid #dddddd; }\n\n.component__revision__actions {\n color: #757575;\n padding-top: 4px; }\n\n.component__content--Discussion {\n overflow: hidden; }\n\n.discussion-content {\n background-color: #eeeeee;\n box-shadow: inset 0 0 3px #aaaaaa; }\n\n.discussion-posts {\n padding: 12px 12px 8px; }\n @media only screen and (min-width: 1280px) {\n .discussion-posts {\n padding: 16px 16px 0; } }\n\n.discussion-post {\n margin: 0 auto 16px;\n max-width: 600px; }\n @media only screen and (min-width: 600px) {\n .discussion-post {\n margin-bottom: 24px; } }\n @media only screen and (min-width: 1280px) {\n .discussion-post {\n margin-bottom: 32px; } }\n .discussion-post md-divider {\n position: relative;\n width: auto; }\n\n.discussion-post__contents {\n padding: 16px; }\n\n.discussion-post__avatar, md-list-item > .md-avatar.discussion-post__avatar {\n margin-right: 8px; }\n\n.discussion-post__avatar--reply, md-list-item > .md-avatar.discussion-post__avatar--reply {\n margin-top: 8px; }\n\n.discussion-post__user, md-list-item .md-list-item-text h3.discussion-post__user {\n padding-bottom: 4px;\n font-weight: 700;\n line-height: 1.3;\n overflow: visible;\n white-space: normal; }\n\n.discussion-post__date {\n color: #aaaaaa; }\n\n.discussion-post__date--reply {\n margin-left: 8px;\n font-weight: 400; }\n\n.discussion-post__content {\n margin-top: 16px;\n white-space: pre-wrap; }\n\n.discussion-post__attachment {\n max-width: 100%;\n height: auto !important;\n margin-top: 16px; }\n\n.discussion-new {\n background-color: #ffffff;\n max-width: 570px;\n margin-left: auto;\n margin-right: auto;\n padding: 8px;\n transition: all 250ms;\n transform: scale(0.95); }\n\n.discussion-new--focused {\n transform: scale(1); }\n\nmd-input-container.discussion-new__input-container {\n margin: 0;\n padding: 0; }\n md-input-container.discussion-new__input-container > textarea.md-input {\n min-height: 68px; }\n\n.discussion-new__input--textarea, .input-container textarea.discussion-new__input--textarea {\n padding: 8px;\n border: 0 none; }\n\n.discussion-new__actions {\n padding: 0 8px; }\n .discussion-new__actions .md-button:first-of-type {\n margin-left: 0; }\n .discussion-new__actions .md-button:last-of-type {\n margin-right: 0; }\n\n.discussion-new__attachment {\n padding: 0;\n margin: 0 0 8px; }\n\n.discussion-new__attachment__content {\n margin-top: 0;\n margin-bottom: 16px; }\n\n.discussion-comments {\n padding: 0; }\n\n.discussion-comments__contents {\n background-color: #f7f7f7; }\n\n.discussion-comments__header {\n background-color: transparent;\n padding: 0; }\n .discussion-comments__header .md-subheader-inner {\n padding-bottom: 8px; }\n\n.discussion-comments__list {\n padding: 0;\n max-height: 9999px;\n overflow-y: auto; }\n @media only screen and (min-width: 600px) {\n .discussion-comments__list {\n max-height: 400px; } }\n\n.input--textarea.discussion-reply__input, .input-container textarea.input--textarea.discussion-reply__input {\n background-color: #ffffff;\n padding: 4px;\n font-size: 14px;\n border: 0 none;\n margin-left: -1px;\n margin-bottom: 0;\n resize: none; }\n\n.discussion-reply, md-list-item.discussion-reply {\n margin: 0 12px 8px;\n padding: 0;\n min-height: 56px; }\n\n.discusstion-reply__details, md-list-item .md-list-item-text.discusstion-reply__details {\n margin: 8px 0; }\n\n.discussion-post__user--reply, md-list-item .md-list-item-text h3.discussion-post__user--reply {\n font-size: 14px;\n padding: 0;\n margin: 0; }\n\n.discusstion-reply__content {\n margin-top: 2px; }\n .discusstion-reply__content p {\n font-weight: 400 !important;\n color: rgba(0, 0, 0, 0.87) !important; }\n\n.discussion-new-reply {\n padding: 8px; }\n\n.discussion-new-reply__input-container {\n padding-top: 0;\n margin: 0; }\n .discussion-new-reply__input-container .md-errors-spacer {\n display: none; }\n\n.discussion-new-reply__actions {\n margin-left: 8px; }\n .discussion-new-reply__actions .md-button {\n margin-top: 0;\n margin-bottom: 0; }\n\n.embedded-content__iframe {\n border: 0 none; }\n\n.graph-select {\n min-width: 150px;\n max-width: 200px; }\n\n.graph-controls {\n margin: 8px 0;\n padding: 8px 0;\n border: 1px solid #eeeeee;\n border-left-width: 0;\n border-right-width: 0; }\n\n.match-content {\n background-color: #eeeeee;\n margin-bottom: 16px;\n padding: 8px;\n box-shadow: inset 0 0 3px #aaaaaa; }\n\n.match-divider {\n margin: 16px 8px 8px; }\n\n@media only screen and (min-width: 960px) {\n .match-divider--horizontal {\n display: none; } }\n\n.match-bucket__header {\n padding: 12px;\n font-weight: 500;\n color: #1C8CA8; }\n\n.match-bucket__content {\n padding: 0; }\n\n.match-bucket--choices .match-bucket__header {\n color: #795C3A; }\n\n.match-bucket__contents {\n min-height: 120px;\n padding: 0 8px 8px;\n background-color: #dddddd;\n transition: background-color 250ms;\n border-bottom-left-radius: 4px;\n border-bottom-right-radius: 4px;\n column-gap: 8px; }\n @media only screen and (max-width: 599px) {\n .match-bucket__contents {\n column-count: 1 !important; } }\n .match-bucket__contents img {\n max-width: 100%;\n height: auto; }\n\n.match-bucket__contents--over {\n background-color: #1C8CA8; }\n\n.match-bucket__item {\n list-style-type: none;\n cursor: move;\n padding-top: 8px;\n break-inside: avoid; }\n\n.match-bucket__item__contents {\n background-color: #ffffff;\n padding: 8px !important;\n border: 1px solid #cccccc; }\n .match-bucket__item__contents .md-list-item-text {\n width: 100%; }\n\n.match-bucket__item__contents__text {\n margin-right: 4px; }\n\n.match-feedback {\n transition: opacity 250ms;\n /*padding-left: 8px;\n border-left: 4px solid;*/\n margin: 8px -8px -8px;\n color: #ffffff;\n padding: 4px 8px; }\n .match-feedback.ng-hide {\n opacity: 0;\n transition: opacity 1ms; }\n .match-feedback md-icon {\n color: #ffffff; }\n\n.outside-content iframe {\n border: 1px solid #eeeeee; }\n\n.outside-content__source {\n margin-top: 4px;\n text-align: end; }\n .outside-content__source a {\n max-width: 240px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n display: inline-block; }\n\n.notebook-toolbar md-divider {\n margin: 8px 0; }\n\n@media only screen and (max-width: 959px) {\n .notebook-toolbar {\n border-top: 1px solid #dddddd; } }\n\n.notebook-toolbar__add-menu {\n position: absolute;\n bottom: 40px; }\n .notebook-toolbar__add-menu .md-fab-action-item {\n background-color: #ffffff; }\n\n.notebook-toolbar__add-icon {\n border-radius: 50%; }\n\n#closeNotebookSettingsButton {\n float: right; }\n\n[dir=rtl] #closeNotebookSettingsButton {\n float: left; }\n\nhighchart {\n display: block; }\n","// 1. Config\n\n// 2. Base\n.l-nav {\n background-color: color('body-bg') !important;\n}\n","// 1. Config\n\n// 2. Base\n.l-node {\n margin-top: $wise-toolbar-height;\n padding: 0;\n\n @media only screen and (min-width: $layout-breakpoint-xs) {\n padding: 0 0 16px;\n background-color: color('body-bg') !important;\n }\n\n @media only screen and (min-width: $layout-breakpoint-sm) {\n padding: 0 0 32px;\n }\n}\n","// 1. Config\n\n// 2. Base\n.l-notebook {\n margin-top: $wise-toolbar-height;\n background-color: color('body-bg') !important;\n}\n","// 1. Config\n\n// 2. Base\n.l-sidebar {\n\n}\n\n.l-sidebar__header {\n background-color: #ffffff !important;\n color: color('accent-1') !important;\n\n md-select {\n color: color('body');\n }\n}",".status-icon {\n margin: 0 4px;\n z-index: 1;\n vertical-align: bottom;\n}\n\n.md-button.status-icon {\n height: auto;\n width: auto;\n min-height: 0;\n line-height: inherit;\n margin: 0 4px;\n padding: 0;\n}\n\n.avatar--icon--alert {\n background-color: #ffffff;\n}\n\n.avatar--icon--alert__icon {\n font-size: 48px;\n margin: -4px 0 0 -4px;\n}\n",".gu-mirror {\n position: fixed !important;\n margin: 0 !important;\n z-index: 9999 !important;\n opacity: 0.8;\n transition: opacity 250ms ease-in-out;\n}\n.gu-hide {\n display: none !important;\n}\n.gu-unselectable {\n -webkit-user-select: none !important;\n -moz-user-select: none !important;\n -ms-user-select: none !important;\n user-select: none !important;\n}\n.gu-transit {\n opacity: 0.2;\n}\n","md-dialog {\n width: $layout-breakpoint-xs;\n}\n\n.dialog--wide {\n width: $layout-breakpoint-sm;\n}\n\n.dialog--wider {\n width: $layout-breakpoint-md;\n}\n",".help-bubble {\n border-radius: $card-border-radius;\n max-width: 320px;\n\n @media (min-width: $layout-breakpoint-xs) {\n max-width: ($layout-breakpoint-xs - 48);\n }\n\n @media (min-width: $layout-breakpoint-sm) {\n max-width: ($layout-breakpoint-sm - 48);\n }\n\n @media (min-width: $layout-breakpoint-md) {\n max-width: ($layout-breakpoint-md - 48);\n }\n}\n\n.help-bubble__title {\n border-top-left-radius: $card-border-radius;\n border-top-right-radius: $card-border-radius;\n}\n\n.help-bubble___title__content {\n border-top-left-radius: $card-border-radius;\n border-top-right-radius: $card-border-radius;\n padding: 0px 0 0 12px;\n background-color: color('info');\n\n .md-icon-button {\n margin-right: 0;\n padding-top: 0;\n padding-bottom: 0;\n }\n}\n\n.help-bubble__content {\n overflow: auto;\n padding: 8px 12px;\n max-height: 480px;\n}\n\n.help-bubble__actions {\n border-bottom-left-radius: $card-border-radius;\n border-bottom-right-radius: $card-border-radius;\n}\n\ndiv.hopscotch-bubble {\n border-radius: $card-border-radius;\n //border: 2px solid color('gray-darker');\n border: 0 none;\n font-family: Roboto, \"Helvetica Neue\", sans-serif;\n font-size: rem(1.4);\n z-index: 6;\n\n .hopscotch-bubble-arrow-container {\n position: absolute;\n width: 20px;\n height: 20px;\n }\n\n .hopscotch-bubble-arrow-container {\n &.up {\n top: 0;\n left: 12px;\n\n .hopscotch-bubble-arrow {\n border-bottom: 10px solid color('info');\n border-left: 10px solid transparent;\n border-right: 10px solid transparent;\n position: relative;\n top: -10px;\n }\n\n .hopscotch-bubble-arrow-border {\n border: 0 none;\n /*border-bottom: 12px solid color('gray-darker');\n border-left: 12px solid transparent;\n border-right: 12px solid transparent;*/\n }\n }\n\n &.down {\n bottom: -34px;\n left: 12px;\n\n .hopscotch-bubble-arrow {\n border-top: 10px solid color('info');\n border-left: 10px solid transparent;\n border-right: 10px solid transparent;\n position: relative;\n top: -10px;\n }\n\n .hopscotch-bubble-arrow-border {\n border: 0 none;\n /*border-top: 12px solid color('gray-darker');\n border-left: 12px solid transparent;\n border-right: 12px solid transparent;*/\n }\n }\n\n &.left {\n top: 12px;\n left: -10px;\n\n .hopscotch-bubble-arrow {\n border-right: 10px solid color('info');\n border-bottom: 10px solid transparent;\n border-top: 10px solid transparent;\n position: relative;\n left: 0;\n top: -10px;\n }\n\n .hopscotch-bubble-arrow-border {\n border: 0 none;\n /*border-right: 12px solid color('gray-darker');\n border-bottom: 12px solid transparent;\n border-top: 12px solid transparent;*/\n }\n }\n\n &.right {\n top: 12px;\n right: -30px;\n\n .hopscotch-bubble-arrow {\n border-left: 10px solid color('info');\n border-bottom: 10px solid transparent;\n border-top: 10px solid transparent;\n position: relative;\n right: 0;\n top: -10px;\n }\n\n .hopscotch-bubble-arrow-border {\n border: 0 none;\n /*border-right: 12px solid color('gray-darker');\n border-bottom: 12px solid transparent;\n border-top: 12px solid transparent;*/\n }\n }\n }\n}\n","// Variables\n$input-action-width: 44px;\n$input-action-vertical-offset: 6px;\n$input-action-vertical-offset-richtext: -7px;\n\n// Base\n.input-container {\n padding-top: 12px;\n}\n\n.input-container--component {\n margin-bottom: 0;\n}\n\n.input-container--open-response {\n &.md-has-icon {\n padding-left: 0;\n }\n\n .md-errors-spacer {\n display: none;\n }\n}\n\n.input-wrapper {\n position: relative;\n}\n\n.input-wrapper--focused {\n .input--textarea__action md-icon {\n color: color('primary');\n }\n}\n\n.input--textarea, .input-container textarea.input--textarea {\n padding: 8px;\n background-color: color('gray-lightest');\n border: 1px solid color('gray');\n margin-bottom: 8px;\n\n &:focus {\n background-color: #ffffff;\n }\n\n &[disabled] {\n color: color('text-secondary');\n }\n}\n\n.input-container textarea.input--textarea {\n width: 100%;\n}\n\n.input--textarea--disabled {\n color: color('text-secondary');\n}\n\n.input--textarea__action {\n position: absolute;\n right: -4px;\n\n &[disabled] md-icon {\n color: color('text-disabled') !important;\n }\n}\n\n.input--textarea__action--notebook {\n top: $input-action-vertical-offset;\n\n .input-wrapper--richtext & {\n top: $input-action-vertical-offset-richtext\n }\n}\n\n.input--textarea__action--revision {\n bottom: $input-action-vertical-offset;\n\n .input-wrapper--richtext & {\n bottom: ($input-action-vertical-offset-richtext + 2px);\n }\n\n}\n\n.input-label, md-input-container:not(.md-input-invalid):not(.md-input-focused).md-input-has-value label.input-label {\n line-height: 1.2;\n color: color('text');\n\n &.input-label--focused {\n color: color('primary');\n }\n}\n\n.autocomplete {\n input {\n text-overflow: ellipsis;\n overflow: hidden;\n word-wrap: none;\n font-weight: 500;\n color: color('text-secondary');\n }\n}\n\n.autocomplete--minwidth {\n @media only screen and (min-width: $layout-breakpoint-xs) {\n min-width: 300px;\n }\n\n @media only screen and (min-width: $layout-breakpoint-sm) {\n min-width: 300px;\n }\n}\n\n.autocomplete--flat {\n md-autocomplete-wrap {\n background-color: #ffffff;\n\n &:not(.md-menu-showing) {\n box-shadow: none;\n background-color: color('gray-lighter');\n }\n }\n}\n\n.select__header {\n height: $menu-item-height;\n\n input {\n height: 100%;\n width: 100%;\n padding: 0 8px;\n outline: none;\n border: 0 none;\n font-size: rem(1.4);\n font-weight: 500;\n }\n}\n",".table {\n max-width: 100%;\n width: auto;\n min-width: 100px;\n margin: 8px 0;\n\n thead,\n tbody,\n tfoot {\n // TODO: remove chaining when bootstrap dependency is removed\n > tr > th,\n > tr > td {\n border: 1px solid color('gray');\n padding: 6px;\n font-size: rem(1.5);\n min-height: 32px;\n height: 32px;\n min-width: 32px;\n vertical-align: top;\n }\n }\n\n td.inactive,\n th {\n background-color: color('gray-lightest');\n opacity: 1;\n visibility: visible;\n }\n\n md-input-container {\n margin: 0;\n }\n\n .md-errors-spacer {\n display: none;\n }\n}\n\n.table--student {\n td {\n &.inactive {\n padding: 8px 10px;\n }\n }\n}\n\n.table--full-width {\n width: 100%;\n}\n\n.table--list {\n border: 0 none;\n border-collapse: collapse;\n background-color: #ffffff;\n max-width: 100%;\n overflow: auto;\n\n th,\n td {\n padding: 0 4px;\n border: 0 none;\n }\n\n td {\n min-height: 56px;\n height: 56px;\n }\n\n tr {\n &.md-button {\n display: table-row;\n text-align: left;\n width: auto;\n text-transform: none;\n font-size: inherit;\n font-weight: normal;\n }\n }\n}\n\n.table--list__wrap {\n min-width: $layout-breakpoint-xs;\n}\n\n.table-wrap-sticky {\n @media only screen and (max-width: $layout-breakpoint-sm - 1) {\n overflow-x: auto;\n }\n}\n\n.table--list__thead {\n font-size: rem(1.4);\n font-weight: 700;\n}\n\n.table--list__thead__tr {\n height: 100%;\n margin: 0;\n}\n\n.table--list__thead__th {\n background-color: color('gray-darker');\n color: color('text-light');\n min-height: $wise-toolbar-height;\n height: $wise-toolbar-height;\n}\n\n.table--list__thead__link {\n color: #ffffff;\n text-transform: none;\n margin: 0;\n min-width: 0;\n white-space: normal;\n line-height: 1.4;\n width: 100%;\n}\n\n.table--list__thead__sort {\n margin: 0;\n}\n\n.table--list__thead__sort--reverse {\n transform: rotate(180deg);\n}\n\n.td--wrap {\n min-width: 180px;\n white-space: normal;\n line-height: 1.2;\n}\n\n.td--max-width {\n @media only screen and (max-width: $layout-breakpoint-sm - 1) {\n max-width: 180px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n}\n",".md-toolbar-tools {\n font-size: 18px;\n\n .autocomplete {\n height: 36px;\n\n md-autocomplete-wrap {\n height: 36px;\n }\n\n input {\n height: 36px;\n }\n }\n}\n\n.md-toolbar--wise {\n min-height: $wise-toolbar-height;\n\n .md-toolbar-tools {\n height: $wise-toolbar-height;\n max-height: $wise-toolbar-height;\n }\n\n .md-button.md-icon-button {\n height: $wise-toolbar-height;\n line-height: $wise-toolbar-height;\n width: $wise-toolbar-height;\n }\n}\n\n.md-toolbar--wise--sm {\n .md-toolbar-tools {\n padding: 0 8px;\n font-size: $body-font-size-base;\n }\n}\n\n.md-toolbar--sidenav {\n background-color: color('gray-darkest') !important;\n\n .md-toolbar-tools {\n font-size: rem(1.6);\n font-weight: 500;\n }\n}\n\n.toolbar {\n position: fixed;\n left: 0;\n right: 0;\n top: $md-toolbar-height;\n z-index: 3;\n}\n\n.toolbar__title {\n margin-left: 8px;\n font-size: rem(1.6);\n font-weight: 500;\n}\n\n.toolbar__tools {\n padding-right: 8px;\n}\n[dir=rtl] .toolbar__tools {\n padding-right: 16px;\n padding-left: 8px;\n}\n\n.toolbar__nav, .md-button.toolbar__nav {\n margin: 0;\n}\n\n.toolbar__select, .md-button.toolbar__select {\n margin: 0 4px;\n min-height: 32px;\n background-color: color('gray-lightest');\n\n .md-select-value {\n height: 32px;\n text-align: left;\n }\n}\n[dir=rtl] {\n .toolbar__select, .md-button.toolbar__select {\n .md-select-value {\n text-align: right;\n }\n }\n}\n\n.toolbar__select--fixedwidth {\n width: 168px;\n\n @media only screen and (min-width: $layout-breakpoint-xs) {\n width: 264px;\n }\n\n @media only screen and (min-width: $layout-breakpoint-sm) {\n width: 432px;\n }\n}\n",".list-item {\n background-color: #ffffff;\n border-bottom: 1px solid color('gray-lighter');\n\n .md-subheader, &.md-subheader {\n color: color('text');\n background-color: #ffffff;\n\n md-icon {\n vertical-align: middle;\n }\n\n .md-subheader-inner {\n padding: 0;\n }\n\n .md-avatar {\n margin-right: 8px;\n }\n }\n\n .autocomplete {\n margin: 8px 0;\n }\n}\n\n.list-item--info {\n &._md-button-wrap>div.md-button:first-child, .md-subheader-content {\n border-left: 4px solid color('info') !important;\n margin-left: -4px;\n }\n}\n\n.list-item--warn {\n //background-color: lighten(color('warn'), 56%);\n\n &._md-button-wrap>div.md-button:first-child, .md-subheader-content {\n border-left: 4px solid color('warn') !important;\n margin-left: -4px;\n }\n}\n\n.list-item--expanded {\n border-bottom-width: 0;\n}\n\n.list-item--noclick, .list-item--noclick.md-button {\n cursor: default;\n background-color: color('gray-lightest');\n}\n\n.list-item--actions {\n padding: 0 8px !important;\n}\n\n.list-item__subheader-button {\n text-transform: none;\n width: 100%;\n padding: 8px 16px;\n margin: 0;\n white-space: normal;\n text-align: left;\n line-height: 1.4;\n}\n\n.user-list {\n font-size: rem(1.5);\n}\n","#nav {\n position: relative;\n}\n\n.nav {\n margin-bottom: 16px;\n}\n\n.nav-mask {\n position: absolute;\n top: 0;\n bottom: 0;\n left: 0;\n right: 0;\n background-color: rgba(0,0,0,0.25);\n z-index: 1;\n\n &.ng-hide {\n opacity: 0;\n }\n}\n\n.nav-head {\n color: color('text-secondary');\n font-weight: 500;\n\n md-icon {\n line-height: 20px;\n }\n}\n\n.nav-contents--root {\n padding: 6px 6px 12px;\n}\n\n.nav-contents--group {\n background-color: color('gray-light');\n padding: 8px;\n}\n\n.nav-contents--root {\n padding: 0;\n}\n\n.nav-contents__list {\n padding: 0;\n\n @media (min-width: $layout-breakpoint-xs) {\n padding: 8px;\n }\n}\n\n.nav-item {\n transition: opacity 250ms ease-in-out;\n\n &.prev {\n md-list-item {\n background-color: color('selected-bg');\n\n /*&._md-button-wrap>div.md-button:first-child {\n border-left: 4px solid color('primary');\n margin-left: -4px;\n }*/\n }\n }\n}\n\n.nav-item--card__content {\n border-top-right-radius: $card-border-radius;\n border-top-left-radius: $card-border-radius;\n\n &:focus {\n outline: none;\n\n .nav-item__title > span {\n border-bottom: 1px dashed color('gray');\n }\n }\n}\n\n.nav-item--root {\n transition: margin 250ms, box-shadow 500ms;\n\n &.expanded {\n flex-basis: 100%;\n //max-width: ($layout-breakpoint-md - 100) !important;\n max-width: 100%;\n max-height: none !important;\n margin: 8px auto;\n padding-left: 4px;\n\n &:first-of-type {\n margin-top: 0;\n }\n\n //@media only screen and (min-width: $layout-breakpoint-sm) {\n //margin: 8px auto;\n //}\n }\n}\n\n//.nav-item--list {\n//}\n\n.nav-item--list__info-item {\n padding: 0 16px 0 4px;\n display: inline-block;\n}\n\n.nav-item--list__reorder {\n margin-left: 8px;\n color: color('text-disabled');\n}\n\n.nav-item--card {\n\n}\n\n.nav-item--card--group {\n &:not(.expanded) {\n box-shadow: 0px 3px 1px -2px rgba(0, 0, 0, 0.14), 0px 2px 2px 0px rgba(0, 0, 0, 0.098),\n 0px 1px 5px 0px rgba(0, 0, 0, 0.084), 3px 3px 0px 1px #d5d5d5, 6px 6px 0px 1px #aaaaaa;\n }\n}\n\n.nav-item__collapse {\n margin: 0;\n}\n\n.nav-item__more {\n border-top: 1px solid color('gray-light');\n border-bottom-right-radius: $card-border-radius;\n border-bottom-left-radius: $card-border-radius;\n padding: 8px 16px;\n min-height: 40px;\n}\n\n.nav-item__users {\n height: auto;\n cursor: pointer;\n color: #ffffff;\n margin: 0;\n padding: 1px 6px;\n\n > md-icon {\n padding-right: 4px;\n color: #ffffff;\n }\n\n &:hover, &:focus {\n &.success-bg {\n background-color: color('success');\n }\n }\n}\n\n.nav-item__title {\n padding-left: 16px;\n line-height: 1.2;\n font-weight: 400;\n}\n[dir=rtl] .nav-item__title {\n padding-left: auto;\n padding-right: 16px;\n}\n\n.nav-item__info {\n padding: 0 8px;\n}\n\n.nav-item__progress {\n width: 48px;\n\n > .md-container {\n top: 0;\n }\n}\n\n.nav-item__progress-value {\n margin-left: 8px;\n width: 36px;\n}\n\n.progress-wrapper {\n padding: 2px 0;\n cursor: pointer;\n}\n","// 1. Variables\n$menu-sidebar-width: 56px;\n\n// 2. Base\n.menu-progress {\n position: absolute;\n top: 10px;\n right: 12px;\n\n path {\n stroke: color('accent-2') !important;\n stroke-width: 2px;\n }\n}\n[dir=rtl] .menu-progress {\n right:auto;\n left:12px;\n}\n\n.menu-sidenav {\n\n}\n\n.menu-sidenav__item {\n font-weight: 700;\n //color: color('text-secondary');\n font-size: rem(1.4);\n}\n\n.menu-sidenav__icon {\n margin-top: 12px !important;\n margin-right: 12px !important;\n margin-left: 12px;\n}\n\n.active {\n .menu-sidenav__icon, .menu-sidenav__item {\n color: color('primary');\n }\n}\n\n.menu-sidebar {\n position: absolute;\n top: $wise-toolbar-height + $md-toolbar-height;\n bottom: 0;\n left: 0;\n background-color: #fff;\n width: $menu-sidebar-width;\n overflow: hidden;\n padding: 8px 0;\n text-align: center;\n border-right: 1px solid color('gray');\n\n @media only screen and (max-width: ($layout-breakpoint-xs - 1)) {\n display: none;\n }\n}\n[dir=rtl] .menu-sidebar {\n right:0;\n left:auto;\n}\n\n.md-button.md-icon-button.menu-sidebar__link {\n margin-top: 6px;\n margin-bottom: 6px;\n}\n",".notice {\n text-align: center;\n padding: 8px;\n background-color: rgba(0,0,0,0.04);\n width: 100%;\n\n @media (min-width: $layout-breakpoint-xs) {\n max-width: 80%;\n border-radius: $button-border-radius;\n margin: 24px auto;\n }\n}","// Variables\n$input-action-width: 48px;\n$input-action-vertical-offset: 0;\n$input-action-vertical-offset-richtext: -7px;\n\n// Base\n#node {\n margin: 0 auto;\n position: absolute;\n left: 0;\n right: 0;\n\n @media only screen and (min-width: $layout-breakpoint-xs) {\n padding: 8px;\n padding: 24px 16px;\n margin-bottom: 32px;\n }\n\n @media only screen and (min-width: $layout-breakpoint-sm) {\n padding: 32px;\n }\n\n &.ng-enter {\n transition: opacity .5s;\n opacity: 0;\n }\n\n &.ng-enter-active {\n opacity: 1;\n }\n}\n\n// TODO: use BEM conventions\n\n.node-notice {\n @media only screen and (min-width: $layout-breakpoint-xs) {\n margin-top: -8px;\n margin-bottom: 16px;\n }\n\n @media only screen and (min-width: $layout-breakpoint-sm) {\n margin-top: -16px;\n }\n}\n\n.node-content {\n padding: 0 0 48px;\n background-color: #ffffff;\n border-radius: $button-border-radius;\n overflow: visible;\n\n @media only screen and (max-width: $layout-breakpoint-xs - 1) {\n box-shadow: none;\n }\n\n @media only screen and (min-width: $layout-breakpoint-xs) {\n padding: 0;\n border-top: 2px solid;\n border-bottom: 2px solid;\n }\n}\n\nmd-content {\n &.node-content {\n background-color: #ffffff;\n }\n}\n\n.node-content__rubric {\n position: absolute;\n top: -22px;\n left: 0;\n right: 0;\n z-index: 1;\n\n .avatar--icon {\n transform: scale(0.94);\n\n @media only screen and (max-width: $layout-breakpoint-xs - 1) {\n transform: scale(0.8);\n }\n }\n}\n\n.node-icon {\n color: #ffffff;\n vertical-align: inherit;\n}\n\n.node-select {\n margin: 0 8px;\n min-width: 0;\n font-weight: 500;\n font-size: $body-font-size-base;\n\n .md-select-value {\n *:first-child {\n transform: translate3d(0,0,0);\n flex: 1 0 0;\n }\n\n .node-select__icon {\n display: none;\n }\n\n .node-select__status {\n display: none;\n }\n }\n\n .md-select-icon {\n margin-left: 0;\n color: color('text');\n }\n}\n\n.node-select-option--group {\n //color: rgba(0,0,0,0.54);\n background-color: color('gray-lightest');\n border-bottom: 1px solid color('gray-lighter');\n border-top: 1px solid color('gray-lighter');\n}\n\n.node-select-option--node {\n padding-left: 20px;\n}\n\n.node-select__icon {\n margin-right: 8px;\n}\n\n.node-select__status {\n margin-left: 8px;\n}\n\n.node-select__text {\n text-overflow: ellipsis;\n white-space: nowrap;\n overflow: hidden;\n\n @media only screen and (min-width: $layout-breakpoint-xs) {\n margin-top: 2px;\n }\n}\n\n.node-title {\n line-height: 1.2;\n text-transform: none;\n margin-top: 3px;\n\n @media only screen and (max-width: $layout-breakpoint-xs - 1) {\n font-size: $body-font-size-base;\n }\n}\n\n.node-content__actions {\n padding: 0 16px 16px;\n\n @media only screen and (min-width: $layout-breakpoint-sm) {\n padding: 0 24px 24px;\n }\n\n @media only screen and (min-width: $layout-breakpoint-md) {\n padding: 0 32px 32px;\n }\n\n .md-button:first-child {\n margin-left: 0;\n }\n\n .md-button:last-child {\n margin-right: 0;\n }\n}\n\n.node-content__actions__info {\n font-style: italic;\n margin-left: 8px;\n color: color('text-secondary');\n}\n\n.node-content__actions__more {\n border-bottom: 1px dotted;\n}\n\n.md-button.md-icon-button.node-nav {\n &:not(:first-of-type) {\n }\n\n &:first-of-type {\n margin-right: 0;\n }\n}\n\n.node-sidebar-active {\n @media only screen and (min-width: $layout-breakpoint-xs) {\n margin-right: ($md-toolbar-height + 16);\n }\n}\n\n.node-sidebar-visible {\n @media only screen and (max-width: $layout-breakpoint-xs - 1) {\n margin-bottom: $wise-toolbar-height;\n }\n}\n\n.node-sidebar {\n position: absolute;\n right: 0;\n top: 0;\n width: $md-toolbar-height;\n}\n\n.node-sidebar__toolbar {\n position: fixed;\n width: $md-toolbar-height;\n background-color: #ffffff;\n padding: 8px 0;\n border-radius: $button-border-radius;\n\n @media only screen and (max-width: $layout-breakpoint-xs - 1) {\n right: 0;\n bottom: 0;\n left: 0;\n width: 100%;\n border-radius: 0;\n padding: 0;\n min-height: 0;\n height: $wise-toolbar-height;\n }\n}\n\n// TODO: move to own sass module file (only gets used by teacher)\n// TODO: use BEM (.node--info)\n.node-info {\n margin: 0;\n\n @media only screen and (max-width: $layout-breakpoint-xs - 1) {\n margin: -16px;\n border-radius: 0;\n }\n\n .divider {\n margin-left: -8px;\n margin-right: -8px;\n }\n\n .component {\n &:first-child {\n margin-top: -16px;\n }\n }\n\n .component, .component__content, .component__header {\n margin-left: -8px;\n margin-right: -8px;\n }\n\n .component__actions {\n display: none;\n }\n}\n\n.node-rubric {\n border: 2px solid color('primary');\n margin: 8px 16px 24px;\n padding: 16px;\n background-color: #ffffff;\n}\n\n.node-rubric--component {\n\n}\n\n.node__label--vertical-alignment {\n vertical-align: middle;\n display: inline-block;\n}\n","// Variables\n$notebook-sidebar-width: 56px;\n\n// Base\n.notebook-launcher {\n @media only screen and (min-width: $layout-breakpoint-xs) {\n &.md-button.md-fab {\n z-index: 61;\n }\n }\n}\n\n.notebook-report {\n border-radius: 4px 4px 0 0;\n margin: 0;\n height: 100%;\n\n .note-toolbar {\n margin: -8px -8px 8px;\n padding: 4px;\n border: 0 none;\n border-top: 1px solid color('gray-light');\n border-bottom: 1px solid color('gray-light');\n border-radius: 0;\n\n .btn-group {\n margin-top: 0;\n }\n }\n\n .note-btn {\n border: 0 none;\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n border-bottom-left-radius: 0;\n border-bottom-right-radius: 0;\n }\n}\n\n.notebook-report-container {\n height: 550px;\n width: 450px;\n max-height: 90%;\n bottom: 0;\n right: 96px;\n position: absolute;\n z-index: 3;\n}\n\n.notes-visible {\n @media only screen and (min-width: $layout-breakpoint-sm) {\n .notebook-report-container {\n right: 516px;\n transition: right 250ms;\n }\n }\n}\n\n.notebook-report-container__full {\n top: 16px;\n bottom: 16px;\n left: 16px;\n right: 16px;\n max-height: none;\n max-width: none;\n height: auto;\n width: auto;\n\n .notebook-report {\n height: 100%;\n width: 100%;\n margin: 0 auto;\n max-height: none;\n border-radius: $card-border-radius;\n }\n}\n\n.notebook-report-container__collapsed {\n width: 300px;\n height: auto;\n\n .notebook-report__content, .notebook-report__actions, .notebook-report__content__header {\n display: none;\n }\n}\n\n.notebook-report__toolbar {\n background-color: color('gray-darkest') !important;\n border-radius: $card-border-radius $card-border-radius 0 0;\n}\n\n.notebook-report__toolbar__title {\n max-width: 150px;\n}\n\n.notebook-report__content {\n h1, h2, h3, h4 {\n font-size: rem(2.2);\n }\n\n background-color: #ffffff;\n\n .note-editor.note-frame {\n border: 0 none;\n border-radius: 0;\n padding: 0;\n box-shadow: none;\n }\n\n .note-resizebar {\n display: none;\n }\n}\n\n.notebook-report__content__header {\n padding: 8px;\n background-color: #ffffff;\n font-size: rem(1.6);\n}\n\n@media only screen and (max-width: $layout-breakpoint-xs - 1) {\n .notebook-report-container {\n &:not(.notebook-report-container__collapsed) {\n top: 0;\n bottom: 0;\n left: 0;\n right: 0;\n max-height: none;\n max-width: none;\n height: auto;\n width: auto;\n\n .notebook-report {\n border-radius: 0;\n }\n }\n }\n\n .notebook-tools--full {\n display: none;\n }\n}\n\n.notebook-report-backdrop {\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n z-index: 3;\n background-color: rgba(33,33,33,1.0);\n opacity: .48;\n}\n\n.notebook-menu {\n transition: opacity 500ms;\n\n &.ng-enter {\n opacity: 0;\n }\n\n .ng-enter-active {\n opacity: 1;\n transition-delay: 250ms;\n }\n\n &.ng-leave-active, &.ng-hide {\n opacity: 0;\n }\n\n &.ng-hide-add, &.ng-hide-add-active,\n &.ng-hide-remove, &.ng-hide-remove-active {\n opacity: 0;\n }\n}\n\n.notebook-item {\n transition: box-shadow 250ms;\n margin: 0 16px 16px;\n display: block;\n}\n\n.notebook-item__content {\n height: 250px;\n min-width: 230px;\n position: relative;\n padding: 0;\n background-color: color('gray');\n border-top-left-radius: $card-border-radius;\n border-top-right-radius: $card-border-radius;\n}\n\n.notebook-item__content__attachment, .notebook-item__content__text {\n position: absolute;\n left: 0;\n right: 0;\n}\n\n.notebook-item__content__attachment {\n background-repeat: no-repeat !important;\n border-top-left-radius: $card-border-radius;\n border-top-right-radius: $card-border-radius;\n background-position: center top !important;\n background-size: cover !important;\n top: 0;\n bottom: 0;\n}\n\n.notebook-item__content__text {\n bottom: 0;\n padding: 8px;\n font-weight: 500;\n overflow: hidden;\n max-height: 120px;\n min-height: 56px;\n background-color: rgba(255,255,255,0.95);\n border-top: 1px solid color('gray-lighter');\n\n &:after {\n content: \"\";\n text-align: right;\n position: absolute;\n bottom: 0;\n right: 0;\n width: 100%;\n height: 0.8em;\n background: linear-gradient(180deg,hsla(0,0%,100%,0),rgba(255,255,255,0.95) 100%);\n }\n}\n\n.notebook-item__content--text-only {\n &:after {\n content: \"note\";\n font-family: 'Material Icons';\n font-weight: normal;\n font-style: normal;\n display: inline-block;\n line-height: 1;\n text-transform: none;\n letter-spacing: normal;\n word-wrap: normal;\n white-space: nowrap;\n direction: ltr;\n\n /* Support for all WebKit browsers. */\n -webkit-font-smoothing: antialiased;\n /* Support for Safari and Chrome. */\n text-rendering: optimizeLegibility;\n\n /* Support for Firefox. */\n -moz-osx-font-smoothing: grayscale;\n\n /* Support for IE. */\n font-feature-settings: 'liga';\n //position: absolute;\n font-size: 80px;\n color: color('text-disabled');\n }\n}\n\n.notebook-item--question__content--text-only {\n content: \"live_help\";\n}\n\n.notebook-item__content__location {\n opacity: 0.9;\n padding: 8px 0;\n\n md-icon {\n font-size: 22px;\n }\n}\n\n.notebook-item__edit {\n cursor: pointer;\n}\n\n.notebook-item__actions {\n margin: 0;\n padding: 0 8px;\n color: #ffffff;\n background-color: color('gray-darkest');\n border-bottom-left-radius: $card-border-radius;\n border-bottom-right-radius: $card-border-radius;\n\n md-icon {\n color: #ffffff;\n }\n}\n\n.notebook-item__text-input {\n margin: 0;\n}\n\n.notebook-item__text-input__textarea {\n padding-left: 0;\n padding-right: 0;\n}\n\n.notebook-item__attachment {\n background-color: color('gray-light');\n padding: 16px;\n margin-bottom: 16px;\n text-align: center;\n position: relative;\n}\n\n.notebook-item__attachment__content {\n max-width: 100%;\n height: auto;\n}\n\n.notebook-item__attachment__delete {\n position: absolute;\n top: 4px;\n right: -2px;\n // TODO: generalize for on item buttons like this (delete attachment, etc)\n width: 34px !important;\n height: 34px !important;\n min-height: 0;\n\n md-icon {\n margin-left: -2px;\n font-size: 22px;\n }\n}\n\n.notebook-item__info {\n font-style: italic;\n opacity: .8;\n color: lighten(color('accent-1'), 5%);\n\n a, md-icon {\n color: lighten(color('accent-1'), 5%);\n }\n\n md-icon {\n font-size: 1.5em;\n min-width: 0;\n width: auto;\n }\n}\n\n.notebook-item__upload {\n text-align: center;\n padding: 24px;\n background-color: color('gray-lighter');\n margin-bottom: 16px;\n color: color('text-secondary');\n border-radius: 4px;\n cursor: pointer;\n border: 1px dashed transparent;\n transition: all 250ms;\n\n md-icon, span {\n transition: color 250ms;\n }\n\n &:hover, &:focus, &.dragover {\n border-color: color('accent');\n background-color: lighten(color('accent'), 35%);\n color: color('accent');\n\n md-icon, span {\n color: color('accent');\n }\n }\n}\n\n.view-notebook-item {\n width: $layout-breakpoint-xs;\n}\n\n.view-notebook-item__content {\n}\n\n.notebook-item--report {\n background-color: #ffffff;\n\n .note-editor {\n margin-bottom: 16px;\n border-color: color('gray');\n }\n}\n\n.notebook-item--report__container {\n &.ui-scrollpoint {\n .notebook-item--report__toolbar {\n position: fixed;\n top: ($wise-toolbar-height + $md-toolbar-height);\n left: 0;\n right: 0;\n z-index: 1;\n\n @media only screen and (min-width: $layout-breakpoint-xs) {\n left: ($notebook-sidebar-width - 2);\n }\n\n @media only screen and (min-width: $layout-breakpoint-xs) {\n padding: 0 24px;\n }\n\n @media only screen and (min-width: $layout-breakpoint-sm) {\n padding: 0 32px;\n }\n\n .note-toolbar {\n margin: 0 16px;\n\n @media only screen and (min-width: $layout-breakpoint-xs) {\n margin: 0 8px;\n }\n\n @media only screen and (min-width: $layout-breakpoint-sm) {\n margin: 0 16px;\n }\n }\n }\n\n .note-editor {\n padding-top: 40px;\n }\n }\n}\n\n.notebook-item--report__toolbar {\n\n .note-toolbar {\n background-color: color('gray-light');\n border: 1px solid color('gray');\n margin-bottom: -2px;\n }\n}\n\n.notebook-item--report__heading {\n text-align: center;\n margin-bottom: 32px;\n}\n\n.notebook-item--report__content {\n}\n\n.notebook-item--report__add-note {\n font-weight: 700;\n}\n\n.notebook-item--report__note-img {\n max-width: 100%;\n height: auto !important;\n}\n\n.notebook-sidebar {\n @media only screen and (min-width: $layout-breakpoint-xs) {\n width: 400px;\n max-width: none;\n }\n\n @media only screen and (min-width: $layout-breakpoint-sm) {\n width: 500px;\n max-width: none;\n }\n}\n\n.notebook-items {\n width: 100%;\n overflow: auto;\n margin-top: 16px;\n margin-bottom: 76px;\n\n .notebook-item {\n width: 100%;\n }\n\n .notebook-item__content {\n height: 200px;\n min-width: 0;\n }\n}\n\n.notebook-items--grading {\n margin-bottom: 0;\n}\n\n@media only screen and (max-width: ($layout-breakpoint-xs - 1)) {\n .notebook-enabled {\n .md-fab-bottom-right, .md-fab-bottom-left {\n bottom: ($wise-toolbar-height + 8) !important;\n }\n }\n}\n\n.notebook-grading {\n background-color: #ffffff;\n display: block;\n}\n",".notification-btn {\n width: 60px !important;\n\n md-icon {\n margin-left: 20px;\n }\n}\n\n.notification-count {\n border-radius: 50%;\n position: absolute;\n background-color: color('accent');\n width: 22px;\n left: 4px;\n height: 22px;\n line-height: 18px;\n font-size: 12px;\n font-weight: 700;\n border: 2px solid;\n\n &:before {\n content: \"\";\n position: absolute;\n right: -7px;\n top: 5px;\n border-left: 6px solid rgba(255,255,255,0.87);\n border-top: 4px solid transparent;\n border-bottom: 4px solid transparent;\n }\n}\n\n.notification-list {\n padding: 8px 0;\n}\n\n.notification-dismiss {\n width: 500px;\n}\n\n.notification-dismiss__input {\n margin-bottom: 0;\n}\n\nmd-list md-list-item .md-list-item-text h4,\nmd-list md-list-item.md-2-line .md-list-item-text h4,\nmd-list md-list-item.md-3-line .md-list-item-text h4 {\n &.notification-list-item__source {\n color: color('text-secondary');\n font-size: rem(1.2);\n\n md-icon {\n font-size: rem(1.8);\n min-width: 0;\n width: auto;\n margin-left: -4px;\n line-height: rem(2);\n }\n }\n}\n",".account-menu {\n border-radius: $card-border-radius;\n padding: 0;\n font-size: $body-font-size-base;\n max-width: 380px;\n\n @media (min-width: $layout-breakpoint-md) {\n min-width: 380px !important;\n }\n\n h3 {\n margin: 0;\n font-weight: 300;\n }\n}\n\n.account-menu--fixed-height {\n height: $max-menu-height;\n}\n\n.account-menu--fixed-width {\n width: 320px;\n\n @media (min-width: $layout-breakpoint-sm) {\n width: 380px;\n }\n}\n\n.account-menu__icon {\n background-color: color('text-light');\n border-radius: 50%;\n}\n\n.account-menu__caret {\n position: absolute;\n right: 28px;\n top: -8px;\n outline: none;\n\n &:before {\n content: '';\n position: absolute;\n border-bottom: 8px solid #fff;\n border-left: 8px solid transparent;\n border-right: 8px solid transparent;\n }\n}\n\n.account-menu__caret--pause, .account-menu__caret--notification {\n right: 80px;\n}\n\n.account-menu__caret--notification--with-pause {\n right: 132px;\n}\n\n[dir=rtl] {\n .account-menu__caret {\n right: auto;\n left: 28px;\n }\n .account-menu__caret--pause, .account-menu__caret--notification {\n left:80px;\n right:auto;\n }\n .account-menu__caret--notification--with-pause {\n left: 132px;\n right:auto;\n }\n}\n\n.account-menu__info {\n padding: 8px 12px;\n}\n\n.account-menu__info__title {\n font-weight: 500;\n}\n\n.account-menu__info__team {\n font-weight: 400;\n color: color('text-secondary');\n}\n\n.account-menu__users {\n padding: 0;\n\n md-list-item {\n padding: 0;\n\n .md-avatar {\n margin: 0 8px 0 0;\n height: 48px;\n width: 48px;\n }\n }\n}\n\n.account-menu__progress {\n md-progress-linear {\n transform: rotate(270deg);\n width: 26px;\n }\n}\n\n.account-menu__grade {\n position: relative;\n margin-right: 4px;\n\n md-icon {\n color: color('score');\n }\n}\n\n.account-menu__grade__overlay {\n position: absolute;\n top: 0;\n width: 100%;\n background-color: rgba(255, 255, 255, 0.6);\n}\n\n.account-menu__actions {\n background-color: color('gray-lightest');\n}\n\n.account-menu__control {\n padding: 16px;\n}\n",".annotations {\n margin: 16px 4px 16px 62px;\n position: relative;\n font-size: rem(1.5);\n\n hr {\n margin: 10px 0 8px;\n border-color: rgba(0,0,0,.12);\n }\n\n &:after {\n content: \"\";\n position: absolute;\n width: 0;\n height: 0;\n left: -16px;\n right: auto;\n top: 0px;\n bottom: auto;\n border-top: 20px solid transparent;\n border-bottom: 20px solid transparent;\n border-right: 16px solid color('gray-darker');\n }\n}\n\n.annotations-container--student--report {\n border-top: 1px solid color('gray-light');\n}\n\n.annotations--report {\n margin-top: 0;\n margin-bottom: 0;\n}\n\n.annotations__header {\n position: relative;\n //border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n padding: 10px 12px;\n font-weight: 700;\n transition: all 1s;\n color: #ffffff;\n background-color: color('gray-darker');\n}\n\n.annotations__avatar {\n background-color: color('accent');\n padding: 2px;\n position: absolute;\n top: 0;\n left: -62px;\n}\n\n.annotations__icon {\n transition: all 1s;\n color: #ffffff;\n}\n\n.annotations__body {\n padding: 12px;\n background-color: #ffffff;\n border-bottom-left-radius: $card-border-radius;\n border-bottom-right-radius: $card-border-radius;\n overflow: auto;\n}\n\n.annotations__status {\n background-color: #ffffff;\n color: color('info');\n display: inline-block;\n margin-left: 8px;\n font-size: rem(1.2);\n\n &.ng-enter, &.ng-leave {\n transition: all 1s;\n }\n\n &.ng-enter, &.ng-leave.ng-leave-active {\n opacity:0;\n }\n\n &.ng-leave, &.ng-enter.ng-enter-active {\n opacity:1;\n }\n}\n\n.annotations__score {\n font-weight: 700;\n}\n\n.annotations__info {\n font-style: italic;\n opacity: 0.8;\n border-bottom: 1px dotted;\n font-size: rem(1.3);\n}\n\n.annotations--inside {\n .annotations {\n margin-left: 72px;\n }\n}\n\n// TODO: move to own file\n.annotations--info {\n margin-bottom: 32px;\n margin-right: 8px;\n margin-left: 72px;\n\n @media only screen and (min-width: ($layout-breakpoint-xs)) {\n margin: 16px 16px 32px 76px;\n }\n\n &:after {\n border-right: 16px solid color('info');\n }\n\n .annotations__avatar {\n background-color: #ffffff;\n }\n\n .annotations__header {\n background-color: color('info');\n }\n}\n",".component {\n position: relative;\n}\n\n.component__wrapper {\n padding: 0 16px;\n margin: 24px 0;\n}\n\n.component__content {\n overflow-x: auto;\n font-size: rem(1.5);\n overflow-y: hidden; // TODO: figure out why this is needed after update to ng-material 1.1.1\n\n @media only screen and (min-width: $layout-breakpoint-xs) {\n padding: 0 8px;\n }\n}\n\nh3.component__header, .component-header {\n padding: 8px 12px;\n margin: 0;\n font-size: rem(1.4);\n}\n\n.component__rubric {\n position: absolute;\n left: -20px;\n top: 12px;\n}\n\n.notebook-enabled {\n .component_content {\n img {\n transition: all 250ms;\n cursor: pointer;\n cursor: copy;\n //position: relative;\n //border: 2px solid transparent;\n\n &:hover, &:focus {\n box-shadow: 0 0 5px 1px color('accent');\n //border: 2px solid #ffffff;\n }\n }\n }\n}\n\n.component__actions {\n .md-button:first-child {\n margin-left: 0;\n }\n\n .md-button:last-child {\n margin-right: 0;\n }\n}\n\n.component__actions__info {\n font-style: italic;\n margin-left: 8px;\n //color: color('accent-1');\n color: color('text-secondary');\n}\n\n.component__actions__more {\n border-bottom: 1px dotted;\n}\n\n.component__prompt {\n margin-bottom: 8px;\n font-weight: 500;\n}\n\n.component__prompt__content {\n display: inline;\n}\n\n.component__attachment {\n position: relative;\n margin: 0 8px;\n padding-bottom: 8px;\n\n @media only screen and (min-width: $layout-breakpoint-xs) {\n padding-top: 8px;\n }\n}\n\n.component__add-attachment {\n @media only screen and (max-width: ($layout-breakpoint-xs - 1)) {\n width: 100%;\n }\n}\n\n.component__attachment__content {\n max-height: 100px;\n width: auto;\n}\n\n.component__attachment__delete {\n position: absolute;\n top: 0;\n right: 0;\n min-width: 0;\n background-color: rgba(255, 255, 255, 0.75) !important;\n border-radius: 0;\n padding: 4px;\n margin: 0;\n\n //@media only screen and (min-width: $layout-breakpoint-sm) {\n //margin-top: 8px;\n //}\n\n > md-icon {\n margin-top: 0;\n }\n}\n\n.component__revision {\n margin: 8px 0;\n padding: 8px;\n\n &:nth-child(odd) {\n background-color: color('gray-lightest');\n }\n}\n\n.component__revision__content {\n padding: 4px 0 8px 0;\n border-bottom: 1px solid color('gray-light');\n}\n\n.component__revision__actions {\n color: color('gray-darker');\n padding-top: 4px;\n}\n","// Variables\n\n// Base\n.component__content--Discussion {\n overflow: hidden;\n}\n\n.discussion-content {\n background-color: color('gray-lighter');\n //margin: 0 0 -16px;\n box-shadow: inset 0 0 3px color('gray-dark');\n}\n\n.discussion-posts {\n padding: 12px 12px 8px;\n\n @media only screen and (min-width: $layout-breakpoint-md) {\n padding: 16px 16px 0;\n }\n}\n\n.discussion-post {\n margin: 0 auto 16px;\n max-width: $layout-breakpoint-xs;\n\n @media only screen and (min-width: $layout-breakpoint-xs) {\n margin-bottom: 24px;\n }\n\n @media only screen and (min-width: $layout-breakpoint-md) {\n margin-bottom: 32px;\n }\n\n // angular-material fix for when discussion posts are shown inside an md-list-item (e.g. in the grading tool)\n md-divider {\n position: relative;\n width: auto;\n }\n}\n\n.discussion-post__contents {\n padding: 16px;\n}\n\n.discussion-post__avatar, md-list-item > .md-avatar.discussion-post__avatar {\n margin-right: 8px;\n}\n\n.discussion-post__avatar--reply, md-list-item > .md-avatar.discussion-post__avatar--reply {\n margin-top: 8px;\n}\n\n\n.discussion-post__user, md-list-item .md-list-item-text h3.discussion-post__user {\n padding-bottom: 4px;\n font-weight: 700;\n line-height: 1.3;\n overflow: visible;\n white-space: normal;\n}\n\n.discussion-post__date {\n color: color('gray-dark');\n}\n\n.discussion-post__date--reply {\n margin-left: 8px;\n font-weight: 400;\n}\n\n.discussion-post__content {\n margin-top: 16px;\n white-space: pre-wrap;\n}\n\n.discussion-post__attachment {\n max-width: 100%;\n height: auto !important;\n margin-top: 16px;\n}\n\n.discussion-new {\n background-color: #ffffff;\n max-width: 570px;\n margin-left: auto;\n margin-right: auto;\n padding: 8px;\n transition: all 250ms;\n transform: scale(0.95);\n}\n\n.discussion-new--focused {\n transform: scale(1);\n}\n\nmd-input-container.discussion-new__input-container {\n margin: 0;\n padding: 0;\n\n > textarea.md-input {\n min-height: 68px;\n }\n}\n\n.discussion-new__input--textarea, .input-container textarea.discussion-new__input--textarea {\n padding: 8px;\n border: 0 none;\n}\n\n.discussion-new__actions {\n padding: 0 8px;\n\n .md-button {\n &:first-of-type {\n margin-left: 0;\n }\n\n &:last-of-type {\n margin-right: 0;\n }\n }\n}\n\n.discussion-new__attachment {\n padding: 0;\n margin: 0 0 8px;\n}\n\n.discussion-new__attachment__content {\n margin-top: 0;\n margin-bottom: 16px;\n}\n\n.discussion-comments {\n padding: 0;\n}\n\n.discussion-comments__contents {\n background-color: color('gray-lightest');\n}\n\n.discussion-comments__header {\n background-color: transparent;\n padding: 0;\n\n .md-subheader-inner {\n padding-bottom: 8px;\n }\n}\n\n.discussion-comments__list {\n padding: 0;\n max-height: 9999px;\n overflow-y: auto;\n\n @media only screen and (min-width: $layout-breakpoint-xs) {\n max-height: 400px;\n }\n}\n\n.input--textarea.discussion-reply__input, .input-container textarea.input--textarea.discussion-reply__input {\n background-color: #ffffff;\n padding: 4px;\n font-size: ($body-font-size-base) - 1;\n border: 0 none;\n margin-left: -1px;\n margin-bottom: 0;\n resize: none;\n}\n\n.discussion-reply, md-list-item.discussion-reply {\n margin: 0 12px 8px;\n padding: 0;\n min-height: 56px;\n}\n\n.discusstion-reply__details, md-list-item .md-list-item-text.discusstion-reply__details {\n margin: 8px 0;\n}\n\n.discussion-post__user--reply, md-list-item .md-list-item-text h3.discussion-post__user--reply {\n font-size: ($body-font-size-base) - 1;\n padding: 0;\n margin: 0;\n}\n\n.discusstion-reply__content {\n margin-top: 2px;\n\n p {\n font-weight: 400 !important;\n color: color('body') !important;\n }\n}\n\n.discussion-new-reply {\n padding: 8px;\n}\n\n.discussion-new-reply__input-container {\n padding-top: 0;\n margin: 0;\n\n .md-errors-spacer {\n display: none;\n }\n}\n\n.discussion-new-reply__actions {\n margin-left: 8px;\n\n .md-button {\n margin-top: 0;\n margin-bottom: 0;\n }\n}\n",".embedded-content {\n\n}\n\n.embedded-content__iframe {\n border: 0 none;\n}\n",".graph-select {\n min-width: 150px;\n max-width: 200px;\n}\n\n.graph-controls {\n margin: 8px 0;\n padding: 8px 0;\n border: 1px solid color('gray-lighter');\n border-left-width: 0;\n border-right-width: 0;\n}\n","// Variables\n\n// Base\n.match-content {\n background-color: color('gray-lighter');\n margin-bottom: 16px;\n padding: 8px;\n box-shadow: inset 0 0 3px color('gray-dark');\n}\n\n.match-divider {\n margin: 16px 8px 8px;\n}\n\n.match-divider--horizontal {\n @media only screen and (min-width: $layout-breakpoint-sm) {\n display: none;\n }\n}\n\n.match-bucket__header {\n padding: 12px;\n font-weight: 500;\n color: color('primary');\n}\n\n.match-bucket__content {\n padding: 0;\n}\n\n.match-bucket--choices {\n .match-bucket__header {\n color: color('accent-1');\n }\n}\n\n.match-bucket__contents {\n min-height: 120px;\n //margin: 0;\n padding: 0 8px 8px;\n background-color: color('gray-light');\n transition: background-color 250ms;\n border-bottom-left-radius: $card-border-radius;\n border-bottom-right-radius: $card-border-radius;\n\n column-gap: 8px;\n\n @media only screen and (max-width: ($layout-breakpoint-xs - 1)) {\n column-count: 1 !important;\n }\n\n img {\n max-width: 100%;\n height: auto;\n }\n}\n\n.match-bucket__contents--over {\n background-color: color('primary');\n}\n\n.match-bucket__item {\n list-style-type: none;\n cursor: move;\n padding-top: 8px;\n break-inside: avoid;\n\n &:hover, &:focus {\n //background-color: rgba(0,0,0,0.2);\n }\n}\n\n.match-bucket__item__contents {\n background-color: #ffffff;\n padding: 8px !important;\n border: 1px solid color('gray');\n\n .md-list-item-text {\n width: 100%;\n }\n}\n\n.match-bucket__item__contents__text {\n margin-right: 4px;\n}\n\n.match-feedback {\n transition: opacity 250ms;\n /*padding-left: 8px;\n border-left: 4px solid;*/\n margin: 8px -8px -8px;\n color: #ffffff;\n padding: 4px 8px;\n\n &.ng-hide {\n opacity: 0;\n transition: opacity 1ms;\n }\n\n md-icon {\n color: #ffffff;\n }\n}\n",".outside-content {\n iframe {\n border: 1px solid color('gray-lighter');\n }\n}\n\n.outside-content__source {\n margin-top: 4px;\n text-align: end;\n\n a {\n max-width: 240px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n display: inline-block;\n }\n}",".notebook-toolbar {\n md-divider {\n margin: 8px 0;\n }\n\n @media only screen and (max-width: ($layout-breakpoint-sm - 1)) {\n border-top: 1px solid color('gray-light');\n }\n}\n\n.notebook-toolbar__add-menu {\n position: absolute;\n bottom: 40px;\n\n .md-fab-action-item {\n background-color: #ffffff;\n }\n}\n\n.notebook-toolbar__add-icon {\n border-radius: 50%;\n}\n\n#closeNotebookSettingsButton {\n float:right;\n}\n\n[dir=rtl] #closeNotebookSettingsButton {\n float:left;\n}","highchart {\n display: block;\n}\n"]} \ No newline at end of file From 2b934b94abea6c3ae60ceb99b6b2b698f92463e2 Mon Sep 17 00:00:00 2001 From: breity Date: Wed, 13 Jan 2021 11:18:24 -0800 Subject: [PATCH 11/26] Deleted notebook-parent.component.ts which was included in previous commit by mistake. #2876 --- .../notebook-parent.component.ts | 54 ------------------- 1 file changed, 54 deletions(-) delete mode 100644 src/main/webapp/site/src/app/notebook/notebook-parent/notebook-parent.component.ts diff --git a/src/main/webapp/site/src/app/notebook/notebook-parent/notebook-parent.component.ts b/src/main/webapp/site/src/app/notebook/notebook-parent/notebook-parent.component.ts deleted file mode 100644 index 3e1789ff6e..0000000000 --- a/src/main/webapp/site/src/app/notebook/notebook-parent/notebook-parent.component.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { Component, Input } from '@angular/core'; -import { ConfigService } from '../../../../../wise5/services/configService'; -import { NotebookService } from '../../../../../wise5/services/notebookService'; -import { UtilService } from '../../../../../wise5/services/utilService'; - -Component({ - selector: 'notebook-parent' -}); -export class NotebookParentComponent { - config: any; - notesVisible: boolean; - - @Input() - workgroupId: number; - - constructor( - private ConfigService: ConfigService, - public NotebookService: NotebookService, - private UtilService: UtilService - ) {} - - ngOnInit(): void { - if (this.workgroupId === null) { - this.workgroupId = this.ConfigService.getWorkgroupId(); - } - - if (this.isStudentNotebook()) { - this.config = this.UtilService.makeCopyOfJSONObject( - this.NotebookService.getStudentNotebookConfig() - ); - } else { - this.config = this.UtilService.makeCopyOfJSONObject( - this.NotebookService.getTeacherNotebookConfig() - ); - } - - if (!this.config.enabled) { - return; - } - - this.initComplete(); - } - - initComplete(): void {} - - isStudentNotebook(): boolean { - return ( - this.ConfigService.getMode() === 'studentRun' || - this.ConfigService.getMode() === 'preview' || - ((this.ConfigService.isRunOwner() || this.ConfigService.isRunSharedTeacher()) && - this.ConfigService.getWorkgroupId() !== this.workgroupId) - ); - } -} From 36b3702223d31af748a60f4c46bdbbfe11395dc3 Mon Sep 17 00:00:00 2001 From: Geoffrey Kwan Date: Wed, 13 Jan 2021 18:26:07 -0500 Subject: [PATCH 12/26] Upgraded Multiple Choice authoring to Angular. #2875 --- .../src/app/common-hybrid-angular.module.ts | 3 + .../src/app/teacher-hybrid-angular.module.ts | 2 + src/main/webapp/site/src/messages.xlf | 154 ++++++++++++++++-- .../component-authoring.component.ts | 25 +++ .../html-authoring.component.ts | 4 +- .../multiple-choice-authoring.component.html | 119 ++++++++++++++ .../multiple-choice-authoring.component.scss | 53 ++++++ .../multiple-choice-authoring.component.ts | 139 ++++++++++++++++ .../multipleChoiceAuthoringComponentModule.ts | 9 +- .../outside-url-authoring.component.html | 6 +- .../outside-url-authoring.component.ts | 16 +- 11 files changed, 502 insertions(+), 28 deletions(-) create mode 100644 src/main/webapp/wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.html create mode 100644 src/main/webapp/wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.scss create mode 100644 src/main/webapp/wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.ts diff --git a/src/main/webapp/site/src/app/common-hybrid-angular.module.ts b/src/main/webapp/site/src/app/common-hybrid-angular.module.ts index 5a65a502b0..c03ae57d53 100644 --- a/src/main/webapp/site/src/app/common-hybrid-angular.module.ts +++ b/src/main/webapp/site/src/app/common-hybrid-angular.module.ts @@ -56,6 +56,7 @@ import { EditorModule, TINYMCE_SCRIPT_SRC } from '@tinymce/tinymce-angular'; import { WiseTinymceEditorComponent } from '../../../wise5/directives/wise-tinymce-editor/wise-tinymce-editor.component'; import { MatAutocompleteModule } from '@angular/material/autocomplete'; import { MatSlideToggleModule } from '@angular/material/slide-toggle'; +import { MatRadioModule } from '@angular/material/radio'; @Component({ template: `` }) export class EmptyComponent {} @@ -85,6 +86,7 @@ export class EmptyComponent {} MatListModule, MatProgressBarModule, MatProgressSpinnerModule, + MatRadioModule, MatSelectModule, MatSlideToggleModule, MatTooltipModule, @@ -142,6 +144,7 @@ export class EmptyComponent {} MatListModule, MatProgressBarModule, MatProgressSpinnerModule, + MatRadioModule, MatSelectModule, MatSlideToggleModule, MatTooltipModule, diff --git a/src/main/webapp/site/src/app/teacher-hybrid-angular.module.ts b/src/main/webapp/site/src/app/teacher-hybrid-angular.module.ts index 95abc38144..5bf8ebb78a 100644 --- a/src/main/webapp/site/src/app/teacher-hybrid-angular.module.ts +++ b/src/main/webapp/site/src/app/teacher-hybrid-angular.module.ts @@ -47,6 +47,7 @@ import { EditOutsideUrlAdvancedComponent } from '../../../wise5/components/outsi import { OpenResponseAuthoring } from '../../../wise5/components/openResponse/open-response-authoring/open-response-authoring.component'; import { HtmlAuthoring } from '../../../wise5/components/html/html-authoring/html-authoring.component'; import { OutsideUrlAuthoring } from '../../../wise5/components/outsideURL/outside-url-authoring/outside-url-authoring.component'; +import { MultipleChoiceAuthoring } from '../../../wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component'; @NgModule({ declarations: [ @@ -69,6 +70,7 @@ import { OutsideUrlAuthoring } from '../../../wise5/components/outsideURL/outsid ManageStudentsComponent, MilestonesComponent, MilestoneReportDataComponent, + MultipleChoiceAuthoring, NavItemProgressComponent, NodeAdvancedGeneralAuthoringComponent, NodeAdvancedJsonAuthoringComponent, diff --git a/src/main/webapp/site/src/messages.xlf b/src/main/webapp/site/src/messages.xlf index d18fc71df1..68f7f644f9 100644 --- a/src/main/webapp/site/src/messages.xlf +++ b/src/main/webapp/site/src/messages.xlf @@ -1225,6 +1225,10 @@ app/student/add-project-dialog/add-project-dialog.component.html 30 + + ../../wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.html + 32 + The unit has been successfully added to the following Google Classroom classes: @@ -6067,6 +6071,10 @@ app/authoring-tool/edit-component-tags/edit-component-tags.component.html 22 + + ../../wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.html + 88 + Move Down @@ -6074,6 +6082,10 @@ app/authoring-tool/edit-component-tags/edit-component-tags.component.html 30 + + ../../wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.html + 100 + Delete @@ -6081,6 +6093,14 @@ app/authoring-tool/edit-component-tags/edit-component-tags.component.html 38 + + ../../wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.html + 111 + + + ../../wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.html + 114 + Component Width @@ -6096,6 +6116,126 @@ 20 + + Prompt + + ../../wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.html + 2 + + + ../../wise5/components/openResponse/open-response-authoring/open-response-authoring.component.html + 11 + + + + Enter Prompt Here + + ../../wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.html + 7 + + + ../../wise5/components/openResponse/open-response-authoring/open-response-authoring.component.html + 15 + + + + Selection Type: + + ../../wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.html + 12 + + + + Single Answer + + ../../wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.html + 17 + + + + Multiple Answer + + ../../wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.html + 21 + + + + There are no choices. Click the "Add Choice" button to add a choice. + + ../../wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.html + 38 + + + + Choice Text + + ../../wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.html + 45 + + + + Type text or choose an image + + ../../wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.html + 49 + + + + Choose an Image + + ../../wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.html + 56 + + + + Image + + ../../wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.html + 59 + + + + Is Correct + + ../../wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.html + 69 + + + + Is Correct + + ../../wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.html + 67 + + + + Feeback + + ../../wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.html + 76 + + + + Optional + + ../../wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.html + 80 + + + + Up + + ../../wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.html + 91 + + + + Down + + ../../wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.html + 103 + + % completed (All periods) @@ -6138,20 +6278,6 @@ 7 - - Prompt - - ../../wise5/components/openResponse/open-response-authoring/open-response-authoring.component.html - 11 - - - - Enter Prompt Here - - ../../wise5/components/openResponse/open-response-authoring/open-response-authoring.component.html - 15 - - Show Open Education Resources diff --git a/src/main/webapp/wise5/authoringTool/components/component-authoring.component.ts b/src/main/webapp/wise5/authoringTool/components/component-authoring.component.ts index 52c282a20b..6634abd0cc 100644 --- a/src/main/webapp/wise5/authoringTool/components/component-authoring.component.ts +++ b/src/main/webapp/wise5/authoringTool/components/component-authoring.component.ts @@ -1,6 +1,7 @@ import { Directive, Input } from '@angular/core'; import { Subject, Subscription } from 'rxjs'; import { debounceTime, distinctUntilChanged } from 'rxjs/operators'; +import { ProjectAssetService } from '../../../site/src/app/services/projectAssetService'; import { ConfigService } from '../../services/configService'; import { NodeService } from '../../services/nodeService'; import { TeacherProjectService } from '../../services/teacherProjectService'; @@ -31,6 +32,7 @@ export abstract class ComponentAuthoring { constructor( protected ConfigService: ConfigService, protected NodeService: NodeService, + protected ProjectAssetService: ProjectAssetService, protected ProjectService: TeacherProjectService ) { this.promptChange @@ -85,4 +87,27 @@ export abstract class ComponentAuthoring { } saveStarterState(starterState: any): void {} + + setShowSubmitButtonValue(show: boolean): void { + if (show == null || show == false) { + this.authoringComponentContent.showSaveButton = false; + this.authoringComponentContent.showSubmitButton = false; + } else { + this.authoringComponentContent.showSaveButton = true; + this.authoringComponentContent.showSubmitButton = true; + } + this.NodeService.broadcastComponentShowSubmitButtonValueChanged({ + nodeId: this.nodeId, + componentId: this.componentId, + showSubmitButton: show + }); + } + + openAssetChooser(params: any): any { + return this.ProjectAssetService.openAssetChooser(params).then((data: any) => { + return this.assetSelected(data); + }); + } + + assetSelected({ nodeId, componentId, assetItem, target }): void {} } diff --git a/src/main/webapp/wise5/components/html/html-authoring/html-authoring.component.ts b/src/main/webapp/wise5/components/html/html-authoring/html-authoring.component.ts index 4497c0c487..4994c3b13d 100644 --- a/src/main/webapp/wise5/components/html/html-authoring/html-authoring.component.ts +++ b/src/main/webapp/wise5/components/html/html-authoring/html-authoring.component.ts @@ -1,4 +1,5 @@ import { Component } from '@angular/core'; +import { ProjectAssetService } from '../../../../site/src/app/services/projectAssetService'; import { ComponentAuthoring } from '../../../authoringTool/components/component-authoring.component'; import { ConfigService } from '../../../services/configService'; import { NodeService } from '../../../services/nodeService'; @@ -15,10 +16,11 @@ export class HtmlAuthoring extends ComponentAuthoring { constructor( protected ConfigService: ConfigService, protected NodeService: NodeService, + protected ProjectAssetService: ProjectAssetService, protected ProjectService: TeacherProjectService, protected UtilService: UtilService ) { - super(ConfigService, NodeService, ProjectService); + super(ConfigService, NodeService, ProjectAssetService, ProjectService); } ngOnInit() { diff --git a/src/main/webapp/wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.html b/src/main/webapp/wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.html new file mode 100644 index 0000000000..34562cfe1b --- /dev/null +++ b/src/main/webapp/wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.html @@ -0,0 +1,119 @@ + + Prompt + + +

Selection Type:

+ + + Single Answer + +
+ + Multiple Answer + +
+ +
+ There are no choices. Click the "Add Choice" button to add a choice. +
+
+
+ + Choice Text + + + +
+ + Is Correct + +
+
+
+ + Feeback + + + + + +
+
\ No newline at end of file diff --git a/src/main/webapp/wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.scss b/src/main/webapp/wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.scss new file mode 100644 index 0000000000..6dedbe4568 --- /dev/null +++ b/src/main/webapp/wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.scss @@ -0,0 +1,53 @@ +.prompt-input-container { + width: 100%; +} + +.selection-type-group { + display: block !important; +} + +.mat-radio-label { + display: inline-flex !important; +} + +.mat-radio-button { + margin-top: 5px; + margin-bottom: 5px; +} + +.add-choice-button { + margin-top: 20px; + margin-bottom: 20px; +} + +.choice-container { + border: 2px solid #dddddd; + border-radius: 5px; + margin-bottom: 20px; + padding: 20px 20px 10px 20px; +} + +.no-choices-message-container { + margin-bottom: 20px; +} + +.choice-authoring-button { + margin-left: 10px; + margin-right: 10px; +} + +.mat-checkbox { + margin-left: 5px; +} + +.mat-checkbox-layout { + display: inline-flex !important; +} + +.choice-text-input-container { + width: 70%; +} + +.choice-feedback-input-container { + width: 70%; +} \ No newline at end of file diff --git a/src/main/webapp/wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.ts b/src/main/webapp/wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.ts new file mode 100644 index 0000000000..5d41c68a26 --- /dev/null +++ b/src/main/webapp/wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.ts @@ -0,0 +1,139 @@ +import { Component, ViewEncapsulation } from '@angular/core'; +import { Subject, Subscription } from 'rxjs'; +import { debounceTime, distinctUntilChanged } from 'rxjs/operators'; +import { ProjectAssetService } from '../../../../site/src/app/services/projectAssetService'; +import { ComponentAuthoring } from '../../../authoringTool/components/component-authoring.component'; +import { ConfigService } from '../../../services/configService'; +import { NodeService } from '../../../services/nodeService'; +import { TeacherProjectService } from '../../../services/teacherProjectService'; +import { UtilService } from '../../../services/utilService'; + +@Component({ + selector: 'multiple-choice-authoring', + templateUrl: 'multiple-choice-authoring.component.html', + styleUrls: ['multiple-choice-authoring.component.scss'], + encapsulation: ViewEncapsulation.None +}) +export class MultipleChoiceAuthoring extends ComponentAuthoring { + allowedConnectedComponentTypes = ['MultipleChoice']; + choiceTextChange: Subject = new Subject(); + feedbackTextChange: Subject = new Subject(); + choiceTextChangeSubscription: Subscription; + feedbackTextChangeSubscription: Subscription; + + constructor( + protected ConfigService: ConfigService, + protected NodeService: NodeService, + protected ProjectAssetService: ProjectAssetService, + protected ProjectService: TeacherProjectService, + protected UtilService: UtilService + ) { + super(ConfigService, NodeService, ProjectAssetService, ProjectService); + this.choiceTextChangeSubscription = this.choiceTextChange + .pipe(debounceTime(1000), distinctUntilChanged()) + .subscribe(() => { + this.componentChanged(); + }); + this.feedbackTextChangeSubscription = this.feedbackTextChange + .pipe(debounceTime(1000), distinctUntilChanged()) + .subscribe(() => { + this.componentChanged(); + }); + } + + ngOnDestroy(): void { + this.choiceTextChangeSubscription.unsubscribe(); + this.feedbackTextChangeSubscription.unsubscribe(); + } + + feedbackChanged(): void { + let show = true; + if (!this.componentHasFeedback()) { + show = false; + } + this.setShowSubmitButtonValue(show); + this.componentChanged(); + } + + componentHasFeedback(): boolean { + for (const choice of this.authoringComponentContent.choices) { + if (choice.isCorrect || (choice.feedback != null && choice.feedback !== '')) { + return true; + } + } + return false; + } + + addChoice(): void { + const newChoice = { + id: this.UtilService.generateKey(10), + text: '', + feedback: '', + isCorrect: false + }; + this.authoringComponentContent.choices.push(newChoice); + this.componentChanged(); + } + + deleteChoice(choiceId: string): void { + if (confirm($localize`Are you sure you want to delete this choice?`)) { + const choices = this.authoringComponentContent.choices; + for (let c = 0; c < choices.length; c++) { + if (choices[c].id === choiceId) { + choices.splice(c, 1); + break; + } + } + this.componentChanged(); + } + } + + moveChoiceUp(choiceId: string): void { + const choices = this.authoringComponentContent.choices; + for (let c = 0; c < choices.length; c++) { + const choice = choices[c]; + if (choice.id === choiceId) { + if (c !== 0) { + choices.splice(c, 1); + choices.splice(c - 1, 0, choice); + } + break; + } + } + this.componentChanged(); + } + + moveChoiceDown(choiceId: string): void { + const choices = this.authoringComponentContent.choices; + for (let c = 0; c < choices.length; c++) { + const choice = choices[c]; + if (choice.id === choiceId) { + if (c !== choices.length - 1) { + choices.splice(c, 1); + choices.splice(c + 1, 0, choice); + } + break; + } + } + this.componentChanged(); + } + + chooseChoiceAsset(choice: any): void { + const params = { + isPopup: true, + nodeId: this.nodeId, + componentId: this.componentId, + target: 'choice', + targetObject: choice + }; + this.openAssetChooser(params); + } + + assetSelected({ nodeId, componentId, assetItem, target, targetObject }): void { + super.assetSelected({ nodeId, componentId, assetItem, target }); + if (target === 'choice') { + targetObject.text = ``; + this.componentChanged(); + } + } +} diff --git a/src/main/webapp/wise5/components/multipleChoice/multipleChoiceAuthoringComponentModule.ts b/src/main/webapp/wise5/components/multipleChoice/multipleChoiceAuthoringComponentModule.ts index c4e9dae8a3..0eb26f3600 100644 --- a/src/main/webapp/wise5/components/multipleChoice/multipleChoiceAuthoringComponentModule.ts +++ b/src/main/webapp/wise5/components/multipleChoice/multipleChoiceAuthoringComponentModule.ts @@ -1,15 +1,18 @@ 'use strict'; import * as angular from 'angular'; -import { downgradeInjectable } from '@angular/upgrade/static'; +import { downgradeComponent, downgradeInjectable } from '@angular/upgrade/static'; import { MultipleChoiceService } from './multipleChoiceService'; -import MultipleChoiceAuthoring from './multipleChoiceAuthoring'; import { EditMultipleChoiceAdvancedComponent } from './edit-multiple-choice-advanced/edit-multiple-choice-advanced.component'; +import { MultipleChoiceAuthoring } from './multiple-choice-authoring/multiple-choice-authoring.component'; const multipleChoiceAuthoringComponentModule = angular .module('multipleChoiceAuthoringComponentModule', ['pascalprecht.translate']) .service('MultipleChoiceService', downgradeInjectable(MultipleChoiceService)) - .component('multipleChoiceAuthoring', MultipleChoiceAuthoring) + .directive( + 'multipleChoiceAuthoring', + downgradeComponent({ component: MultipleChoiceAuthoring }) as angular.IDirectiveFactory + ) .component('editMultipleChoiceAdvanced', EditMultipleChoiceAdvancedComponent) .config([ '$translatePartialLoaderProvider', diff --git a/src/main/webapp/wise5/components/outsideURL/outside-url-authoring/outside-url-authoring.component.html b/src/main/webapp/wise5/components/outsideURL/outside-url-authoring/outside-url-authoring.component.html index bc3fa4e14c..ab699bda16 100644 --- a/src/main/webapp/wise5/components/outsideURL/outside-url-authoring/outside-url-authoring.component.html +++ b/src/main/webapp/wise5/components/outsideURL/outside-url-authoring/outside-url-authoring.component.html @@ -91,7 +91,7 @@ URL @@ -102,14 +102,14 @@ + (ngModelChange)="widthChange.next($event)"/> Height (px) + (ngModelChange)="heightChange.next($event)"/> -
There are no choices. Click the "Add Choice" button to add a choice.
-
-
- +
+
+ Choice Text + [(ngModel)]="choice.text" + (ngModelChange)="choiceTextChange.next($event)" + i18n-placeholder + placeholder="Type text or choose an image" />
- Is Correct
-
- +
+ Feeback + [(ngModel)]="choice.feedback" + (ngModelChange)="feedbackTextChange.next($event)" + i18n-placeholder + placeholder="Optional" />
-
\ No newline at end of file +
diff --git a/src/main/webapp/wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.scss b/src/main/webapp/wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.scss index 6dedbe4568..6a2a0ea5e7 100644 --- a/src/main/webapp/wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.scss +++ b/src/main/webapp/wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.scss @@ -1,18 +1,20 @@ -.prompt-input-container { +@import '~style/abstracts/variables'; + +.mat-form-field { width: 100%; } -.selection-type-group { - display: block !important; +.mat-radio-group { + display: block; } -.mat-radio-label { - display: inline-flex !important; +label.mat-radio-label { + display: inline-flex; } -.mat-radio-button { - margin-top: 5px; - margin-bottom: 5px; +.mat-radio-button.choice-radio-button { + display: block; + margin-top: 10px; } .add-choice-button { @@ -22,13 +24,15 @@ .choice-container { border: 2px solid #dddddd; - border-radius: 5px; + border-radius: $card-border-radius; margin-bottom: 20px; padding: 20px 20px 10px 20px; } -.no-choices-message-container { +.info-block { margin-bottom: 20px; + text-align: center; + font-weight: 500; } .choice-authoring-button { @@ -40,14 +44,6 @@ margin-left: 5px; } -.mat-checkbox-layout { - display: inline-flex !important; -} - -.choice-text-input-container { - width: 70%; -} - -.choice-feedback-input-container { +.choice-text-input-container, .choice-feedback-input-container { width: 70%; } \ No newline at end of file From 88ebfb566a0e8e3cb3495c061df4fb1cf11d1833 Mon Sep 17 00:00:00 2001 From: Geoffrey Kwan Date: Fri, 15 Jan 2021 12:45:03 -0500 Subject: [PATCH 15/26] Upgraded Concept Map authoring to Angular. #2885 --- .../src/app/teacher-hybrid-angular.module.ts | 2 + src/main/webapp/site/src/messages.xlf | 332 +++++++++++--- .../components/conceptMap/authoring.html | 432 ------------------ .../concept-map-authoring.component.html | 249 ++++++++++ .../concept-map-authoring.component.scss | 70 +++ .../concept-map-authoring.component.ts} | 143 ++---- .../conceptMapAuthoringComponentModule.ts | 9 +- 7 files changed, 641 insertions(+), 596 deletions(-) delete mode 100644 src/main/webapp/wise5/components/conceptMap/authoring.html create mode 100644 src/main/webapp/wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html create mode 100644 src/main/webapp/wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.scss rename src/main/webapp/wise5/components/conceptMap/{conceptMapAuthoring.ts => concept-map-authoring/concept-map-authoring.component.ts} (56%) diff --git a/src/main/webapp/site/src/app/teacher-hybrid-angular.module.ts b/src/main/webapp/site/src/app/teacher-hybrid-angular.module.ts index 5bf8ebb78a..64ea6c1c44 100644 --- a/src/main/webapp/site/src/app/teacher-hybrid-angular.module.ts +++ b/src/main/webapp/site/src/app/teacher-hybrid-angular.module.ts @@ -48,6 +48,7 @@ import { OpenResponseAuthoring } from '../../../wise5/components/openResponse/op import { HtmlAuthoring } from '../../../wise5/components/html/html-authoring/html-authoring.component'; import { OutsideUrlAuthoring } from '../../../wise5/components/outsideURL/outside-url-authoring/outside-url-authoring.component'; import { MultipleChoiceAuthoring } from '../../../wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component'; +import { ConceptMapAuthoring } from '../../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component'; @NgModule({ declarations: [ @@ -59,6 +60,7 @@ import { MultipleChoiceAuthoring } from '../../../wise5/components/multipleChoic ChooseNewComponentLocation, ComponentNewWorkBadgeComponent, ComponentSelectComponent, + ConceptMapAuthoring, EditComponentRubricComponent, EditComponentJsonComponent, EditComponentMaxScoreComponent, diff --git a/src/main/webapp/site/src/messages.xlf b/src/main/webapp/site/src/messages.xlf index e6b1586225..0ea014d3a1 100644 --- a/src/main/webapp/site/src/messages.xlf +++ b/src/main/webapp/site/src/messages.xlf @@ -6002,71 +6002,165 @@ 19 - - Rubric + + Prompt - app/authoring-tool/edit-component-rubric/edit-component-rubric.component.html + ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html + 2 + + + ../../wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.html 2 + + ../../wise5/components/openResponse/open-response-authoring/open-response-authoring.component.html + 11 + - - Edit Component Rubric + + Enter Prompt Here - app/authoring-tool/edit-component-rubric/edit-component-rubric.component.html - 5 + ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html + 7 + + + ../../wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.html + 6 + + + ../../wise5/components/openResponse/open-response-authoring/open-response-authoring.component.html + 15 - - JSON + + Background Image (Optional) - app/authoring-tool/edit-component-json/edit-component-json.component.html - 2 + ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html + 14 - - Close the JSON view to save the changes + + Choose an Image - app/authoring-tool/edit-component-json/edit-component-json.component.html - 10 + ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html + 23 + + + ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html + 100 + + + ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html + 103 + + + ../../wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.html + 56 - - Edit Component JSON + + Stretch Background - app/authoring-tool/edit-component-json/edit-component-json.component.html - 12 + ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html + 34 - - Max Score + + Canvas Width (Optional) - app/authoring-tool/edit-component-max-score/edit-component-max-score.component.html - 2 + ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html + 40 - - Tags + + Canvas Height (Optional) - app/authoring-tool/edit-component-tags/edit-component-tags.component.html - 2 + ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html + 47 - - Add Tag + + Show Node Labels - app/authoring-tool/edit-component-tags/edit-component-tags.component.html - 5 + ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html + 59 - - Tag Name + + Nodes - app/authoring-tool/edit-component-tags/edit-component-tags.component.html - 15 + ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html + 64 + + + + Add Node + + ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html + 69 + + + ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html + 72 + + + + There are no nodes. Click the "Add Node" button to add a node. + + ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html + 78 + + + + Label + + ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html + 85 + + + ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html + 179 + + + + File Name + + ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html + 91 + + + + Width + + ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html + 108 + + + + Height + + ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html + 115 Move Up + + ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html + 126 + + + ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html + 129 + + + ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html + 195 + + + ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html + 198 + app/authoring-tool/edit-component-tags/edit-component-tags.component.html 22 @@ -6078,6 +6172,22 @@ Move Down + + ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html + 138 + + + ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html + 141 + + + ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html + 207 + + + ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html + 210 + app/authoring-tool/edit-component-tags/edit-component-tags.component.html 30 @@ -6089,6 +6199,22 @@ Delete + + ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html + 149 + + + ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html + 152 + + + ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html + 218 + + + ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html + 221 + app/authoring-tool/edit-component-tags/edit-component-tags.component.html 38 @@ -6102,42 +6228,137 @@ 113 - - Component Width + + Links - app/authoring-tool/edit-component-width/edit-component-width.component.html + ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html + 158 + + + + Add Link + + ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html + 163 + + + ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html + 166 + + + + There are no links. Click the "Add Link" button to add a link. + + ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html + 172 + + + + Color + + ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html + 185 + + + + Save Starter Concept Map + + ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html + 231 + + + ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html + 234 + + + + Delete Starter Concept Map + + ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html + 242 + + + ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html + 245 + + + + Rubric + + app/authoring-tool/edit-component-rubric/edit-component-rubric.component.html 2 - - ~ Report Available ~ + + Edit Component Rubric - app/classroom-monitor/milestones/milestones.component.html - 20 + app/authoring-tool/edit-component-rubric/edit-component-rubric.component.html + 5 - - Prompt + + JSON - ../../wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.html + app/authoring-tool/edit-component-json/edit-component-json.component.html + 2 + + + + Close the JSON view to save the changes + + app/authoring-tool/edit-component-json/edit-component-json.component.html + 10 + + + + Edit Component JSON + + app/authoring-tool/edit-component-json/edit-component-json.component.html + 12 + + + + Max Score + + app/authoring-tool/edit-component-max-score/edit-component-max-score.component.html 2 + + + Tags - ../../wise5/components/openResponse/open-response-authoring/open-response-authoring.component.html - 11 + app/authoring-tool/edit-component-tags/edit-component-tags.component.html + 2 - - Enter Prompt Here + + Add Tag - ../../wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.html - 6 + app/authoring-tool/edit-component-tags/edit-component-tags.component.html + 5 + + + Tag Name - ../../wise5/components/openResponse/open-response-authoring/open-response-authoring.component.html + app/authoring-tool/edit-component-tags/edit-component-tags.component.html 15 + + Component Width + + app/authoring-tool/edit-component-width/edit-component-width.component.html + 2 + + + + ~ Report Available ~ + + app/classroom-monitor/milestones/milestones.component.html + 20 + + Single Answer @@ -6173,13 +6394,6 @@ 49 - - Choose an Image - - ../../wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.html - 56 - - Image diff --git a/src/main/webapp/wise5/components/conceptMap/authoring.html b/src/main/webapp/wise5/components/conceptMap/authoring.html deleted file mode 100644 index be69dfc8ea..0000000000 --- a/src/main/webapp/wise5/components/conceptMap/authoring.html +++ /dev/null @@ -1,432 +0,0 @@ -
- -
-
- {{ ::'conceptMap.showAutoScore' | translate }}: - -
- {{ ::'conceptMap.showAutoFeedback' | translate }}: - -
- {{ ::'conceptMap.rules' | translate }} -
- {{ ::'conceptMap.ruleName' | translate }}: - - - {{ ::'conceptMap.contain' | translate }} - - - - {{ ::'conceptMap.withLink' | translate }} - - - {{ ::'conceptMap.to' | translate }} - - - - -
-
- - - - - -
-
-
- -
-
- {{ ::'conceptMap.customRuleEvaluator' | translate }} -
- -
-
-
- - {{ ::'SHOW_SAVE_BUTTON' | translate }} - -
-
- - {{ ::'SHOW_SUBMIT_BUTTON' | translate }} - -
-
- - {{ ::'SHOW_ADD_TO_NOTEBOOK_BUTTON' | translate }} - -
-
- - - - -
-
- - - - -
-
-
- - - add - - {{ ::'ADD_CONNECTED_COMPONENT' | translate }} - - -
-
-
- - - - - {{ conceptMapController.getNodePositionAndTitleByNodeId(item.$key) }} - - - - - - - - {{ componentIndex + 1 }}. {{ ::component.type }} - - ({{ ::'thisComponent' | translate }}) - - - - - - - - - {{ ::'importWork' | translate }} - - - {{ ::'showWork' | translate }} - - - - - - - delete - - {{ ::'DELETE' | translate }} - - - -
- - - {{ ::'importWorkAsBackground' | translate }} - - -
-
-
-
- -
-
-
- - - - -
-
- - - - - - insert_photo - - {{ ::'chooseAnImage' | translate }} - - -
-
- - - {{ ::'conceptMap.stretchBackground' | translate }} - - -
-
- - - - - - - - -
-
- - - {{ ::'conceptMap.showNodeLabels' | translate }} - - -
-
-
{{ ::'conceptMap.nodes' | translate }}
- - add - - {{ ::'conceptMap.addNode' | translate }} - - -
-
-

{{ ::'conceptMap.thereAreNoNodes' | translate }}

-
-
-
- - - - - - - - - - insert_photo - - {{ ::'CHOOSE' | translate}} - - - - - - - - - - - - arrow_upward - - {{ ::'UP' | translate}} - - - - arrow_downward - - {{ ::'DOWN' | translate}} - - - - delete - - {{ ::'DELETE' | translate}} - - -
-
-
{{ ::'conceptMap.links' | translate }}
- - add - - {{ ::'conceptMap.addLink' | translate }} - - -
-
-

{{ ::'conceptMap.thereAreNoLinks' | translate }}

-
-
-
- - - - - - - - - - arrow_upward - - {{ ::'UP' | translate}} - - - - arrow_downward - - {{ ::'DOWN' | translate}} - - - - delete - - {{ ::'DELETE' | translate}} - - -
- - create - - {{ ::'conceptMap.saveStarterConceptMap' | translate }} - - - - delete_sweep - - {{ ::'conceptMap.deleteStarterConceptMap' | translate }} - - -
-
-
diff --git a/src/main/webapp/wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html b/src/main/webapp/wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html new file mode 100644 index 0000000000..8e4ecc03d2 --- /dev/null +++ b/src/main/webapp/wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html @@ -0,0 +1,249 @@ + + Prompt + + +
+ + Background Image (Optional) + + + +
+
+ + Stretch Background + +
+
+ + Canvas Width (Optional) + + + + Canvas Height (Optional) + + +
+
+ + Show Node Labels + +
+
+ Nodes + +
+
+

There are no nodes. Click the "Add Node" button to add a node.

+
+
+ + Label + + + + File Name + + + + + Width + + + + Height + + + + + +
+
+ Links + +
+
+

There are no links. Click the "Add Link" button to add a link.

+
+ +
+ + +
\ No newline at end of file diff --git a/src/main/webapp/wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.scss b/src/main/webapp/wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.scss new file mode 100644 index 0000000000..f6439cb715 --- /dev/null +++ b/src/main/webapp/wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.scss @@ -0,0 +1,70 @@ +.prompt-input-container { + width: 100%; +} + +.background-input-container { + width: 90%; +} + +.authoring-button { + margin-left: 10px; + margin-right: 10px; +} + +.canvas-dimension-input-container { + width: 20%; + margin-top: 20px; + margin-right: 20px; +} + +.show-node-labels-checkbox-container { + margin-top: 10px; + margin-bottom: 10px; +} + +.add-nodes-container { + margin-top: 20px; + margin-bottom: 20px; +} + +.node-row { + width: 100%; +} + +.node-label-container { + width: 30%; + margin-right: 20px; +} + +.node-file-name-container { + width: 30%; +} + +.node-dimension-container { + width: 6%; + margin-left: 10px; + margin-right: 10px; +} + +.node-authoring-button { + margin-left: 10px; + margin-right: 10px; +} + +.link-row { + width: 100%; +} + +.link-label-container { + width: 30%; + margin-right: 20px; +} + +.link-color-container { + width: 30%; +} + +.starter-concept-map-buttons-container { + margin-top: 20px; + margin-bottom: 20px; +} \ No newline at end of file diff --git a/src/main/webapp/wise5/components/conceptMap/conceptMapAuthoring.ts b/src/main/webapp/wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.ts similarity index 56% rename from src/main/webapp/wise5/components/conceptMap/conceptMapAuthoring.ts rename to src/main/webapp/wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.ts index 5da7215f29..5a140470a4 100644 --- a/src/main/webapp/wise5/components/conceptMap/conceptMapAuthoring.ts +++ b/src/main/webapp/wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.ts @@ -1,47 +1,45 @@ 'use strict'; -import { Directive } from '@angular/core'; -import { EditComponentController } from '../../authoringTool/components/editComponentController'; - -@Directive() -class ConceptMapAuthoringController extends EditComponentController { +import { Component } from '@angular/core'; +import { Subject, Subscription } from 'rxjs'; +import { debounceTime, distinctUntilChanged } from 'rxjs/operators'; +import { ProjectAssetService } from '../../../../site/src/app/services/projectAssetService'; +import { ComponentAuthoring } from '../../../authoringTool/components/component-authoring.component'; +import { ConfigService } from '../../../services/configService'; +import { NodeService } from '../../../services/nodeService'; +import { TeacherProjectService } from '../../../services/teacherProjectService'; +import { UtilService } from '../../../services/utilService'; +import { ConceptMapService } from '../conceptMapService'; + +@Component({ + selector: 'concept-map-authoring', + templateUrl: 'concept-map-authoring.component.html', + styleUrls: ['concept-map-authoring.component.scss'] +}) +export class ConceptMapAuthoring extends ComponentAuthoring { availableNodes: any[]; availableLinks: any[]; - - static $inject = [ - '$filter', - 'ConceptMapService', - 'ConfigService', - 'NodeService', - 'NotificationService', - 'ProjectAssetService', - 'ProjectService', - 'UtilService' - ]; + inputChange: Subject = new Subject(); + inputChangeSubscription: Subscription; constructor( - $filter, - private ConceptMapService, - ConfigService, - NodeService, - NotificationService, - ProjectAssetService, - ProjectService, - UtilService + private ConceptMapService: ConceptMapService, + protected ConfigService: ConfigService, + protected NodeService: NodeService, + protected ProjectAssetService: ProjectAssetService, + protected ProjectService: TeacherProjectService, + protected UtilService: UtilService ) { - super( - $filter, - ConfigService, - NodeService, - NotificationService, - ProjectAssetService, - ProjectService, - UtilService - ); + super(ConfigService, NodeService, ProjectAssetService, ProjectService); + this.inputChangeSubscription = this.inputChange + .pipe(debounceTime(1000), distinctUntilChanged()) + .subscribe(() => { + this.componentChanged(); + }); } - $onInit() { - super.$onInit(); + ngOnInit() { + super.ngOnInit(); this.availableNodes = this.componentContent.nodes; this.availableLinks = this.componentContent.links; @@ -52,28 +50,20 @@ class ConceptMapAuthoringController extends EditComponentController { } } - /** - * A move node up button was clicked in the authoring tool - * @param index the index of the node that we will move - */ + ngOnDestroy() { + this.inputChangeSubscription.unsubscribe(); + } + moveNodeUpButtonClicked(index: number): void { this.UtilService.moveObjectUp(this.authoringComponentContent.nodes, index); this.componentChanged(); } - /** - * A move node down button was clicked in the authoring tool. - * @param index the index of the node that we will move - */ moveNodeDownButtonClicked(index: number): void { this.UtilService.moveObjectDown(this.authoringComponentContent.nodes, index); this.componentChanged(); } - /** - * A node delete button was clicked in the authoring tool. - * @param index the index of the node that we will delete - */ nodeDeleteButtonClicked(index: number): void { const nodes = this.authoringComponentContent.nodes; const node = nodes[index]; @@ -81,10 +71,7 @@ class ConceptMapAuthoringController extends EditComponentController { const nodeLabel = node.label; if ( confirm( - this.$translate('conceptMap.areYouSureYouWantToDeleteThisNode', { - nodeFileName: nodeFileName, - nodeLabel: nodeLabel - }) + $localize`Are you sure you want to delete this node?\n\nFile Name: ${nodeFileName}\nLabel: ${nodeLabel}` ) ) { nodes.splice(index, 1); @@ -92,37 +79,21 @@ class ConceptMapAuthoringController extends EditComponentController { } } - /** - * A move link up button was clicked in the authoring tool. - * @param index the index of the link - */ moveLinkUpButtonClicked(index: number): void { this.UtilService.moveObjectUp(this.authoringComponentContent.links, index); this.componentChanged(); } - /** - * A move link down button was clicked in the authoring tool. - * @param index the index of the link - */ moveLinkDownButtonClicked(index: number): void { this.UtilService.moveObjectDown(this.authoringComponentContent.links, index); this.componentChanged(); } - /** - * A link delete button was clicked in the authoring tool. - * @param index the index of the link - */ linkDeleteButtonClicked(index: number): void { const links = this.authoringComponentContent.links; const link = links[index]; const linkLabel = link.label; - if ( - confirm( - this.$translate('conceptMap.areYouSureYouWantToDeleteThisLink', { linkLabel: linkLabel }) - ) - ) { + if (confirm($localize`Are you sure you want to delete this link?\n\nLabel: ${linkLabel}`)) { links.splice(index, 1); this.componentChanged(); } @@ -140,11 +111,6 @@ class ConceptMapAuthoringController extends EditComponentController { this.componentChanged(); } - /** - * Get the concept map node with the given id - * @param nodeId the concept map node id - * @return the concept map node with the given node id - */ getNodeById(nodeId: number): any { for (const node of this.authoringComponentContent.nodes) { if (nodeId === node.id) { @@ -164,24 +130,16 @@ class ConceptMapAuthoringController extends EditComponentController { this.componentChanged(); } - /** - * Get a new ConceptMapNode id that isn't being used - * @returns a new ConceptMapNode id e.g. 'node3' - */ getNewConceptMapNodeId(): string { return this.ConceptMapService.getNextAvailableId(this.authoringComponentContent.nodes, 'node'); } - /** - * Get a new ConceptMapLink id that isn't being used - * @returns a new ConceptMapLink id e.g. 'link3' - */ getNewConceptMapLinkId(): string { return this.ConceptMapService.getNextAvailableId(this.authoringComponentContent.links, 'link'); } saveStarterConceptMap(): void { - if (confirm(this.$translate('conceptMap.areYouSureYouWantToSaveTheStarterConceptMap'))) { + if (confirm($localize`Are you sure you want to save the starter concept map?`)) { this.NodeService.requestStarterState({ nodeId: this.nodeId, componentId: this.componentId }); } } @@ -192,15 +150,12 @@ class ConceptMapAuthoringController extends EditComponentController { } deleteStarterConceptMap(): void { - if (confirm(this.$translate('conceptMap.areYouSureYouWantToDeleteTheStarterConceptMap'))) { + if (confirm($localize`Are you sure you want to delete the starter concept map?`)) { this.authoringComponentContent.starterConceptMap = null; this.componentChanged(); } } - /** - * Show the asset popup to allow the author to choose the background image - */ chooseBackgroundImage(): void { const params = { isPopup: true, @@ -211,10 +166,6 @@ class ConceptMapAuthoringController extends EditComponentController { this.openAssetChooser(params); } - /** - * Show the asset popup to allow the author to choose an image for the node - * @param conceptMapNodeId the id of the node in the concept map - */ chooseNodeImage(conceptMapNodeId: string): void { const params = { isPopup: true, @@ -238,15 +189,3 @@ class ConceptMapAuthoringController extends EditComponentController { } } } - -const ConceptMapAuthoring = { - bindings: { - nodeId: '@', - componentId: '@' - }, - controller: ConceptMapAuthoringController, - controllerAs: 'conceptMapController', - templateUrl: 'wise5/components/conceptMap/authoring.html' -}; - -export default ConceptMapAuthoring; diff --git a/src/main/webapp/wise5/components/conceptMap/conceptMapAuthoringComponentModule.ts b/src/main/webapp/wise5/components/conceptMap/conceptMapAuthoringComponentModule.ts index f9d72cc94a..d39b24644b 100644 --- a/src/main/webapp/wise5/components/conceptMap/conceptMapAuthoringComponentModule.ts +++ b/src/main/webapp/wise5/components/conceptMap/conceptMapAuthoringComponentModule.ts @@ -1,15 +1,18 @@ 'use strict'; import * as angular from 'angular'; -import { downgradeInjectable } from '@angular/upgrade/static'; +import { downgradeComponent, downgradeInjectable } from '@angular/upgrade/static'; import { ConceptMapService } from './conceptMapService'; -import ConceptMapAuthoring from './conceptMapAuthoring'; import { EditConceptMapAdvancedComponent } from './edit-concept-map-advanced/edit-concept-map-advanced.component'; +import { ConceptMapAuthoring } from './concept-map-authoring/concept-map-authoring.component'; const conceptMapAuthoringComponentModule = angular .module('conceptMapAuthoringComponentModule', ['pascalprecht.translate']) .service('ConceptMapService', downgradeInjectable(ConceptMapService)) - .component('conceptMapAuthoring', ConceptMapAuthoring) + .directive( + 'conceptMapAuthoring', + downgradeComponent({ component: ConceptMapAuthoring }) as angular.IDirectiveFactory + ) .component('editConceptMapAdvanced', EditConceptMapAdvancedComponent) .config([ '$translatePartialLoaderProvider', From 9fd00ce6c531f2a1dd6e904ac90e46a065394643 Mon Sep 17 00:00:00 2001 From: Geoffrey Kwan Date: Tue, 19 Jan 2021 17:40:06 -0500 Subject: [PATCH 16/26] Upgraded Draw authoring to Angular. #2889 --- .../src/app/teacher-hybrid-angular.module.ts | 2 + src/main/webapp/site/src/messages.xlf | 267 ++++++++++++- .../wise5/components/draw/authoring.html | 366 ------------------ .../draw-authoring.component.html | 292 ++++++++++++++ .../draw-authoring.component.scss | 39 ++ .../draw-authoring.component.ts | 338 ++++++++++++++++ .../wise5/components/draw/drawAuthoring.ts | 265 ------------- .../draw/drawAuthoringComponentModule.ts | 9 +- 8 files changed, 930 insertions(+), 648 deletions(-) delete mode 100644 src/main/webapp/wise5/components/draw/authoring.html create mode 100644 src/main/webapp/wise5/components/draw/draw-authoring/draw-authoring.component.html create mode 100644 src/main/webapp/wise5/components/draw/draw-authoring/draw-authoring.component.scss create mode 100644 src/main/webapp/wise5/components/draw/draw-authoring/draw-authoring.component.ts delete mode 100644 src/main/webapp/wise5/components/draw/drawAuthoring.ts diff --git a/src/main/webapp/site/src/app/teacher-hybrid-angular.module.ts b/src/main/webapp/site/src/app/teacher-hybrid-angular.module.ts index 64ea6c1c44..0e0d886a96 100644 --- a/src/main/webapp/site/src/app/teacher-hybrid-angular.module.ts +++ b/src/main/webapp/site/src/app/teacher-hybrid-angular.module.ts @@ -49,6 +49,7 @@ import { HtmlAuthoring } from '../../../wise5/components/html/html-authoring/htm import { OutsideUrlAuthoring } from '../../../wise5/components/outsideURL/outside-url-authoring/outside-url-authoring.component'; import { MultipleChoiceAuthoring } from '../../../wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component'; import { ConceptMapAuthoring } from '../../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component'; +import { DrawAuthoring } from '../../../wise5/components/draw/draw-authoring/draw-authoring.component'; @NgModule({ declarations: [ @@ -61,6 +62,7 @@ import { ConceptMapAuthoring } from '../../../wise5/components/conceptMap/concep ComponentNewWorkBadgeComponent, ComponentSelectComponent, ConceptMapAuthoring, + DrawAuthoring, EditComponentRubricComponent, EditComponentJsonComponent, EditComponentMaxScoreComponent, diff --git a/src/main/webapp/site/src/messages.xlf b/src/main/webapp/site/src/messages.xlf index 0ea014d3a1..3d0ef23e3f 100644 --- a/src/main/webapp/site/src/messages.xlf +++ b/src/main/webapp/site/src/messages.xlf @@ -1225,6 +1225,10 @@ app/student/add-project-dialog/add-project-dialog.component.html 30
+ + ../../wise5/components/draw/draw-authoring/draw-authoring.component.html + 204 + ../../wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.html 32 @@ -6008,6 +6012,10 @@ ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html 2 + + ../../wise5/components/draw/draw-authoring/draw-authoring.component.html + 2 + ../../wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.html 2 @@ -6023,6 +6031,10 @@ ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html 7 + + ../../wise5/components/draw/draw-authoring/draw-authoring.component.html + 6 + ../../wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.html 6 @@ -6038,6 +6050,10 @@ ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html 14 + + ../../wise5/components/draw/draw-authoring/draw-authoring.component.html + 13 +
Choose an Image @@ -6053,6 +6069,26 @@ ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html 103 + + ../../wise5/components/draw/draw-authoring/draw-authoring.component.html + 22 + + + ../../wise5/components/draw/draw-authoring/draw-authoring.component.html + 25 + + + ../../wise5/components/draw/draw-authoring/draw-authoring.component.html + 218 + + + ../../wise5/components/draw/draw-authoring/draw-authoring.component.html + 225 + + + ../../wise5/components/draw/draw-authoring/draw-authoring.component.html + 228 + ../../wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.html 56 @@ -6078,6 +6114,10 @@ ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html 47 + + ../../wise5/components/draw/draw-authoring/draw-authoring.component.html + 39 + Show Node Labels @@ -6161,6 +6201,10 @@ ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html 198 + + ../../wise5/components/draw/draw-authoring/draw-authoring.component.html + 237 + app/authoring-tool/edit-component-tags/edit-component-tags.component.html 22 @@ -6215,6 +6259,18 @@ ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html 221 + + ../../wise5/components/draw/draw-authoring/draw-authoring.component.html + 260 + + + ../../wise5/components/draw/draw-authoring/draw-authoring.component.html + 263 + + + ../../wise5/components/draw/draw-authoring/draw-authoring.component.html + 288 + app/authoring-tool/edit-component-tags/edit-component-tags.component.html 38 @@ -6282,6 +6338,203 @@ 245 + + Enable All Tools + + ../../wise5/components/draw/draw-authoring/draw-authoring.component.html + 50 + + + + Enable All + + ../../wise5/components/draw/draw-authoring/draw-authoring.component.html + 53 + + + + Disable All Tools + + ../../wise5/components/draw/draw-authoring/draw-authoring.component.html + 61 + + + + Disable All + + ../../wise5/components/draw/draw-authoring/draw-authoring.component.html + 64 + + + + Select Tool + + ../../wise5/components/draw/draw-authoring/draw-authoring.component.html + 74 + + + + Line Tool + + ../../wise5/components/draw/draw-authoring/draw-authoring.component.html + 82 + + + + Shape Tool + + ../../wise5/components/draw/draw-authoring/draw-authoring.component.html + 90 + + + + Free Hand Tool + + ../../wise5/components/draw/draw-authoring/draw-authoring.component.html + 98 + + + + Text Tool + + ../../wise5/components/draw/draw-authoring/draw-authoring.component.html + 107 + + + + Stamp Tool + + ../../wise5/components/draw/draw-authoring/draw-authoring.component.html + 115 + + + + Clone Tool + + ../../wise5/components/draw/draw-authoring/draw-authoring.component.html + 123 + + + + Stroke Color Tool + + ../../wise5/components/draw/draw-authoring/draw-authoring.component.html + 131 + + + + Fill Color Tool + + ../../wise5/components/draw/draw-authoring/draw-authoring.component.html + 140 + + + + Stroke Width Tool + + ../../wise5/components/draw/draw-authoring/draw-authoring.component.html + 148 + + + + Send Back Tool + + ../../wise5/components/draw/draw-authoring/draw-authoring.component.html + 156 + + + + Send Forward Tool + + ../../wise5/components/draw/draw-authoring/draw-authoring.component.html + 164 + + + + Undo Tool + + ../../wise5/components/draw/draw-authoring/draw-authoring.component.html + 173 + + + + Redo Tool + + ../../wise5/components/draw/draw-authoring/draw-authoring.component.html + 181 + + + + Delete Tool + + ../../wise5/components/draw/draw-authoring/draw-authoring.component.html + 189 + + + + Stamps + + ../../wise5/components/draw/draw-authoring/draw-authoring.component.html + 196 + + + + Add Stamp + + ../../wise5/components/draw/draw-authoring/draw-authoring.component.html + 201 + + + + Up + + ../../wise5/components/draw/draw-authoring/draw-authoring.component.html + 240 + + + ../../wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.html + 90 + + + + above + + ../../wise5/components/draw/draw-authoring/draw-authoring.component.html + 250 + + + + Down + + ../../wise5/components/draw/draw-authoring/draw-authoring.component.html + 252 + + + ../../wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.html + 102 + + + + Save Starter Drawing + + ../../wise5/components/draw/draw-authoring/draw-authoring.component.html + 274 + + + + Save + + ../../wise5/components/draw/draw-authoring/draw-authoring.component.html + 277 + + + + Delete Starter Drawing + + ../../wise5/components/draw/draw-authoring/draw-authoring.component.html + 285 + + Rubric @@ -6429,20 +6682,6 @@ 79 - - Up - - ../../wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.html - 90 - - - - Down - - ../../wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.html - 102 - - % completed (All periods) diff --git a/src/main/webapp/wise5/components/draw/authoring.html b/src/main/webapp/wise5/components/draw/authoring.html deleted file mode 100644 index 2dff626942..0000000000 --- a/src/main/webapp/wise5/components/draw/authoring.html +++ /dev/null @@ -1,366 +0,0 @@ - - -
-
-
-
- - {{ ::'SHOW_SAVE_BUTTON' | translate }} - -
-
- - {{ ::'SHOW_SUBMIT_BUTTON' | translate }} - -
-
- - {{ ::'SHOW_ADD_TO_NOTEBOOK_BUTTON' | translate }} - -
-
- - - - -
-
- - - - -
-
-
- - - add - - {{ ::'ADD_CONNECTED_COMPONENT' | translate }} - - -
-
-
- - - - - {{ drawController.getNodePositionAndTitleByNodeId(item.$key) }} - - - - - - - - {{ componentIndex + 1 }}. {{ component.type }} - - ({{ ::'thisComponent' | translate }}) - - - - - - - - - {{ ::'importWork' | translate }} - - - {{ ::'showWork' | translate }} - - - - - - - delete - - {{ ::'DELETE' | translate }} - - - -
- - - {{ ::'importWorkAsBackground' | translate }} - - -
-
-
-
- -
-
-
- - - - -
- - - - - - insert_photo - - {{ ::'chooseAnImage' | translate }} - - -
-
- - - - - - - - -
-
- - check - - {{ ::'draw.enableAllTools' | translate }} - - - - check_box_outline_blank - - {{ ::'draw.disableAllTools' | translate }} - - -
-
-
- - {{ ::'draw.selectTool' | translate }} - -
- - {{ ::'draw.lineTool' | translate }} - -
- - {{ ::'draw.shapeTool' | translate }} - -
- - {{ ::'draw.freeHandTool' | translate }} - -
-
- - {{ ::'draw.textTool' | translate }} - -
- - {{ ::'draw.stampTool' | translate }} - -
- - {{ ::'draw.cloneTool' | translate }} - -
- - {{ ::'draw.strokeColorTool' | translate }} - -
-
- - {{ ::'draw.fillColorTool' | translate }} - -
- - {{ ::'draw.strokeWidthTool' | translate }} - -
- - {{ ::'draw.sendBackTool' | translate }} - -
- - {{ ::'draw.sendForwardTool' | translate }} - -
-
- - {{ ::'draw.undoTool' | translate }} - -
- - {{ ::'draw.redoTool' | translate }} - -
- - {{ ::'draw.deleteTool' | translate }} - -
-
-
-

{{ ::'draw.stamps' | translate }}

-
-
- {{ ::'draw.thereAreNoStampsClickTheAddStampButtonToAddAStamp' | translate }} -
-
- - - - - - insert_photo - - {{ ::'chooseAnImage' | translate }} - - - - arrow_upward - - {{ ::'UP' | translate }} - - - - arrow_downward - - {{ ::'DOWN' | translate }} - - - - delete - - {{ ::'DELETE' | translate }} - - -
-
- - library_add - - {{ ::'draw.addStamp' | translate }} - - -
- - create - - {{ ::'draw.saveStarterDrawing' | translate }} - - - - delete_sweep - - {{ ::'draw.deleteStarterDrawing' | translate }} - - -
-
-
diff --git a/src/main/webapp/wise5/components/draw/draw-authoring/draw-authoring.component.html b/src/main/webapp/wise5/components/draw/draw-authoring/draw-authoring.component.html new file mode 100644 index 0000000000..32e2c6a229 --- /dev/null +++ b/src/main/webapp/wise5/components/draw/draw-authoring/draw-authoring.component.html @@ -0,0 +1,292 @@ + + Prompt + + +
+ + Background Image (Optional) + + + +
+
+ + Canvas Width (Optional) + + + + Canvas Height (Optional) + + +
+ + +
+
+ + Select Tool + +
+ + Line Tool + +
+ + Shape Tool + +
+ + Free Hand Tool + +
+
+ + Text Tool + +
+ + Stamp Tool + +
+ + Clone Tool + +
+ + Stroke Color Tool + +
+
+ + Fill Color Tool + +
+ + Stroke Width Tool + +
+ + Send Back Tool + +
+ + Send Forward Tool + +
+
+ + Undo Tool + +
+ + Redo Tool + +
+ + Delete Tool + +
+
+
+
+ Stamps + +
+
+ There are no stamps. Click the "Add Stamp" button to add a stamp. +
+
+ + Stamp Image + + + + + + +
+
+
+ + +
\ No newline at end of file diff --git a/src/main/webapp/wise5/components/draw/draw-authoring/draw-authoring.component.scss b/src/main/webapp/wise5/components/draw/draw-authoring/draw-authoring.component.scss new file mode 100644 index 0000000000..497bb01262 --- /dev/null +++ b/src/main/webapp/wise5/components/draw/draw-authoring/draw-authoring.component.scss @@ -0,0 +1,39 @@ +.prompt-input-container { + width: 100%; +} + +.background-input-container { + width: 90%; +} + +.enable-all-tools-button { + margin-right: 20px; +} + +.disable-all-tools-button { + margin-right: 20px; +} + +.authoring-button { + margin-left: 10px; + margin-right: 10px; +} + +.canvas-dimension-container { + width: 20%; + margin-right: 20px; +} + +.add-stamps-container { + margin-top: 20px; + margin-bottom: 20px; +} + +.stamp-image-input-container { + width: 50%; +} + +.starter-draw-buttons-container { + margin-top: 20px; + margin-bottom: 20px; +} \ No newline at end of file diff --git a/src/main/webapp/wise5/components/draw/draw-authoring/draw-authoring.component.ts b/src/main/webapp/wise5/components/draw/draw-authoring/draw-authoring.component.ts new file mode 100644 index 0000000000..78755ca185 --- /dev/null +++ b/src/main/webapp/wise5/components/draw/draw-authoring/draw-authoring.component.ts @@ -0,0 +1,338 @@ +'use strict'; + +import * as angular from 'angular'; +import { Component } from '@angular/core'; +import { ComponentAuthoring } from '../../../authoringTool/components/component-authoring.component'; +import { ConfigService } from '../../../services/configService'; +import { NodeService } from '../../../services/nodeService'; +import { ProjectAssetService } from '../../../../site/src/app/services/projectAssetService'; +import { TeacherProjectService } from '../../../services/teacherProjectService'; +import { UtilService } from '../../../services/utilService'; +import { Subject, Subscription } from 'rxjs'; +import { debounceTime, distinctUntilChanged } from 'rxjs/operators'; + +@Component({ + selector: 'draw-authoring', + templateUrl: 'draw-authoring.component.html', + styleUrls: ['draw-authoring.component.scss'] +}) +export class DrawAuthoring extends ComponentAuthoring { + width: number; + height: number; + defaultWidth: number = 800; + defaultHeight: number = 600; + stamps: any[] = []; + + inputChange: Subject = new Subject(); + backgroundImageChange: Subject = new Subject(); + canvasWidthChange: Subject = new Subject(); + canvasHeightChange: Subject = new Subject(); + stampImageChange: Subject = new Subject(); + + inputChangeSubscription: Subscription; + backgroundImageChangeSubscription: Subscription; + canvasWidthChangeSubscription: Subscription; + canvasHeightChangeSubscription: Subscription; + stampImageChangeSubscription: Subscription; + + constructor( + protected ConfigService: ConfigService, + protected NodeService: NodeService, + protected ProjectAssetService: ProjectAssetService, + protected ProjectService: TeacherProjectService, + protected UtilService: UtilService + ) { + super(ConfigService, NodeService, ProjectAssetService, ProjectService); + this.inputChangeSubscription = this.inputChange + .pipe(debounceTime(1000), distinctUntilChanged()) + .subscribe(() => { + this.componentChanged(); + }); + this.backgroundImageChangeSubscription = this.backgroundImageChange + .pipe(debounceTime(1000), distinctUntilChanged()) + .subscribe(() => { + this.updateStarterDrawDataBackgroundAndSave(); + }); + this.canvasWidthChangeSubscription = this.canvasWidthChange + .pipe(debounceTime(1000), distinctUntilChanged()) + .subscribe(() => { + this.canvasWidthChanged(); + }); + this.canvasHeightChangeSubscription = this.canvasHeightChange + .pipe(debounceTime(1000), distinctUntilChanged()) + .subscribe(() => { + this.canvasHeightChanged(); + }); + this.stampImageChangeSubscription = this.stampImageChange + .pipe(debounceTime(1000), distinctUntilChanged()) + .subscribe(() => { + this.updateAuthoringComponentContentStampsAndSave(); + }); + } + + ngOnInit() { + super.ngOnInit(); + this.stamps = this.convertStampStringsToStampObjects( + this.authoringComponentContent.stamps.Stamps + ); + } + + ngOnDestroy() { + this.inputChangeSubscription.unsubscribe(); + this.backgroundImageChangeSubscription.unsubscribe(); + this.canvasWidthChangeSubscription.unsubscribe(); + this.canvasHeightChangeSubscription.unsubscribe(); + this.stampImageChangeSubscription.unsubscribe(); + } + + enableAllToolsButtonClicked(): void { + if (this.authoringComponentContent.tools == null) { + this.authoringComponentContent.tools = {}; + } + this.authoringComponentContent.tools.select = true; + this.authoringComponentContent.tools.line = true; + this.authoringComponentContent.tools.shape = true; + this.authoringComponentContent.tools.freeHand = true; + this.authoringComponentContent.tools.text = true; + this.authoringComponentContent.tools.stamp = true; + this.authoringComponentContent.tools.strokeColor = true; + this.authoringComponentContent.tools.fillColor = true; + this.authoringComponentContent.tools.clone = true; + this.authoringComponentContent.tools.strokeWidth = true; + this.authoringComponentContent.tools.sendBack = true; + this.authoringComponentContent.tools.sendForward = true; + this.authoringComponentContent.tools.undo = true; + this.authoringComponentContent.tools.redo = true; + this.authoringComponentContent.tools.delete = true; + this.componentChanged(); + } + + disableAllToolsButtonClicked(): void { + if (this.authoringComponentContent.tools == null) { + this.authoringComponentContent.tools = {}; + } + this.authoringComponentContent.tools.select = false; + this.authoringComponentContent.tools.line = false; + this.authoringComponentContent.tools.shape = false; + this.authoringComponentContent.tools.freeHand = false; + this.authoringComponentContent.tools.text = false; + this.authoringComponentContent.tools.stamp = false; + this.authoringComponentContent.tools.strokeColor = false; + this.authoringComponentContent.tools.fillColor = false; + this.authoringComponentContent.tools.clone = false; + this.authoringComponentContent.tools.strokeWidth = false; + this.authoringComponentContent.tools.sendBack = false; + this.authoringComponentContent.tools.sendForward = false; + this.authoringComponentContent.tools.undo = false; + this.authoringComponentContent.tools.redo = false; + this.authoringComponentContent.tools.delete = false; + this.componentChanged(); + } + + saveStarterDrawData(): void { + if (confirm($localize`Are you sure you want to save the starter drawing?`)) { + this.NodeService.requestStarterState({ nodeId: this.nodeId, componentId: this.componentId }); + } + } + + saveStarterState(starterState: any): void { + this.authoringComponentContent.starterDrawData = starterState; + this.componentChanged(); + } + + deleteStarterDrawData(): void { + if (confirm($localize`Are you sure you want to delete the starter drawing?`)) { + this.authoringComponentContent.starterDrawData = null; + this.componentChanged(); + } + } + + canvasWidthChanged(): void { + this.width = this.authoringComponentContent.width; + this.updateStarterDrawDataWidth(); + this.componentChanged(); + } + + updateStarterDrawDataWidth(): void { + if (this.authoringComponentContent.starterDrawData != null) { + const starterDrawDataJSONObject = angular.fromJson( + this.authoringComponentContent.starterDrawData + ); + if (starterDrawDataJSONObject != null && starterDrawDataJSONObject.dt != null) { + if (this.width == null) { + starterDrawDataJSONObject.dt.width = this.defaultWidth; + } else { + starterDrawDataJSONObject.dt.width = this.width; + } + this.authoringComponentContent.starterDrawData = angular.toJson(starterDrawDataJSONObject); + } + } + } + + canvasHeightChanged(): void { + this.height = this.authoringComponentContent.height; + this.updateStarterDrawDataHeight(); + this.componentChanged(); + } + + updateStarterDrawDataHeight(): void { + if (this.authoringComponentContent.starterDrawData != null) { + const starterDrawDataJSONObject = angular.fromJson( + this.authoringComponentContent.starterDrawData + ); + if (starterDrawDataJSONObject != null && starterDrawDataJSONObject.dt != null) { + if (this.height == null) { + starterDrawDataJSONObject.dt.height = this.defaultHeight; + } else { + starterDrawDataJSONObject.dt.height = this.height; + } + this.authoringComponentContent.starterDrawData = angular.toJson(starterDrawDataJSONObject); + } + } + } + + toolClicked(): void { + this.componentChanged(); + } + + chooseBackgroundImage(): void { + const params = { + isPopup: true, + nodeId: this.nodeId, + componentId: this.componentId, + target: 'background' + }; + this.openAssetChooser(params); + } + + chooseStampImage(stampIndex: number): void { + const params = { + isPopup: true, + nodeId: this.nodeId, + componentId: this.componentId, + target: 'stamp', + targetObject: stampIndex + }; + this.openAssetChooser(params); + } + + assetSelected({ nodeId, componentId, assetItem, target, targetObject }): void { + super.assetSelected({ nodeId, componentId, assetItem, target }); + const fileName = assetItem.fileName; + if (target === 'background') { + this.authoringComponentContent.background = fileName; + this.updateStarterDrawDataBackgroundAndSave(); + } else if (target === 'stamp') { + const stampIndex = targetObject; + this.setStampImage(stampIndex, fileName); + this.updateAuthoringComponentContentStampsAndSave(); + } + } + + updateStarterDrawDataBackgroundAndSave(): void { + this.updateStarterDrawDataBackground(); + this.componentChanged(); + } + + updateStarterDrawDataBackground(): void { + const starterDrawData = this.authoringComponentContent.starterDrawData; + if (starterDrawData != null) { + const starterDrawDataJSON = angular.fromJson(starterDrawData); + if ( + starterDrawDataJSON != null && + starterDrawDataJSON.canvas != null && + starterDrawDataJSON.canvas.backgroundImage != null && + starterDrawDataJSON.canvas.backgroundImage.src != null + ) { + const projectAssetsDirectoryPath = this.ConfigService.getProjectAssetsDirectoryPath(true); + const background = this.authoringComponentContent.background; + const newSrc = projectAssetsDirectoryPath + '/' + background; + starterDrawDataJSON.canvas.backgroundImage.src = newSrc; + this.authoringComponentContent.starterDrawData = angular.toJson(starterDrawDataJSON); + } + } + } + + addStamp(): void { + this.initializeAuthoringComponentContentStampsIfNecessary(); + this.stamps.push(this.createStamp()); + this.updateAuthoringComponentContentStampsAndSave(); + } + + initializeAuthoringComponentContentStampsIfNecessary(): void { + if (this.authoringComponentContent.stamps == null) { + this.authoringComponentContent.stamps = {}; + } + if (this.authoringComponentContent.stamps.Stamps == null) { + this.authoringComponentContent.stamps.Stamps = []; + } + } + + createStamp(image: string = ''): any { + return { image: image }; + } + + updateAuthoringComponentContentStampsAndSave(): void { + this.updateAuthoringComponentContentStamps(); + this.componentChanged(); + } + + updateAuthoringComponentContentStamps(): void { + this.authoringComponentContent.stamps.Stamps = this.convertStampObjectsToStampStrings( + this.stamps + ); + } + + moveStampUp(index: number): void { + if (index != 0) { + const stamp = this.stamps[index]; + this.stamps.splice(index, 1); + this.stamps.splice(index - 1, 0, stamp); + this.updateAuthoringComponentContentStampsAndSave(); + } + } + + moveStampDown(index: number): void { + if (index != this.authoringComponentContent.stamps.Stamps.length - 1) { + const stamp = this.stamps[index]; + this.stamps.splice(index, 1); + this.stamps.splice(index + 1, 0, stamp); + this.updateAuthoringComponentContentStampsAndSave(); + } + } + + deleteStamp(index: number): void { + if ( + confirm( + $localize`Are you sure you want to delete this stamp?\n\n${this.authoringComponentContent.stamps.Stamps[index]}` + ) + ) { + this.stamps.splice(index, 1); + this.updateAuthoringComponentContentStampsAndSave(); + } + } + + setStampImage(index: number, fileName: string): void { + this.stamps[index].image = fileName; + } + + stampChanged(stampImage: string, index: number): void { + this.stampImageChange.next(`${index}-${stampImage}`); + } + + convertStampStringsToStampObjects(stampStrings: string[]): any[] { + const stampObjects: any[] = []; + for (let stampString of stampStrings) { + stampObjects.push(this.createStamp(stampString)); + } + return stampObjects; + } + + convertStampObjectsToStampStrings(stampObjects: any[]): string[] { + const stampStrings: string[] = []; + for (let stampObject of stampObjects) { + stampStrings.push(stampObject.image); + } + return stampStrings; + } +} diff --git a/src/main/webapp/wise5/components/draw/drawAuthoring.ts b/src/main/webapp/wise5/components/draw/drawAuthoring.ts deleted file mode 100644 index 3b4ebccbf5..0000000000 --- a/src/main/webapp/wise5/components/draw/drawAuthoring.ts +++ /dev/null @@ -1,265 +0,0 @@ -'use strict'; - -import * as angular from 'angular'; -import { Directive } from '@angular/core'; -import { EditComponentController } from '../../authoringTool/components/editComponentController'; - -@Directive() -class DrawAuthoringController extends EditComponentController { - width: number; - height: number; - - static $inject = [ - '$filter', - 'ConfigService', - 'NodeService', - 'NotificationService', - 'ProjectAssetService', - 'ProjectService', - 'UtilService' - ]; - - constructor( - $filter, - ConfigService, - NodeService, - NotificationService, - ProjectAssetService, - ProjectService, - UtilService - ) { - super( - $filter, - ConfigService, - NodeService, - NotificationService, - ProjectAssetService, - ProjectService, - UtilService - ); - } - - addStampButtonClicked() { - this.initializeAuthoringComponentContentStampsIfNecessary(); - this.authoringComponentContent.stamps.Stamps.push(''); - this.componentChanged(); - } - - initializeAuthoringComponentContentStampsIfNecessary() { - if (this.authoringComponentContent != null) { - if (this.authoringComponentContent.stamps == null) { - this.authoringComponentContent.stamps = {}; - } - if (this.authoringComponentContent.stamps.Stamps == null) { - this.authoringComponentContent.stamps.Stamps = []; - } - } - } - - moveStampUp(index) { - if (index != 0) { - const stamp = this.authoringComponentContent.stamps.Stamps[index]; - this.authoringComponentContent.stamps.Stamps.splice(index, 1); - this.authoringComponentContent.stamps.Stamps.splice(index - 1, 0, stamp); - this.componentChanged(); - } - } - - moveStampDown(index) { - if (index != this.authoringComponentContent.stamps.Stamps.length - 1) { - const stamp = this.authoringComponentContent.stamps.Stamps[index]; - this.authoringComponentContent.stamps.Stamps.splice(index, 1); - this.authoringComponentContent.stamps.Stamps.splice(index + 1, 0, stamp); - this.componentChanged(); - } - } - - deleteStampClicked(index) { - if ( - confirm( - this.$translate('draw.areYouSureYouWantToDeleteThisStamp') + - '\n\n' + - this.authoringComponentContent.stamps.Stamps[index] - ) - ) { - this.authoringComponentContent.stamps.Stamps.splice(index, 1); - this.componentChanged(); - } - } - - enableAllToolsButtonClicked() { - if (this.authoringComponentContent.tools == null) { - this.authoringComponentContent.tools = {}; - } - this.authoringComponentContent.tools.select = true; - this.authoringComponentContent.tools.line = true; - this.authoringComponentContent.tools.shape = true; - this.authoringComponentContent.tools.freeHand = true; - this.authoringComponentContent.tools.text = true; - this.authoringComponentContent.tools.stamp = true; - this.authoringComponentContent.tools.strokeColor = true; - this.authoringComponentContent.tools.fillColor = true; - this.authoringComponentContent.tools.clone = true; - this.authoringComponentContent.tools.strokeWidth = true; - this.authoringComponentContent.tools.sendBack = true; - this.authoringComponentContent.tools.sendForward = true; - this.authoringComponentContent.tools.undo = true; - this.authoringComponentContent.tools.redo = true; - this.authoringComponentContent.tools.delete = true; - this.componentChanged(); - } - - disableAllToolsButtonClicked() { - if (this.authoringComponentContent.tools == null) { - this.authoringComponentContent.tools = {}; - } - this.authoringComponentContent.tools.select = false; - this.authoringComponentContent.tools.line = false; - this.authoringComponentContent.tools.shape = false; - this.authoringComponentContent.tools.freeHand = false; - this.authoringComponentContent.tools.text = false; - this.authoringComponentContent.tools.stamp = false; - this.authoringComponentContent.tools.strokeColor = false; - this.authoringComponentContent.tools.fillColor = false; - this.authoringComponentContent.tools.clone = false; - this.authoringComponentContent.tools.strokeWidth = false; - this.authoringComponentContent.tools.sendBack = false; - this.authoringComponentContent.tools.sendForward = false; - this.authoringComponentContent.tools.undo = false; - this.authoringComponentContent.tools.redo = false; - this.authoringComponentContent.tools.delete = false; - this.componentChanged(); - } - - saveStarterDrawData() { - if (confirm(this.$translate('draw.areYouSureYouWantToSaveTheStarterDrawing'))) { - this.NodeService.requestStarterState({ nodeId: this.nodeId, componentId: this.componentId }); - } - } - - saveStarterState(starterState) { - this.authoringComponentContent.starterDrawData = starterState; - this.componentChanged(); - } - - deleteStarterDrawData() { - if (confirm(this.$translate('draw.areYouSureYouWantToDeleteTheStarterDrawing'))) { - this.authoringComponentContent.starterDrawData = null; - this.componentChanged(); - } - } - - viewWidthChanged() { - this.width = this.authoringComponentContent.width; - this.updateStarterDrawDataWidth(); - this.componentChanged(); - } - - updateStarterDrawDataWidth() { - if (this.authoringComponentContent.starterDrawData != null) { - const starterDrawDataJSONObject = angular.fromJson( - this.authoringComponentContent.starterDrawData - ); - if (starterDrawDataJSONObject != null && starterDrawDataJSONObject.dt != null) { - starterDrawDataJSONObject.dt.width = this.width; - this.authoringComponentContent.starterDrawData = angular.toJson(starterDrawDataJSONObject); - } - } - } - - viewHeightChanged() { - this.height = this.authoringComponentContent.height; - this.updateStarterDrawDataHeight(); - this.componentChanged(); - } - - updateStarterDrawDataHeight() { - if (this.authoringComponentContent.starterDrawData != null) { - const starterDrawDataJSONObject = angular.fromJson( - this.authoringComponentContent.starterDrawData - ); - if (starterDrawDataJSONObject != null && starterDrawDataJSONObject.dt != null) { - starterDrawDataJSONObject.dt.height = this.height; - this.authoringComponentContent.starterDrawData = angular.toJson(starterDrawDataJSONObject); - } - } - } - - toolClicked() { - this.componentChanged(); - } - - chooseBackgroundImage() { - const params = { - isPopup: true, - nodeId: this.nodeId, - componentId: this.componentId, - target: 'background' - }; - this.openAssetChooser(params); - } - - chooseStampImage(stampIndex) { - const params = { - isPopup: true, - nodeId: this.nodeId, - componentId: this.componentId, - target: 'stamp', - targetObject: stampIndex - }; - this.openAssetChooser(params); - } - - assetSelected({ nodeId, componentId, assetItem, target, targetObject }) { - super.assetSelected({ nodeId, componentId, assetItem, target }); - const fileName = assetItem.fileName; - if (target === 'background') { - this.authoringComponentContent.background = fileName; - this.backgroundChanged(); - } else if (target === 'stamp') { - const stampIndex = targetObject; - this.setStampImage(stampIndex, fileName); - this.backgroundChanged(); - } - } - - backgroundChanged() { - this.updateStarterDrawDataBackground(); - this.componentChanged(); - } - - updateStarterDrawDataBackground() { - const starterDrawData = this.authoringComponentContent.starterDrawData; - if (starterDrawData != null) { - const starterDrawDataJSON = angular.fromJson(starterDrawData); - if ( - starterDrawDataJSON != null && - starterDrawDataJSON.canvas != null && - starterDrawDataJSON.canvas.backgroundImage != null && - starterDrawDataJSON.canvas.backgroundImage.src != null - ) { - const projectAssetsDirectoryPath = this.ConfigService.getProjectAssetsDirectoryPath(true); - const background = this.authoringComponentContent.background; - const newSrc = projectAssetsDirectoryPath + '/' + background; - starterDrawDataJSON.canvas.backgroundImage.src = newSrc; - this.authoringComponentContent.starterDrawData = angular.toJson(starterDrawDataJSON); - } - } - } - - setStampImage(index, fileName) { - this.authoringComponentContent.stamps.Stamps[index] = fileName; - } -} - -const DrawAuthoring = { - bindings: { - nodeId: '@', - componentId: '@' - }, - controller: DrawAuthoringController, - controllerAs: 'drawController', - templateUrl: 'wise5/components/draw/authoring.html' -}; - -export default DrawAuthoring; diff --git a/src/main/webapp/wise5/components/draw/drawAuthoringComponentModule.ts b/src/main/webapp/wise5/components/draw/drawAuthoringComponentModule.ts index 4886711294..4c29265168 100644 --- a/src/main/webapp/wise5/components/draw/drawAuthoringComponentModule.ts +++ b/src/main/webapp/wise5/components/draw/drawAuthoringComponentModule.ts @@ -1,15 +1,18 @@ 'use strict'; import * as angular from 'angular'; -import { downgradeInjectable } from '@angular/upgrade/static'; +import { downgradeComponent, downgradeInjectable } from '@angular/upgrade/static'; import { DrawService } from './drawService'; -import DrawAuthoring from './drawAuthoring'; import { EditDrawAdvancedComponent } from './edit-draw-advanced/edit-draw-advanced.component'; +import { DrawAuthoring } from './draw-authoring/draw-authoring.component'; const drawAuthoringComponentModule = angular .module('drawAuthoringComponentModule', ['pascalprecht.translate']) .service('DrawService', downgradeInjectable(DrawService)) - .component('drawAuthoring', DrawAuthoring) + .directive( + 'drawAuthoring', + downgradeComponent({ component: DrawAuthoring }) as angular.IDirectiveFactory + ) .component('editDrawAdvanced', EditDrawAdvancedComponent) .config([ '$translatePartialLoaderProvider', From 9a0742017bbc7cc06f6e0941fe0cfcd674c3a601 Mon Sep 17 00:00:00 2001 From: Geoffrey Kwan Date: Tue, 19 Jan 2021 18:04:37 -0500 Subject: [PATCH 17/26] Fixed a problem where the Draw JSON authoring would display null. #2891 --- .../edit-draw-advanced/edit-draw-advanced.component.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/webapp/wise5/components/draw/edit-draw-advanced/edit-draw-advanced.component.html b/src/main/webapp/wise5/components/draw/edit-draw-advanced/edit-draw-advanced.component.html index 2a83f3d275..bd15acab2e 100644 --- a/src/main/webapp/wise5/components/draw/edit-draw-advanced/edit-draw-advanced.component.html +++ b/src/main/webapp/wise5/components/draw/edit-draw-advanced/edit-draw-advanced.component.html @@ -106,11 +106,11 @@ -
+
{{ ::'importWorkAsBackground' | translate }} @@ -119,5 +119,5 @@
- +
\ No newline at end of file From a3913c95081adc76da1755f59b2a18d5f1724c87 Mon Sep 17 00:00:00 2001 From: breity Date: Tue, 19 Jan 2021 15:39:03 -0800 Subject: [PATCH 18/26] Fixed a layout bug causing authoring content overflow container as screen narrows. #2885 --- .../webapp/wise5/authoringTool/node/node.html | 151 +++++++++--------- .../wise5/authoringTool/project/project.html | 29 ++-- 2 files changed, 89 insertions(+), 91 deletions(-) diff --git a/src/main/webapp/wise5/authoringTool/node/node.html b/src/main/webapp/wise5/authoringTool/node/node.html index 80daf060aa..26492e8ace 100644 --- a/src/main/webapp/wise5/authoringTool/node/node.html +++ b/src/main/webapp/wise5/authoringTool/node/node.html @@ -9,82 +9,83 @@
- - arrow_back - + + arrow_back + + {{ ::'backToProjectView' | translate }} + + + {{ ::'backToStepView' | translate }} + + + + add + {{ ::'addNewComponent' | translate }} + + + system_update_alt + {{ ::'importComponent' | translate }} + + + redo + {{ ::'move' | translate }} + + + content_copy + {{ ::'copy' | translate }} + + + delete + {{ ::'DELETE' | translate }} + + - {{ ::'backToProjectView' | translate }} - - - {{ ::'backToStepView' | translate }} - - - - add - {{ ::'addNewComponent' | translate }} - - - system_update_alt - {{ ::'importComponent' | translate }} - - - redo - {{ ::'move' | translate }} - - - content_copy - {{ ::'copy' | translate }} - - - delete - {{ ::'DELETE' | translate }} - - - message - {{ ::'editStepRubric' | translate }} - - - build - {{ ::'ADVANCED' | translate }} - - - undo - {{ ::'undo' | translate }} - -
+ message + {{ ::'editStepRubric' | translate }} + + + build + {{ ::'ADVANCED' | translate }} + + + undo + {{ ::'undo' | translate }} + + diff --git a/src/main/webapp/wise5/authoringTool/project/project.html b/src/main/webapp/wise5/authoringTool/project/project.html index c7956ae929..de2fa81116 100644 --- a/src/main/webapp/wise5/authoringTool/project/project.html +++ b/src/main/webapp/wise5/authoringTool/project/project.html @@ -1,10 +1,10 @@
-
-
+
+
{{projectController.currentAuthorsMessage}}
  @@ -80,17 +80,11 @@ height: auto !important; white-space: pre-line; } - .preview-toolbar { - float:right; - } - [dir=rtl] .preview-toolbar { - float:left; - }
- +
@@ -175,17 +169,20 @@ build {{ ::'ADVANCED' | translate }} - -
- + visibility - {{ ::'previewProject' | translate }} + {{ ::'previewProject' | translate }} + - visibility_off - {{ ::'previewProjectWithoutConstraints' | translate }} + {{ + ::'previewProjectWithoutConstraints' | translate }}
From 7bf7e54ab35f338544d76111fc43fa3d755dd603 Mon Sep 17 00:00:00 2001 From: breity Date: Wed, 20 Jan 2021 10:53:03 -0800 Subject: [PATCH 19/26] Made some minor Concept Map authoring style updates and simplified component class names. #2885 --- src/main/webapp/site/src/messages.xlf | 78 +++++++++---------- .../concept-map-authoring.component.html | 61 ++++++--------- .../concept-map-authoring.component.scss | 32 ++++---- 3 files changed, 77 insertions(+), 94 deletions(-) diff --git a/src/main/webapp/site/src/messages.xlf b/src/main/webapp/site/src/messages.xlf index 0ea014d3a1..a65d722f3f 100644 --- a/src/main/webapp/site/src/messages.xlf +++ b/src/main/webapp/site/src/messages.xlf @@ -6021,7 +6021,7 @@ Enter Prompt Here ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html - 7 + 6 ../../wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.html @@ -6036,22 +6036,22 @@ Background Image (Optional) ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html - 14 + 13 Choose an Image ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html - 23 + 21 ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html - 100 + 96 ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html - 103 + 99 ../../wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.html @@ -6062,104 +6062,104 @@ Stretch Background ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html - 34 + 32 Canvas Width (Optional) ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html - 40 + 38 Canvas Height (Optional) ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html - 47 + 45 Show Node Labels ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html - 59 + 57 Nodes ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html - 64 + 62 Add Node ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html - 69 + 66 ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html - 72 + 69 There are no nodes. Click the "Add Node" button to add a node. ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html - 78 + 75 Label ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html - 85 + 82 ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html - 179 + 171 File Name ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html - 91 + 88 Width ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html - 108 + 104 Height ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html - 115 + 111 Move Up ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html - 126 + 121 ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html - 129 + 124 ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html - 195 + 186 ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html - 198 + 189 app/authoring-tool/edit-component-tags/edit-component-tags.component.html @@ -6174,19 +6174,19 @@ Move Down ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html - 138 + 132 ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html - 141 + 135 ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html - 207 + 197 ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html - 210 + 200 app/authoring-tool/edit-component-tags/edit-component-tags.component.html @@ -6201,19 +6201,19 @@ Delete ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html - 149 + 142 ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html - 152 + 145 ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html - 218 + 207 ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html - 221 + 210 app/authoring-tool/edit-component-tags/edit-component-tags.component.html @@ -6232,54 +6232,54 @@ Links ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html - 158 + 151 Add Link ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html - 163 + 155 ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html - 166 + 158 There are no links. Click the "Add Link" button to add a link. ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html - 172 + 164 Color ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html - 185 + 177 Save Starter Concept Map ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html - 231 + 219 ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html - 234 + 222 Delete Starter Concept Map ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html - 242 + 229 ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html - 245 + 232 diff --git a/src/main/webapp/wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html b/src/main/webapp/wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html index 8e4ecc03d2..4a4d779517 100644 --- a/src/main/webapp/wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html +++ b/src/main/webapp/wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html @@ -1,7 +1,6 @@ - + Prompt - - -
- - Background Image (Optional) - +
+ + Prompt + - -
-
- - Stretch Background - -
-
- - Canvas Width (Optional) - - - - Canvas Height (Optional) - - -
-
- - Show Node Labels - -
-
- Nodes - -
-
-

There are no nodes. Click the "Add Node" button to add a node.

-
-
- - Label - - - - File Name - - - - - Width - - - - Height - - - - - -
-
- Links - -
-
-

There are no links. Click the "Add Link" button to add a link.

-
- -
- - +
+
+ + Background Image (Optional) + + + +
+ + Stretch Background + +
+
+ + Canvas Width (Optional) + + + + Canvas Height (Optional) + + +
+
+
+ Nodes + +
+
+

There are no nodes. Click the "Add Node" button to add a node.

+
+
+
+ + Node Label + + +
+ + Image + + + +
+
+ + Width + + + + Height + + +
+ +
+ + + +
+
+
+
+ + Show Node Labels + +
+
+
+
+ Links + +
+
+

There are no links. Click the "Add Link" button to add a link.

+
+
+
+ + Link Label + + + + Color + + + +
+ + + +
+
+
+
+
+ (Optional) Create a starting state for the concept map by editing the "Student Preview" below and then saving here: + + +
diff --git a/src/main/webapp/wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.scss b/src/main/webapp/wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.scss index 77fcf91724..d61fbb2e2c 100644 --- a/src/main/webapp/wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.scss +++ b/src/main/webapp/wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.scss @@ -1,3 +1,7 @@ +@import '~style/abstracts/variables', '~style/themes/default'; + +$notice-bg: map-get($default-colors, notice-bg); + .prompt { width: 100%; } @@ -7,15 +11,14 @@ width: 360px; } -button { - margin-left: 10px; - margin-right: 10px; -} - .canvas-dimension { - width: 20%; margin-top: 20px; - margin-right: 20px; +} + +.add-content { + padding: 16px; + border: 2px solid #dddddd; + border-radius: $card-border-radius; } .node-labels { @@ -23,44 +26,26 @@ button { margin-bottom: 10px; } -.add-nodes { - margin-top: 20px; - margin-bottom: 20px; -} +.content-item { + border: 1px solid #dddddd; + background-color: $notice-bg; + border-radius: $card-border-radius; + padding: 8px; + margin-bottom: 8px; -.node-row { - width: 100%; + &:last-of-type { + margin-bottom: 0; + } } -.node-label { - width: 30%; - margin-right: 20px; -} - -.node-file-name { - width: 30%; +.content-item-setting { + padding: 0 8px; } .node-dimension { - width: 6%; - margin-left: 10px; - margin-right: 10px; -} - -.link-row { - width: 100%; -} - -.link-label { - width: 30%; - margin-right: 20px; -} - -.link-color { - width: 30%; + max-width: 80px; } .starter-concept-map { - margin-top: 20px; - margin-bottom: 20px; + margin-bottom: 16px; } \ No newline at end of file From 2bd40a5276d5a1aee704e68d2a41a96f4222d85c Mon Sep 17 00:00:00 2001 From: Geoffrey Kwan Date: Thu, 21 Jan 2021 12:23:07 -0500 Subject: [PATCH 21/26] Upgraded Match authoring to Angular. #2893 --- .../src/app/teacher-hybrid-angular.module.ts | 2 + .../wise5/components/match/authoring.html | 327 --------------- .../match-authoring.component.html | 221 ++++++++++ .../match-authoring.component.scss | 55 +++ .../match-authoring.component.ts | 337 +++++++++++++++ .../wise5/components/match/matchAuthoring.ts | 396 ------------------ .../match/matchAuthoringComponentModule.ts | 9 +- 7 files changed, 621 insertions(+), 726 deletions(-) delete mode 100644 src/main/webapp/wise5/components/match/authoring.html create mode 100644 src/main/webapp/wise5/components/match/match-authoring/match-authoring.component.html create mode 100644 src/main/webapp/wise5/components/match/match-authoring/match-authoring.component.scss create mode 100644 src/main/webapp/wise5/components/match/match-authoring/match-authoring.component.ts delete mode 100644 src/main/webapp/wise5/components/match/matchAuthoring.ts diff --git a/src/main/webapp/site/src/app/teacher-hybrid-angular.module.ts b/src/main/webapp/site/src/app/teacher-hybrid-angular.module.ts index 64ea6c1c44..a044dd400c 100644 --- a/src/main/webapp/site/src/app/teacher-hybrid-angular.module.ts +++ b/src/main/webapp/site/src/app/teacher-hybrid-angular.module.ts @@ -49,6 +49,7 @@ import { HtmlAuthoring } from '../../../wise5/components/html/html-authoring/htm import { OutsideUrlAuthoring } from '../../../wise5/components/outsideURL/outside-url-authoring/outside-url-authoring.component'; import { MultipleChoiceAuthoring } from '../../../wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component'; import { ConceptMapAuthoring } from '../../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component'; +import { MatchAuthoring } from '../../../wise5/components/match/match-authoring/match-authoring.component'; @NgModule({ declarations: [ @@ -70,6 +71,7 @@ import { ConceptMapAuthoring } from '../../../wise5/components/conceptMap/concep EditOutsideUrlAdvancedComponent, HtmlAuthoring, ManageStudentsComponent, + MatchAuthoring, MilestonesComponent, MilestoneReportDataComponent, MultipleChoiceAuthoring, diff --git a/src/main/webapp/wise5/components/match/authoring.html b/src/main/webapp/wise5/components/match/authoring.html deleted file mode 100644 index 42baa82f50..0000000000 --- a/src/main/webapp/wise5/components/match/authoring.html +++ /dev/null @@ -1,327 +0,0 @@ -
-
-
-
- - {{ ::'match.studentCanCreateChoices' | translate }} - -
-
- - {{ ::'match.showSourceBucketOnTheLeftAndTargetBucketsOnTheRight' | translate }} - -
-
- - {{ ::'SHOW_SAVE_BUTTON' | translate }} - -
-
- - {{ ::'SHOW_SUBMIT_BUTTON' | translate }} - -
-
- - {{ ::'match.showPrivateNotesAsChoices' | translate }} - -
-
- - - - -
-
- - - - -
-
-
- - - add - - {{ ::'ADD_CONNECTED_COMPONENT' | translate }} - - -
-
-
- - - - - {{ ::matchController.getNodePositionAndTitleByNodeId(item.$key) }} - - - - - - - - {{ ::(componentIndex + 1) }}. {{ ::component.type }} - - ({{ ::'thisComponent' | translate }}) - - - - - - - - - {{ ::'importWork' | translate }} - - - {{ ::'showWork' | translate }} - - - - - - - delete - - {{ ::'DELETE' | translate }} - - - -
-
-
- -
-
-
- - - - -
- -
{{ ::'match.choices' | translate }}
- - add - - {{ ::'match.addChoice' | translate }} - - -
-
{{ ::'match.thereAreNoChoices' | translate }}
-
- - - - - - insert_photo - - {{ ::'chooseAnImage' | translate }} - - - - arrow_upward - - {{ ::'UP' | translate }} - - - - arrow_downward - - {{ ::'DOWN' | translate }} - - - - delete - - {{ ::'match.deleteChoice' | translate }} - - -
-
-
- - - - - -
{{ ::'match.targetBuckets' | translate }}
- - add - - {{ ::'match.addTargetBucket' | translate }} - - -
-
{{ ::'match.thereAreNoTargetBuckets' | translate }}
-
- - - - - - insert_photo - - {{ ::'chooseAnImage' | translate }} - - - - arrow_upward - - {{ ::'UP' | translate }} - - - - arrow_downward - - {{ ::'DOWN' | translate }} - - - - delete - - {{ ::'match.deleteBucket' | translate }} - - -
-
-
-
{{ ::'FEEDBACK' | translate }}
- - {{ ::'match.choicesNeedToBeOrderedWithinBuckets' | translate }} - -
-

- {{matchController.getBucketNameById(bucketFeedback.bucketId)}} -

-
- - - {{matchController.getChoiceTextById(choiceFeedback.choiceId)}} - - -
- - - - - - - {{ ::'IS_CORRECT' | translate }} - - -
-
- - - - - - - - -
-
-
-
-
-
-
diff --git a/src/main/webapp/wise5/components/match/match-authoring/match-authoring.component.html b/src/main/webapp/wise5/components/match/match-authoring/match-authoring.component.html new file mode 100644 index 0000000000..d4b218ad2d --- /dev/null +++ b/src/main/webapp/wise5/components/match/match-authoring/match-authoring.component.html @@ -0,0 +1,221 @@ + + Prompt + + +
+ Choices + +
+ There are no choices. Click the "Add Choice" button to add a choice. +
+
+ + Choice Name + + + + + + +
+
+
+ + Source Bucket Name + + +
+ Target Buckets + +
+ There are no target buckets. Click the "Add Target Bucket" button to add a bucket. +
+
+ + Target Bucket Name + + + + + + +
+
+
+
+

Feedback

+ + Choices need to be ordered within buckets + +
+
+

+ Bucket Name: {{getBucketNameById(bucketFeedback.bucketId)}} +

+
+ + Choice: {{getChoiceTextById(choiceFeedback.choiceId)}} + +
+ + + Is Correct + +
+
+ + Position + + + + Incorrect Position Feedback + + +
+
+
+
\ No newline at end of file diff --git a/src/main/webapp/wise5/components/match/match-authoring/match-authoring.component.scss b/src/main/webapp/wise5/components/match/match-authoring/match-authoring.component.scss new file mode 100644 index 0000000000..55319f11c3 --- /dev/null +++ b/src/main/webapp/wise5/components/match/match-authoring/match-authoring.component.scss @@ -0,0 +1,55 @@ +@import '~style/abstracts/variables', '~style/themes/default'; + +$notice-bg: map-get($default-colors, notice-bg); + +.prompt { + width: 100%; +} + +.section { + border: 2px solid #dddddd; + border-radius: 5px; + margin-bottom: 10px; + padding: 20px 20px 10px 20px; +} + +.authoring-button { + margin-left: 10px; + margin-right: 10px; +} + +.add-button { + margin-bottom: 10px; +} + +.name { + width: 60%; + margin-right: 10px; +} + +.choice-ordered-checkbox { + margin-bottom: 10px; +} + +.choice-feedback-container { + background: $notice-bg; + border: 1px solid #dddddd; + border-radius: 5px; + padding: 20px; + margin: 10px; +} + +.feedback { + width: 80%; + margin-right: 20px; +} + +.position { + width: 10%; + margin-right: 20px; +} + +.incorrect-position-feedback { + width: 80%; + margin-right: 20px; +} diff --git a/src/main/webapp/wise5/components/match/match-authoring/match-authoring.component.ts b/src/main/webapp/wise5/components/match/match-authoring/match-authoring.component.ts new file mode 100644 index 0000000000..83f0a48ffd --- /dev/null +++ b/src/main/webapp/wise5/components/match/match-authoring/match-authoring.component.ts @@ -0,0 +1,337 @@ +import { Component } from '@angular/core'; +import { Subject, Subscription } from 'rxjs'; +import { debounceTime, distinctUntilChanged } from 'rxjs/operators'; +import { ProjectAssetService } from '../../../../site/src/app/services/projectAssetService'; +import { ComponentAuthoring } from '../../../authoringTool/components/component-authoring.component'; +import { ConfigService } from '../../../services/configService'; +import { NodeService } from '../../../services/nodeService'; +import { TeacherProjectService } from '../../../services/teacherProjectService'; +import { UtilService } from '../../../services/utilService'; +import { MatchService } from '../matchService'; + +@Component({ + selector: 'match-authoring', + templateUrl: 'match-authoring.component.html', + styleUrls: ['match-authoring.component.scss'] +}) +export class MatchAuthoring extends ComponentAuthoring { + defaultSourceBucketId: string = '0'; + + inputChange: Subject = new Subject(); + feedbackChange: Subject = new Subject(); + + inputChangeSubscription: Subscription; + feedbackChangeSubscription: Subscription; + + constructor( + protected ConfigService: ConfigService, + private MatchService: MatchService, + protected NodeService: NodeService, + protected ProjectAssetService: ProjectAssetService, + protected ProjectService: TeacherProjectService, + protected UtilService: UtilService + ) { + super(ConfigService, NodeService, ProjectAssetService, ProjectService); + this.inputChangeSubscription = this.inputChange + .pipe(debounceTime(1000), distinctUntilChanged()) + .subscribe(() => { + this.componentChanged(); + }); + this.feedbackChangeSubscription = this.feedbackChange + .pipe(debounceTime(1000), distinctUntilChanged()) + .subscribe(() => { + this.turnOnSubmitButtonIfFeedbackExists(); + this.componentChanged(); + }); + } + + ngOnDestroy() { + this.inputChangeSubscription.unsubscribe(); + this.feedbackChangeSubscription.unsubscribe(); + } + + turnOnSubmitButtonIfFeedbackExists() { + const show = this.componentHasFeedback(); + if (show) { + this.setShowSubmitButtonValue(show); + } + } + + addChoice(): void { + const newChoice = { + id: this.UtilService.generateKey(10), + value: '', + type: 'choice' + }; + this.authoringComponentContent.choices.push(newChoice); + this.addChoiceToFeedback(newChoice.id); + this.componentChanged(); + } + + addBucket(): void { + const newBucket = { + id: this.UtilService.generateKey(10), + value: '', + type: 'bucket' + }; + this.authoringComponentContent.buckets.push(newBucket); + this.addBucketToFeedback(newBucket.id); + this.componentChanged(); + } + + moveChoiceUp(index: number): void { + if (index != 0) { + this.moveChoiceUpInChoices(index); + this.moveChoiceUpInAllBucketFeedback(index); + this.componentChanged(); + } + } + + moveChoiceDown(index: number): void { + if (index < this.authoringComponentContent.choices.length - 1) { + this.moveChoiceDownInChoices(index); + this.moveChoiceDownInAllBucketFeedback(index); + this.componentChanged(); + } + } + + moveChoiceUpInChoices(index: number) { + this.moveChoiceInChoices(index, -1); + } + + moveChoiceDownInChoices(index: number) { + this.moveChoiceInChoices(index, 1); + } + + moveChoiceInChoices(index: number, amountToShift: number) { + const choice = this.authoringComponentContent.choices[index]; + this.authoringComponentContent.choices.splice(index, 1); + this.authoringComponentContent.choices.splice(index + amountToShift, 0, choice); + } + + moveChoiceUpInAllBucketFeedback(index: number) { + this.moveChoiceInAllBucketFeedback(index, -1); + } + + moveChoiceDownInAllBucketFeedback(index: number) { + this.moveChoiceInAllBucketFeedback(index, 1); + } + + moveChoiceInAllBucketFeedback(index: number, amountToShift: number) { + const feedback = this.authoringComponentContent.feedback; + for (const bucketFeedbackObj of feedback) { + const bucketFeedbackChoices = bucketFeedbackObj.choices; + const tempChoice = bucketFeedbackChoices[index]; + bucketFeedbackChoices.splice(index, 1); + bucketFeedbackChoices.splice(index + amountToShift, 0, tempChoice); + } + } + + deleteChoice(index: number): void { + if (confirm($localize`Are you sure you want to delete this choice?`)) { + const deletedChoice = this.authoringComponentContent.choices.splice(index, 1); + this.removeChoiceFromFeedback(deletedChoice[0].id); + this.componentChanged(); + } + } + + moveBucketUp(index: number): void { + if (index > 0) { + this.moveBucketUpInBuckets(index); + this.moveBucketUpInBucketFeedback(index); + this.componentChanged(); + } + } + + moveBucketDown(index: number): void { + if (index < this.authoringComponentContent.buckets.length - 1) { + this.moveBucketDownInBuckets(index); + this.moveBucketDownInBucketFeedback(index); + this.componentChanged(); + } + } + + moveBucketUpInBuckets(index: number) { + this.moveBucketInBuckets(index, -1); + } + + moveBucketDownInBuckets(index: number) { + this.moveBucketInBuckets(index, 1); + } + + moveBucketInBuckets(index: number, amountToShift: number) { + const bucket = this.authoringComponentContent.buckets[index]; + this.authoringComponentContent.buckets.splice(index, 1); + this.authoringComponentContent.buckets.splice(index + amountToShift, 0, bucket); + } + + moveBucketUpInBucketFeedback(index: number) { + this.moveBucketInBucketFeedback(index, -1); + } + + moveBucketDownInBucketFeedback(index: number) { + this.moveBucketInBucketFeedback(index, 1); + } + + moveBucketInBucketFeedback(index: number, amountToShift: number) { + // the bucket feedback index for authored buckets starts at 1 because the source bucket is at 0 + const bucketFeedbackIndex = index + 1; + const bucketFeedbackObj = this.authoringComponentContent.feedback[bucketFeedbackIndex]; + this.authoringComponentContent.feedback.splice(bucketFeedbackIndex, 1); + this.authoringComponentContent.feedback.splice( + bucketFeedbackIndex + amountToShift, + 0, + bucketFeedbackObj + ); + } + + deleteBucket(index: number): void { + if (confirm($localize`Are you sure you want to delete this bucket?`)) { + const deletedBucket = this.authoringComponentContent.buckets.splice(index, 1); + if (deletedBucket != null && deletedBucket.length > 0) { + this.removeBucketFromFeedback(deletedBucket[0].id); + } + this.componentChanged(); + } + } + + addChoiceToFeedback(choiceId: string): void { + const feedback = this.authoringComponentContent.feedback; + for (const bucketFeedback of feedback) { + const feedbackText = ''; + const isCorrect = false; + bucketFeedback.choices.push(this.createFeedbackObject(choiceId, feedbackText, isCorrect)); + } + } + + addBucketToFeedback(bucketId: string): void { + const feedback = this.authoringComponentContent.feedback; + const bucket = { + bucketId: bucketId, + choices: [] + }; + const choices = this.authoringComponentContent.choices; + for (const choice of choices) { + const choiceId = choice.id; + const feedbackText = ''; + const isCorrect = false; + bucket.choices.push(this.createFeedbackObject(choiceId, feedbackText, isCorrect)); + } + feedback.push(bucket); + } + + createFeedbackObject( + choiceId: string, + feedback: string, + isCorrect: boolean, + position: number = null, + incorrectPositionFeedback: string = null + ): any { + return { + choiceId: choiceId, + feedback: feedback, + isCorrect: isCorrect, + position: position, + incorrectPositionFeedback: incorrectPositionFeedback + }; + } + + removeChoiceFromFeedback(choiceId: string): void { + const feedback = this.authoringComponentContent.feedback; + for (const bucketFeedback of feedback) { + const choices = bucketFeedback.choices; + for (let c = 0; c < choices.length; c++) { + const choice = choices[c]; + if (choiceId === choice.choiceId) { + choices.splice(c, 1); + break; + } + } + } + } + + removeBucketFromFeedback(bucketId: string): void { + const feedback = this.authoringComponentContent.feedback; + for (let f = 0; f < feedback.length; f++) { + const bucketFeedback = feedback[f]; + if (bucketFeedback != null) { + if (bucketId === bucketFeedback.bucketId) { + feedback.splice(f, 1); + break; + } + } + } + } + + componentHasFeedback(): boolean { + const feedback = this.authoringComponentContent.feedback; + for (const tempFeedback of feedback) { + const tempChoices = tempFeedback.choices; + for (const tempChoice of tempChoices) { + if (tempChoice.isCorrect || (tempChoice.feedback != null && tempChoice.feedback != '')) { + return true; + } + } + } + return false; + } + + isCorrectClicked(feedback: any): void { + if (!feedback.isCorrect) { + delete feedback.position; + delete feedback.incorrectPositionFeedback; + } + this.turnOnSubmitButtonIfFeedbackExists(); + this.componentChanged(); + } + + chooseChoiceAsset(choice: any): void { + const params = { + isPopup: true, + nodeId: this.nodeId, + componentId: this.componentId, + target: 'choice', + targetObject: choice + }; + this.openAssetChooser(params); + } + + chooseBucketAsset(bucket: any): void { + const params = { + isPopup: true, + nodeId: this.nodeId, + componentId: this.componentId, + target: 'bucket', + targetObject: bucket + }; + this.openAssetChooser(params); + } + + assetSelected({ nodeId, componentId, assetItem, target, targetObject }): void { + super.assetSelected({ nodeId, componentId, assetItem, target }); + if (target === 'choice' || target === 'bucket') { + targetObject.value = ''; + this.componentChanged(); + } + } + + getChoiceTextById(choiceId: string): string { + const choice = this.MatchService.getChoiceById( + choiceId, + this.authoringComponentContent.choices + ); + return choice ? choice.value : null; + } + + getBucketNameById(bucketId: string): string { + if (bucketId === this.defaultSourceBucketId) { + const choicesLabel = this.authoringComponentContent.choicesLabel; + return choicesLabel ? choicesLabel : $localize`Choices`; + } + const bucket = this.MatchService.getBucketById( + bucketId, + this.authoringComponentContent.buckets + ); + return bucket ? bucket.value : null; + } +} diff --git a/src/main/webapp/wise5/components/match/matchAuthoring.ts b/src/main/webapp/wise5/components/match/matchAuthoring.ts deleted file mode 100644 index bf233fa5eb..0000000000 --- a/src/main/webapp/wise5/components/match/matchAuthoring.ts +++ /dev/null @@ -1,396 +0,0 @@ -'use strict'; - -import { Directive } from '@angular/core'; -import { EditComponentController } from '../../authoringTool/components/editComponentController'; - -@Directive() -class MatchAuthoringController extends EditComponentController { - defaultSourceBucketId: string = '0'; - - static $inject = [ - '$filter', - 'ConfigService', - 'MatchService', - 'NodeService', - 'NotebookService', - 'NotificationService', - 'ProjectAssetService', - 'ProjectService', - 'UtilService' - ]; - - constructor( - $filter, - ConfigService, - private MatchService, - NodeService, - private NotebookService, - NotificationService, - protected ProjectAssetService, - ProjectService, - UtilService - ) { - super( - $filter, - ConfigService, - NodeService, - NotificationService, - ProjectAssetService, - ProjectService, - UtilService - ); - } - - addChoice(): void { - const newChoice = { - id: this.UtilService.generateKey(10), - value: '', - type: 'choice' - }; - this.authoringComponentContent.choices.push(newChoice); - this.addChoiceToFeedback(newChoice.id); - this.componentChanged(); - } - - addBucket(): void { - const newBucket = { - id: this.UtilService.generateKey(10), - value: '', - type: 'bucket' - }; - this.authoringComponentContent.buckets.push(newBucket); - this.addBucketToFeedback(newBucket.id); - this.componentChanged(); - } - - moveChoiceUp(index: number): void { - if (index != 0) { - const choice = this.authoringComponentContent.choices[index]; - this.authoringComponentContent.choices.splice(index, 1); - this.authoringComponentContent.choices.splice(index - 1, 0, choice); - const feedback = this.authoringComponentContent.feedback; - if (feedback != null) { - for (const bucketFeedback of feedback) { - const bucketFeedbackChoices = bucketFeedback.choices; - if (bucketFeedbackChoices != null) { - const tempChoice = bucketFeedbackChoices[index]; - if (tempChoice != null) { - bucketFeedbackChoices.splice(index, 1); - bucketFeedbackChoices.splice(index - 1, 0, tempChoice); - } - } - } - } - this.componentChanged(); - } - } - - moveChoiceDown(index: number): void { - if (index < this.authoringComponentContent.choices.length - 1) { - const choice = this.authoringComponentContent.choices[index]; - this.authoringComponentContent.choices.splice(index, 1); - this.authoringComponentContent.choices.splice(index + 1, 0, choice); - const feedback = this.authoringComponentContent.feedback; - if (feedback != null) { - for (const bucketFeedback of feedback) { - const bucketFeedbackChoices = bucketFeedback.choices; - if (bucketFeedbackChoices != null) { - const tempChoice = bucketFeedbackChoices[index]; - if (tempChoice != null) { - bucketFeedbackChoices.splice(index, 1); - bucketFeedbackChoices.splice(index + 1, 0, tempChoice); - } - } - } - } - this.componentChanged(); - } - } - - deleteChoice(index: number): void { - if (confirm(this.$translate('match.areYouSureYouWantToDeleteThisChoice'))) { - const deletedChoice = this.authoringComponentContent.choices.splice(index, 1); - if (deletedChoice != null && deletedChoice.length > 0) { - this.removeChoiceFromFeedback(deletedChoice[0].id); - } - this.componentChanged(); - } - } - - moveBucketUp(index: number): void { - if (index > 0) { - const bucket = this.authoringComponentContent.buckets[index]; - this.authoringComponentContent.buckets.splice(index, 1); - this.authoringComponentContent.buckets.splice(index - 1, 0, bucket); - - /* - * Remember the bucket feedback. The first element of the feedback - * contains the origin bucket. The first authored bucket is located - * at index 1. This means we need the index of the bucket feedback - * that we want is located at index + 1. - */ - const bucketFeedback = this.authoringComponentContent.feedback[index + 1]; - if (bucketFeedback != null) { - this.authoringComponentContent.feedback.splice(index + 1, 1); - this.authoringComponentContent.feedback.splice(index, 0, bucketFeedback); - } - this.componentChanged(); - } - } - - moveBucketDown(index: number): void { - if (index < this.authoringComponentContent.buckets.length - 1) { - const bucket = this.authoringComponentContent.buckets[index]; - this.authoringComponentContent.buckets.splice(index, 1); - this.authoringComponentContent.buckets.splice(index + 1, 0, bucket); - - /* - * Remember the bucket feedback. The first element of the feedback - * contains the origin bucket. The first authored bucket is located - * at index 1. This means we need the index of the bucket feedback - * that we want is located at index + 1. - */ - const bucketFeedback = this.authoringComponentContent.feedback[index + 1]; - if (bucketFeedback != null) { - this.authoringComponentContent.feedback.splice(index + 1, 1); - this.authoringComponentContent.feedback.splice(index + 2, 0, bucketFeedback); - } - this.componentChanged(); - } - } - - /** - * Delete a bucket - * @param index the index of the bucket in the bucket array - */ - deleteBucket(index: number): void { - if (confirm(this.$translate('match.areYouSureYouWantToDeleteThisBucket'))) { - const deletedBucket = this.authoringComponentContent.buckets.splice(index, 1); - if (deletedBucket != null && deletedBucket.length > 0) { - this.removeBucketFromFeedback(deletedBucket[0].id); - } - this.componentChanged(); - } - } - - addChoiceToFeedback(choiceId: string): void { - const feedback = this.authoringComponentContent.feedback; - if (feedback != null) { - for (const bucketFeedback of feedback) { - const feedbackText = ''; - const isCorrect = false; - bucketFeedback.choices.push(this.createFeedbackObject(choiceId, feedbackText, isCorrect)); - } - } - } - - addBucketToFeedback(bucketId: string): void { - const feedback = this.authoringComponentContent.feedback; - if (feedback != null) { - const bucket = { - bucketId: bucketId, - choices: [] - }; - const choices = this.authoringComponentContent.choices; - for (const choice of choices) { - const choiceId = choice.id; - const feedbackText = ''; - const isCorrect = false; - bucket.choices.push(this.createFeedbackObject(choiceId, feedbackText, isCorrect)); - } - feedback.push(bucket); - } - } - - /** - * Create a feedback object - * @param choiceId the choice id - * @param feedback the feedback - * @param isCorrect whether the choice is correct - * @param position (optional) the position - * @param incorrectPositionFeedback (optional) the feedback for when the - * choice is in the correct but wrong position - * @returns the feedback object - */ - createFeedbackObject( - choiceId: string, - feedback: string, - isCorrect: boolean, - position: number = null, - incorrectPositionFeedback: string = null - ): any { - return { - choiceId: choiceId, - feedback: feedback, - isCorrect: isCorrect, - position: position, - incorrectPositionFeedback: incorrectPositionFeedback - }; - } - - /** - * Remove a choice from the feedback - * @param choiceId the choice id to remove - */ - removeChoiceFromFeedback(choiceId: string): void { - const feedback = this.authoringComponentContent.feedback; - if (feedback != null) { - for (const bucketFeedback of feedback) { - const choices = bucketFeedback.choices; - for (let c = 0; c < choices.length; c++) { - const choice = choices[c]; - if (choiceId === choice.choiceId) { - choices.splice(c, 1); - break; - } - } - } - } - } - - /** - * Remove a bucket from the feedback - * @param bucketId the bucket id to remove - */ - removeBucketFromFeedback(bucketId: string): void { - const feedback = this.authoringComponentContent.feedback; - if (feedback != null) { - for (let f = 0; f < feedback.length; f++) { - const bucketFeedback = feedback[f]; - if (bucketFeedback != null) { - if (bucketId === bucketFeedback.bucketId) { - feedback.splice(f, 1); - break; - } - } - } - } - } - - feedbackChanged(): void { - let show = true; - if (this.componentHasFeedback()) { - show = true; - } else { - show = false; - } - this.setShowSubmitButtonValue(show); - this.componentChanged(); - } - - /** - * Check if this component has been authored to have feedback or a correct choice - * @return whether this component has feedback or a correct choice - */ - componentHasFeedback(): boolean { - const feedback = this.authoringComponentContent.feedback; - if (feedback != null) { - for (const tempFeedback of feedback) { - const tempChoices = tempFeedback.choices; - if (tempChoices != null) { - for (const tempChoice of tempChoices) { - if (tempChoice.feedback != null && tempChoice.feedback != '') { - return true; - } - if (tempChoice.isCorrect) { - return true; - } - } - } - } - } - return false; - } - - /** - * The "Is Correct" checkbox for a choice feedback has been clicked. - * @param feedback The choice feedback. - */ - isCorrectClicked(feedback: any): void { - if (!feedback.isCorrect) { - delete feedback.position; - delete feedback.incorrectPositionFeedback; - } - this.componentChanged(); - } - - /** - * Show the asset popup to allow the author to choose an image for the choice - * @param choice the choice object to set the image into - */ - chooseChoiceAsset(choice: any): void { - const params = { - isPopup: true, - nodeId: this.nodeId, - componentId: this.componentId, - target: 'choice', - targetObject: choice - }; - this.openAssetChooser(params); - } - - /** - * Show the asset popup to allow the author to choose an image for the bucket - * @param bucket the bucket object to set the image into - */ - chooseBucketAsset(bucket: any): void { - const params = { - isPopup: true, - nodeId: this.nodeId, - componentId: this.componentId, - target: 'bucket', - targetObject: bucket - }; - this.openAssetChooser(params); - } - - assetSelected({ nodeId, componentId, assetItem, target, targetObject }): void { - super.assetSelected({ nodeId, componentId, assetItem, target }); - const fileName = assetItem.fileName; - if (target === 'choice') { - targetObject.value = ''; - this.componentChanged(); - } else if (target === 'bucket') { - targetObject.value = ''; - this.componentChanged(); - } - } - - getChoiceTextById(choiceId: string): string { - const choice = this.MatchService.getChoiceById( - choiceId, - this.authoringComponentContent.choices - ); - if (choice != null) { - return choice.value; - } - return null; - } - - getBucketNameById(bucketId: string): string { - if (bucketId === this.defaultSourceBucketId) { - const choicesLabel = this.authoringComponentContent.choicesLabel; - return choicesLabel ? choicesLabel : this.$translate('match.choices'); - } - const bucket = this.MatchService.getBucketById( - bucketId, - this.authoringComponentContent.buckets - ); - if (bucket != null) { - return bucket.value; - } - return null; - } -} - -const MatchAuthoring = { - bindings: { - nodeId: '@', - componentId: '@' - }, - controller: MatchAuthoringController, - controllerAs: 'matchController', - templateUrl: 'wise5/components/match/authoring.html' -}; - -export default MatchAuthoring; diff --git a/src/main/webapp/wise5/components/match/matchAuthoringComponentModule.ts b/src/main/webapp/wise5/components/match/matchAuthoringComponentModule.ts index 69e76d9b13..1fb70f01dd 100644 --- a/src/main/webapp/wise5/components/match/matchAuthoringComponentModule.ts +++ b/src/main/webapp/wise5/components/match/matchAuthoringComponentModule.ts @@ -1,15 +1,18 @@ 'use strict'; import * as angular from 'angular'; -import { downgradeInjectable } from '@angular/upgrade/static'; +import { downgradeComponent, downgradeInjectable } from '@angular/upgrade/static'; import { MatchService } from './matchService'; -import MatchAuthoring from './matchAuthoring'; import { EditMatchAdvancedComponent } from './edit-match-advanced/edit-match-advanced.component'; +import { MatchAuthoring } from './match-authoring/match-authoring.component'; let matchAuthoringComponentModule = angular .module('matchAuthoringComponentModule', ['pascalprecht.translate']) .service('MatchService', downgradeInjectable(MatchService)) - .component('matchAuthoring', MatchAuthoring) + .directive( + 'matchAuthoring', + downgradeComponent({ component: MatchAuthoring }) as angular.IDirectiveFactory + ) .component('editMatchAdvanced', EditMatchAdvancedComponent) .config([ '$translatePartialLoaderProvider', From 4bdc553ab217f64fbe8e41c9e3b509e10722f8d4 Mon Sep 17 00:00:00 2001 From: Geoffrey Kwan Date: Thu, 21 Jan 2021 12:29:14 -0500 Subject: [PATCH 22/26] Upgraded Draw authoring to Angular. #2889 --- .../src/app/teacher-hybrid-angular.module.ts | 2 + src/main/webapp/site/src/messages.xlf | 267 ++++++++++++- .../wise5/components/draw/authoring.html | 366 ------------------ .../draw-authoring.component.html | 292 ++++++++++++++ .../draw-authoring.component.scss | 39 ++ .../draw-authoring.component.ts | 338 ++++++++++++++++ .../wise5/components/draw/drawAuthoring.ts | 265 ------------- .../draw/drawAuthoringComponentModule.ts | 9 +- 8 files changed, 930 insertions(+), 648 deletions(-) delete mode 100644 src/main/webapp/wise5/components/draw/authoring.html create mode 100644 src/main/webapp/wise5/components/draw/draw-authoring/draw-authoring.component.html create mode 100644 src/main/webapp/wise5/components/draw/draw-authoring/draw-authoring.component.scss create mode 100644 src/main/webapp/wise5/components/draw/draw-authoring/draw-authoring.component.ts delete mode 100644 src/main/webapp/wise5/components/draw/drawAuthoring.ts diff --git a/src/main/webapp/site/src/app/teacher-hybrid-angular.module.ts b/src/main/webapp/site/src/app/teacher-hybrid-angular.module.ts index 64ea6c1c44..0e0d886a96 100644 --- a/src/main/webapp/site/src/app/teacher-hybrid-angular.module.ts +++ b/src/main/webapp/site/src/app/teacher-hybrid-angular.module.ts @@ -49,6 +49,7 @@ import { HtmlAuthoring } from '../../../wise5/components/html/html-authoring/htm import { OutsideUrlAuthoring } from '../../../wise5/components/outsideURL/outside-url-authoring/outside-url-authoring.component'; import { MultipleChoiceAuthoring } from '../../../wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component'; import { ConceptMapAuthoring } from '../../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component'; +import { DrawAuthoring } from '../../../wise5/components/draw/draw-authoring/draw-authoring.component'; @NgModule({ declarations: [ @@ -61,6 +62,7 @@ import { ConceptMapAuthoring } from '../../../wise5/components/conceptMap/concep ComponentNewWorkBadgeComponent, ComponentSelectComponent, ConceptMapAuthoring, + DrawAuthoring, EditComponentRubricComponent, EditComponentJsonComponent, EditComponentMaxScoreComponent, diff --git a/src/main/webapp/site/src/messages.xlf b/src/main/webapp/site/src/messages.xlf index a8dc3bee89..926583eb7b 100644 --- a/src/main/webapp/site/src/messages.xlf +++ b/src/main/webapp/site/src/messages.xlf @@ -1225,6 +1225,10 @@ app/student/add-project-dialog/add-project-dialog.component.html 30 + + ../../wise5/components/draw/draw-authoring/draw-authoring.component.html + 204 + ../../wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.html 32 @@ -6008,6 +6012,10 @@ ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html 3 + + ../../wise5/components/draw/draw-authoring/draw-authoring.component.html + 2 + ../../wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.html 2 @@ -6023,6 +6031,10 @@ ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html 7 + + ../../wise5/components/draw/draw-authoring/draw-authoring.component.html + 6 + ../../wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.html 6 @@ -6038,6 +6050,10 @@ ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html 15 + + ../../wise5/components/draw/draw-authoring/draw-authoring.component.html + 13 + Choose an Image @@ -6053,6 +6069,26 @@ ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html 98 + + ../../wise5/components/draw/draw-authoring/draw-authoring.component.html + 22 + + + ../../wise5/components/draw/draw-authoring/draw-authoring.component.html + 25 + + + ../../wise5/components/draw/draw-authoring/draw-authoring.component.html + 218 + + + ../../wise5/components/draw/draw-authoring/draw-authoring.component.html + 225 + + + ../../wise5/components/draw/draw-authoring/draw-authoring.component.html + 228 + ../../wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.html 56 @@ -6078,6 +6114,10 @@ ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html 45 + + ../../wise5/components/draw/draw-authoring/draw-authoring.component.html + 39 + Nodes @@ -6154,6 +6194,10 @@ ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html 209 + + ../../wise5/components/draw/draw-authoring/draw-authoring.component.html + 237 + app/authoring-tool/edit-component-tags/edit-component-tags.component.html 22 @@ -6208,6 +6252,18 @@ ../../wise5/components/conceptMap/concept-map-authoring/concept-map-authoring.component.html 230 + + ../../wise5/components/draw/draw-authoring/draw-authoring.component.html + 260 + + + ../../wise5/components/draw/draw-authoring/draw-authoring.component.html + 263 + + + ../../wise5/components/draw/draw-authoring/draw-authoring.component.html + 288 + app/authoring-tool/edit-component-tags/edit-component-tags.component.html 38 @@ -6296,6 +6352,203 @@ 259 + + Enable All Tools + + ../../wise5/components/draw/draw-authoring/draw-authoring.component.html + 50 + + + + Enable All + + ../../wise5/components/draw/draw-authoring/draw-authoring.component.html + 53 + + + + Disable All Tools + + ../../wise5/components/draw/draw-authoring/draw-authoring.component.html + 61 + + + + Disable All + + ../../wise5/components/draw/draw-authoring/draw-authoring.component.html + 64 + + + + Select Tool + + ../../wise5/components/draw/draw-authoring/draw-authoring.component.html + 74 + + + + Line Tool + + ../../wise5/components/draw/draw-authoring/draw-authoring.component.html + 82 + + + + Shape Tool + + ../../wise5/components/draw/draw-authoring/draw-authoring.component.html + 90 + + + + Free Hand Tool + + ../../wise5/components/draw/draw-authoring/draw-authoring.component.html + 98 + + + + Text Tool + + ../../wise5/components/draw/draw-authoring/draw-authoring.component.html + 107 + + + + Stamp Tool + + ../../wise5/components/draw/draw-authoring/draw-authoring.component.html + 115 + + + + Clone Tool + + ../../wise5/components/draw/draw-authoring/draw-authoring.component.html + 123 + + + + Stroke Color Tool + + ../../wise5/components/draw/draw-authoring/draw-authoring.component.html + 131 + + + + Fill Color Tool + + ../../wise5/components/draw/draw-authoring/draw-authoring.component.html + 140 + + + + Stroke Width Tool + + ../../wise5/components/draw/draw-authoring/draw-authoring.component.html + 148 + + + + Send Back Tool + + ../../wise5/components/draw/draw-authoring/draw-authoring.component.html + 156 + + + + Send Forward Tool + + ../../wise5/components/draw/draw-authoring/draw-authoring.component.html + 164 + + + + Undo Tool + + ../../wise5/components/draw/draw-authoring/draw-authoring.component.html + 173 + + + + Redo Tool + + ../../wise5/components/draw/draw-authoring/draw-authoring.component.html + 181 + + + + Delete Tool + + ../../wise5/components/draw/draw-authoring/draw-authoring.component.html + 189 + + + + Stamps + + ../../wise5/components/draw/draw-authoring/draw-authoring.component.html + 196 + + + + Add Stamp + + ../../wise5/components/draw/draw-authoring/draw-authoring.component.html + 201 + + + + Up + + ../../wise5/components/draw/draw-authoring/draw-authoring.component.html + 240 + + + ../../wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.html + 90 + + + + above + + ../../wise5/components/draw/draw-authoring/draw-authoring.component.html + 250 + + + + Down + + ../../wise5/components/draw/draw-authoring/draw-authoring.component.html + 252 + + + ../../wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.html + 102 + + + + Save Starter Drawing + + ../../wise5/components/draw/draw-authoring/draw-authoring.component.html + 274 + + + + Save + + ../../wise5/components/draw/draw-authoring/draw-authoring.component.html + 277 + + + + Delete Starter Drawing + + ../../wise5/components/draw/draw-authoring/draw-authoring.component.html + 285 + + Rubric @@ -6436,20 +6689,6 @@ 79 - - Up - - ../../wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.html - 90 - - - - Down - - ../../wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.html - 102 - - % completed (All periods) diff --git a/src/main/webapp/wise5/components/draw/authoring.html b/src/main/webapp/wise5/components/draw/authoring.html deleted file mode 100644 index 2dff626942..0000000000 --- a/src/main/webapp/wise5/components/draw/authoring.html +++ /dev/null @@ -1,366 +0,0 @@ - - -
-
-
-
- - {{ ::'SHOW_SAVE_BUTTON' | translate }} - -
-
- - {{ ::'SHOW_SUBMIT_BUTTON' | translate }} - -
-
- - {{ ::'SHOW_ADD_TO_NOTEBOOK_BUTTON' | translate }} - -
-
- - - - -
-
- - - - -
-
-
- - - add - - {{ ::'ADD_CONNECTED_COMPONENT' | translate }} - - -
-
-
- - - - - {{ drawController.getNodePositionAndTitleByNodeId(item.$key) }} - - - - - - - - {{ componentIndex + 1 }}. {{ component.type }} - - ({{ ::'thisComponent' | translate }}) - - - - - - - - - {{ ::'importWork' | translate }} - - - {{ ::'showWork' | translate }} - - - - - - - delete - - {{ ::'DELETE' | translate }} - - - -
- - - {{ ::'importWorkAsBackground' | translate }} - - -
-
-
-
- -
-
-
- - - - -
- - - - - - insert_photo - - {{ ::'chooseAnImage' | translate }} - - -
-
- - - - - - - - -
-
- - check - - {{ ::'draw.enableAllTools' | translate }} - - - - check_box_outline_blank - - {{ ::'draw.disableAllTools' | translate }} - - -
-
-
- - {{ ::'draw.selectTool' | translate }} - -
- - {{ ::'draw.lineTool' | translate }} - -
- - {{ ::'draw.shapeTool' | translate }} - -
- - {{ ::'draw.freeHandTool' | translate }} - -
-
- - {{ ::'draw.textTool' | translate }} - -
- - {{ ::'draw.stampTool' | translate }} - -
- - {{ ::'draw.cloneTool' | translate }} - -
- - {{ ::'draw.strokeColorTool' | translate }} - -
-
- - {{ ::'draw.fillColorTool' | translate }} - -
- - {{ ::'draw.strokeWidthTool' | translate }} - -
- - {{ ::'draw.sendBackTool' | translate }} - -
- - {{ ::'draw.sendForwardTool' | translate }} - -
-
- - {{ ::'draw.undoTool' | translate }} - -
- - {{ ::'draw.redoTool' | translate }} - -
- - {{ ::'draw.deleteTool' | translate }} - -
-
-
-

{{ ::'draw.stamps' | translate }}

-
-
- {{ ::'draw.thereAreNoStampsClickTheAddStampButtonToAddAStamp' | translate }} -
-
- - - - - - insert_photo - - {{ ::'chooseAnImage' | translate }} - - - - arrow_upward - - {{ ::'UP' | translate }} - - - - arrow_downward - - {{ ::'DOWN' | translate }} - - - - delete - - {{ ::'DELETE' | translate }} - - -
-
- - library_add - - {{ ::'draw.addStamp' | translate }} - - -
- - create - - {{ ::'draw.saveStarterDrawing' | translate }} - - - - delete_sweep - - {{ ::'draw.deleteStarterDrawing' | translate }} - - -
-
-
diff --git a/src/main/webapp/wise5/components/draw/draw-authoring/draw-authoring.component.html b/src/main/webapp/wise5/components/draw/draw-authoring/draw-authoring.component.html new file mode 100644 index 0000000000..32e2c6a229 --- /dev/null +++ b/src/main/webapp/wise5/components/draw/draw-authoring/draw-authoring.component.html @@ -0,0 +1,292 @@ + + Prompt + + +
+ + Background Image (Optional) + + + +
+
+ + Canvas Width (Optional) + + + + Canvas Height (Optional) + + +
+ + +
+
+ + Select Tool + +
+ + Line Tool + +
+ + Shape Tool + +
+ + Free Hand Tool + +
+
+ + Text Tool + +
+ + Stamp Tool + +
+ + Clone Tool + +
+ + Stroke Color Tool + +
+
+ + Fill Color Tool + +
+ + Stroke Width Tool + +
+ + Send Back Tool + +
+ + Send Forward Tool + +
+
+ + Undo Tool + +
+ + Redo Tool + +
+ + Delete Tool + +
+
+
+
+ Stamps + +
+
+ There are no stamps. Click the "Add Stamp" button to add a stamp. +
+
+ + Stamp Image + + + + + + +
+
+
+ + +
\ No newline at end of file diff --git a/src/main/webapp/wise5/components/draw/draw-authoring/draw-authoring.component.scss b/src/main/webapp/wise5/components/draw/draw-authoring/draw-authoring.component.scss new file mode 100644 index 0000000000..497bb01262 --- /dev/null +++ b/src/main/webapp/wise5/components/draw/draw-authoring/draw-authoring.component.scss @@ -0,0 +1,39 @@ +.prompt-input-container { + width: 100%; +} + +.background-input-container { + width: 90%; +} + +.enable-all-tools-button { + margin-right: 20px; +} + +.disable-all-tools-button { + margin-right: 20px; +} + +.authoring-button { + margin-left: 10px; + margin-right: 10px; +} + +.canvas-dimension-container { + width: 20%; + margin-right: 20px; +} + +.add-stamps-container { + margin-top: 20px; + margin-bottom: 20px; +} + +.stamp-image-input-container { + width: 50%; +} + +.starter-draw-buttons-container { + margin-top: 20px; + margin-bottom: 20px; +} \ No newline at end of file diff --git a/src/main/webapp/wise5/components/draw/draw-authoring/draw-authoring.component.ts b/src/main/webapp/wise5/components/draw/draw-authoring/draw-authoring.component.ts new file mode 100644 index 0000000000..78755ca185 --- /dev/null +++ b/src/main/webapp/wise5/components/draw/draw-authoring/draw-authoring.component.ts @@ -0,0 +1,338 @@ +'use strict'; + +import * as angular from 'angular'; +import { Component } from '@angular/core'; +import { ComponentAuthoring } from '../../../authoringTool/components/component-authoring.component'; +import { ConfigService } from '../../../services/configService'; +import { NodeService } from '../../../services/nodeService'; +import { ProjectAssetService } from '../../../../site/src/app/services/projectAssetService'; +import { TeacherProjectService } from '../../../services/teacherProjectService'; +import { UtilService } from '../../../services/utilService'; +import { Subject, Subscription } from 'rxjs'; +import { debounceTime, distinctUntilChanged } from 'rxjs/operators'; + +@Component({ + selector: 'draw-authoring', + templateUrl: 'draw-authoring.component.html', + styleUrls: ['draw-authoring.component.scss'] +}) +export class DrawAuthoring extends ComponentAuthoring { + width: number; + height: number; + defaultWidth: number = 800; + defaultHeight: number = 600; + stamps: any[] = []; + + inputChange: Subject = new Subject(); + backgroundImageChange: Subject = new Subject(); + canvasWidthChange: Subject = new Subject(); + canvasHeightChange: Subject = new Subject(); + stampImageChange: Subject = new Subject(); + + inputChangeSubscription: Subscription; + backgroundImageChangeSubscription: Subscription; + canvasWidthChangeSubscription: Subscription; + canvasHeightChangeSubscription: Subscription; + stampImageChangeSubscription: Subscription; + + constructor( + protected ConfigService: ConfigService, + protected NodeService: NodeService, + protected ProjectAssetService: ProjectAssetService, + protected ProjectService: TeacherProjectService, + protected UtilService: UtilService + ) { + super(ConfigService, NodeService, ProjectAssetService, ProjectService); + this.inputChangeSubscription = this.inputChange + .pipe(debounceTime(1000), distinctUntilChanged()) + .subscribe(() => { + this.componentChanged(); + }); + this.backgroundImageChangeSubscription = this.backgroundImageChange + .pipe(debounceTime(1000), distinctUntilChanged()) + .subscribe(() => { + this.updateStarterDrawDataBackgroundAndSave(); + }); + this.canvasWidthChangeSubscription = this.canvasWidthChange + .pipe(debounceTime(1000), distinctUntilChanged()) + .subscribe(() => { + this.canvasWidthChanged(); + }); + this.canvasHeightChangeSubscription = this.canvasHeightChange + .pipe(debounceTime(1000), distinctUntilChanged()) + .subscribe(() => { + this.canvasHeightChanged(); + }); + this.stampImageChangeSubscription = this.stampImageChange + .pipe(debounceTime(1000), distinctUntilChanged()) + .subscribe(() => { + this.updateAuthoringComponentContentStampsAndSave(); + }); + } + + ngOnInit() { + super.ngOnInit(); + this.stamps = this.convertStampStringsToStampObjects( + this.authoringComponentContent.stamps.Stamps + ); + } + + ngOnDestroy() { + this.inputChangeSubscription.unsubscribe(); + this.backgroundImageChangeSubscription.unsubscribe(); + this.canvasWidthChangeSubscription.unsubscribe(); + this.canvasHeightChangeSubscription.unsubscribe(); + this.stampImageChangeSubscription.unsubscribe(); + } + + enableAllToolsButtonClicked(): void { + if (this.authoringComponentContent.tools == null) { + this.authoringComponentContent.tools = {}; + } + this.authoringComponentContent.tools.select = true; + this.authoringComponentContent.tools.line = true; + this.authoringComponentContent.tools.shape = true; + this.authoringComponentContent.tools.freeHand = true; + this.authoringComponentContent.tools.text = true; + this.authoringComponentContent.tools.stamp = true; + this.authoringComponentContent.tools.strokeColor = true; + this.authoringComponentContent.tools.fillColor = true; + this.authoringComponentContent.tools.clone = true; + this.authoringComponentContent.tools.strokeWidth = true; + this.authoringComponentContent.tools.sendBack = true; + this.authoringComponentContent.tools.sendForward = true; + this.authoringComponentContent.tools.undo = true; + this.authoringComponentContent.tools.redo = true; + this.authoringComponentContent.tools.delete = true; + this.componentChanged(); + } + + disableAllToolsButtonClicked(): void { + if (this.authoringComponentContent.tools == null) { + this.authoringComponentContent.tools = {}; + } + this.authoringComponentContent.tools.select = false; + this.authoringComponentContent.tools.line = false; + this.authoringComponentContent.tools.shape = false; + this.authoringComponentContent.tools.freeHand = false; + this.authoringComponentContent.tools.text = false; + this.authoringComponentContent.tools.stamp = false; + this.authoringComponentContent.tools.strokeColor = false; + this.authoringComponentContent.tools.fillColor = false; + this.authoringComponentContent.tools.clone = false; + this.authoringComponentContent.tools.strokeWidth = false; + this.authoringComponentContent.tools.sendBack = false; + this.authoringComponentContent.tools.sendForward = false; + this.authoringComponentContent.tools.undo = false; + this.authoringComponentContent.tools.redo = false; + this.authoringComponentContent.tools.delete = false; + this.componentChanged(); + } + + saveStarterDrawData(): void { + if (confirm($localize`Are you sure you want to save the starter drawing?`)) { + this.NodeService.requestStarterState({ nodeId: this.nodeId, componentId: this.componentId }); + } + } + + saveStarterState(starterState: any): void { + this.authoringComponentContent.starterDrawData = starterState; + this.componentChanged(); + } + + deleteStarterDrawData(): void { + if (confirm($localize`Are you sure you want to delete the starter drawing?`)) { + this.authoringComponentContent.starterDrawData = null; + this.componentChanged(); + } + } + + canvasWidthChanged(): void { + this.width = this.authoringComponentContent.width; + this.updateStarterDrawDataWidth(); + this.componentChanged(); + } + + updateStarterDrawDataWidth(): void { + if (this.authoringComponentContent.starterDrawData != null) { + const starterDrawDataJSONObject = angular.fromJson( + this.authoringComponentContent.starterDrawData + ); + if (starterDrawDataJSONObject != null && starterDrawDataJSONObject.dt != null) { + if (this.width == null) { + starterDrawDataJSONObject.dt.width = this.defaultWidth; + } else { + starterDrawDataJSONObject.dt.width = this.width; + } + this.authoringComponentContent.starterDrawData = angular.toJson(starterDrawDataJSONObject); + } + } + } + + canvasHeightChanged(): void { + this.height = this.authoringComponentContent.height; + this.updateStarterDrawDataHeight(); + this.componentChanged(); + } + + updateStarterDrawDataHeight(): void { + if (this.authoringComponentContent.starterDrawData != null) { + const starterDrawDataJSONObject = angular.fromJson( + this.authoringComponentContent.starterDrawData + ); + if (starterDrawDataJSONObject != null && starterDrawDataJSONObject.dt != null) { + if (this.height == null) { + starterDrawDataJSONObject.dt.height = this.defaultHeight; + } else { + starterDrawDataJSONObject.dt.height = this.height; + } + this.authoringComponentContent.starterDrawData = angular.toJson(starterDrawDataJSONObject); + } + } + } + + toolClicked(): void { + this.componentChanged(); + } + + chooseBackgroundImage(): void { + const params = { + isPopup: true, + nodeId: this.nodeId, + componentId: this.componentId, + target: 'background' + }; + this.openAssetChooser(params); + } + + chooseStampImage(stampIndex: number): void { + const params = { + isPopup: true, + nodeId: this.nodeId, + componentId: this.componentId, + target: 'stamp', + targetObject: stampIndex + }; + this.openAssetChooser(params); + } + + assetSelected({ nodeId, componentId, assetItem, target, targetObject }): void { + super.assetSelected({ nodeId, componentId, assetItem, target }); + const fileName = assetItem.fileName; + if (target === 'background') { + this.authoringComponentContent.background = fileName; + this.updateStarterDrawDataBackgroundAndSave(); + } else if (target === 'stamp') { + const stampIndex = targetObject; + this.setStampImage(stampIndex, fileName); + this.updateAuthoringComponentContentStampsAndSave(); + } + } + + updateStarterDrawDataBackgroundAndSave(): void { + this.updateStarterDrawDataBackground(); + this.componentChanged(); + } + + updateStarterDrawDataBackground(): void { + const starterDrawData = this.authoringComponentContent.starterDrawData; + if (starterDrawData != null) { + const starterDrawDataJSON = angular.fromJson(starterDrawData); + if ( + starterDrawDataJSON != null && + starterDrawDataJSON.canvas != null && + starterDrawDataJSON.canvas.backgroundImage != null && + starterDrawDataJSON.canvas.backgroundImage.src != null + ) { + const projectAssetsDirectoryPath = this.ConfigService.getProjectAssetsDirectoryPath(true); + const background = this.authoringComponentContent.background; + const newSrc = projectAssetsDirectoryPath + '/' + background; + starterDrawDataJSON.canvas.backgroundImage.src = newSrc; + this.authoringComponentContent.starterDrawData = angular.toJson(starterDrawDataJSON); + } + } + } + + addStamp(): void { + this.initializeAuthoringComponentContentStampsIfNecessary(); + this.stamps.push(this.createStamp()); + this.updateAuthoringComponentContentStampsAndSave(); + } + + initializeAuthoringComponentContentStampsIfNecessary(): void { + if (this.authoringComponentContent.stamps == null) { + this.authoringComponentContent.stamps = {}; + } + if (this.authoringComponentContent.stamps.Stamps == null) { + this.authoringComponentContent.stamps.Stamps = []; + } + } + + createStamp(image: string = ''): any { + return { image: image }; + } + + updateAuthoringComponentContentStampsAndSave(): void { + this.updateAuthoringComponentContentStamps(); + this.componentChanged(); + } + + updateAuthoringComponentContentStamps(): void { + this.authoringComponentContent.stamps.Stamps = this.convertStampObjectsToStampStrings( + this.stamps + ); + } + + moveStampUp(index: number): void { + if (index != 0) { + const stamp = this.stamps[index]; + this.stamps.splice(index, 1); + this.stamps.splice(index - 1, 0, stamp); + this.updateAuthoringComponentContentStampsAndSave(); + } + } + + moveStampDown(index: number): void { + if (index != this.authoringComponentContent.stamps.Stamps.length - 1) { + const stamp = this.stamps[index]; + this.stamps.splice(index, 1); + this.stamps.splice(index + 1, 0, stamp); + this.updateAuthoringComponentContentStampsAndSave(); + } + } + + deleteStamp(index: number): void { + if ( + confirm( + $localize`Are you sure you want to delete this stamp?\n\n${this.authoringComponentContent.stamps.Stamps[index]}` + ) + ) { + this.stamps.splice(index, 1); + this.updateAuthoringComponentContentStampsAndSave(); + } + } + + setStampImage(index: number, fileName: string): void { + this.stamps[index].image = fileName; + } + + stampChanged(stampImage: string, index: number): void { + this.stampImageChange.next(`${index}-${stampImage}`); + } + + convertStampStringsToStampObjects(stampStrings: string[]): any[] { + const stampObjects: any[] = []; + for (let stampString of stampStrings) { + stampObjects.push(this.createStamp(stampString)); + } + return stampObjects; + } + + convertStampObjectsToStampStrings(stampObjects: any[]): string[] { + const stampStrings: string[] = []; + for (let stampObject of stampObjects) { + stampStrings.push(stampObject.image); + } + return stampStrings; + } +} diff --git a/src/main/webapp/wise5/components/draw/drawAuthoring.ts b/src/main/webapp/wise5/components/draw/drawAuthoring.ts deleted file mode 100644 index 3b4ebccbf5..0000000000 --- a/src/main/webapp/wise5/components/draw/drawAuthoring.ts +++ /dev/null @@ -1,265 +0,0 @@ -'use strict'; - -import * as angular from 'angular'; -import { Directive } from '@angular/core'; -import { EditComponentController } from '../../authoringTool/components/editComponentController'; - -@Directive() -class DrawAuthoringController extends EditComponentController { - width: number; - height: number; - - static $inject = [ - '$filter', - 'ConfigService', - 'NodeService', - 'NotificationService', - 'ProjectAssetService', - 'ProjectService', - 'UtilService' - ]; - - constructor( - $filter, - ConfigService, - NodeService, - NotificationService, - ProjectAssetService, - ProjectService, - UtilService - ) { - super( - $filter, - ConfigService, - NodeService, - NotificationService, - ProjectAssetService, - ProjectService, - UtilService - ); - } - - addStampButtonClicked() { - this.initializeAuthoringComponentContentStampsIfNecessary(); - this.authoringComponentContent.stamps.Stamps.push(''); - this.componentChanged(); - } - - initializeAuthoringComponentContentStampsIfNecessary() { - if (this.authoringComponentContent != null) { - if (this.authoringComponentContent.stamps == null) { - this.authoringComponentContent.stamps = {}; - } - if (this.authoringComponentContent.stamps.Stamps == null) { - this.authoringComponentContent.stamps.Stamps = []; - } - } - } - - moveStampUp(index) { - if (index != 0) { - const stamp = this.authoringComponentContent.stamps.Stamps[index]; - this.authoringComponentContent.stamps.Stamps.splice(index, 1); - this.authoringComponentContent.stamps.Stamps.splice(index - 1, 0, stamp); - this.componentChanged(); - } - } - - moveStampDown(index) { - if (index != this.authoringComponentContent.stamps.Stamps.length - 1) { - const stamp = this.authoringComponentContent.stamps.Stamps[index]; - this.authoringComponentContent.stamps.Stamps.splice(index, 1); - this.authoringComponentContent.stamps.Stamps.splice(index + 1, 0, stamp); - this.componentChanged(); - } - } - - deleteStampClicked(index) { - if ( - confirm( - this.$translate('draw.areYouSureYouWantToDeleteThisStamp') + - '\n\n' + - this.authoringComponentContent.stamps.Stamps[index] - ) - ) { - this.authoringComponentContent.stamps.Stamps.splice(index, 1); - this.componentChanged(); - } - } - - enableAllToolsButtonClicked() { - if (this.authoringComponentContent.tools == null) { - this.authoringComponentContent.tools = {}; - } - this.authoringComponentContent.tools.select = true; - this.authoringComponentContent.tools.line = true; - this.authoringComponentContent.tools.shape = true; - this.authoringComponentContent.tools.freeHand = true; - this.authoringComponentContent.tools.text = true; - this.authoringComponentContent.tools.stamp = true; - this.authoringComponentContent.tools.strokeColor = true; - this.authoringComponentContent.tools.fillColor = true; - this.authoringComponentContent.tools.clone = true; - this.authoringComponentContent.tools.strokeWidth = true; - this.authoringComponentContent.tools.sendBack = true; - this.authoringComponentContent.tools.sendForward = true; - this.authoringComponentContent.tools.undo = true; - this.authoringComponentContent.tools.redo = true; - this.authoringComponentContent.tools.delete = true; - this.componentChanged(); - } - - disableAllToolsButtonClicked() { - if (this.authoringComponentContent.tools == null) { - this.authoringComponentContent.tools = {}; - } - this.authoringComponentContent.tools.select = false; - this.authoringComponentContent.tools.line = false; - this.authoringComponentContent.tools.shape = false; - this.authoringComponentContent.tools.freeHand = false; - this.authoringComponentContent.tools.text = false; - this.authoringComponentContent.tools.stamp = false; - this.authoringComponentContent.tools.strokeColor = false; - this.authoringComponentContent.tools.fillColor = false; - this.authoringComponentContent.tools.clone = false; - this.authoringComponentContent.tools.strokeWidth = false; - this.authoringComponentContent.tools.sendBack = false; - this.authoringComponentContent.tools.sendForward = false; - this.authoringComponentContent.tools.undo = false; - this.authoringComponentContent.tools.redo = false; - this.authoringComponentContent.tools.delete = false; - this.componentChanged(); - } - - saveStarterDrawData() { - if (confirm(this.$translate('draw.areYouSureYouWantToSaveTheStarterDrawing'))) { - this.NodeService.requestStarterState({ nodeId: this.nodeId, componentId: this.componentId }); - } - } - - saveStarterState(starterState) { - this.authoringComponentContent.starterDrawData = starterState; - this.componentChanged(); - } - - deleteStarterDrawData() { - if (confirm(this.$translate('draw.areYouSureYouWantToDeleteTheStarterDrawing'))) { - this.authoringComponentContent.starterDrawData = null; - this.componentChanged(); - } - } - - viewWidthChanged() { - this.width = this.authoringComponentContent.width; - this.updateStarterDrawDataWidth(); - this.componentChanged(); - } - - updateStarterDrawDataWidth() { - if (this.authoringComponentContent.starterDrawData != null) { - const starterDrawDataJSONObject = angular.fromJson( - this.authoringComponentContent.starterDrawData - ); - if (starterDrawDataJSONObject != null && starterDrawDataJSONObject.dt != null) { - starterDrawDataJSONObject.dt.width = this.width; - this.authoringComponentContent.starterDrawData = angular.toJson(starterDrawDataJSONObject); - } - } - } - - viewHeightChanged() { - this.height = this.authoringComponentContent.height; - this.updateStarterDrawDataHeight(); - this.componentChanged(); - } - - updateStarterDrawDataHeight() { - if (this.authoringComponentContent.starterDrawData != null) { - const starterDrawDataJSONObject = angular.fromJson( - this.authoringComponentContent.starterDrawData - ); - if (starterDrawDataJSONObject != null && starterDrawDataJSONObject.dt != null) { - starterDrawDataJSONObject.dt.height = this.height; - this.authoringComponentContent.starterDrawData = angular.toJson(starterDrawDataJSONObject); - } - } - } - - toolClicked() { - this.componentChanged(); - } - - chooseBackgroundImage() { - const params = { - isPopup: true, - nodeId: this.nodeId, - componentId: this.componentId, - target: 'background' - }; - this.openAssetChooser(params); - } - - chooseStampImage(stampIndex) { - const params = { - isPopup: true, - nodeId: this.nodeId, - componentId: this.componentId, - target: 'stamp', - targetObject: stampIndex - }; - this.openAssetChooser(params); - } - - assetSelected({ nodeId, componentId, assetItem, target, targetObject }) { - super.assetSelected({ nodeId, componentId, assetItem, target }); - const fileName = assetItem.fileName; - if (target === 'background') { - this.authoringComponentContent.background = fileName; - this.backgroundChanged(); - } else if (target === 'stamp') { - const stampIndex = targetObject; - this.setStampImage(stampIndex, fileName); - this.backgroundChanged(); - } - } - - backgroundChanged() { - this.updateStarterDrawDataBackground(); - this.componentChanged(); - } - - updateStarterDrawDataBackground() { - const starterDrawData = this.authoringComponentContent.starterDrawData; - if (starterDrawData != null) { - const starterDrawDataJSON = angular.fromJson(starterDrawData); - if ( - starterDrawDataJSON != null && - starterDrawDataJSON.canvas != null && - starterDrawDataJSON.canvas.backgroundImage != null && - starterDrawDataJSON.canvas.backgroundImage.src != null - ) { - const projectAssetsDirectoryPath = this.ConfigService.getProjectAssetsDirectoryPath(true); - const background = this.authoringComponentContent.background; - const newSrc = projectAssetsDirectoryPath + '/' + background; - starterDrawDataJSON.canvas.backgroundImage.src = newSrc; - this.authoringComponentContent.starterDrawData = angular.toJson(starterDrawDataJSON); - } - } - } - - setStampImage(index, fileName) { - this.authoringComponentContent.stamps.Stamps[index] = fileName; - } -} - -const DrawAuthoring = { - bindings: { - nodeId: '@', - componentId: '@' - }, - controller: DrawAuthoringController, - controllerAs: 'drawController', - templateUrl: 'wise5/components/draw/authoring.html' -}; - -export default DrawAuthoring; diff --git a/src/main/webapp/wise5/components/draw/drawAuthoringComponentModule.ts b/src/main/webapp/wise5/components/draw/drawAuthoringComponentModule.ts index 4886711294..4c29265168 100644 --- a/src/main/webapp/wise5/components/draw/drawAuthoringComponentModule.ts +++ b/src/main/webapp/wise5/components/draw/drawAuthoringComponentModule.ts @@ -1,15 +1,18 @@ 'use strict'; import * as angular from 'angular'; -import { downgradeInjectable } from '@angular/upgrade/static'; +import { downgradeComponent, downgradeInjectable } from '@angular/upgrade/static'; import { DrawService } from './drawService'; -import DrawAuthoring from './drawAuthoring'; import { EditDrawAdvancedComponent } from './edit-draw-advanced/edit-draw-advanced.component'; +import { DrawAuthoring } from './draw-authoring/draw-authoring.component'; const drawAuthoringComponentModule = angular .module('drawAuthoringComponentModule', ['pascalprecht.translate']) .service('DrawService', downgradeInjectable(DrawService)) - .component('drawAuthoring', DrawAuthoring) + .directive( + 'drawAuthoring', + downgradeComponent({ component: DrawAuthoring }) as angular.IDirectiveFactory + ) .component('editDrawAdvanced', EditDrawAdvancedComponent) .config([ '$translatePartialLoaderProvider', From 3e49085647e35cbc4a1fdc676995bb17dadd09d5 Mon Sep 17 00:00:00 2001 From: Hiroki Terashima Date: Thu, 21 Jan 2021 09:30:55 -0800 Subject: [PATCH 23/26] Refactored enable/disable tools in Draw authoring. #2889 --- .../draw-authoring.component.html | 6 +- .../draw-authoring.component.ts | 63 +++++++------------ 2 files changed, 25 insertions(+), 44 deletions(-) diff --git a/src/main/webapp/wise5/components/draw/draw-authoring/draw-authoring.component.html b/src/main/webapp/wise5/components/draw/draw-authoring/draw-authoring.component.html index 32e2c6a229..3707e3eb49 100644 --- a/src/main/webapp/wise5/components/draw/draw-authoring/draw-authoring.component.html +++ b/src/main/webapp/wise5/components/draw/draw-authoring/draw-authoring.component.html @@ -46,7 +46,7 @@ -
\ No newline at end of file +
diff --git a/src/main/webapp/wise5/components/draw/draw-authoring/draw-authoring.component.ts b/src/main/webapp/wise5/components/draw/draw-authoring/draw-authoring.component.ts index 78755ca185..930150944a 100644 --- a/src/main/webapp/wise5/components/draw/draw-authoring/draw-authoring.component.ts +++ b/src/main/webapp/wise5/components/draw/draw-authoring/draw-authoring.component.ts @@ -7,7 +7,6 @@ import { ConfigService } from '../../../services/configService'; import { NodeService } from '../../../services/nodeService'; import { ProjectAssetService } from '../../../../site/src/app/services/projectAssetService'; import { TeacherProjectService } from '../../../services/teacherProjectService'; -import { UtilService } from '../../../services/utilService'; import { Subject, Subscription } from 'rxjs'; import { debounceTime, distinctUntilChanged } from 'rxjs/operators'; @@ -17,6 +16,23 @@ import { debounceTime, distinctUntilChanged } from 'rxjs/operators'; styleUrls: ['draw-authoring.component.scss'] }) export class DrawAuthoring extends ComponentAuthoring { + allToolNames: string[] = [ + 'select', + 'line', + 'shape', + 'freeHand', + 'text', + 'stamp', + 'strokeColor', + 'fillColor', + 'clone', + 'strokeWidth', + 'sendBack', + 'sendForward', + 'undo', + 'redo', + 'delete' + ]; width: number; height: number; defaultWidth: number = 800; @@ -39,8 +55,7 @@ export class DrawAuthoring extends ComponentAuthoring { protected ConfigService: ConfigService, protected NodeService: NodeService, protected ProjectAssetService: ProjectAssetService, - protected ProjectService: TeacherProjectService, - protected UtilService: UtilService + protected ProjectService: TeacherProjectService ) { super(ConfigService, NodeService, ProjectAssetService, ProjectService); this.inputChangeSubscription = this.inputChange @@ -85,47 +100,13 @@ export class DrawAuthoring extends ComponentAuthoring { this.stampImageChangeSubscription.unsubscribe(); } - enableAllToolsButtonClicked(): void { + enableAllTools(doEnable: boolean) { if (this.authoringComponentContent.tools == null) { this.authoringComponentContent.tools = {}; } - this.authoringComponentContent.tools.select = true; - this.authoringComponentContent.tools.line = true; - this.authoringComponentContent.tools.shape = true; - this.authoringComponentContent.tools.freeHand = true; - this.authoringComponentContent.tools.text = true; - this.authoringComponentContent.tools.stamp = true; - this.authoringComponentContent.tools.strokeColor = true; - this.authoringComponentContent.tools.fillColor = true; - this.authoringComponentContent.tools.clone = true; - this.authoringComponentContent.tools.strokeWidth = true; - this.authoringComponentContent.tools.sendBack = true; - this.authoringComponentContent.tools.sendForward = true; - this.authoringComponentContent.tools.undo = true; - this.authoringComponentContent.tools.redo = true; - this.authoringComponentContent.tools.delete = true; - this.componentChanged(); - } - - disableAllToolsButtonClicked(): void { - if (this.authoringComponentContent.tools == null) { - this.authoringComponentContent.tools = {}; - } - this.authoringComponentContent.tools.select = false; - this.authoringComponentContent.tools.line = false; - this.authoringComponentContent.tools.shape = false; - this.authoringComponentContent.tools.freeHand = false; - this.authoringComponentContent.tools.text = false; - this.authoringComponentContent.tools.stamp = false; - this.authoringComponentContent.tools.strokeColor = false; - this.authoringComponentContent.tools.fillColor = false; - this.authoringComponentContent.tools.clone = false; - this.authoringComponentContent.tools.strokeWidth = false; - this.authoringComponentContent.tools.sendBack = false; - this.authoringComponentContent.tools.sendForward = false; - this.authoringComponentContent.tools.undo = false; - this.authoringComponentContent.tools.redo = false; - this.authoringComponentContent.tools.delete = false; + this.allToolNames.map((toolName) => { + this.authoringComponentContent.tools[toolName] = doEnable; + }); this.componentChanged(); } From f8e923342cb704510e0d429053cfd342f7c81b89 Mon Sep 17 00:00:00 2001 From: Hiroki Terashima Date: Thu, 21 Jan 2021 17:17:55 -0800 Subject: [PATCH 24/26] Removed duplicate code and simplified functions in MatchAuthoring. #2893 --- .../match-authoring.component.ts | 53 ++++++++----------- 1 file changed, 22 insertions(+), 31 deletions(-) diff --git a/src/main/webapp/wise5/components/match/match-authoring/match-authoring.component.ts b/src/main/webapp/wise5/components/match/match-authoring/match-authoring.component.ts index 83f0a48ffd..a47efbadb2 100644 --- a/src/main/webapp/wise5/components/match/match-authoring/match-authoring.component.ts +++ b/src/main/webapp/wise5/components/match/match-authoring/match-authoring.component.ts @@ -51,9 +51,8 @@ export class MatchAuthoring extends ComponentAuthoring { } turnOnSubmitButtonIfFeedbackExists() { - const show = this.componentHasFeedback(); - if (show) { - this.setShowSubmitButtonValue(show); + if (this.componentHasFeedback()) { + this.setShowSubmitButtonValue(true); } } @@ -237,16 +236,10 @@ export class MatchAuthoring extends ComponentAuthoring { } removeChoiceFromFeedback(choiceId: string): void { - const feedback = this.authoringComponentContent.feedback; - for (const bucketFeedback of feedback) { - const choices = bucketFeedback.choices; - for (let c = 0; c < choices.length; c++) { - const choice = choices[c]; - if (choiceId === choice.choiceId) { - choices.splice(c, 1); - break; - } - } + for (const bucketFeedback of this.authoringComponentContent.feedback) { + bucketFeedback.choices = bucketFeedback.choices.filter((choice) => { + return choice.choiceId !== choiceId; + }); } } @@ -264,11 +257,9 @@ export class MatchAuthoring extends ComponentAuthoring { } componentHasFeedback(): boolean { - const feedback = this.authoringComponentContent.feedback; - for (const tempFeedback of feedback) { - const tempChoices = tempFeedback.choices; - for (const tempChoice of tempChoices) { - if (tempChoice.isCorrect || (tempChoice.feedback != null && tempChoice.feedback != '')) { + for (const feedback of this.authoringComponentContent.feedback) { + for (const choice of feedback.choices) { + if (choice.isCorrect || this.isNonEmpty(choice.feedback)) { return true; } } @@ -276,6 +267,10 @@ export class MatchAuthoring extends ComponentAuthoring { return false; } + isNonEmpty(str: string): boolean { + return str != null && str != ''; + } + isCorrectClicked(feedback: any): void { if (!feedback.isCorrect) { delete feedback.position; @@ -286,25 +281,21 @@ export class MatchAuthoring extends ComponentAuthoring { } chooseChoiceAsset(choice: any): void { - const params = { - isPopup: true, - nodeId: this.nodeId, - componentId: this.componentId, - target: 'choice', - targetObject: choice - }; - this.openAssetChooser(params); + this.openAssetChooserHelper('choice', choice); } chooseBucketAsset(bucket: any): void { - const params = { + this.openAssetChooserHelper('bucket', bucket); + } + + openAssetChooserHelper(target: string, targetObject: any): void { + this.openAssetChooser({ isPopup: true, nodeId: this.nodeId, componentId: this.componentId, - target: 'bucket', - targetObject: bucket - }; - this.openAssetChooser(params); + target: target, + targetObject: targetObject + }); } assetSelected({ nodeId, componentId, assetItem, target, targetObject }): void { From 8d6cb15ce827562de5c6b4f99d339b073e5ad6a9 Mon Sep 17 00:00:00 2001 From: Geoffrey Kwan Date: Fri, 22 Jan 2021 13:56:15 -0500 Subject: [PATCH 25/26] Set the Multiple Choice authoring styling to be encapsulated. Also fixed some other minor styling issues. --- src/main/webapp/site/src/messages.xlf | 40 ++++++++++--------- .../multiple-choice-authoring.component.html | 29 +++++++++----- .../multiple-choice-authoring.component.scss | 7 +++- .../multiple-choice-authoring.component.ts | 3 +- 4 files changed, 49 insertions(+), 30 deletions(-) diff --git a/src/main/webapp/site/src/messages.xlf b/src/main/webapp/site/src/messages.xlf index 257755f479..2419779b02 100644 --- a/src/main/webapp/site/src/messages.xlf +++ b/src/main/webapp/site/src/messages.xlf @@ -1239,7 +1239,7 @@ ../../wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.html - 32 + 41 @@ -6123,7 +6123,7 @@ ../../wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.html - 56 + 65 @@ -6191,7 +6191,7 @@ ../../wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.html - 59 + 68 @@ -6244,7 +6244,7 @@ ../../wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.html - 87 + 98 @@ -6279,7 +6279,7 @@ ../../wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.html - 99 + 110 @@ -6326,11 +6326,11 @@ ../../wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.html - 110 + 121 ../../wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.html - 113 + 124 @@ -6571,7 +6571,7 @@ ../../wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.html - 90 + 101 @@ -6597,7 +6597,7 @@ ../../wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.html - 102 + 113 @@ -6697,6 +6697,10 @@ ../../wise5/components/match/match-authoring/match-authoring.component.html 12 + + ../../wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.html + 32 + Add Choice @@ -6731,7 +6735,7 @@ ../../wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.html - 49 + 58 @@ -6847,56 +6851,56 @@ Single Answer ../../wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.html - 16 + 20 Multiple Answer ../../wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.html - 19 + 27 There are no choices. Click the "Add Choice" button to add a choice. ../../wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.html - 37 + 46 Choice Text ../../wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.html - 44 + 53 Is Correct ../../wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.html - 67 + 78 Is Correct ../../wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.html - 66 + 77 Feeback ../../wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.html - 74 + 85 Optional ../../wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.html - 79 + 90 diff --git a/src/main/webapp/wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.html b/src/main/webapp/wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.html index 6194971d46..b6502bb22a 100644 --- a/src/main/webapp/wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.html +++ b/src/main/webapp/wise5/components/multipleChoice/multiple-choice-authoring/multiple-choice-authoring.component.html @@ -1,4 +1,4 @@ - + Prompt