├── .gitignore ├── CONTRIBUTING.md ├── LICENSE ├── NOTICE ├── config-overrides.js ├── lib ├── domReady.js ├── humane.js ├── mustache.js ├── picoModal.js ├── require.js ├── sugar-web │ ├── LICENSE │ ├── README.md │ ├── activity │ │ ├── activity.js │ │ └── shortcut.js │ ├── bus.js │ ├── bus │ │ ├── sugarizer.js │ │ └── sugaros.js │ ├── datastore.js │ ├── datastore │ │ ├── sugarizer.js │ │ └── sugaros.js │ ├── dictstore.js │ ├── env.js │ ├── graphics │ │ ├── README.md │ │ ├── activitypalette.html │ │ ├── activitypalette.js │ │ ├── css │ │ │ ├── sugar-200dpi.css │ │ │ ├── sugar-200dpi.less │ │ │ ├── sugar-96dpi.css │ │ │ ├── sugar-96dpi.less │ │ │ └── sugar.less │ │ ├── grid.js │ │ ├── icon.js │ │ ├── icons │ │ │ ├── actions │ │ │ │ ├── activity-abecedarium.svg │ │ │ │ ├── activity-journal.svg │ │ │ │ ├── activity-stop.svg │ │ │ │ ├── checkbox-checked-selected.svg │ │ │ │ ├── checkbox-checked.svg │ │ │ │ ├── checkbox-unchecked-selected.svg │ │ │ │ ├── checkbox-unchecked.svg │ │ │ │ ├── dialog-cancel-active.svg │ │ │ │ ├── dialog-cancel.svg │ │ │ │ ├── dialog-ok-active.svg │ │ │ │ ├── dialog-ok.svg │ │ │ │ ├── entry-cancel-active.svg │ │ │ │ ├── entry-cancel-disabled.svg │ │ │ │ ├── entry-cancel.svg │ │ │ │ ├── photo.svg │ │ │ │ ├── radio-active-selected.svg │ │ │ │ ├── radio-active.svg │ │ │ │ ├── radio-selected.svg │ │ │ │ ├── radio.svg │ │ │ │ ├── zoom-groups.svg │ │ │ │ ├── zoom-home.svg │ │ │ │ └── zoom-neighborhood.svg │ │ │ └── emblems │ │ │ │ ├── arrow-down.svg │ │ │ │ ├── arrow-up.svg │ │ │ │ └── favorite.svg │ │ ├── journalchooser.js │ │ ├── menupalette.html │ │ ├── menupalette.js │ │ ├── palette.js │ │ ├── presencepalette.js │ │ ├── radiobuttonsgroup.js │ │ └── xocolor.js │ ├── package.json │ ├── presence.js │ └── test │ │ ├── functional │ │ ├── datastoreSpec.js │ │ └── toolkitContractSpec.js │ │ ├── graphics │ │ ├── iconSpec.js │ │ ├── menupaletteSpec.js │ │ ├── paletteSpec.js │ │ └── radiobuttonsgroupSpec.js │ │ ├── karma-shared.conf.js │ │ ├── karma-unit.conf.js │ │ ├── karma.conf.js │ │ ├── loader.js │ │ └── unit │ │ ├── busSpec.js │ │ ├── datastoreSpec.js │ │ ├── dictstoreSpec.js │ │ └── envSpec.js ├── text.js └── webL10n.js ├── package-lock.json ├── package.json ├── public ├── activity │ ├── activity-icon.svg │ └── activity.info ├── index.html ├── lib │ ├── domReady.js │ ├── humane.js │ ├── mustache.js │ ├── picoModal.js │ ├── require.js │ ├── sugar-web │ │ ├── LICENSE │ │ ├── README.md │ │ ├── activity │ │ │ ├── activity.js │ │ │ └── shortcut.js │ │ ├── bus.js │ │ ├── bus │ │ │ ├── sugarizer.js │ │ │ └── sugaros.js │ │ ├── datastore.js │ │ ├── datastore │ │ │ ├── sugarizer.js │ │ │ └── sugaros.js │ │ ├── dictstore.js │ │ ├── env.js │ │ ├── graphics │ │ │ ├── README.md │ │ │ ├── activitypalette.html │ │ │ ├── activitypalette.js │ │ │ ├── css │ │ │ │ ├── sugar-200dpi.css │ │ │ │ ├── sugar-200dpi.less │ │ │ │ ├── sugar-96dpi.css │ │ │ │ ├── sugar-96dpi.less │ │ │ │ └── sugar.less │ │ │ ├── evaluationpalette.js │ │ │ ├── grid.js │ │ │ ├── icon.js │ │ │ ├── icons │ │ │ │ ├── actions │ │ │ │ │ ├── activity-abecedarium.svg │ │ │ │ │ ├── activity-journal.svg │ │ │ │ │ ├── activity-stop.svg │ │ │ │ │ ├── checkbox-checked-selected.svg │ │ │ │ │ ├── checkbox-checked.svg │ │ │ │ │ ├── checkbox-unchecked-selected.svg │ │ │ │ │ ├── checkbox-unchecked.svg │ │ │ │ │ ├── dialog-cancel-active.svg │ │ │ │ │ ├── dialog-cancel.svg │ │ │ │ │ ├── dialog-ok-active.svg │ │ │ │ │ ├── dialog-ok.svg │ │ │ │ │ ├── entry-cancel-active.svg │ │ │ │ │ ├── entry-cancel-disabled.svg │ │ │ │ │ ├── entry-cancel.svg │ │ │ │ │ ├── photo.svg │ │ │ │ │ ├── radio-active-selected.svg │ │ │ │ │ ├── radio-active.svg │ │ │ │ │ ├── radio-selected.svg │ │ │ │ │ ├── radio.svg │ │ │ │ │ ├── zoom-groups.svg │ │ │ │ │ ├── zoom-home.svg │ │ │ │ │ └── zoom-neighborhood.svg │ │ │ │ └── emblems │ │ │ │ │ ├── arrow-down.svg │ │ │ │ │ ├── arrow-up.svg │ │ │ │ │ └── favorite.svg │ │ │ ├── journalchooser.js │ │ │ ├── menupalette.html │ │ │ ├── menupalette.js │ │ │ ├── palette.js │ │ │ ├── presencepalette.js │ │ │ ├── radiobuttonsgroup.js │ │ │ └── xocolor.js │ │ ├── package.json │ │ ├── presence.js │ │ └── test │ │ │ ├── functional │ │ │ ├── datastoreSpec.js │ │ │ └── toolkitContractSpec.js │ │ │ ├── graphics │ │ │ ├── iconSpec.js │ │ │ ├── menupaletteSpec.js │ │ │ ├── paletteSpec.js │ │ │ └── radiobuttonsgroupSpec.js │ │ │ ├── karma-shared.conf.js │ │ │ ├── karma-unit.conf.js │ │ │ ├── karma.conf.js │ │ │ ├── loader.js │ │ │ └── unit │ │ │ ├── busSpec.js │ │ │ ├── datastoreSpec.js │ │ │ ├── dictstoreSpec.js │ │ │ └── envSpec.js │ ├── text.js │ └── webL10n.js └── manifest.json ├── readme.md ├── screenshots ├── CLOZE_Player.png ├── Detailed_Results.png ├── FREETEXT_Player.png ├── GROUP_Player.png ├── Home_Screen.png ├── Image_Editor.png ├── MATCH_Player.png ├── MCQPlayer.png ├── MCQ_FORM.png ├── Presence.png ├── REORDER_Form.png ├── REORER_Player.png ├── Scores_Screen.png ├── Template_Screen.png ├── Tutorial.png └── screenshots.gif └── src ├── components ├── DragList.js ├── Exercise.js ├── ExerciseListItem.js ├── ImageEditor.js ├── MainToolbar.js ├── MultimediaJSX.js ├── Navbar.js ├── Tutorial.js ├── UserIcon.js ├── UserList.js └── WithMultimedia.js ├── containers ├── App.js ├── Builders │ ├── CLOZEForm.js │ ├── FreeTextInputForm.js │ ├── GroupAssignmentForm.js │ ├── MCQForm.js │ ├── MatchingForm.js │ ├── REORDERForm.js │ ├── Template.js │ └── WordPuzzleForm.js ├── ExerciseList.js ├── Players │ ├── CLOZEPlayer.js │ ├── FreeTextInputPlayer.js │ ├── GroupAssignmentPlayer.js │ ├── MCQPlayer.js │ ├── MatchingPlayer.js │ ├── REORDERPlayer.js │ └── WordPuzzlePlayer.js ├── Router.js ├── Scores │ ├── PresenceScores.js │ ├── ScoreHoc.js │ └── Scores.js ├── Sugarizer.js └── translation.js ├── css ├── CLOZEForm.css ├── CLOZEPlayer.css ├── DragList.css ├── Evaluation.css ├── Exercise.css ├── ExerciseDragList.css ├── ExerciseList.css ├── FreeTextInputForm.css ├── FreeTextInputPlayer.css ├── GroupAssignmentForm.css ├── GroupAssignmentPlayer.css ├── ImageEditor.css ├── MCQForm.css ├── MCQPlayer.css ├── MatchingForm.css ├── MatchingPlayer.css ├── Navbar.css ├── NewExerciseTemplate.css ├── PresenceScores.css ├── REORDERForm.css ├── REORDERPlayer.css ├── Scores.css ├── Tutorial.css ├── UserList.css ├── WordPuzzleForm.css ├── WordPuzzlePlayer.css ├── index.css └── libnotify.css ├── default_activities.json ├── icons ├── exercise │ ├── ArrowSmall.cur │ ├── audio.svg │ ├── clock.svg │ ├── cog.svg │ ├── correct.png │ ├── delete.svg │ ├── details.png │ ├── dialog-cancel.svg │ ├── dialog-ok.svg │ ├── down.svg │ ├── edit.svg │ ├── eval-mode.svg │ ├── go-left.svg │ ├── go-right.svg │ ├── help.svg │ ├── insert-image.svg │ ├── left.svg │ ├── owner-icon.svg │ ├── percent.svg │ ├── photo.svg │ ├── play-button.svg │ ├── redo.svg │ ├── reorder-drag.png │ ├── reorder-drag.svg2 │ ├── result.svg │ ├── right.svg │ ├── sound_off_black.png │ ├── sound_off_white.png │ ├── sound_on_black.png │ ├── sound_on_white.png │ ├── speak.svg │ ├── sub.svg │ ├── up.svg │ ├── user.svg │ ├── video.svg │ └── wrong.png └── navbar │ ├── activity-icon.svg │ ├── add.svg │ ├── async-mode.svg │ ├── cog.svg │ ├── eval-mode.svg │ ├── export-eval.svg │ ├── fullscreen.svg │ ├── home.svg │ ├── play.svg │ ├── shareAll.svg │ ├── unfullscreen.svg │ ├── zoom-home.svg │ └── zoom-neighborhood.svg ├── index.js ├── media ├── defaultExerciseThumbnail │ ├── images │ │ ├── README.md │ │ ├── animal.png │ │ ├── bark.png │ │ ├── canoe.png │ │ ├── car.png │ │ ├── cat.png │ │ ├── conjugate.jpg │ │ ├── cow.png │ │ ├── discount.png │ │ ├── dog.png │ │ ├── numerals.jpg │ │ ├── plane.png │ │ ├── puzzle.png │ │ ├── sail.png │ │ ├── sheep.png │ │ └── world.png │ ├── sounds │ │ ├── README.md │ │ ├── cat.mp3 │ │ ├── cow.mp3 │ │ ├── dog.mp3 │ │ ├── sheep.mp3 │ │ └── train.mp3 │ └── videos │ │ └── README.md └── template │ ├── cloze_image.svg │ ├── freetext_input_image.svg │ ├── group_image.svg │ ├── list_reorder_image.svg │ ├── matching_pair_image.svg │ ├── mcq_image.svg │ └── word_puzzle_image.svg ├── mespeak_config.json ├── registerServiceWorker.js ├── store ├── actionTypes.js ├── actions │ ├── evaluation.js │ ├── exercises.js │ ├── increment_counter.js │ ├── presence.js │ └── sugarizer.js ├── index.js └── reducers │ ├── current_user.js │ ├── evaluation_exercise.js │ ├── evaluation_mode.js │ ├── exercise_counter.js │ ├── exercises.js │ ├── index.js │ ├── presence │ ├── isHost.js │ ├── isShared.js │ ├── shareAll.js │ ├── sharedExercises.js │ └── users.js │ ├── run_all.js │ └── run_all_exercise_index.js ├── translations ├── en.json ├── es.json ├── fr.json └── lang.js ├── tutorialSteps.js └── utils.js /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | .idea/ 3 | 4 | build/ 5 | 6 | # dependencies 7 | /node_modules 8 | 9 | # testing 10 | /coverage 11 | 12 | # production 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | As with all Open Source software, contributions to Exerciser are welcome. 2 | 3 | An excellent way to contribute is testing and trying Exerciser to find some issues. If you find one, don't be shy; submit your issue [here](https://github.com/llaske/Exerciser/issues) by giving the maximum information on it and precisely detailed steps to reproduce it. We will check your issue and ask you for more information if needed. 4 | 5 | If you're a developer, the best way is to first [setup Exerciser](https://github.com/llaske/ExerciserReact#steps-to-run-project) and then also [setup Sugarizer](https://github.com/llaske/sugarizer/blob/dev/docs/tutorial/VanillaJS/step0.md). Testing the functionality requires using Sugarizer features like `Sharing the Exercise,` `Using Journals,` and many more need Exerciser to be deployed on Sugarizer. 6 | 7 | When you think you're ready, you could try to fix some current issues [here](https://github.com/llaske/Exerciser/issues). If you find a fix, send a Pull Request, and we will be pleased to review it. 8 | 9 | 10 | So to send your Pull Request: 11 | 12 | * [ ] Clone this repository, 13 | * [ ] Fork the **master** branch of your new repository, 14 | * [ ] Update this new branch with your contribution and commit your changes, 15 | * [ ] Send a pull request from your new branch to the dev branch of this repository. 16 | 17 | Few rules to respect when you fix an issue: 18 | 19 | * [ ] Ensure your pull request contains only updates related to the fix, 20 | * [ ] Respect indentation of the original file, 21 | * [ ] Mention the issue number in the pull request but not in the commit message 22 | 23 | 24 | Please note there is no need to ask permission to work on an issue. You should check for pull requests linked to an issue you are addressing; if there are none, assume nobody has done anything. Begin to fix the problem, test, make your commits, push your commits, then make a pull request. These practices allow the competition of ideas and give priority to meritocracy. 25 | 26 | Thanks in advance for your contribution, and become a member of the Exerciser community! -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | Copyright 2018 Mankirat Singh and Avinash Agarwal 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /config-overrides.js: -------------------------------------------------------------------------------- 1 | const CopyWebpackPlugin = require('copy-webpack-plugin'); 2 | 3 | module.exports = function override(config, env) { 4 | let resolvePlugin = (config.resolveLoader ? config.resolveLoader : {}); 5 | resolvePlugin['alias'] = { 6 | 'text': 'text-loader' 7 | }; 8 | config['resolveLoader'] = resolvePlugin; 9 | 10 | let resolvePath = config.resolve.alias; 11 | resolvePath['webL10n'] = 'lib/webL10n'; 12 | resolvePath['sugar-web'] = 'lib/sugar-web'; 13 | resolvePath['mustache'] = 'lib/mustache'; 14 | resolvePath['picoModal'] = 'lib/picoModal'; 15 | config.resolve.alias = resolvePath; 16 | 17 | return config; 18 | } -------------------------------------------------------------------------------- /lib/sugar-web/README.md: -------------------------------------------------------------------------------- 1 | Sugar Web 2 | ========= 3 | 4 | These are the tools that a developer can use to make a web 5 | activity. 6 | 7 | For details see: http://developer.sugarlabs.org/ 8 | -------------------------------------------------------------------------------- /lib/sugar-web/activity/shortcut.js: -------------------------------------------------------------------------------- 1 | define(function () { 2 | 3 | 'use strict'; 4 | 5 | var shortcut = {}; 6 | 7 | shortcut._allShortcuts = []; 8 | 9 | shortcut.add = function (modifiersString, key, callback) { 10 | // Parse the modifiers. For example "Ctrl+Alt" will become 11 | // {'ctrlKey': true, 'altKey': true, 'shiftKey': false} 12 | var modifiersList = modifiersString.toLowerCase().split("+"); 13 | var modifiers = { 14 | 'ctrlKey': modifiersList.indexOf('ctrl') >= 0, 15 | 'altKey': modifiersList.indexOf('alt') >= 0, 16 | 'shiftKey': modifiersList.indexOf('shift') >= 0 17 | }; 18 | 19 | this._allShortcuts.push({ 20 | 'modifiers': modifiers, 21 | 'key': key.toLowerCase(), 22 | 'callback': callback 23 | }); 24 | }; 25 | 26 | document.onkeypress = function (e) { 27 | e = e || window.event; 28 | 29 | var modifiers = { 30 | 'ctrlKey': e.ctrlKey, 31 | 'altKey': e.altKey, 32 | 'shiftKey': e.shiftKey 33 | }; 34 | 35 | // Obtain the key 36 | var charCode; 37 | if (typeof e.which == "number") { 38 | charCode = e.which; 39 | } else { 40 | charCode = e.keyCode; 41 | } 42 | var key = String.fromCharCode(charCode).toLowerCase(); 43 | 44 | // Search for a matching shortcut 45 | for (var i = 0; i < shortcut._allShortcuts.length; i += 1) { 46 | var currentShortcut = shortcut._allShortcuts[i]; 47 | 48 | var match = currentShortcut.key == key && 49 | currentShortcut.modifiers.ctrlKey == modifiers.ctrlKey && 50 | currentShortcut.modifiers.altKey == modifiers.altKey && 51 | currentShortcut.modifiers.shiftKey == modifiers.shiftKey; 52 | if (match) { 53 | currentShortcut.callback(); 54 | return; 55 | } 56 | } 57 | }; 58 | 59 | return shortcut; 60 | }); 61 | -------------------------------------------------------------------------------- /lib/sugar-web/bus.js: -------------------------------------------------------------------------------- 1 | define(["sugar-web/env", "sugar-web/bus/sugarizer", "sugar-web/bus/sugaros"], function(env, sugarizer, sugaros) { 2 | 3 | 'use strict'; 4 | 5 | var bus; 6 | 7 | if (env.isSugarizer()) { 8 | bus = sugarizer; 9 | } else { 10 | bus = sugaros; 11 | } 12 | 13 | return bus; 14 | }); 15 | -------------------------------------------------------------------------------- /lib/sugar-web/bus/sugarizer.js: -------------------------------------------------------------------------------- 1 | define(["sugar-web/env"], function (env) { 2 | 3 | 'use strict'; 4 | 5 | var bus = {}; 6 | 7 | function WebSocketClient(environment) { 8 | } 9 | 10 | WebSocketClient.prototype.send = function (data) { 11 | }; 12 | 13 | WebSocketClient.prototype.close = function () { 14 | }; 15 | 16 | function InputStream() { 17 | } 18 | 19 | InputStream.prototype.open = function (callback) { 20 | }; 21 | 22 | InputStream.prototype.read = function (count, callback) { 23 | }; 24 | 25 | InputStream.prototype.gotData = function (buffer) { 26 | }; 27 | 28 | InputStream.prototype.close = function (callback) { 29 | }; 30 | 31 | function OutputStream() { 32 | } 33 | 34 | OutputStream.prototype.open = function (callback) { 35 | }; 36 | 37 | OutputStream.prototype.write = function (data) { 38 | }; 39 | 40 | OutputStream.prototype.close = function (callback) { 41 | }; 42 | 43 | bus.createInputStream = function (callback) { 44 | }; 45 | 46 | bus.createOutputStream = function (callback) { 47 | }; 48 | 49 | bus.sendMessage = function (method, params, callback) { 50 | if (method == "activity.close") { 51 | window.location = "../../index.html"; 52 | } else if (method == "activity.get_xo_color") { 53 | var color = {stroke: "#FF2B34", fill: "#005FE4"}; 54 | if (typeof chrome != 'undefined' && chrome.app && chrome.app.runtime) { 55 | chrome.storage.local.get("sugar_settings", function(values) { 56 | color = JSON.parse(values.sugar_settings).colorvalue; 57 | callback(null, [[color.stroke, color.fill]]); 58 | }); 59 | } else if (typeof(Storage)!=="undefined" && typeof(window.localStorage)!=="undefined") { 60 | try { 61 | color = JSON.parse(window.localStorage.getItem("sugar_settings")).colorvalue; 62 | } catch(err) {} 63 | } 64 | callback(null, [[color.stroke, color.fill]]); 65 | } 66 | return; 67 | }; 68 | 69 | bus.onNotification = function (method, callback) { 70 | }; 71 | 72 | bus.sendBinary = function (buffer, callback) { 73 | }; 74 | 75 | bus.listen = function (customClient) { 76 | }; 77 | 78 | bus.close = function () { 79 | }; 80 | 81 | return bus; 82 | }); 83 | -------------------------------------------------------------------------------- /lib/sugar-web/datastore.js: -------------------------------------------------------------------------------- 1 | define(["sugar-web/env", "sugar-web/datastore/sugarizer", "sugar-web/datastore/sugaros"], function(env, sugarizer, sugaros) { 2 | 3 | 'use strict'; 4 | 5 | var datastore ; 6 | 7 | if (env.isSugarizer()) { 8 | datastore = sugarizer; 9 | } else { 10 | datastore = sugaros; 11 | } 12 | 13 | return datastore; 14 | }); 15 | -------------------------------------------------------------------------------- /lib/sugar-web/dictstore.js: -------------------------------------------------------------------------------- 1 | define(["sugar-web/activity/activity", "sugar-web/env"], function (activity, env) { 2 | 3 | 'use strict'; 4 | 5 | // This is a helper module that allows to persist key/value data 6 | // using the standard localStorage object. 7 | // 8 | // Usage: 9 | // ------ 10 | // 11 | // // 1. Setup: 12 | // 13 | // dictstore.init(onReadyCallback); 14 | // 15 | // // 2. Use localStorage directly, and then call save(): 16 | // 17 | // var value = localStorage['key']; 18 | // localStorage['key'] = newValue; 19 | // dictstore.save(onSavedCallback); 20 | // 21 | var dictstore = {}; 22 | 23 | dictstore.init = function (callback) { 24 | 25 | if (env.isStandalone()) { 26 | // In standalone mode, use localStorage as is. 27 | callback(); 28 | 29 | } else { 30 | // In Sugar, set localStorage from the datastore. 31 | localStorage.clear(); 32 | 33 | var onLoaded = function (error, metadata, jsonData) { 34 | var data = JSON.parse(jsonData); 35 | for (var i in data) { 36 | localStorage[i] = data[i]; 37 | } 38 | 39 | callback(); 40 | 41 | }; 42 | activity.getDatastoreObject().loadAsText(onLoaded); 43 | } 44 | }; 45 | 46 | // Internally, the key/values are stored as text in the Sugar 47 | // datastore, using the JSON format. 48 | dictstore.save = function (callback) { 49 | if (callback === undefined) { 50 | callback = function () {}; 51 | } 52 | 53 | if (env.isStandalone()) { 54 | // In standalone mode, use localStorage as is. 55 | callback(); 56 | } else { 57 | var datastoreObject = activity.getDatastoreObject(); 58 | var jsonData = JSON.stringify(localStorage); 59 | datastoreObject.setDataAsText(jsonData); 60 | datastoreObject.save(function (error) { 61 | callback(error); 62 | }); 63 | } 64 | }; 65 | 66 | return dictstore; 67 | 68 | }); 69 | -------------------------------------------------------------------------------- /lib/sugar-web/graphics/README.md: -------------------------------------------------------------------------------- 1 | Sugar Graphics 2 | ============== 3 | 4 | Sugar widgets and graphics, implementing the [Sugar Interface 5 | Guidelines](http://wiki.sugarlabs.org/go/Human_Interface_Guidelines). 6 | 7 | Modifying the CSS 8 | ----------------- 9 | 10 | We use [LESS](http://lesscss.org) and then compile the CSS files. 11 | This is to be able to use calculations and variables for colors and 12 | measures. And to be able to output different CSS files for different 13 | screen resolutions. 14 | 15 | To compile the CSS files do: 16 | 17 | lessc graphics/css/sugar-96dpi.less graphics/css/sugar-96dpi.css 18 | lessc graphics/css/sugar-200dpi.less graphics/css/sugar-200dpi.css 19 | 20 | Be sure to compile them before commit. 21 | 22 | The grid helper 23 | --------------- 24 | 25 | The grid is a visual debug tool that allows you to check if the 26 | elements have the correct size. To activate the grid, open the 27 | inspector console and paste the following. 28 | 29 | If RequireJS is not in the page head (ie. in the sugar-web-samples), 30 | load it: 31 | 32 | var script = document.createElement('script'); 33 | script.src = 'lib/require.js'; 34 | document.head.appendChild(script); 35 | 36 | requirejs.config({ baseUrl: "lib" }); 37 | 38 | Then do: 39 | 40 | require(["sugar-web/graphics/grid"], function (grid) { grid.addGrid(11) }); 41 | -------------------------------------------------------------------------------- /lib/sugar-web/graphics/activitypalette.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 |
5 | 6 |
7 |
8 | 9 |
10 | -------------------------------------------------------------------------------- /lib/sugar-web/graphics/activitypalette.js: -------------------------------------------------------------------------------- 1 | define(["sugar-web/graphics/palette", 2 | "text!sugar-web/graphics/activitypalette.html"], function (palette, template) { 3 | 4 | 'use strict'; 5 | 6 | var activitypalette = {}; 7 | 8 | activitypalette.ActivityPalette = function (activityButton, 9 | datastoreObject) { 10 | 11 | palette.Palette.call(this, activityButton); 12 | 13 | var activityTitle; 14 | var descriptionLabel; 15 | var descriptionBox; 16 | 17 | this.getPalette().id = "activity-palette"; 18 | 19 | var containerElem = document.createElement('div'); 20 | containerElem.innerHTML = template; 21 | this.setContent([containerElem]); 22 | 23 | this.titleElem = containerElem.querySelector('#title'); 24 | this.descriptionElem = containerElem.querySelector('#description'); 25 | 26 | this.titleElem.onblur = function () { 27 | datastoreObject.setMetadata({ 28 | "title": this.value, 29 | "title_set_by_user": "1" 30 | }); 31 | datastoreObject.save(); 32 | }; 33 | 34 | this.descriptionElem.onblur = function () { 35 | datastoreObject.setMetadata({ 36 | "description": this.value 37 | }); 38 | datastoreObject.save(); 39 | }; 40 | }; 41 | 42 | // Fill the text inputs with the received metadata. 43 | var setTitleDescription = function (metadata) { 44 | this.titleElem.value = metadata.title; 45 | 46 | if (metadata.description !== undefined) { 47 | this.descriptionElem.value = metadata.description; 48 | } 49 | }; 50 | 51 | activitypalette.ActivityPalette.prototype = 52 | Object.create(palette.Palette.prototype, { 53 | setTitleDescription: { 54 | value: setTitleDescription, 55 | enumerable: true, 56 | configurable: true, 57 | writable: true 58 | } 59 | }); 60 | 61 | return activitypalette; 62 | }); 63 | -------------------------------------------------------------------------------- /lib/sugar-web/graphics/css/sugar-200dpi.less: -------------------------------------------------------------------------------- 1 | @subcell-size: 15px; 2 | @import "sugar.less"; 3 | -------------------------------------------------------------------------------- /lib/sugar-web/graphics/css/sugar-96dpi.less: -------------------------------------------------------------------------------- 1 | @subcell-size: 11px; 2 | @import "sugar.less"; 3 | -------------------------------------------------------------------------------- /lib/sugar-web/graphics/grid.js: -------------------------------------------------------------------------------- 1 | define(function () { 2 | 3 | 'use strict'; 4 | 5 | var grid = {}; 6 | 7 | // Add a grid overlay with lines spaced by subcellSize, for visual 8 | // debugging. This is useful while doing the activity layout or 9 | // while developing widgets. 10 | grid.addGrid = function (subcellSize) { 11 | var canvas = document.createElement('canvas'); 12 | canvas.className = "grid"; 13 | document.body.appendChild(canvas); 14 | 15 | var updateGrid = function () { 16 | canvas.width = window.innerWidth; 17 | canvas.height = window.innerHeight; 18 | 19 | var ctx = canvas.getContext("2d"); 20 | ctx.strokeStyle = "#00FFFF"; 21 | 22 | var subcellsVertical = window.innerHeight / subcellSize; 23 | for (i = 0; i < subcellsVertical; i++) { 24 | if ((i + 1) % 5 === 0) { 25 | ctx.lineWidth = 1; 26 | } else { 27 | ctx.lineWidth = 0.5; 28 | } 29 | ctx.beginPath(); 30 | ctx.moveTo(0, subcellSize * (i + 1)); 31 | ctx.lineTo(canvas.width, subcellSize * (i + 1)); 32 | ctx.stroke(); 33 | } 34 | 35 | var subcellsHorizontal = window.innerWidth / subcellSize; 36 | for (i = 0; i < subcellsHorizontal; i++) { 37 | if ((i + 1) % 5 === 0) { 38 | ctx.lineWidth = 1; 39 | } else { 40 | ctx.lineWidth = 0.5; 41 | } 42 | ctx.beginPath(); 43 | ctx.moveTo(subcellSize * (i + 1), 0); 44 | ctx.lineTo(subcellSize * (i + 1), canvas.height); 45 | ctx.stroke(); 46 | } 47 | }; 48 | 49 | updateGrid(); 50 | 51 | window.onresize = function (event) { 52 | updateGrid(); 53 | }; 54 | }; 55 | 56 | return grid; 57 | }); 58 | -------------------------------------------------------------------------------- /lib/sugar-web/graphics/icons/actions/activity-journal.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | ]> 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /lib/sugar-web/graphics/icons/actions/activity-stop.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | ]> 5 | 6 | -------------------------------------------------------------------------------- /lib/sugar-web/graphics/icons/actions/checkbox-checked-selected.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | ]> 6 | 12 | 15 | 22 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /lib/sugar-web/graphics/icons/actions/checkbox-checked.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | ]> 6 | 12 | 15 | 22 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /lib/sugar-web/graphics/icons/actions/checkbox-unchecked-selected.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | ]> 5 | 11 | 14 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /lib/sugar-web/graphics/icons/actions/checkbox-unchecked.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | ]> 5 | 11 | 14 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /lib/sugar-web/graphics/icons/actions/dialog-cancel-active.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | ]> 5 | 6 | 7 | -------------------------------------------------------------------------------- /lib/sugar-web/graphics/icons/actions/dialog-cancel.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | ]> 5 | 6 | -------------------------------------------------------------------------------- /lib/sugar-web/graphics/icons/actions/dialog-ok-active.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | ]> 5 | 6 | 7 | -------------------------------------------------------------------------------- /lib/sugar-web/graphics/icons/actions/dialog-ok.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | ]> 5 | 6 | -------------------------------------------------------------------------------- /lib/sugar-web/graphics/icons/actions/entry-cancel-active.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | ]> 6 | 24 | -------------------------------------------------------------------------------- /lib/sugar-web/graphics/icons/actions/entry-cancel-disabled.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | ]> 6 | 17 | 21 | 22 | -------------------------------------------------------------------------------- /lib/sugar-web/graphics/icons/actions/entry-cancel.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | ]> 6 | 17 | 21 | 22 | -------------------------------------------------------------------------------- /lib/sugar-web/graphics/icons/actions/radio-active-selected.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | ]> 6 | 12 | 15 | 20 | 25 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /lib/sugar-web/graphics/icons/actions/radio-active.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | ]> 6 | 12 | 15 | 20 | 25 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /lib/sugar-web/graphics/icons/actions/radio-selected.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | ]> 6 | 12 | 15 | 20 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /lib/sugar-web/graphics/icons/actions/radio.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | ]> 6 | 12 | 15 | 20 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /lib/sugar-web/graphics/icons/actions/zoom-groups.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | ]> 5 | 6 | -------------------------------------------------------------------------------- /lib/sugar-web/graphics/icons/actions/zoom-home.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | ]> 5 | 6 | -------------------------------------------------------------------------------- /lib/sugar-web/graphics/icons/actions/zoom-neighborhood.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | ]> 5 | 6 | -------------------------------------------------------------------------------- /lib/sugar-web/graphics/icons/emblems/arrow-down.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | ]> 6 | 12 | 15 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /lib/sugar-web/graphics/icons/emblems/arrow-up.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | ]> 6 | 12 | 15 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /lib/sugar-web/graphics/icons/emblems/favorite.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | ]> 5 | 6 | -------------------------------------------------------------------------------- /lib/sugar-web/graphics/menupalette.html: -------------------------------------------------------------------------------- 1 | {{#.}} 2 |
  • 3 | 7 |
  • 8 | {{/.}} 9 | -------------------------------------------------------------------------------- /lib/sugar-web/graphics/menupalette.js: -------------------------------------------------------------------------------- 1 | define(["sugar-web/graphics/palette", 2 | "text!sugar-web/graphics/menupalette.html", "mustache"], function (palette, template, mustache) { 3 | 4 | 'use strict'; 5 | 6 | var menupalette = {}; 7 | 8 | menupalette.MenuPalette = function (invoker, primaryText, menuData) { 9 | palette.Palette.call(this, invoker, primaryText); 10 | 11 | this.selectItemEvent = document.createEvent("CustomEvent"); 12 | this.selectItemEvent.initCustomEvent('selectItem', true, true, { 13 | 'item': undefined 14 | }); 15 | 16 | var menuElem = document.createElement('ul'); 17 | menuElem.className = "menu"; 18 | menuElem.innerHTML = mustache.render(template, menuData); 19 | this.setContent([menuElem]); 20 | 21 | // Pop-down the palette when a item in the menu is clicked. 22 | 23 | this.buttons = menuElem.querySelectorAll('button'); 24 | 25 | var that = this; 26 | 27 | function popDownOnButtonClick(event) { 28 | that.selectItemEvent.detail.target = event.target; 29 | that.getPalette().dispatchEvent(that.selectItemEvent); 30 | that.popDown(); 31 | } 32 | 33 | for (var i = 0; i < this.buttons.length; i++) { 34 | this.buttons[i].addEventListener('click', popDownOnButtonClick); 35 | } 36 | }; 37 | 38 | var addEventListener = function (type, listener, useCapture) { 39 | return this.getPalette().addEventListener(type, listener, useCapture); 40 | }; 41 | 42 | menupalette.MenuPalette.prototype = 43 | Object.create(palette.Palette.prototype, { 44 | addEventListener: { 45 | value: addEventListener, 46 | enumerable: true, 47 | configurable: true, 48 | writable: true 49 | } 50 | }); 51 | 52 | return menupalette; 53 | }); 54 | -------------------------------------------------------------------------------- /lib/sugar-web/graphics/radiobuttonsgroup.js: -------------------------------------------------------------------------------- 1 | define(function () { 2 | 3 | 'use strict'; 4 | 5 | var radioButtonsGroup = {}; 6 | 7 | // ## RadioButtonsGroup 8 | // 9 | // A group of elements where only one can be active at the same 10 | // time. 11 | // 12 | // When an element is clicked, it becomes the active one. The 13 | // active element gains the 'active' CSS class. 14 | // 15 | // Parameters: 16 | // 17 | // * **elems** Array of elements of the group. 18 | radioButtonsGroup.RadioButtonsGroup = function (elems) { 19 | this.elems = elems; 20 | var active; 21 | 22 | for (var i = 0; i < elems.length; i++) { 23 | var elem = elems[i]; 24 | elem.addEventListener("click", clickHandler); 25 | 26 | // The first element that has 'active' CSS class is made 27 | // the active of the group on startup. 28 | if (active === undefined && elem.classList.contains('active')) { 29 | active = elem; 30 | } 31 | } 32 | 33 | // If no element has 'active' CSS class, the first element of 34 | // the array is made the active. 35 | if (active === undefined) { 36 | active = elems[0]; 37 | updateClasses(); 38 | } 39 | 40 | function clickHandler(evt) { 41 | active = evt.target; 42 | updateClasses(); 43 | } 44 | 45 | function updateClasses() { 46 | for (i = 0; i < elems.length; i++) { 47 | var elem = elems[i]; 48 | elem.classList.remove('active'); 49 | } 50 | active.classList.add('active'); 51 | } 52 | 53 | // Get the active element. 54 | this.getActive = function () { 55 | return active; 56 | }; 57 | 58 | }; 59 | 60 | return radioButtonsGroup; 61 | 62 | }); 63 | -------------------------------------------------------------------------------- /lib/sugar-web/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "volo": { 3 | "baseUrl": "lib", 4 | "dependencies": { 5 | "webL10n": "github:sugarlabs/webL10n", 6 | "mustache": "github:janl/mustache.js/0.7.2", 7 | "text": "github:requirejs/text" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /lib/sugar-web/test/functional/toolkitContractSpec.js: -------------------------------------------------------------------------------- 1 | define(["sugar-web/env"], function (env) { 2 | 3 | 'use strict'; 4 | 5 | describe("Environment object", function () { 6 | 7 | it("should have valid properties", function () { 8 | //FIXME: we shouldn't stub this here. 9 | //current implementation of isStandalone fails with sugar-web-test 10 | spyOn(env, 'isStandalone').andReturn(false); 11 | 12 | var expectedEnv; 13 | 14 | runs(function () { 15 | env.getEnvironment(function (error, environment) { 16 | expectedEnv = environment; 17 | }); 18 | }); 19 | 20 | waitsFor(function () { 21 | return expectedEnv !== undefined; 22 | }, "should get sugar environment"); 23 | 24 | runs(function () { 25 | expect(expectedEnv.bundleId).not.toBeUndefined(); 26 | expect(expectedEnv.activityId).not.toBeUndefined(); 27 | expect(expectedEnv.activityName).not.toBeUndefined(); 28 | }); 29 | }); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /lib/sugar-web/test/graphics/iconSpec.js: -------------------------------------------------------------------------------- 1 | define(["sugar-web/graphics/icon"], function (icon) { 2 | 3 | 'use strict'; 4 | 5 | describe("icon", function () { 6 | var wasLoaded; 7 | var iconUrlResult; 8 | 9 | it("should be able to change icon more than once", function () { 10 | var elem = document.createElement('div'); 11 | var iconUrl; 12 | 13 | function callback(url) { 14 | iconUrlResult = url; 15 | wasLoaded = true; 16 | } 17 | 18 | runs(function () { 19 | wasLoaded = false; 20 | iconUrl = "/base/graphics/icons/actions/dialog-ok-active.svg"; 21 | var iconInfo = { 22 | "uri": iconUrl, 23 | "strokeColor": '#B20008', 24 | "fillColor": '#FF2B34' 25 | }; 26 | icon.load(iconInfo, callback); 27 | }); 28 | 29 | waitsFor(function () { 30 | return wasLoaded; 31 | }, "icon loaded"); 32 | 33 | runs(function () { 34 | expect(iconUrlResult).not.toBe(iconUrl); 35 | }); 36 | 37 | runs(function () { 38 | wasLoaded = false; 39 | iconUrl = iconUrlResult; 40 | var iconInfo = { 41 | "uri": iconUrl, 42 | "strokeColor": '#FF2B34', 43 | "fillColor": '#B20008' 44 | }; 45 | icon.load(iconInfo, callback); 46 | }); 47 | 48 | waitsFor(function () { 49 | return wasLoaded; 50 | }, "icon loaded"); 51 | 52 | runs(function () { 53 | expect(iconUrlResult).not.toBe(iconUrl); 54 | }); 55 | 56 | }); 57 | }); 58 | 59 | }); 60 | -------------------------------------------------------------------------------- /lib/sugar-web/test/graphics/paletteSpec.js: -------------------------------------------------------------------------------- 1 | define(["sugar-web/graphics/palette"], function (palette) { 2 | 3 | 'use strict'; 4 | 5 | describe("palette", function () { 6 | it("should start down", function () { 7 | var invoker = document.createElement('button'); 8 | var myPalette = new palette.Palette(invoker); 9 | expect(myPalette.isDown()).toBe(true); 10 | }); 11 | 12 | it("should toggle", function () { 13 | var invoker = document.createElement('button'); 14 | var myPalette = new palette.Palette(invoker); 15 | myPalette.toggle(); 16 | expect(myPalette.isDown()).toBe(false); 17 | myPalette.toggle(); 18 | expect(myPalette.isDown()).toBe(true); 19 | }); 20 | 21 | it("if one palette in a group popups, the others popdown", function () { 22 | var invokerA = document.createElement('button'); 23 | var invokerB = document.createElement('button'); 24 | var myPaletteA = new palette.Palette(invokerA); 25 | var myPaletteB = new palette.Palette(invokerB); 26 | myPaletteA.toggle(); 27 | expect(myPaletteA.isDown()).toBe(false); 28 | expect(myPaletteB.isDown()).toBe(true); 29 | myPaletteB.toggle(); 30 | expect(myPaletteA.isDown()).toBe(true); 31 | expect(myPaletteB.isDown()).toBe(false); 32 | }); 33 | 34 | }); 35 | 36 | }); 37 | -------------------------------------------------------------------------------- /lib/sugar-web/test/karma-shared.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration 2 | // Generated on Mon May 13 2013 10:01:17 GMT-0300 (ART) 3 | 4 | 5 | // list of files / patterns to load in the browser 6 | module.exports = function (config) { 7 | config.set({ 8 | frameworks: ['jasmine', 'requirejs'], 9 | 10 | 11 | // base path, that will be used to resolve files and exclude 12 | basePath: '..', 13 | 14 | 15 | files: [ 16 | 'test/loader.js', 17 | { 18 | pattern: 'lib/**/*.js', 19 | included: false 20 | }, { 21 | pattern: '*.js', 22 | included: false 23 | }, { 24 | pattern: 'activity/**/*.js', 25 | included: false 26 | }, { 27 | pattern: 'graphics/**/*', 28 | included: false 29 | } 30 | ], 31 | 32 | 33 | // list of files to exclude 34 | exclude: [], 35 | 36 | 37 | // test results reporter to use 38 | // possible values: 'dots', 'progress', 'junit' 39 | reporters: ['progress'], 40 | 41 | 42 | // web server port 43 | port: 9876, 44 | 45 | 46 | // cli runner port 47 | runnerPort: 9100, 48 | 49 | 50 | // enable / disable colors in the output (reporters and logs) 51 | colors: true, 52 | 53 | 54 | // level of logging 55 | // possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || 56 | // LOG_DEBUG 57 | logLevel: config.LOG_INFO, 58 | 59 | 60 | // enable / disable watching file and executing tests whenever any file 61 | // changes 62 | autoWatch: true, 63 | 64 | 65 | // If browser does not capture in given timeout [ms], kill it 66 | captureTimeout: 60000, 67 | 68 | 69 | // Continuous Integration mode 70 | // if true, it capture browsers, run tests and exit 71 | singleRun: false, 72 | 73 | preprocessors: {} 74 | }); 75 | }; 76 | -------------------------------------------------------------------------------- /lib/sugar-web/test/karma-unit.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration for unit tests 2 | 3 | sharedConfig = require("./karma-shared.conf.js"); 4 | 5 | module.exports = function (config) { 6 | var testFiles = [ 7 | { 8 | pattern: 'test/unit/*Spec.js', 9 | included: false 10 | }, { 11 | pattern: 'test/graphics/*Spec.js', 12 | included: false 13 | }, 14 | ]; 15 | 16 | sharedConfig(config); 17 | 18 | config.files = config.files.concat(testFiles); 19 | }; 20 | -------------------------------------------------------------------------------- /lib/sugar-web/test/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration for all the tests 2 | 3 | sharedConfig = require("./karma-shared.conf.js"); 4 | 5 | module.exports = function (config) { 6 | var testFiles = [ 7 | { 8 | pattern: 'test/**/*Spec.js', 9 | included: false 10 | } 11 | ]; 12 | 13 | sharedConfig(config); 14 | 15 | config.files = config.files.concat(testFiles); 16 | }; 17 | -------------------------------------------------------------------------------- /lib/sugar-web/test/loader.js: -------------------------------------------------------------------------------- 1 | var tests = Object.keys(window.__karma__.files).filter(function (file) { 2 | return (/Spec\.js$/).test(file); 3 | }); 4 | 5 | requirejs.config({ 6 | // Karma serves files from '/base' 7 | baseUrl: "/base", 8 | 9 | paths: { 10 | "sugar-web": ".", 11 | "mustache": "lib/mustache", 12 | "text": "lib/text", 13 | "webL10n": "lib/webL10n" 14 | }, 15 | 16 | // ask Require.js to load these files (all our tests) 17 | deps: tests, 18 | 19 | // start test run, once Require.js is done 20 | callback: window.__karma__.start 21 | }); 22 | -------------------------------------------------------------------------------- /lib/sugar-web/test/unit/datastoreSpec.js: -------------------------------------------------------------------------------- 1 | define(["sugar-web/env", "sugar-web/datastore"], function (env, datastore) { 2 | 3 | 'use strict'; 4 | 5 | describe("Ensure the datastore object has an objectId", function () { 6 | 7 | // FIXME does not work in standalone mode 8 | it("should have objectId", function () { 9 | var objectId = "objectId"; 10 | spyOn(env, "getObjectId").andCallFake(function (callback) { 11 | setTimeout(function () { 12 | callback(objectId); 13 | }, 0); 14 | }); 15 | var callback = jasmine.createSpy(); 16 | 17 | var datastoreObject = new datastore.DatastoreObject(); 18 | 19 | runs(function () { 20 | datastoreObject.ensureObjectId(callback); 21 | }); 22 | 23 | waitsFor(function () { 24 | return datastoreObject.objectId !== undefined; 25 | }, "should have objectId received from the environment"); 26 | 27 | runs(function () { 28 | expect(callback).toHaveBeenCalled(); 29 | }); 30 | }); 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /lib/sugar-web/test/unit/dictstoreSpec.js: -------------------------------------------------------------------------------- 1 | define(["sugar-web/dictstore", "sugar-web/env"], function (dictstore, env) { 2 | 3 | 'use strict'; 4 | 5 | describe("dictstore on standalone mode", function () { 6 | 7 | beforeEach(function () { 8 | spyOn(env, 'isStandalone').andReturn(true); 9 | }); 10 | 11 | describe("init method", function () { 12 | 13 | it("should execute callback", function () { 14 | var callback = jasmine.createSpy(); 15 | 16 | dictstore.init(callback); 17 | expect(callback).toHaveBeenCalled(); 18 | }); 19 | 20 | it("should maintain localStorage", function () { 21 | localStorage.testKey = "test"; 22 | 23 | dictstore.init(function () {}); 24 | expect(localStorage.testKey).toBe("test"); 25 | }); 26 | }); 27 | 28 | describe("save method", function () { 29 | 30 | it("should just execute the callback", function () { 31 | var callbackExecuted; 32 | 33 | localStorage.test_key = "test"; 34 | 35 | runs(function () { 36 | callbackExecuted = false; 37 | 38 | dictstore.save(function () { 39 | callbackExecuted = true; 40 | }); 41 | }); 42 | 43 | waitsFor(function () { 44 | return callbackExecuted === true; 45 | }, "The callback should executed"); 46 | 47 | runs(function () { 48 | expect(localStorage.test_key).toBe("test"); 49 | }); 50 | }); 51 | }); 52 | 53 | }); 54 | }); 55 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "exerciser", 3 | "version": "0.1.0", 4 | "private": true, 5 | "homepage": ".", 6 | "dependencies": { 7 | "ajv": "^8.11.0", 8 | "bootstrap": "^4.1.1", 9 | "chart.js": "^2.9.4", 10 | "copy-webpack-plugin": "^11.0.0", 11 | "cropper": "^4.1.0", 12 | "font-awesome": "^4.7.0", 13 | "interactjs": "^1.10.14", 14 | "intro.js": "5.1.0", 15 | "intro.js-react": "^0.7.1", 16 | "jquery": "^3.6.0", 17 | "jsplumb": "^2.15.6", 18 | "lz-string": "^1.4.4", 19 | "mespeak": "^2.0.2", 20 | "picomodal": "^3.0.0", 21 | "popper.js": "^1.14.3", 22 | "react": "^17.0.2", 23 | "react-app-rewired": "^2.1.3", 24 | "react-beautiful-dnd": "^8.0.5", 25 | "react-burger-menu": "^3.0.8", 26 | "react-chartjs-2": "^2.11.2", 27 | "react-dom": "^17.0.2", 28 | "react-intl": "^2.9.0", 29 | "react-modal": "^3.15.1", 30 | "react-redux": "^5.1.2", 31 | "react-router-dom": "^4.3.1", 32 | "react-scripts": "^1.1.4", 33 | "react-select": "^1.2.1", 34 | "react-tooltip": "^4.2.21", 35 | "react-use-dimensions": "^1.2.1", 36 | "redux": "^4.2.0", 37 | "source-map-explorer": "^2.5.2", 38 | "styled-components": "^5.3.5", 39 | "text-loader": "0.0.1" 40 | }, 41 | "scripts": { 42 | "analyze": "source-map-explorer 'build/static/js/*.js'", 43 | "start": "react-app-rewired start --scripts-version react-scripts", 44 | "build": "react-app-rewired build --scripts-version react-scripts", 45 | "test": "react-app-rewired test --env=jsdom --scripts-version react-scripts", 46 | "eject": "react-scripts eject" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /public/activity/activity.info: -------------------------------------------------------------------------------- 1 | [Activity] 2 | name = Exerciser 3 | activity_version = 1 4 | bundle_id = org.sugarlabs.Exerciser 5 | exec = sugar-activity-web 6 | icon = activity-icon 7 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Exerciser Activity 6 | 7 | 9 | 11 | 12 | 13 | 14 | 17 |
    18 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /public/lib/sugar-web/README.md: -------------------------------------------------------------------------------- 1 | Sugar Web 2 | ========= 3 | 4 | These are the tools that a developer can use to make a web 5 | activity. 6 | 7 | For details see: http://developer.sugarlabs.org/ 8 | -------------------------------------------------------------------------------- /public/lib/sugar-web/activity/shortcut.js: -------------------------------------------------------------------------------- 1 | define(function () { 2 | 3 | 'use strict'; 4 | 5 | var shortcut = {}; 6 | 7 | shortcut._allShortcuts = []; 8 | 9 | shortcut.add = function (modifiersString, key, callback) { 10 | // Parse the modifiers. For example "Ctrl+Alt" will become 11 | // {'ctrlKey': true, 'altKey': true, 'shiftKey': false} 12 | var modifiersList = modifiersString.toLowerCase().split("+"); 13 | var modifiers = { 14 | 'ctrlKey': modifiersList.indexOf('ctrl') >= 0, 15 | 'altKey': modifiersList.indexOf('alt') >= 0, 16 | 'shiftKey': modifiersList.indexOf('shift') >= 0 17 | }; 18 | 19 | this._allShortcuts.push({ 20 | 'modifiers': modifiers, 21 | 'key': key.toLowerCase(), 22 | 'callback': callback 23 | }); 24 | }; 25 | 26 | document.onkeypress = function (e) { 27 | e = e || window.event; 28 | 29 | var modifiers = { 30 | 'ctrlKey': e.ctrlKey, 31 | 'altKey': e.altKey, 32 | 'shiftKey': e.shiftKey 33 | }; 34 | 35 | // Obtain the key 36 | var charCode; 37 | if (typeof e.which == "number") { 38 | charCode = e.which; 39 | } else { 40 | charCode = e.keyCode; 41 | } 42 | var key = String.fromCharCode(charCode).toLowerCase(); 43 | 44 | // Search for a matching shortcut 45 | for (var i = 0; i < shortcut._allShortcuts.length; i += 1) { 46 | var currentShortcut = shortcut._allShortcuts[i]; 47 | 48 | var match = currentShortcut.key == key && 49 | currentShortcut.modifiers.ctrlKey == modifiers.ctrlKey && 50 | currentShortcut.modifiers.altKey == modifiers.altKey && 51 | currentShortcut.modifiers.shiftKey == modifiers.shiftKey; 52 | if (match) { 53 | currentShortcut.callback(); 54 | return; 55 | } 56 | } 57 | }; 58 | 59 | return shortcut; 60 | }); 61 | -------------------------------------------------------------------------------- /public/lib/sugar-web/bus.js: -------------------------------------------------------------------------------- 1 | define(["sugar-web/env", "sugar-web/bus/sugarizer", "sugar-web/bus/sugaros"], function(env, sugarizer, sugaros) { 2 | 3 | 'use strict'; 4 | 5 | var bus; 6 | 7 | if (env.isSugarizer()) { 8 | bus = sugarizer; 9 | } else { 10 | bus = sugaros; 11 | } 12 | 13 | return bus; 14 | }); 15 | -------------------------------------------------------------------------------- /public/lib/sugar-web/bus/sugarizer.js: -------------------------------------------------------------------------------- 1 | define(["sugar-web/env"], function (env) { 2 | 3 | 'use strict'; 4 | 5 | var bus = {}; 6 | 7 | function WebSocketClient(environment) { 8 | } 9 | 10 | WebSocketClient.prototype.send = function (data) { 11 | }; 12 | 13 | WebSocketClient.prototype.close = function () { 14 | }; 15 | 16 | function InputStream() { 17 | } 18 | 19 | InputStream.prototype.open = function (callback) { 20 | }; 21 | 22 | InputStream.prototype.read = function (count, callback) { 23 | }; 24 | 25 | InputStream.prototype.gotData = function (buffer) { 26 | }; 27 | 28 | InputStream.prototype.close = function (callback) { 29 | }; 30 | 31 | function OutputStream() { 32 | } 33 | 34 | OutputStream.prototype.open = function (callback) { 35 | }; 36 | 37 | OutputStream.prototype.write = function (data) { 38 | }; 39 | 40 | OutputStream.prototype.close = function (callback) { 41 | }; 42 | 43 | bus.createInputStream = function (callback) { 44 | }; 45 | 46 | bus.createOutputStream = function (callback) { 47 | }; 48 | 49 | bus.sendMessage = function (method, params, callback) { 50 | if (method == "activity.close") { 51 | window.location = "../../index.html"; 52 | } else if (method == "activity.get_xo_color") { 53 | var color = {stroke: "#FF2B34", fill: "#005FE4"}; 54 | if (typeof chrome != 'undefined' && chrome.app && chrome.app.runtime) { 55 | chrome.storage.local.get("sugar_settings", function(values) { 56 | color = JSON.parse(values.sugar_settings).colorvalue; 57 | callback(null, [[color.stroke, color.fill]]); 58 | }); 59 | } else if (typeof(Storage)!=="undefined" && typeof(window.localStorage)!=="undefined") { 60 | try { 61 | color = JSON.parse(window.localStorage.getItem("sugar_settings")).colorvalue; 62 | } catch(err) {} 63 | } 64 | callback(null, [[color.stroke, color.fill]]); 65 | } 66 | return; 67 | }; 68 | 69 | bus.onNotification = function (method, callback) { 70 | }; 71 | 72 | bus.sendBinary = function (buffer, callback) { 73 | }; 74 | 75 | bus.listen = function (customClient) { 76 | }; 77 | 78 | bus.close = function () { 79 | }; 80 | 81 | return bus; 82 | }); 83 | -------------------------------------------------------------------------------- /public/lib/sugar-web/datastore.js: -------------------------------------------------------------------------------- 1 | define(["sugar-web/env", "sugar-web/datastore/sugarizer", "sugar-web/datastore/sugaros"], function(env, sugarizer, sugaros) { 2 | 3 | 'use strict'; 4 | 5 | var datastore ; 6 | 7 | if (env.isSugarizer()) { 8 | datastore = sugarizer; 9 | } else { 10 | datastore = sugaros; 11 | } 12 | 13 | return datastore; 14 | }); 15 | -------------------------------------------------------------------------------- /public/lib/sugar-web/dictstore.js: -------------------------------------------------------------------------------- 1 | define(["sugar-web/activity/activity", "sugar-web/env"], function (activity, env) { 2 | 3 | 'use strict'; 4 | 5 | // This is a helper module that allows to persist key/value data 6 | // using the standard localStorage object. 7 | // 8 | // Usage: 9 | // ------ 10 | // 11 | // // 1. Setup: 12 | // 13 | // dictstore.init(onReadyCallback); 14 | // 15 | // // 2. Use localStorage directly, and then call save(): 16 | // 17 | // var value = localStorage['key']; 18 | // localStorage['key'] = newValue; 19 | // dictstore.save(onSavedCallback); 20 | // 21 | var dictstore = {}; 22 | 23 | dictstore.init = function (callback) { 24 | 25 | if (env.isStandalone()) { 26 | // In standalone mode, use localStorage as is. 27 | callback(); 28 | 29 | } else { 30 | // In Sugar, set localStorage from the datastore. 31 | localStorage.clear(); 32 | 33 | var onLoaded = function (error, metadata, jsonData) { 34 | var data = JSON.parse(jsonData); 35 | for (var i in data) { 36 | localStorage[i] = data[i]; 37 | } 38 | 39 | callback(); 40 | 41 | }; 42 | activity.getDatastoreObject().loadAsText(onLoaded); 43 | } 44 | }; 45 | 46 | // Internally, the key/values are stored as text in the Sugar 47 | // datastore, using the JSON format. 48 | dictstore.save = function (callback) { 49 | if (callback === undefined) { 50 | callback = function () {}; 51 | } 52 | 53 | if (env.isStandalone()) { 54 | // In standalone mode, use localStorage as is. 55 | callback(); 56 | } else { 57 | var datastoreObject = activity.getDatastoreObject(); 58 | var jsonData = JSON.stringify(localStorage); 59 | datastoreObject.setDataAsText(jsonData); 60 | datastoreObject.save(function (error) { 61 | callback(error); 62 | }); 63 | } 64 | }; 65 | 66 | return dictstore; 67 | 68 | }); 69 | -------------------------------------------------------------------------------- /public/lib/sugar-web/graphics/README.md: -------------------------------------------------------------------------------- 1 | Sugar Graphics 2 | ============== 3 | 4 | Sugar widgets and graphics, implementing the [Sugar Interface 5 | Guidelines](http://wiki.sugarlabs.org/go/Human_Interface_Guidelines). 6 | 7 | Modifying the CSS 8 | ----------------- 9 | 10 | We use [LESS](http://lesscss.org) and then compile the CSS files. 11 | This is to be able to use calculations and variables for colors and 12 | measures. And to be able to output different CSS files for different 13 | screen resolutions. 14 | 15 | To compile the CSS files do: 16 | 17 | lessc graphics/css/sugar-96dpi.less graphics/css/sugar-96dpi.css 18 | lessc graphics/css/sugar-200dpi.less graphics/css/sugar-200dpi.css 19 | 20 | Be sure to compile them before commit. 21 | 22 | The grid helper 23 | --------------- 24 | 25 | The grid is a visual debug tool that allows you to check if the 26 | elements have the correct size. To activate the grid, open the 27 | inspector console and paste the following. 28 | 29 | If RequireJS is not in the page head (ie. in the sugar-web-samples), 30 | load it: 31 | 32 | var script = document.createElement('script'); 33 | script.src = 'lib/require.js'; 34 | document.head.appendChild(script); 35 | 36 | requirejs.config({ baseUrl: "lib" }); 37 | 38 | Then do: 39 | 40 | require(["sugar-web/graphics/grid"], function (grid) { grid.addGrid(11) }); 41 | -------------------------------------------------------------------------------- /public/lib/sugar-web/graphics/activitypalette.html: -------------------------------------------------------------------------------- 1 |
    2 | 3 |
    4 |
    5 | 6 |
    7 |
    8 | 9 |
    10 | -------------------------------------------------------------------------------- /public/lib/sugar-web/graphics/activitypalette.js: -------------------------------------------------------------------------------- 1 | define(["sugar-web/graphics/palette", 2 | "text!sugar-web/graphics/activitypalette.html"], function (palette, template) { 3 | 4 | 'use strict'; 5 | 6 | var activitypalette = {}; 7 | 8 | activitypalette.ActivityPalette = function (activityButton, 9 | datastoreObject) { 10 | 11 | palette.Palette.call(this, activityButton); 12 | 13 | var activityTitle; 14 | var descriptionLabel; 15 | var descriptionBox; 16 | 17 | this.getPalette().id = "activity-palette"; 18 | 19 | var containerElem = document.createElement('div'); 20 | containerElem.innerHTML = template; 21 | this.setContent([containerElem]); 22 | 23 | this.titleElem = containerElem.querySelector('#title'); 24 | this.descriptionElem = containerElem.querySelector('#description'); 25 | 26 | this.titleElem.onblur = function () { 27 | datastoreObject.setMetadata({ 28 | "title": this.value, 29 | "title_set_by_user": "1" 30 | }); 31 | datastoreObject.save(); 32 | }; 33 | 34 | this.descriptionElem.onblur = function () { 35 | datastoreObject.setMetadata({ 36 | "description": this.value 37 | }); 38 | datastoreObject.save(); 39 | }; 40 | }; 41 | 42 | // Fill the text inputs with the received metadata. 43 | var setTitleDescription = function (metadata) { 44 | this.titleElem.value = metadata.title; 45 | 46 | if (metadata.description !== undefined) { 47 | this.descriptionElem.value = metadata.description; 48 | } 49 | }; 50 | 51 | activitypalette.ActivityPalette.prototype = 52 | Object.create(palette.Palette.prototype, { 53 | setTitleDescription: { 54 | value: setTitleDescription, 55 | enumerable: true, 56 | configurable: true, 57 | writable: true 58 | } 59 | }); 60 | 61 | return activitypalette; 62 | }); 63 | -------------------------------------------------------------------------------- /public/lib/sugar-web/graphics/css/sugar-200dpi.less: -------------------------------------------------------------------------------- 1 | @subcell-size: 15px; 2 | @import "sugar.less"; 3 | -------------------------------------------------------------------------------- /public/lib/sugar-web/graphics/css/sugar-96dpi.less: -------------------------------------------------------------------------------- 1 | @subcell-size: 11px; 2 | @import "sugar.less"; 3 | -------------------------------------------------------------------------------- /public/lib/sugar-web/graphics/evaluationpalette.js: -------------------------------------------------------------------------------- 1 | define(["sugar-web/graphics/palette"], function (palette) { 2 | "use strict"; 3 | 4 | var evaluationpalette = {}; 5 | 6 | evaluationpalette.EvaluationPalette = function (invoker, primaryText) { 7 | palette.Palette.call(this, invoker, primaryText); 8 | 9 | var template = "Hello palette!"; 10 | var containerElem = document.createElement("div"); 11 | containerElem.innerHTML = template; 12 | this.setContent([containerElem]); 13 | }; 14 | 15 | var addEventListener = function (type, listener, useCapture) { 16 | return this.getPalette().addEventListener(type, listener, useCapture); 17 | }; 18 | 19 | evaluationpalette.EvaluationPalette.prototype = Object.create( 20 | palette.Palette.prototype, 21 | { 22 | addEventListener: { 23 | value: addEventListener, 24 | enumerable: true, 25 | configurable: true, 26 | writable: true, 27 | }, 28 | } 29 | ); 30 | 31 | return evaluationpalette; 32 | }); 33 | -------------------------------------------------------------------------------- /public/lib/sugar-web/graphics/grid.js: -------------------------------------------------------------------------------- 1 | define(function () { 2 | 3 | 'use strict'; 4 | 5 | var grid = {}; 6 | 7 | // Add a grid overlay with lines spaced by subcellSize, for visual 8 | // debugging. This is useful while doing the activity layout or 9 | // while developing widgets. 10 | grid.addGrid = function (subcellSize) { 11 | var canvas = document.createElement('canvas'); 12 | canvas.className = "grid"; 13 | document.body.appendChild(canvas); 14 | 15 | var updateGrid = function () { 16 | canvas.width = window.innerWidth; 17 | canvas.height = window.innerHeight; 18 | 19 | var ctx = canvas.getContext("2d"); 20 | ctx.strokeStyle = "#00FFFF"; 21 | 22 | var subcellsVertical = window.innerHeight / subcellSize; 23 | for (i = 0; i < subcellsVertical; i++) { 24 | if ((i + 1) % 5 === 0) { 25 | ctx.lineWidth = 1; 26 | } else { 27 | ctx.lineWidth = 0.5; 28 | } 29 | ctx.beginPath(); 30 | ctx.moveTo(0, subcellSize * (i + 1)); 31 | ctx.lineTo(canvas.width, subcellSize * (i + 1)); 32 | ctx.stroke(); 33 | } 34 | 35 | var subcellsHorizontal = window.innerWidth / subcellSize; 36 | for (i = 0; i < subcellsHorizontal; i++) { 37 | if ((i + 1) % 5 === 0) { 38 | ctx.lineWidth = 1; 39 | } else { 40 | ctx.lineWidth = 0.5; 41 | } 42 | ctx.beginPath(); 43 | ctx.moveTo(subcellSize * (i + 1), 0); 44 | ctx.lineTo(subcellSize * (i + 1), canvas.height); 45 | ctx.stroke(); 46 | } 47 | }; 48 | 49 | updateGrid(); 50 | 51 | window.onresize = function (event) { 52 | updateGrid(); 53 | }; 54 | }; 55 | 56 | return grid; 57 | }); 58 | -------------------------------------------------------------------------------- /public/lib/sugar-web/graphics/icons/actions/activity-journal.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | ]> 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /public/lib/sugar-web/graphics/icons/actions/activity-stop.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | ]> 5 | 6 | -------------------------------------------------------------------------------- /public/lib/sugar-web/graphics/icons/actions/checkbox-checked-selected.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | ]> 6 | 12 | 15 | 22 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /public/lib/sugar-web/graphics/icons/actions/checkbox-checked.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | ]> 6 | 12 | 15 | 22 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /public/lib/sugar-web/graphics/icons/actions/checkbox-unchecked-selected.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | ]> 5 | 11 | 14 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /public/lib/sugar-web/graphics/icons/actions/checkbox-unchecked.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | ]> 5 | 11 | 14 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /public/lib/sugar-web/graphics/icons/actions/dialog-cancel-active.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | ]> 5 | 6 | 7 | -------------------------------------------------------------------------------- /public/lib/sugar-web/graphics/icons/actions/dialog-cancel.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | ]> 5 | 6 | -------------------------------------------------------------------------------- /public/lib/sugar-web/graphics/icons/actions/dialog-ok-active.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | ]> 5 | 6 | 7 | -------------------------------------------------------------------------------- /public/lib/sugar-web/graphics/icons/actions/dialog-ok.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | ]> 5 | 6 | -------------------------------------------------------------------------------- /public/lib/sugar-web/graphics/icons/actions/entry-cancel-active.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | ]> 6 | 24 | -------------------------------------------------------------------------------- /public/lib/sugar-web/graphics/icons/actions/entry-cancel-disabled.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | ]> 6 | 17 | 21 | 22 | -------------------------------------------------------------------------------- /public/lib/sugar-web/graphics/icons/actions/entry-cancel.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | ]> 6 | 17 | 21 | 22 | -------------------------------------------------------------------------------- /public/lib/sugar-web/graphics/icons/actions/radio-active-selected.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | ]> 6 | 12 | 15 | 20 | 25 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /public/lib/sugar-web/graphics/icons/actions/radio-active.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | ]> 6 | 12 | 15 | 20 | 25 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /public/lib/sugar-web/graphics/icons/actions/radio-selected.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | ]> 6 | 12 | 15 | 20 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /public/lib/sugar-web/graphics/icons/actions/radio.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | ]> 6 | 12 | 15 | 20 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /public/lib/sugar-web/graphics/icons/actions/zoom-groups.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | ]> 5 | 6 | -------------------------------------------------------------------------------- /public/lib/sugar-web/graphics/icons/actions/zoom-home.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | ]> 5 | 6 | -------------------------------------------------------------------------------- /public/lib/sugar-web/graphics/icons/actions/zoom-neighborhood.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | ]> 5 | 6 | -------------------------------------------------------------------------------- /public/lib/sugar-web/graphics/icons/emblems/arrow-down.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | ]> 6 | 12 | 15 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /public/lib/sugar-web/graphics/icons/emblems/arrow-up.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | ]> 6 | 12 | 15 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /public/lib/sugar-web/graphics/icons/emblems/favorite.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | ]> 5 | 6 | -------------------------------------------------------------------------------- /public/lib/sugar-web/graphics/menupalette.html: -------------------------------------------------------------------------------- 1 | {{#.}} 2 |
  • 3 | 7 |
  • 8 | {{/.}} 9 | -------------------------------------------------------------------------------- /public/lib/sugar-web/graphics/menupalette.js: -------------------------------------------------------------------------------- 1 | define(["sugar-web/graphics/palette", 2 | "text!sugar-web/graphics/menupalette.html", "mustache"], function (palette, template, mustache) { 3 | 4 | 'use strict'; 5 | 6 | var menupalette = {}; 7 | 8 | menupalette.MenuPalette = function (invoker, primaryText, menuData) { 9 | palette.Palette.call(this, invoker, primaryText); 10 | 11 | this.selectItemEvent = document.createEvent("CustomEvent"); 12 | this.selectItemEvent.initCustomEvent('selectItem', true, true, { 13 | 'item': undefined 14 | }); 15 | 16 | var menuElem = document.createElement('ul'); 17 | menuElem.className = "menu"; 18 | menuElem.innerHTML = mustache.render(template, menuData); 19 | this.setContent([menuElem]); 20 | 21 | // Pop-down the palette when a item in the menu is clicked. 22 | 23 | this.buttons = menuElem.querySelectorAll('button'); 24 | 25 | var that = this; 26 | 27 | function popDownOnButtonClick(event) { 28 | that.selectItemEvent.detail.target = event.target; 29 | that.getPalette().dispatchEvent(that.selectItemEvent); 30 | that.popDown(); 31 | } 32 | 33 | for (var i = 0; i < this.buttons.length; i++) { 34 | this.buttons[i].addEventListener('click', popDownOnButtonClick); 35 | } 36 | }; 37 | 38 | var addEventListener = function (type, listener, useCapture) { 39 | return this.getPalette().addEventListener(type, listener, useCapture); 40 | }; 41 | 42 | menupalette.MenuPalette.prototype = 43 | Object.create(palette.Palette.prototype, { 44 | addEventListener: { 45 | value: addEventListener, 46 | enumerable: true, 47 | configurable: true, 48 | writable: true 49 | } 50 | }); 51 | 52 | return menupalette; 53 | }); 54 | -------------------------------------------------------------------------------- /public/lib/sugar-web/graphics/radiobuttonsgroup.js: -------------------------------------------------------------------------------- 1 | define(function () { 2 | 3 | 'use strict'; 4 | 5 | var radioButtonsGroup = {}; 6 | 7 | // ## RadioButtonsGroup 8 | // 9 | // A group of elements where only one can be active at the same 10 | // time. 11 | // 12 | // When an element is clicked, it becomes the active one. The 13 | // active element gains the 'active' CSS class. 14 | // 15 | // Parameters: 16 | // 17 | // * **elems** Array of elements of the group. 18 | radioButtonsGroup.RadioButtonsGroup = function (elems) { 19 | this.elems = elems; 20 | var active; 21 | 22 | for (var i = 0; i < elems.length; i++) { 23 | var elem = elems[i]; 24 | elem.addEventListener("click", clickHandler); 25 | 26 | // The first element that has 'active' CSS class is made 27 | // the active of the group on startup. 28 | if (active === undefined && elem.classList.contains('active')) { 29 | active = elem; 30 | } 31 | } 32 | 33 | // If no element has 'active' CSS class, the first element of 34 | // the array is made the active. 35 | if (active === undefined) { 36 | active = elems[0]; 37 | updateClasses(); 38 | } 39 | 40 | function clickHandler(evt) { 41 | active = evt.target; 42 | updateClasses(); 43 | } 44 | 45 | function updateClasses() { 46 | for (i = 0; i < elems.length; i++) { 47 | var elem = elems[i]; 48 | elem.classList.remove('active'); 49 | } 50 | active.classList.add('active'); 51 | } 52 | 53 | // Get the active element. 54 | this.getActive = function () { 55 | return active; 56 | }; 57 | 58 | }; 59 | 60 | return radioButtonsGroup; 61 | 62 | }); 63 | -------------------------------------------------------------------------------- /public/lib/sugar-web/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "volo": { 3 | "baseUrl": "lib", 4 | "dependencies": { 5 | "webL10n": "github:sugarlabs/webL10n", 6 | "mustache": "github:janl/mustache.js/0.7.2", 7 | "text": "github:requirejs/text" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /public/lib/sugar-web/test/functional/toolkitContractSpec.js: -------------------------------------------------------------------------------- 1 | define(["sugar-web/env"], function (env) { 2 | 3 | 'use strict'; 4 | 5 | describe("Environment object", function () { 6 | 7 | it("should have valid properties", function () { 8 | //FIXME: we shouldn't stub this here. 9 | //current implementation of isStandalone fails with sugar-web-test 10 | spyOn(env, 'isStandalone').andReturn(false); 11 | 12 | var expectedEnv; 13 | 14 | runs(function () { 15 | env.getEnvironment(function (error, environment) { 16 | expectedEnv = environment; 17 | }); 18 | }); 19 | 20 | waitsFor(function () { 21 | return expectedEnv !== undefined; 22 | }, "should get sugar environment"); 23 | 24 | runs(function () { 25 | expect(expectedEnv.bundleId).not.toBeUndefined(); 26 | expect(expectedEnv.activityId).not.toBeUndefined(); 27 | expect(expectedEnv.activityName).not.toBeUndefined(); 28 | }); 29 | }); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /public/lib/sugar-web/test/graphics/iconSpec.js: -------------------------------------------------------------------------------- 1 | define(["sugar-web/graphics/icon"], function (icon) { 2 | 3 | 'use strict'; 4 | 5 | describe("icon", function () { 6 | var wasLoaded; 7 | var iconUrlResult; 8 | 9 | it("should be able to change icon more than once", function () { 10 | var elem = document.createElement('div'); 11 | var iconUrl; 12 | 13 | function callback(url) { 14 | iconUrlResult = url; 15 | wasLoaded = true; 16 | } 17 | 18 | runs(function () { 19 | wasLoaded = false; 20 | iconUrl = "/base/graphics/icons/actions/dialog-ok-active.svg"; 21 | var iconInfo = { 22 | "uri": iconUrl, 23 | "strokeColor": '#B20008', 24 | "fillColor": '#FF2B34' 25 | }; 26 | icon.load(iconInfo, callback); 27 | }); 28 | 29 | waitsFor(function () { 30 | return wasLoaded; 31 | }, "icon loaded"); 32 | 33 | runs(function () { 34 | expect(iconUrlResult).not.toBe(iconUrl); 35 | }); 36 | 37 | runs(function () { 38 | wasLoaded = false; 39 | iconUrl = iconUrlResult; 40 | var iconInfo = { 41 | "uri": iconUrl, 42 | "strokeColor": '#FF2B34', 43 | "fillColor": '#B20008' 44 | }; 45 | icon.load(iconInfo, callback); 46 | }); 47 | 48 | waitsFor(function () { 49 | return wasLoaded; 50 | }, "icon loaded"); 51 | 52 | runs(function () { 53 | expect(iconUrlResult).not.toBe(iconUrl); 54 | }); 55 | 56 | }); 57 | }); 58 | 59 | }); 60 | -------------------------------------------------------------------------------- /public/lib/sugar-web/test/graphics/paletteSpec.js: -------------------------------------------------------------------------------- 1 | define(["sugar-web/graphics/palette"], function (palette) { 2 | 3 | 'use strict'; 4 | 5 | describe("palette", function () { 6 | it("should start down", function () { 7 | var invoker = document.createElement('button'); 8 | var myPalette = new palette.Palette(invoker); 9 | expect(myPalette.isDown()).toBe(true); 10 | }); 11 | 12 | it("should toggle", function () { 13 | var invoker = document.createElement('button'); 14 | var myPalette = new palette.Palette(invoker); 15 | myPalette.toggle(); 16 | expect(myPalette.isDown()).toBe(false); 17 | myPalette.toggle(); 18 | expect(myPalette.isDown()).toBe(true); 19 | }); 20 | 21 | it("if one palette in a group popups, the others popdown", function () { 22 | var invokerA = document.createElement('button'); 23 | var invokerB = document.createElement('button'); 24 | var myPaletteA = new palette.Palette(invokerA); 25 | var myPaletteB = new palette.Palette(invokerB); 26 | myPaletteA.toggle(); 27 | expect(myPaletteA.isDown()).toBe(false); 28 | expect(myPaletteB.isDown()).toBe(true); 29 | myPaletteB.toggle(); 30 | expect(myPaletteA.isDown()).toBe(true); 31 | expect(myPaletteB.isDown()).toBe(false); 32 | }); 33 | 34 | }); 35 | 36 | }); 37 | -------------------------------------------------------------------------------- /public/lib/sugar-web/test/karma-shared.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration 2 | // Generated on Mon May 13 2013 10:01:17 GMT-0300 (ART) 3 | 4 | 5 | // list of files / patterns to load in the browser 6 | module.exports = function (config) { 7 | config.set({ 8 | frameworks: ['jasmine', 'requirejs'], 9 | 10 | 11 | // base path, that will be used to resolve files and exclude 12 | basePath: '..', 13 | 14 | 15 | files: [ 16 | 'test/loader.js', 17 | { 18 | pattern: 'lib/**/*.js', 19 | included: false 20 | }, { 21 | pattern: '*.js', 22 | included: false 23 | }, { 24 | pattern: 'activity/**/*.js', 25 | included: false 26 | }, { 27 | pattern: 'graphics/**/*', 28 | included: false 29 | } 30 | ], 31 | 32 | 33 | // list of files to exclude 34 | exclude: [], 35 | 36 | 37 | // test results reporter to use 38 | // possible values: 'dots', 'progress', 'junit' 39 | reporters: ['progress'], 40 | 41 | 42 | // web server port 43 | port: 9876, 44 | 45 | 46 | // cli runner port 47 | runnerPort: 9100, 48 | 49 | 50 | // enable / disable colors in the output (reporters and logs) 51 | colors: true, 52 | 53 | 54 | // level of logging 55 | // possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || 56 | // LOG_DEBUG 57 | logLevel: config.LOG_INFO, 58 | 59 | 60 | // enable / disable watching file and executing tests whenever any file 61 | // changes 62 | autoWatch: true, 63 | 64 | 65 | // If browser does not capture in given timeout [ms], kill it 66 | captureTimeout: 60000, 67 | 68 | 69 | // Continuous Integration mode 70 | // if true, it capture browsers, run tests and exit 71 | singleRun: false, 72 | 73 | preprocessors: {} 74 | }); 75 | }; 76 | -------------------------------------------------------------------------------- /public/lib/sugar-web/test/karma-unit.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration for unit tests 2 | 3 | sharedConfig = require("./karma-shared.conf.js"); 4 | 5 | module.exports = function (config) { 6 | var testFiles = [ 7 | { 8 | pattern: 'test/unit/*Spec.js', 9 | included: false 10 | }, { 11 | pattern: 'test/graphics/*Spec.js', 12 | included: false 13 | }, 14 | ]; 15 | 16 | sharedConfig(config); 17 | 18 | config.files = config.files.concat(testFiles); 19 | }; 20 | -------------------------------------------------------------------------------- /public/lib/sugar-web/test/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration for all the tests 2 | 3 | sharedConfig = require("./karma-shared.conf.js"); 4 | 5 | module.exports = function (config) { 6 | var testFiles = [ 7 | { 8 | pattern: 'test/**/*Spec.js', 9 | included: false 10 | } 11 | ]; 12 | 13 | sharedConfig(config); 14 | 15 | config.files = config.files.concat(testFiles); 16 | }; 17 | -------------------------------------------------------------------------------- /public/lib/sugar-web/test/loader.js: -------------------------------------------------------------------------------- 1 | var tests = Object.keys(window.__karma__.files).filter(function (file) { 2 | return (/Spec\.js$/).test(file); 3 | }); 4 | 5 | requirejs.config({ 6 | // Karma serves files from '/base' 7 | baseUrl: "/base", 8 | 9 | paths: { 10 | "sugar-web": ".", 11 | "mustache": "lib/mustache", 12 | "text": "lib/text", 13 | "webL10n": "lib/webL10n" 14 | }, 15 | 16 | // ask Require.js to load these files (all our tests) 17 | deps: tests, 18 | 19 | // start test run, once Require.js is done 20 | callback: window.__karma__.start 21 | }); 22 | -------------------------------------------------------------------------------- /public/lib/sugar-web/test/unit/datastoreSpec.js: -------------------------------------------------------------------------------- 1 | define(["sugar-web/env", "sugar-web/datastore"], function (env, datastore) { 2 | 3 | 'use strict'; 4 | 5 | describe("Ensure the datastore object has an objectId", function () { 6 | 7 | // FIXME does not work in standalone mode 8 | it("should have objectId", function () { 9 | var objectId = "objectId"; 10 | spyOn(env, "getObjectId").andCallFake(function (callback) { 11 | setTimeout(function () { 12 | callback(objectId); 13 | }, 0); 14 | }); 15 | var callback = jasmine.createSpy(); 16 | 17 | var datastoreObject = new datastore.DatastoreObject(); 18 | 19 | runs(function () { 20 | datastoreObject.ensureObjectId(callback); 21 | }); 22 | 23 | waitsFor(function () { 24 | return datastoreObject.objectId !== undefined; 25 | }, "should have objectId received from the environment"); 26 | 27 | runs(function () { 28 | expect(callback).toHaveBeenCalled(); 29 | }); 30 | }); 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /public/lib/sugar-web/test/unit/dictstoreSpec.js: -------------------------------------------------------------------------------- 1 | define(["sugar-web/dictstore", "sugar-web/env"], function (dictstore, env) { 2 | 3 | 'use strict'; 4 | 5 | describe("dictstore on standalone mode", function () { 6 | 7 | beforeEach(function () { 8 | spyOn(env, 'isStandalone').andReturn(true); 9 | }); 10 | 11 | describe("init method", function () { 12 | 13 | it("should execute callback", function () { 14 | var callback = jasmine.createSpy(); 15 | 16 | dictstore.init(callback); 17 | expect(callback).toHaveBeenCalled(); 18 | }); 19 | 20 | it("should maintain localStorage", function () { 21 | localStorage.testKey = "test"; 22 | 23 | dictstore.init(function () {}); 24 | expect(localStorage.testKey).toBe("test"); 25 | }); 26 | }); 27 | 28 | describe("save method", function () { 29 | 30 | it("should just execute the callback", function () { 31 | var callbackExecuted; 32 | 33 | localStorage.test_key = "test"; 34 | 35 | runs(function () { 36 | callbackExecuted = false; 37 | 38 | dictstore.save(function () { 39 | callbackExecuted = true; 40 | }); 41 | }); 42 | 43 | waitsFor(function () { 44 | return callbackExecuted === true; 45 | }, "The callback should executed"); 46 | 47 | runs(function () { 48 | expect(localStorage.test_key).toBe("test"); 49 | }); 50 | }); 51 | }); 52 | 53 | }); 54 | }); 55 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "Exersizer", 3 | "name": "Exerciser Activity", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": "./index.html", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /screenshots/CLOZE_Player.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaske/ExerciserReact/2e2b8104273efd7c8a642d71647a748f4c5974da/screenshots/CLOZE_Player.png -------------------------------------------------------------------------------- /screenshots/Detailed_Results.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaske/ExerciserReact/2e2b8104273efd7c8a642d71647a748f4c5974da/screenshots/Detailed_Results.png -------------------------------------------------------------------------------- /screenshots/FREETEXT_Player.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaske/ExerciserReact/2e2b8104273efd7c8a642d71647a748f4c5974da/screenshots/FREETEXT_Player.png -------------------------------------------------------------------------------- /screenshots/GROUP_Player.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaske/ExerciserReact/2e2b8104273efd7c8a642d71647a748f4c5974da/screenshots/GROUP_Player.png -------------------------------------------------------------------------------- /screenshots/Home_Screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaske/ExerciserReact/2e2b8104273efd7c8a642d71647a748f4c5974da/screenshots/Home_Screen.png -------------------------------------------------------------------------------- /screenshots/Image_Editor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaske/ExerciserReact/2e2b8104273efd7c8a642d71647a748f4c5974da/screenshots/Image_Editor.png -------------------------------------------------------------------------------- /screenshots/MATCH_Player.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaske/ExerciserReact/2e2b8104273efd7c8a642d71647a748f4c5974da/screenshots/MATCH_Player.png -------------------------------------------------------------------------------- /screenshots/MCQPlayer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaske/ExerciserReact/2e2b8104273efd7c8a642d71647a748f4c5974da/screenshots/MCQPlayer.png -------------------------------------------------------------------------------- /screenshots/MCQ_FORM.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaske/ExerciserReact/2e2b8104273efd7c8a642d71647a748f4c5974da/screenshots/MCQ_FORM.png -------------------------------------------------------------------------------- /screenshots/Presence.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaske/ExerciserReact/2e2b8104273efd7c8a642d71647a748f4c5974da/screenshots/Presence.png -------------------------------------------------------------------------------- /screenshots/REORDER_Form.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaske/ExerciserReact/2e2b8104273efd7c8a642d71647a748f4c5974da/screenshots/REORDER_Form.png -------------------------------------------------------------------------------- /screenshots/REORER_Player.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaske/ExerciserReact/2e2b8104273efd7c8a642d71647a748f4c5974da/screenshots/REORER_Player.png -------------------------------------------------------------------------------- /screenshots/Scores_Screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaske/ExerciserReact/2e2b8104273efd7c8a642d71647a748f4c5974da/screenshots/Scores_Screen.png -------------------------------------------------------------------------------- /screenshots/Template_Screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaske/ExerciserReact/2e2b8104273efd7c8a642d71647a748f4c5974da/screenshots/Template_Screen.png -------------------------------------------------------------------------------- /screenshots/Tutorial.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaske/ExerciserReact/2e2b8104273efd7c8a642d71647a748f4c5974da/screenshots/Tutorial.png -------------------------------------------------------------------------------- /screenshots/screenshots.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaske/ExerciserReact/2e2b8104273efd7c8a642d71647a748f4c5974da/screenshots/screenshots.gif -------------------------------------------------------------------------------- /src/components/Tutorial.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { tutorialSteps } from "../tutorialSteps"; 3 | import { injectIntl } from "react-intl"; 4 | import { NEXT_SHORT, PREV, END } from "../containers/translation"; 5 | import "../css/Tutorial.css"; 6 | import 'intro.js/introjs.css'; 7 | import { Steps } from 'intro.js-react'; 8 | 9 | class Tutorial extends Component { 10 | isSingleStepTutorial = tutorialSteps(this.props.pathname, this.props.intl).some(step => !Object.keys(step).length); 11 | render() { 12 | return ( 13 | {this.props.unmount();}} 26 | onBeforeChange={(stepIntex)=>{ 27 | if (!this.isSingleStepTutorial || stepIntex >=0 && Object.keys(tutorialSteps(this.props.pathname, this.props.intl)[stepIntex+1]).length) return true; 28 | document.querySelector(".introjs-nextbutton") ? document.querySelector(".introjs-nextbutton").classList.add("introjs-disabled"): ""; 29 | return false; 30 | }} 31 | /> 32 | ); 33 | } 34 | } 35 | 36 | export default injectIntl(Tutorial); 37 | -------------------------------------------------------------------------------- /src/components/UserIcon.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | 4 | class UserIcon extends Component { 5 | render() { 6 | const { stroke_color, fill_color, width, height } = this.props; 7 | 8 | return ( 9 | 14 | 17 | 23 | 37 | 50 | 51 | 52 | 53 | ) 54 | } 55 | } 56 | 57 | export default UserIcon; -------------------------------------------------------------------------------- /src/components/UserList.js: -------------------------------------------------------------------------------- 1 | import { slide as Menu } from 'react-burger-menu' 2 | import React from 'react'; 3 | import "../css/UserList.css" 4 | 5 | function UserList(props) { 6 | const { userList, stroke, isOpen } = props; 7 | 8 | let styles = { 9 | bmCrossButton: { 10 | height: '30px', 11 | width: '30px' 12 | }, 13 | bmCross: { 14 | background: stroke 15 | }, 16 | bmMenu: { 17 | background: '#808080', 18 | } 19 | }; 20 | 21 | return ( 22 | 28 | {userList} 29 | 30 | ); 31 | } 32 | 33 | export default UserList; 34 | -------------------------------------------------------------------------------- /src/containers/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { Provider } from "react-redux"; 3 | import { configureStore } from "../store"; 4 | // import { DndProvider } from "react-dnd"; 5 | // import { HTML5Backend } from "react-dnd-html5-backend" 6 | 7 | import Sugarizer from "./Sugarizer"; 8 | 9 | import "../css/index.css"; 10 | 11 | const store = configureStore(); 12 | 13 | class App extends Component { 14 | render() { 15 | return ( 16 |
    17 | {/* */} 18 | 19 | 20 | 21 | {/* */} 22 |
    23 | ); 24 | } 25 | } 26 | 27 | export default App; 28 | -------------------------------------------------------------------------------- /src/css/CLOZEPlayer.css: -------------------------------------------------------------------------------- 1 | .input-ans{ 2 | display: block; 3 | font-size: 15px; 4 | max-height: 30px!important; 5 | border-radius: 10px; 6 | border-width: 0; 7 | min-width: 180px; 8 | margin-bottom: 5px; 9 | } 10 | 11 | .checked-ans{ 12 | font-size: 15px; 13 | max-height: 30px!important; 14 | border-radius: 10px; 15 | border-width: 0; 16 | min-width: 180px; 17 | } 18 | 19 | .Select-placeholder{ 20 | border-radius: 15px!important; 21 | } 22 | 23 | .Select{ 24 | padding: 0!important; 25 | } 26 | 27 | 28 | .wrong{ 29 | padding: 4px 8px; 30 | border-radius: 10px; 31 | background-color: orangered; 32 | } 33 | 34 | .right{ 35 | padding: 4px 8px; 36 | border-radius: 10px; 37 | background-color: lime; 38 | } 39 | 40 | .cloze-span{ 41 | display: inline-block; 42 | margin-bottom: 15px; 43 | } 44 | 45 | div{ 46 | line-height: 1.5; 47 | } 48 | 49 | #cloze-container{ 50 | margin-top: 5%; 51 | margin-bottom: 5%; 52 | } 53 | 54 | 55 | .jumbotron { 56 | background-color: #dfdfdf !important; 57 | } 58 | 59 | .lead { 60 | font-weight: bold !important; 61 | } 62 | -------------------------------------------------------------------------------- /src/css/DragList.css: -------------------------------------------------------------------------------- 1 | * { 2 | padding: 0; 3 | margin: 0; 4 | -webkit-user-select: none; 5 | -moz-user-select: none; 6 | user-select: none; 7 | } 8 | body { 9 | /*cursor: url('cursor.png') 39 39, auto;*/ 10 | } 11 | .demo8-outer { 12 | background-color: #EEE; 13 | color: #FFF; 14 | position: absolute; 15 | width: 100%; 16 | height: 100%; 17 | font: 28px/1em "Helvetica"; 18 | display: flex; 19 | justify-content: center; 20 | align-items: center; 21 | display: -webkit-flex; 22 | -webkit-justify-content: center; 23 | -webkit-align-items: center; 24 | } 25 | .demo8 { 26 | width: 320px; 27 | height: 400px; 28 | } 29 | .demo8-item { 30 | position: absolute; 31 | width: 320px; 32 | height: 90px; 33 | overflow: visible; 34 | pointer-events: auto; 35 | transform-origin: 50% 50% 0px; 36 | border-radius: 4px; 37 | color: rgb(153, 153, 153); 38 | line-height: 96px; 39 | padding-left: 32px; 40 | font-size: 24px; 41 | font-weight: 400; 42 | background-color: rgb(255, 255, 255); 43 | box-sizing: border-box; 44 | -webkit-box-sizing: border-box; 45 | } 46 | .link { 47 | position: absolute; 48 | color: rgb(76, 76, 76); 49 | text-decoration: none; 50 | font: 14px/1em "Helvetica"; 51 | padding: 10px; 52 | top: 0; 53 | left: 0; 54 | } -------------------------------------------------------------------------------- /src/css/Evaluation.css: -------------------------------------------------------------------------------- 1 | #evaluation-container{ 2 | margin-top: 1rem; 3 | } 4 | 5 | #evaluation-heading{ 6 | font-size: 2rem; 7 | justify-content: center; 8 | color: #6F6F6F; 9 | font-weight: 600; 10 | margin: 1rem; 11 | } -------------------------------------------------------------------------------- /src/css/ExerciseDragList.css: -------------------------------------------------------------------------------- 1 | .drag-exercise{ 2 | flex-basis: 50% !important; 3 | display: flex; 4 | align-items: flex-start !important; 5 | margin-bottom: 2rem !important; 6 | } 7 | .draggable-card{ 8 | margin-top: 0; 9 | } 10 | .draggable-exercise{ 11 | padding-left: 0 !important; 12 | } 13 | .drag-handler{ 14 | margin-top: 0.5rem; 15 | } -------------------------------------------------------------------------------- /src/css/ExerciseList.css: -------------------------------------------------------------------------------- 1 | .exercise-div{ 2 | display: inline-block; 3 | } 4 | 5 | 6 | .user:hover{ 7 | background-color: transparent!important; 8 | border-color: transparent!important; 9 | } 10 | 11 | .user-icon{ 12 | max-width: 50px; 13 | padding: 0!important; 14 | max-height: 50px; 15 | } 16 | 17 | .user-container{ 18 | position: absolute; 19 | top: 100px; 20 | right: 50px; 21 | z-index: 1000; 22 | } 23 | 24 | .evaluation-container{ 25 | position: absolute; 26 | top: 160px; 27 | right: 68px; 28 | z-index: 1000; 29 | } 30 | 31 | .user-text{ 32 | min-height: 40px!important; 33 | margin-bottom: 10px; 34 | font-size: 20px; 35 | vertical-align: center; 36 | } 37 | 38 | .tooltip-react{ 39 | max-width: 200px; 40 | padding-left: 0; 41 | padding-right: 0; 42 | } 43 | 44 | .badge-notify{ 45 | background:red; 46 | position:relative; 47 | color: white; 48 | font-size: 15px!important; 49 | top: -45px!important; 50 | left: -25px!important; 51 | } 52 | 53 | .user-list{ 54 | font-size: 14px; 55 | text-align: center; 56 | max-height: 50px; 57 | line-height: 50px; 58 | height: 100%; 59 | } 60 | 61 | .exercise-list-container{ 62 | margin: 0!important; 63 | padding: 3%; 64 | height: 100%; 65 | } 66 | 67 | .total-score{ 68 | width: fit-content; 69 | padding: 0.5rem; 70 | margin: auto; 71 | border: 2px solid; 72 | border-radius: 8px; 73 | font-size: 1.5rem; 74 | } 75 | 76 | .user-list-button{ 77 | width: 50px!important; 78 | height: 50px!important; 79 | background-image: url("../icons/navbar/zoom-neighborhood.svg"); 80 | background-repeat: no-repeat; 81 | background-size: contain; 82 | border-radius: 25px; 83 | background-color: black!important; 84 | } 85 | 86 | .realtime-evaluation-button{ 87 | width: 50px!important; 88 | height: 50px!important; 89 | background-image: url("../icons/navbar/eval-mode.svg"); 90 | background-repeat: no-repeat; 91 | background-size: contain; 92 | border-radius: 25px; 93 | background-color: black; 94 | } 95 | 96 | .home-container{ 97 | height: auto; 98 | min-height: 100%; 99 | } 100 | 101 | .Exercise-List-NoPadding{ 102 | padding: 0px !important; 103 | } -------------------------------------------------------------------------------- /src/css/MCQPlayer.css: -------------------------------------------------------------------------------- 1 | .form-group{ 2 | /*margin-top: 20px;*/ 3 | } 4 | 5 | .choices-but{ 6 | width: 85%; 7 | margin-right: 0px!important; 8 | } 9 | 10 | .choices-button:focus, .choices-button:active{ 11 | box-shadow: none!important; 12 | border: 2px solid #808080!important; 13 | } 14 | 15 | .choices{ 16 | display: inline-block!important; 17 | margin: 10px 0px; 18 | } 19 | 20 | .choices-container{ 21 | display: flex; 22 | flex-direction: column; 23 | align-items: center; 24 | } 25 | 26 | @media (min-width: 576px) { 27 | .jumbotron { 28 | padding: 2rem 1rem!important; 29 | } 30 | } 31 | 32 | .jumbotron{ 33 | margin-bottom: 1rem!important; 34 | background-color: #dfdfdf!important; 35 | } 36 | 37 | .d-flex{ 38 | margin-top: 1rem; 39 | } 40 | 41 | .next-button{ 42 | width: 180px; 43 | text-align: center; 44 | align-self: center; 45 | border-radius: 20px!important; 46 | font-size: 15px!important; 47 | background-color: #808080!important; 48 | margin-bottom: 10px; 49 | } 50 | 51 | #mcq-container{ 52 | margin-top: 5%; 53 | margin-bottom: 5%; 54 | } 55 | 56 | .lead { 57 | font-weight: bold !important; 58 | } 59 | 60 | .btn-outline-secondary { 61 | color: #6c757d!important; 62 | border-color: #6c757d!important; 63 | } 64 | 65 | .btn-selected{ 66 | color: #6c757d!important; 67 | border: 2px solid #808080!important; 68 | } 69 | 70 | .button-on{ 71 | content: url(../icons/exercise/sound_on_black.png); 72 | vertical-align: middle; 73 | } 74 | 75 | .button-off{ 76 | content: url(../icons/exercise/sound_off_black.png); 77 | vertical-align: middle; 78 | } 79 | 80 | .options-radio{ 81 | margin-right: 5px!important; 82 | background: #6c757d; 83 | border-radius: 15px; 84 | border: 4px solid #dfdfdf; 85 | } 86 | 87 | .options-radio:hover{ 88 | border: 4px solid #808080; 89 | } 90 | 91 | 92 | input[type=radio]:checked{ 93 | background: #6c757d; 94 | border-radius: 15px; 95 | border: 4px solid #dfdfdf; 96 | } 97 | 98 | .audio-option{ 99 | max-width: -webkit-fill-available; 100 | } -------------------------------------------------------------------------------- /src/css/PresenceScores.css: -------------------------------------------------------------------------------- 1 | .score-button{ 2 | width: 40px; 3 | height: 40px; 4 | background-image: url("../icons/exercise/percent.svg"); 5 | background-repeat: no-repeat; 6 | background-size: contain; 7 | border-radius: 20px; 8 | margin-top: 10px; 9 | } 10 | 11 | .score-button:hover{ 12 | background-color: lightskyblue!important; 13 | border: 2px solid transparent!important; 14 | } 15 | 16 | .time-button{ 17 | width: 40px; 18 | height: 40px; 19 | background-image: url("../icons/exercise/clock.svg"); 20 | background-repeat: no-repeat; 21 | background-size: contain; 22 | border-radius: 20px; 23 | margin-top: 10px; 24 | } 25 | 26 | .time-button:hover{ 27 | background-color: lightskyblue!important; 28 | border: 2px solid transparent!important; 29 | } 30 | 31 | .detail-button{ 32 | width: 40px; 33 | height: 40px; 34 | background-image: url("../icons/exercise/details.png"); 35 | background-repeat: no-repeat; 36 | background-size: cover; 37 | border-radius: 20px; 38 | margin-top: 10px; 39 | } 40 | 41 | .detail-button:hover{ 42 | background-color: lightskyblue!important; 43 | border: 2px solid transparent!important; 44 | } 45 | 46 | .active{ 47 | background-color: deepskyblue; 48 | } 49 | 50 | th, td { 51 | padding: 15px; 52 | text-align: center !important; 53 | border-bottom: 1px solid #ddd; 54 | display: table-cell!important; 55 | } 56 | 57 | .td-userans { 58 | display: flex!important; 59 | align-items: center; 60 | } 61 | 62 | .shared-results-user-selected :hover { 63 | background-color: #cccccc; 64 | } -------------------------------------------------------------------------------- /src/css/REORDERForm.css: -------------------------------------------------------------------------------- 1 | .button-choices-add .button-choices-sub { 2 | background-color: gray!important; 3 | } 4 | 5 | #reorder-form{ 6 | margin-top: 5%; 7 | margin-bottom: 5%; 8 | } 9 | 10 | .button-thumbnail { 11 | background-image: url(../icons/exercise/insert-image.svg); 12 | background-repeat: no-repeat; 13 | background-size: contain; 14 | float: right; 15 | width: 30px; 16 | height: 30px; 17 | margin-bottom: 5px; 18 | margin-right: 0px!important; 19 | } 20 | 21 | .button-cancel{ 22 | background-image: url(../icons/exercise/sub.svg); 23 | background-repeat: no-repeat; 24 | background-size: contain; 25 | background-position: center; 26 | position: absolute; 27 | right: 0; 28 | background-color: gray!important; 29 | height: 30px; 30 | width: 30px; 31 | border-radius: 16px!important; 32 | margin-top: 5px; 33 | } 34 | 35 | .thumbnail { 36 | background-color: #e5e5e5; 37 | text-align: center; 38 | margin-bottom: 10px; 39 | } 40 | -------------------------------------------------------------------------------- /src/css/REORDERPlayer.css: -------------------------------------------------------------------------------- 1 | #reorder-player{ 2 | margin-top: 40px; 3 | margin-bottom: 40px; 4 | } 5 | 6 | .container{ 7 | margin-bottom: 50px; 8 | } 9 | 10 | .list-item{ 11 | user-select: none; 12 | padding: 16px; 13 | margin: 0 0 8px 0; 14 | border-radius:10px; 15 | text-align:center; 16 | } 17 | 18 | .jumbotron { 19 | background-color: #dfdfdf !important; 20 | } 21 | .lead { 22 | font-weight: bold !important; 23 | } 24 | .btn-normal{ 25 | background-color: white; 26 | } -------------------------------------------------------------------------------- /src/css/Scores.css: -------------------------------------------------------------------------------- 1 | #scores{ 2 | margin-top: 5%; 3 | margin-bottom: 5%; 4 | } 5 | 6 | .button-redo{ 7 | width: 60px; 8 | height: 60px; 9 | background-image: url("../icons/exercise/redo.svg"); 10 | background-repeat: no-repeat; 11 | background-size: contain; 12 | border-radius: 30px; 13 | } 14 | 15 | .button-redo:hover{ 16 | background-color: limegreen!important; 17 | border: 2px solid transparent!important; 18 | } 19 | 20 | button{ 21 | margin-right: 10px!important; 22 | background-color: #808080; 23 | color: white!important; 24 | border: 2px solid transparent; 25 | border-radius: 22px; 26 | line-height: 22px; 27 | padding: 2px 4px; 28 | -moz-user-select: none; 29 | -webkit-user-select: none; 30 | } 31 | 32 | .button-container{ 33 | display: flex; 34 | flex-direction: row; 35 | float: right; 36 | } 37 | 38 | th, td { 39 | padding: 15px; 40 | text-align: center !important; 41 | border-bottom: 1px solid #ddd; 42 | display: table-cell!important; 43 | } 44 | 45 | .td-userans { 46 | display: flex!important; 47 | align-items: center; 48 | } -------------------------------------------------------------------------------- /src/css/UserList.css: -------------------------------------------------------------------------------- 1 | /* Position and sizing of burger button */ 2 | .bm-burger-button { 3 | position: fixed; 4 | width: 36px; 5 | height: 30px; 6 | right: 50px; 7 | top: 100px; 8 | } 9 | 10 | /* Color/shape of burger icon bars */ 11 | .bm-burger-bars { 12 | background: #373a47; 13 | } 14 | 15 | /* General sidebar styles */ 16 | .bm-menu { 17 | padding: 2.5em 1.5em 0; 18 | font-size: 1.15em; 19 | } 20 | 21 | /* Wrapper for item list */ 22 | .bm-item-list { 23 | color: #b8b7ad; 24 | padding: 0.8em; 25 | } 26 | 27 | /* Individual item */ 28 | .bm-item { 29 | display: inline-block; 30 | } 31 | 32 | /* Styling of overlay */ 33 | .bm-overlay { 34 | width: 100%; 35 | height: 100%; 36 | } 37 | 38 | .user{ 39 | width: 70px; 40 | height: 70px; 41 | background-repeat: no-repeat; 42 | border-radius: 30px; 43 | background-color: transparent; 44 | } 45 | 46 | .user:hover{ 47 | background-color: transparent!important; 48 | border-color: transparent!important; 49 | } 50 | 51 | .user-icon{ 52 | max-width: 50px; 53 | padding: 0!important; 54 | max-height: 50px; 55 | display: inline-block; 56 | } 57 | 58 | .user-container{ 59 | position: absolute; 60 | top: 100px; 61 | right: 50px; 62 | z-index: 1000; 63 | } 64 | 65 | .user-text{ 66 | min-height: 40px!important; 67 | margin-bottom: 10px; 68 | display: inline-block; 69 | text-align: center; 70 | vertical-align: middle; 71 | font-size: 25px 72 | } 73 | 74 | .badge-notify{ 75 | background:red; 76 | position:relative; 77 | color: white; 78 | font-size: 15px!important; 79 | top: -50px; 80 | left: -30px; 81 | } 82 | 83 | .user-list{ 84 | font-size: 14px; 85 | text-align: center; 86 | max-height: 50px; 87 | line-height: 50px; 88 | height: 100%; 89 | } -------------------------------------------------------------------------------- /src/css/WordPuzzleForm.css: -------------------------------------------------------------------------------- 1 | /* Form uses some styles from MCQForm.css */ 2 | #wp-form { 3 | margin-block: 40px; 4 | } 5 | #wp-form .media-background { 6 | border-radius: 13px; 7 | } 8 | 9 | #wp-form .title, 10 | .wp-title, 11 | .wp-question, 12 | .wp-answer, 13 | .wp-add-question span { 14 | font-weight: bold; 15 | color: #282828; 16 | } 17 | 18 | .wp-add-question { 19 | width: fit-content !important; 20 | background: #e6e6e6; 21 | padding-right: 15px; 22 | border-radius: 15px; 23 | } 24 | .wp-add-question span { 25 | display: inline-block; 26 | vertical-align: middle; 27 | } 28 | -------------------------------------------------------------------------------- /src/css/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | width: 100%; 3 | height: 100vh; 4 | margin: 0; 5 | padding: 0; 6 | font-family: sans-serif; 7 | overflow: hidden; 8 | } 9 | 10 | #root{ 11 | width: 100%; 12 | height: 100%; 13 | margin: 0; 14 | padding: 0; 15 | font-family: sans-serif; 16 | } 17 | 18 | .App{ 19 | width: 100%; 20 | height: 100%; 21 | margin: 0; 22 | padding: 0; 23 | font-family: sans-serif; 24 | } 25 | 26 | .App-container{ 27 | width: 100%; 28 | height: 100%; 29 | margin: 0; 30 | padding: 0; 31 | font-family: sans-serif; 32 | overflow: hidden; 33 | display: flex; 34 | flex-direction: column; 35 | } 36 | 37 | .main-container{ 38 | overflow-y: auto; 39 | flex-grow: 1; 40 | } 41 | 42 | .fullScreenMargin, .fullScreenPaddingMargin{ 43 | margin-top: 4px !important; 44 | margin-bottom: 4px !important; 45 | } 46 | 47 | .fullScreenPadding, .fullScreenPaddingMargin{ 48 | padding-left: 0px !important; 49 | padding-right: 0px !important; 50 | } 51 | input[type='text'].expand { 52 | width: 100%; 53 | } 54 | .wrapper > .container { 55 | width: 100%; 56 | margin-right: auto; 57 | margin-left: auto; 58 | padding: 0px; 59 | padding-bottom: 3px; 60 | } 61 | 62 | textarea.expand { 63 | width: 100%; 64 | } -------------------------------------------------------------------------------- /src/icons/exercise/ArrowSmall.cur: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaske/ExerciserReact/2e2b8104273efd7c8a642d71647a748f4c5974da/src/icons/exercise/ArrowSmall.cur -------------------------------------------------------------------------------- /src/icons/exercise/clock.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/icons/exercise/correct.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaske/ExerciserReact/2e2b8104273efd7c8a642d71647a748f4c5974da/src/icons/exercise/correct.png -------------------------------------------------------------------------------- /src/icons/exercise/delete.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | ]> 5 | 6 | -------------------------------------------------------------------------------- /src/icons/exercise/details.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaske/ExerciserReact/2e2b8104273efd7c8a642d71647a748f4c5974da/src/icons/exercise/details.png -------------------------------------------------------------------------------- /src/icons/exercise/dialog-cancel.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | ]> 5 | 6 | -------------------------------------------------------------------------------- /src/icons/exercise/dialog-ok.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | ]> 5 | 6 | -------------------------------------------------------------------------------- /src/icons/exercise/down.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/icons/exercise/edit.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/icons/exercise/go-left.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | ]> 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/icons/exercise/go-right.svg: -------------------------------------------------------------------------------- 1 | 2 | 21 | 25 | 29 | 34 | 35 | -------------------------------------------------------------------------------- /src/icons/exercise/help.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | ]> 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/icons/exercise/left.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | ]> 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/icons/exercise/percent.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/icons/exercise/play-button.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/icons/exercise/redo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/icons/exercise/reorder-drag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaske/ExerciserReact/2e2b8104273efd7c8a642d71647a748f4c5974da/src/icons/exercise/reorder-drag.png -------------------------------------------------------------------------------- /src/icons/exercise/reorder-drag.svg2: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/exercise/result.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/icons/exercise/right.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | ]> 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/icons/exercise/sound_off_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaske/ExerciserReact/2e2b8104273efd7c8a642d71647a748f4c5974da/src/icons/exercise/sound_off_black.png -------------------------------------------------------------------------------- /src/icons/exercise/sound_off_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaske/ExerciserReact/2e2b8104273efd7c8a642d71647a748f4c5974da/src/icons/exercise/sound_off_white.png -------------------------------------------------------------------------------- /src/icons/exercise/sound_on_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaske/ExerciserReact/2e2b8104273efd7c8a642d71647a748f4c5974da/src/icons/exercise/sound_on_black.png -------------------------------------------------------------------------------- /src/icons/exercise/sound_on_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaske/ExerciserReact/2e2b8104273efd7c8a642d71647a748f4c5974da/src/icons/exercise/sound_on_white.png -------------------------------------------------------------------------------- /src/icons/exercise/speak.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | ]> 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/icons/exercise/sub.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/icons/exercise/up.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/icons/exercise/wrong.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaske/ExerciserReact/2e2b8104273efd7c8a642d71647a748f4c5974da/src/icons/exercise/wrong.png -------------------------------------------------------------------------------- /src/icons/navbar/add.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | ]> 5 | 6 | -------------------------------------------------------------------------------- /src/icons/navbar/async-mode.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | ]> 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/icons/navbar/fullscreen.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | ]> 5 | 6 | -------------------------------------------------------------------------------- /src/icons/navbar/home.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /src/icons/navbar/play.svg: -------------------------------------------------------------------------------- 1 | 2 | image/svg+xml -------------------------------------------------------------------------------- /src/icons/navbar/shareAll.svg: -------------------------------------------------------------------------------- 1 | 2 | 17 | 19 | 23 | 28 | 33 | 34 | -------------------------------------------------------------------------------- /src/icons/navbar/zoom-home.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | ]> 5 | 6 | -------------------------------------------------------------------------------- /src/icons/navbar/zoom-neighborhood.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | ]> 5 | 6 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './containers/App'; 4 | import { unregister } from './registerServiceWorker'; 5 | import 'bootstrap/dist/css/bootstrap.min.css'; 6 | import './css/index.css'; 7 | import Modal from 'react-modal'; 8 | 9 | Modal.setAppElement('#root'); 10 | ReactDOM.render(, document.getElementById('root')); 11 | // registerServiceWorker(); 12 | unregister(); 13 | -------------------------------------------------------------------------------- /src/media/defaultExerciseThumbnail/images/README.md: -------------------------------------------------------------------------------- 1 | ## Guidelines 2 | This folder contains images used in default exercises. The supported formats are - png, jpeg, jpg. -------------------------------------------------------------------------------- /src/media/defaultExerciseThumbnail/images/animal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaske/ExerciserReact/2e2b8104273efd7c8a642d71647a748f4c5974da/src/media/defaultExerciseThumbnail/images/animal.png -------------------------------------------------------------------------------- /src/media/defaultExerciseThumbnail/images/bark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaske/ExerciserReact/2e2b8104273efd7c8a642d71647a748f4c5974da/src/media/defaultExerciseThumbnail/images/bark.png -------------------------------------------------------------------------------- /src/media/defaultExerciseThumbnail/images/canoe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaske/ExerciserReact/2e2b8104273efd7c8a642d71647a748f4c5974da/src/media/defaultExerciseThumbnail/images/canoe.png -------------------------------------------------------------------------------- /src/media/defaultExerciseThumbnail/images/car.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaske/ExerciserReact/2e2b8104273efd7c8a642d71647a748f4c5974da/src/media/defaultExerciseThumbnail/images/car.png -------------------------------------------------------------------------------- /src/media/defaultExerciseThumbnail/images/cat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaske/ExerciserReact/2e2b8104273efd7c8a642d71647a748f4c5974da/src/media/defaultExerciseThumbnail/images/cat.png -------------------------------------------------------------------------------- /src/media/defaultExerciseThumbnail/images/conjugate.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaske/ExerciserReact/2e2b8104273efd7c8a642d71647a748f4c5974da/src/media/defaultExerciseThumbnail/images/conjugate.jpg -------------------------------------------------------------------------------- /src/media/defaultExerciseThumbnail/images/cow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaske/ExerciserReact/2e2b8104273efd7c8a642d71647a748f4c5974da/src/media/defaultExerciseThumbnail/images/cow.png -------------------------------------------------------------------------------- /src/media/defaultExerciseThumbnail/images/discount.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaske/ExerciserReact/2e2b8104273efd7c8a642d71647a748f4c5974da/src/media/defaultExerciseThumbnail/images/discount.png -------------------------------------------------------------------------------- /src/media/defaultExerciseThumbnail/images/dog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaske/ExerciserReact/2e2b8104273efd7c8a642d71647a748f4c5974da/src/media/defaultExerciseThumbnail/images/dog.png -------------------------------------------------------------------------------- /src/media/defaultExerciseThumbnail/images/numerals.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaske/ExerciserReact/2e2b8104273efd7c8a642d71647a748f4c5974da/src/media/defaultExerciseThumbnail/images/numerals.jpg -------------------------------------------------------------------------------- /src/media/defaultExerciseThumbnail/images/plane.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaske/ExerciserReact/2e2b8104273efd7c8a642d71647a748f4c5974da/src/media/defaultExerciseThumbnail/images/plane.png -------------------------------------------------------------------------------- /src/media/defaultExerciseThumbnail/images/puzzle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaske/ExerciserReact/2e2b8104273efd7c8a642d71647a748f4c5974da/src/media/defaultExerciseThumbnail/images/puzzle.png -------------------------------------------------------------------------------- /src/media/defaultExerciseThumbnail/images/sail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaske/ExerciserReact/2e2b8104273efd7c8a642d71647a748f4c5974da/src/media/defaultExerciseThumbnail/images/sail.png -------------------------------------------------------------------------------- /src/media/defaultExerciseThumbnail/images/sheep.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaske/ExerciserReact/2e2b8104273efd7c8a642d71647a748f4c5974da/src/media/defaultExerciseThumbnail/images/sheep.png -------------------------------------------------------------------------------- /src/media/defaultExerciseThumbnail/images/world.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaske/ExerciserReact/2e2b8104273efd7c8a642d71647a748f4c5974da/src/media/defaultExerciseThumbnail/images/world.png -------------------------------------------------------------------------------- /src/media/defaultExerciseThumbnail/sounds/README.md: -------------------------------------------------------------------------------- 1 | ## Guidelines 2 | This folder contains audios used in default exercises. The supported formats are - mpeg, wav, mp3. -------------------------------------------------------------------------------- /src/media/defaultExerciseThumbnail/sounds/cat.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaske/ExerciserReact/2e2b8104273efd7c8a642d71647a748f4c5974da/src/media/defaultExerciseThumbnail/sounds/cat.mp3 -------------------------------------------------------------------------------- /src/media/defaultExerciseThumbnail/sounds/cow.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaske/ExerciserReact/2e2b8104273efd7c8a642d71647a748f4c5974da/src/media/defaultExerciseThumbnail/sounds/cow.mp3 -------------------------------------------------------------------------------- /src/media/defaultExerciseThumbnail/sounds/dog.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaske/ExerciserReact/2e2b8104273efd7c8a642d71647a748f4c5974da/src/media/defaultExerciseThumbnail/sounds/dog.mp3 -------------------------------------------------------------------------------- /src/media/defaultExerciseThumbnail/sounds/sheep.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaske/ExerciserReact/2e2b8104273efd7c8a642d71647a748f4c5974da/src/media/defaultExerciseThumbnail/sounds/sheep.mp3 -------------------------------------------------------------------------------- /src/media/defaultExerciseThumbnail/sounds/train.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaske/ExerciserReact/2e2b8104273efd7c8a642d71647a748f4c5974da/src/media/defaultExerciseThumbnail/sounds/train.mp3 -------------------------------------------------------------------------------- /src/media/defaultExerciseThumbnail/videos/README.md: -------------------------------------------------------------------------------- 1 | ## Guidelines 2 | This folder contains videos used in default exercises. The supported formats are - webm, mp4. -------------------------------------------------------------------------------- /src/store/actionTypes.js: -------------------------------------------------------------------------------- 1 | // new exercise object 2 | export const ADD_NEW_EXERCISE_TYPE = "ADD_NEW_EXERCISE_TYPE"; 3 | export const ADD_NEW_EXERCISE_QUES = "ADD_NEW_EXERCISE_QUES"; 4 | export const ADD_NEW_EXERCISE_ID = "ADD_NEW_EXERCISE_ID"; 5 | 6 | // exercises array 7 | export const ADD_NEW_EXERCISE = "ADD_NEW_EXERCISE"; 8 | export const ADD_SCORE_TIME = "ADD_SCORE_TIME"; 9 | export const EDIT_EXERCISE = "EDIT_EXERCISE"; 10 | export const SET_ALL_EXERCISES = "SET_ALL_EXERCISES"; 11 | export const REMOVE_EXERCISE = "REMOVE_EXERCISE"; 12 | export const INCREMENT_ID = "INCREMENT_ID"; 13 | export const SET_COUNTER = "SET_COUNTER"; 14 | 15 | // isHost 16 | export const IS_HOST = "IS_HOST"; 17 | export const IS_SHARED = "IS_SHARED"; 18 | export const ADD_SHARED_EXERCISE = "ADD_SHARED_EXERCISE"; 19 | export const GET_SHARED_EXERCISES = "GET_SHARED_EXERCISE"; 20 | export const REMOVE_SHARED_EXERCISE = "REMOVE_SHARED_EXERCISE"; 21 | export const ADD_USER = "ADD_USER"; 22 | export const REMOVE_USER = "REMOVE_USER"; 23 | export const ADD_SHARED_RESULT = "ADD_SHARED_RESULT"; 24 | export const SHARE_ALL_EXERCISES = "SHARE_ALL_EXERCISES"; 25 | 26 | // Sugarizer 27 | export const SET_USER = "SET_USER"; 28 | 29 | // Run All Exercises 30 | export const SET_RUN_ALL_EXERCISE = "SET RUN ALL EXERCISE"; 31 | export const SET_EXERCISE_INDEX = "SET EXERCISE INDEX"; 32 | 33 | // Evaluation Mode 34 | export const SET_EVALUATION_MODE = "SET EVALUATION MODE"; 35 | export const SET_EVALUATION_EXERCISE = "SET EVALUATION EXERCISE"; 36 | export const ADD_EVALUATION_EXERCISE = "ADD EVALUATION EXERCISE"; 37 | export const UPDATE_EVALUATED_EXERCISE = "UPDATE EVALUATED EXERCISE"; 38 | export const PRESENCE_ADD_EVALUATION_EXERCISES = 39 | "PRESENCE ADD EVALUATION EXERCISES"; 40 | -------------------------------------------------------------------------------- /src/store/actions/evaluation.js: -------------------------------------------------------------------------------- 1 | import { 2 | ADD_EVALUATION_EXERCISE, 3 | PRESENCE_ADD_EVALUATION_EXERCISES, 4 | SET_EVALUATION_EXERCISE, 5 | SET_EVALUATION_MODE, 6 | UPDATE_EVALUATED_EXERCISE, 7 | } from "../actionTypes"; 8 | 9 | export const setEvaluationMode = (mode) => { 10 | return { 11 | type: SET_EVALUATION_MODE, 12 | mode, 13 | }; 14 | }; 15 | 16 | export const setEvaluationExercise = (exercises) => { 17 | return { 18 | type: SET_EVALUATION_EXERCISE, 19 | exercises, 20 | }; 21 | }; 22 | 23 | export const addEvaluationExercise = (exercise) => { 24 | return { 25 | type: ADD_EVALUATION_EXERCISE, 26 | exercise: { ...exercise, shared: false }, 27 | }; 28 | }; 29 | 30 | export const updateEvaluatedExercise = (id, evaluation) => { 31 | return { 32 | type: UPDATE_EVALUATED_EXERCISE, 33 | id, 34 | evaluation, 35 | }; 36 | }; 37 | 38 | export const addEvaluationExercisesPresence = (exercises) => { 39 | return { 40 | type: PRESENCE_ADD_EVALUATION_EXERCISES, 41 | exercises, 42 | }; 43 | }; 44 | -------------------------------------------------------------------------------- /src/store/actions/exercises.js: -------------------------------------------------------------------------------- 1 | import { SET_ALL_EXERCISES, REMOVE_EXERCISE, ADD_NEW_EXERCISE, EDIT_EXERCISE, ADD_SCORE_TIME } from "../actionTypes"; 2 | 3 | export const setExercises = (exercises) => { 4 | return { 5 | type: SET_ALL_EXERCISES, 6 | exercises 7 | } 8 | }; 9 | 10 | export const removeExercises = (id) => ({ 11 | type: REMOVE_EXERCISE, 12 | id 13 | }); 14 | 15 | export const addNewExercise = (exercise) => ({ 16 | type: ADD_NEW_EXERCISE, 17 | exercise: { ...exercise, shared: false } 18 | }); 19 | 20 | export const editExercise = (exercise) => ({ 21 | type: EDIT_EXERCISE, 22 | exercise 23 | }); 24 | 25 | export const addScoreTime = (id, score, time) => ({ 26 | type: ADD_SCORE_TIME, 27 | id, 28 | score, 29 | time 30 | }); -------------------------------------------------------------------------------- /src/store/actions/increment_counter.js: -------------------------------------------------------------------------------- 1 | import { INCREMENT_ID, SET_COUNTER } from "../actionTypes"; 2 | 3 | export const incrementExerciseCounter = () => ({ 4 | type: INCREMENT_ID 5 | }); 6 | 7 | export const setExerciseCounter = (counter) => ({ 8 | type: SET_COUNTER, 9 | counter 10 | }); -------------------------------------------------------------------------------- /src/store/actions/presence.js: -------------------------------------------------------------------------------- 1 | import { 2 | IS_HOST, 3 | IS_SHARED, 4 | GET_SHARED_EXERCISES, 5 | ADD_SHARED_EXERCISE, 6 | REMOVE_SHARED_EXERCISE, 7 | ADD_USER, 8 | REMOVE_USER, 9 | ADD_SHARED_RESULT, 10 | SHARE_ALL_EXERCISES, 11 | } from "../actionTypes"; 12 | 13 | export const setIsHost = () => ({ 14 | type: IS_HOST, 15 | }); 16 | 17 | export const setIsShared = () => ({ 18 | type: IS_SHARED, 19 | }); 20 | 21 | export const getSharedExercises = () => ({ 22 | type: GET_SHARED_EXERCISES, 23 | }); 24 | 25 | export const addSharedExercise = (exercise) => ({ 26 | type: ADD_SHARED_EXERCISE, 27 | exercise, 28 | }); 29 | 30 | export const addSharedResult = (result) => ({ 31 | type: ADD_SHARED_RESULT, 32 | result, 33 | }); 34 | 35 | export const removeSharedExercise = (id) => ({ 36 | type: REMOVE_SHARED_EXERCISE, 37 | id, 38 | }); 39 | 40 | export const addUser = (user) => ({ 41 | type: ADD_USER, 42 | user, 43 | }); 44 | 45 | export const removeUser = (user) => ({ 46 | type: REMOVE_USER, 47 | user, 48 | }); 49 | 50 | export const shareAllExercise = (shared_exercises) => ({ 51 | type: SHARE_ALL_EXERCISES, 52 | shared_exercises, 53 | }); 54 | -------------------------------------------------------------------------------- /src/store/actions/sugarizer.js: -------------------------------------------------------------------------------- 1 | import { 2 | SET_EXERCISE_INDEX, 3 | SET_RUN_ALL_EXERCISE, 4 | SET_USER, 5 | } from "../actionTypes"; 6 | 7 | export const setUser = (user) => ({ 8 | type: SET_USER, 9 | user, 10 | }); 11 | 12 | export const setRunAllExercise = (runAll) => ({ 13 | type: SET_RUN_ALL_EXERCISE, 14 | runAll, 15 | }); 16 | 17 | export const setExerciseIndex = (index) => ({ 18 | type: SET_EXERCISE_INDEX, 19 | index, 20 | }); 21 | -------------------------------------------------------------------------------- /src/store/index.js: -------------------------------------------------------------------------------- 1 | import rootReducer from "./reducers"; 2 | import { createStore } from "redux"; 3 | 4 | export function configureStore() { 5 | const store = createStore(rootReducer); 6 | return store; 7 | } -------------------------------------------------------------------------------- /src/store/reducers/current_user.js: -------------------------------------------------------------------------------- 1 | import { SET_USER } from "../actionTypes"; 2 | 3 | const current_user = (state = {}, action) => { 4 | switch (action.type) { 5 | case SET_USER: 6 | return action.user; 7 | default: 8 | return state 9 | } 10 | }; 11 | 12 | export default current_user; -------------------------------------------------------------------------------- /src/store/reducers/evaluation_exercise.js: -------------------------------------------------------------------------------- 1 | import { 2 | ADD_EVALUATION_EXERCISE, 3 | PRESENCE_ADD_EVALUATION_EXERCISES, 4 | SET_EVALUATION_EXERCISE, 5 | UPDATE_EVALUATED_EXERCISE, 6 | } from "../actionTypes"; 7 | 8 | const DEFAULT_STATE = []; 9 | 10 | const evaluation_exercise = (state = DEFAULT_STATE, action) => { 11 | switch (action.type) { 12 | case SET_EVALUATION_EXERCISE: 13 | return [...action.exercises]; 14 | case ADD_EVALUATION_EXERCISE: 15 | return [...state, action.exercise]; 16 | case UPDATE_EVALUATED_EXERCISE: 17 | let exercises = []; 18 | state.forEach((exercise) => { 19 | if (exercise.id === action.id) { 20 | exercises.push({ ...exercise, evaluation: action.evaluation }); 21 | } else { 22 | exercises.push(exercise); 23 | } 24 | }); 25 | state = exercises; 26 | return state; 27 | case PRESENCE_ADD_EVALUATION_EXERCISES: 28 | let temp = state; 29 | action.exercises.forEach((exercise) => { 30 | if (!temp.find((x) => x.id === exercise.id)) { 31 | temp.push(exercise); 32 | } 33 | }); 34 | state = temp; 35 | return state; 36 | default: 37 | return state; 38 | } 39 | }; 40 | 41 | export default evaluation_exercise; 42 | -------------------------------------------------------------------------------- /src/store/reducers/evaluation_mode.js: -------------------------------------------------------------------------------- 1 | import { SET_EVALUATION_MODE } from "../actionTypes"; 2 | 3 | const evaluationMode = (state = "", action) => { 4 | switch (action.type) { 5 | case SET_EVALUATION_MODE: 6 | return action.mode; 7 | default: 8 | return state; 9 | } 10 | }; 11 | 12 | export default evaluationMode; 13 | -------------------------------------------------------------------------------- /src/store/reducers/exercise_counter.js: -------------------------------------------------------------------------------- 1 | import { INCREMENT_ID, SET_COUNTER } from "../actionTypes"; 2 | 3 | const exercise_counter = (state = 1, action) => { 4 | switch (action.type) { 5 | case INCREMENT_ID: 6 | return state + 1; 7 | case SET_COUNTER: 8 | return action.counter; 9 | default: 10 | return state 11 | } 12 | }; 13 | 14 | export default exercise_counter; -------------------------------------------------------------------------------- /src/store/reducers/exercises.js: -------------------------------------------------------------------------------- 1 | import { 2 | SET_ALL_EXERCISES, 3 | REMOVE_EXERCISE, 4 | ADD_NEW_EXERCISE, 5 | EDIT_EXERCISE, 6 | ADD_SCORE_TIME, 7 | } from "../actionTypes"; 8 | 9 | const DEFAULT_STATE = []; 10 | 11 | const exercises = (state = DEFAULT_STATE, actions) => { 12 | switch (actions.type) { 13 | case SET_ALL_EXERCISES: 14 | return [...actions.exercises]; 15 | case ADD_NEW_EXERCISE: 16 | return [...state, actions.exercise]; 17 | case EDIT_EXERCISE: 18 | return state.map((exercise, i) => { 19 | return exercise.id === actions.exercise.id 20 | ? actions.exercise 21 | : exercise; 22 | }); 23 | case REMOVE_EXERCISE: 24 | return state.filter((exercise) => exercise.id !== actions.id); 25 | case ADD_SCORE_TIME: 26 | return state.map((exercise, i) => { 27 | if (exercise.id === actions.id) { 28 | let temp = exercise; 29 | temp.scores.push(actions.score); 30 | temp.times.push(actions.time); 31 | return temp; 32 | } 33 | return exercise; 34 | }); 35 | default: 36 | return state; 37 | } 38 | }; 39 | 40 | export default exercises; 41 | -------------------------------------------------------------------------------- /src/store/reducers/index.js: -------------------------------------------------------------------------------- 1 | import { combineReducers } from "redux"; 2 | import exercises from "./exercises"; 3 | import exercise_counter from "./exercise_counter"; 4 | import isHost from "./presence/isHost"; 5 | import isShared from "./presence/isShared"; 6 | import sharedExercises from "./presence/sharedExercises"; 7 | import users from "./presence/users"; 8 | import sharedAllExercises from "./presence/shareAll"; 9 | import current_user from "./current_user"; 10 | import run_all from "./run_all"; 11 | import run_all_exercise from "./run_all_exercise_index"; 12 | import evaluationMode from "./evaluation_mode"; 13 | import evaluation_exercise from "./evaluation_exercise"; 14 | 15 | const rootReducer = combineReducers({ 16 | exercises, 17 | exercise_counter, 18 | isHost: isHost, 19 | isShared: isShared, 20 | shared_exercises: sharedExercises, 21 | users: users, 22 | current_user: current_user, 23 | isRunAll: run_all, 24 | exerciseRunning: run_all_exercise, 25 | shared_all_exercises: sharedAllExercises, 26 | evaluation_mode: evaluationMode, 27 | evaluation_exercise: evaluation_exercise, 28 | }); 29 | 30 | export default rootReducer; 31 | -------------------------------------------------------------------------------- /src/store/reducers/presence/isHost.js: -------------------------------------------------------------------------------- 1 | import { IS_HOST } from "../../actionTypes"; 2 | 3 | const isHost = (state = false, action) => { 4 | switch (action.type) { 5 | case IS_HOST: 6 | return true; 7 | default: 8 | return state 9 | } 10 | }; 11 | 12 | export default isHost; -------------------------------------------------------------------------------- /src/store/reducers/presence/isShared.js: -------------------------------------------------------------------------------- 1 | import { IS_SHARED } from "../../actionTypes"; 2 | 3 | const isShared = (state = false, action) => { 4 | switch (action.type) { 5 | case IS_SHARED: 6 | return true; 7 | default: 8 | return state 9 | } 10 | }; 11 | 12 | export default isShared; -------------------------------------------------------------------------------- /src/store/reducers/presence/shareAll.js: -------------------------------------------------------------------------------- 1 | import { SHARE_ALL_EXERCISES } from "../../actionTypes"; 2 | 3 | const sharedAllExercises = (state = {}, action) => { 4 | switch (action.type) { 5 | case SHARE_ALL_EXERCISES: 6 | return { exercises: [...action.shared_exercises], allow_run_all: true }; 7 | default: 8 | return state; 9 | } 10 | }; 11 | 12 | export default sharedAllExercises; 13 | -------------------------------------------------------------------------------- /src/store/reducers/presence/sharedExercises.js: -------------------------------------------------------------------------------- 1 | import { 2 | GET_SHARED_EXERCISES, 3 | ADD_SHARED_EXERCISE, 4 | REMOVE_SHARED_EXERCISE, 5 | ADD_SHARED_RESULT, 6 | } from "../../actionTypes"; 7 | 8 | const sharedExercises = (state = [], action) => { 9 | switch (action.type) { 10 | case GET_SHARED_EXERCISES: 11 | return state; 12 | case ADD_SHARED_EXERCISE: 13 | return [...state, { ...action.exercise, shared_results: [] }]; 14 | case REMOVE_SHARED_EXERCISE: 15 | return state.filter((exercise) => exercise.id !== action.id); 16 | case ADD_SHARED_RESULT: 17 | return state.map((exercise, i) => { 18 | if (exercise.id === action.result.id) { 19 | let temp = exercise; 20 | let score_added = false; 21 | temp.shared_results = temp.shared_results.map((result) => { 22 | if (result.user.name === action.result.user.name) { 23 | score_added = true; 24 | return action.result; 25 | } 26 | return result; 27 | }); 28 | if (!score_added) temp.shared_results.push(action.result); 29 | return temp; 30 | } 31 | return exercise; 32 | }); 33 | default: 34 | return state; 35 | } 36 | }; 37 | 38 | export default sharedExercises; 39 | -------------------------------------------------------------------------------- /src/store/reducers/presence/users.js: -------------------------------------------------------------------------------- 1 | import { ADD_USER, REMOVE_USER } from "../../actionTypes"; 2 | 3 | const users = (state = [], action) => { 4 | switch (action.type) { 5 | case ADD_USER: 6 | return [...state, action.user]; 7 | case REMOVE_USER: 8 | return state.filter((user) => user.networkId !== action.user.networkId); 9 | default: 10 | return state; 11 | } 12 | }; 13 | 14 | export default users; 15 | -------------------------------------------------------------------------------- /src/store/reducers/run_all.js: -------------------------------------------------------------------------------- 1 | import { SET_RUN_ALL_EXERCISE } from "../actionTypes"; 2 | 3 | const run_all = (state = false, action) => { 4 | switch (action.type) { 5 | case SET_RUN_ALL_EXERCISE: 6 | return action.runAll; 7 | default: 8 | return state; 9 | } 10 | }; 11 | 12 | export default run_all; 13 | -------------------------------------------------------------------------------- /src/store/reducers/run_all_exercise_index.js: -------------------------------------------------------------------------------- 1 | import { SET_EXERCISE_INDEX } from "../actionTypes"; 2 | 3 | const run_all_exercise_index = (state = -1, action) => { 4 | switch (action.type) { 5 | case SET_EXERCISE_INDEX: 6 | return action.index; 7 | default: 8 | return state; 9 | } 10 | }; 11 | 12 | export default run_all_exercise_index; 13 | -------------------------------------------------------------------------------- /src/translations/lang.js: -------------------------------------------------------------------------------- 1 | import en from "./en" 2 | import es from "./es" 3 | import fr from "./fr" 4 | 5 | let lang = { 6 | en: en, 7 | es: es, 8 | fr: fr 9 | }; 10 | 11 | export default lang; -------------------------------------------------------------------------------- /src/utils.js: -------------------------------------------------------------------------------- 1 | export const MULTIMEDIA = { 2 | text: 'text', 3 | image: 'image', 4 | audio: 'audio', 5 | textToSpeech: 'text-to-speech', 6 | video: 'video' 7 | }; 8 | 9 | export function setDefaultMedia(li) { 10 | if (li.type === MULTIMEDIA.image && (!li.data.startsWith('data:image') && !li.data.includes('/static/'))) { 11 | return { 12 | type: li.type, 13 | data: require(`./media/defaultExerciseThumbnail/images/${li.data}`) 14 | } 15 | } else if (li.type === MULTIMEDIA.audio && (!li.data.startsWith('data:audio') && !li.data.includes('/static/'))) { 16 | return { 17 | type: li.type, 18 | data: require(`./media/defaultExerciseThumbnail/sounds/${li.data}`) 19 | } 20 | } else if (li.type === MULTIMEDIA.video && (!li.data.startsWith('data:video') && !li.data.includes('/static/'))) { 21 | return { 22 | type: li.type, 23 | data: require(`./media/defaultExerciseThumbnail/videos/${li.data}`) 24 | } 25 | } else { 26 | return li; 27 | } 28 | } --------------------------------------------------------------------------------