From a700739cb78d40e452628855eae9105832a80118 Mon Sep 17 00:00:00 2001 From: Graham McGregor Date: Tue, 27 Jun 2017 22:36:38 -0400 Subject: [PATCH] Autoformat code --- app/scripts/App.js | 78 ++++--- app/scripts/AppFreezer.js | 10 +- app/scripts/index.js | 7 +- app/scripts/services/analytics_service.js | 8 +- app/scripts/services/bar_generator.js | 152 ++++++------- app/scripts/services/key_converter.js | 85 +++----- app/scripts/services/level_service.js | 36 ++-- app/scripts/services/metronome_service.js | 16 +- app/scripts/services/midi_service.js | 65 +++--- .../services/pitch_statistic_service.js | 87 ++++---- app/scripts/services/rhythm_checker.js | 13 +- .../services/rhythm_statistic_service.js | 81 ++++--- app/scripts/services/stat_evolver.js | 10 +- app/scripts/spec/key_converter_spec.js | 27 +-- app/scripts/spec/level_service_spec.js | 16 +- app/scripts/spec/midi_service_spec.js | 51 ++--- app/scripts/spec/rhythm_checker_spec.js | 57 ++--- app/scripts/spec/stat_evolver_spec.js | 24 +-- app/scripts/views/animated_number.js | 15 +- app/scripts/views/beat_visualization.js | 99 +++++---- app/scripts/views/claviature_view.js | 46 ++-- app/scripts/views/collapsable_container.js | 20 +- app/scripts/views/game_button.js | 47 ++-- app/scripts/views/level_view.js | 69 +++--- app/scripts/views/metronome_view.js | 47 ++-- app/scripts/views/newsletter_form.js | 33 +-- app/scripts/views/pie_chart.js | 34 +-- app/scripts/views/pitch_reading_view.js | 201 +++++++++--------- app/scripts/views/pitch_settings_view.js | 168 +++++++-------- app/scripts/views/pitch_statistic_view.js | 26 +-- app/scripts/views/privacy_policy_modal.js | 34 ++- app/scripts/views/range_setting_component.js | 37 ++-- app/scripts/views/rhythm_reading_view.js | 164 +++++++------- app/scripts/views/rhythm_settings_view.js | 111 +++++----- app/scripts/views/rhythm_statistic_view.js | 43 ++-- app/scripts/views/setting_line.js | 13 +- app/scripts/views/star_animation.js | 6 +- app/scripts/views/stave_renderer.js | 47 ++-- karma.conf.js | 34 +-- server.js | 18 +- webpack.config.js | 38 ++-- webpack.config.production.js | 35 +-- 42 files changed, 1027 insertions(+), 1181 deletions(-) diff --git a/app/scripts/App.js b/app/scripts/App.js index eb42018..79bbd2c 100644 --- a/app/scripts/App.js +++ b/app/scripts/App.js @@ -2,7 +2,7 @@ require("html!../index.html"); require("../styles/index.less"); require("font-awesome-webpack"); -import React, {Component} from "react"; +import React, { Component } from "react"; import PitchReadingView from "./views/pitch_reading_view"; import RhythmReadingView from "./views/rhythm_reading_view"; import PrivacyPolicyModal from "./views/privacy_policy_modal"; @@ -11,19 +11,17 @@ import PitchStatisticService from "./services/pitch_statistic_service.js"; import RhythmStatisticService from "./services/rhythm_statistic_service.js"; import AnalyticsService from "./services/analytics_service.js"; import AppFreezer from "./AppFreezer.js"; -import { Nav, NavItem, Button, Input } from 'react-bootstrap'; -import classNames from 'classnames'; +import { Nav, NavItem, Button, Input } from "react-bootstrap"; +import classNames from "classnames"; const pianoBackgroundJpg = require("file!../images/piano-background.jpg"); - export default class App extends Component { - constructor(props, context) { super(props, context); this.state = { activeGame: "pitch", - showPrivacyPolicy: false, + showPrivacyPolicy: false }; } @@ -32,7 +30,7 @@ export default class App extends Component { activeGame: newGame }); - AnalyticsService.sendEvent('GameSelection', newGame); + AnalyticsService.sendEvent("GameSelection", newGame); } render() { @@ -49,11 +47,17 @@ export default class App extends Component {
@@ -61,21 +65,20 @@ export default class App extends Component {
- ); } componentDidMount() { - AppFreezer.on('update', () => this.forceUpdate()); + AppFreezer.on("update", () => this.forceUpdate()); } } diff --git a/app/scripts/AppFreezer.js b/app/scripts/AppFreezer.js index 88ac8a0..d68e086 100644 --- a/app/scripts/AppFreezer.js +++ b/app/scripts/AppFreezer.js @@ -7,17 +7,17 @@ let defaultSettings = { accuracyGoal: 0.85, timeGoal: 2000, amount: 5, - newNotesShare: 0.6, + newNotesShare: 0.6 }, chordSizeRanges: { treble: [1, 3], - bass: [1, 3], + bass: [1, 3] }, keySignature: [7, 7], useAccidentals: false, midi: { inputs: Freezer.createLeaf([]), - activeInputIndex: 0, + activeInputIndex: 0 } }, rhythmReading: { @@ -29,7 +29,7 @@ let defaultSettings = { eighthNotes: true, sixteenthNotes: false, dottedNotes: false, - triplets: false, + triplets: false } }; @@ -44,7 +44,7 @@ const AppFreezer = new Freezer({ settings: defaultSettings }); -AppFreezer.on('update', () => { +AppFreezer.on("update", () => { const settingsJson = JSON.stringify(AppFreezer.get().settings.toJS()); localStorage.setItem("SheetMusicTutor-settings", settingsJson); }); diff --git a/app/scripts/index.js b/app/scripts/index.js index 53f1d8b..468dea2 100644 --- a/app/scripts/index.js +++ b/app/scripts/index.js @@ -1,8 +1,5 @@ import React from "react"; -import {render} from "react-dom"; +import { render } from "react-dom"; import App from "./App"; -render( - , - document.getElementById("root") -); +render(, document.getElementById("root")); diff --git a/app/scripts/services/analytics_service.js b/app/scripts/services/analytics_service.js index 60cabaa..981695b 100644 --- a/app/scripts/services/analytics_service.js +++ b/app/scripts/services/analytics_service.js @@ -4,11 +4,11 @@ export default { return; } - ga('send', { - hitType: 'event', + ga("send", { + hitType: "event", eventCategory, eventAction, - eventValue, + eventValue }); } -} +}; diff --git a/app/scripts/services/bar_generator.js b/app/scripts/services/bar_generator.js index 5be9d9e..5146d19 100644 --- a/app/scripts/services/bar_generator.js +++ b/app/scripts/services/bar_generator.js @@ -24,17 +24,13 @@ function sampleWithoutReplacement(options) { function randomInvokeAOrB(probability, functionA, functionB) { if (Math.random() < probability) { return functionA(); - } else { - return functionB(); } -}; + return functionB(); +} export default { - generateKeySignature: function(settings) { - const keySignatureIndex = _.sample( - _.range(settings.keySignature[0], settings.keySignature[1] + 1) - ); + const keySignatureIndex = _.sample(_.range(settings.keySignature[0], settings.keySignature[1] + 1)); return KeyConverter.keySignatureValueToString(keySignatureIndex); }, @@ -49,18 +45,14 @@ export default { }, generateRhythmBar: function(settings) { - const calcBarLength = (durations) => { - return durations.map((el) => 1 / Math.abs(el)).reduce((a, b) => a + b, 0); - } + const calcBarLength = durations => { + return durations.map(el => 1 / Math.abs(el)).reduce((a, b) => a + b, 0); + }; const generateRandomDurations = () => { const durations = []; - while (calcBarLength(durations) < 1) { - const possibleNotes = _.flatten([ - [4, 2], - settings.eighthNotes ? 8 : null, - settings.sixteenthNotes ? 16 : null, - ]); + while (calcBarLength(durations) < 1) { + const possibleNotes = _.flatten([[4, 2], settings.eighthNotes ? 8 : null, settings.sixteenthNotes ? 16 : null]); let newDuration = _.sample(possibleNotes); if (settings.rests && Math.random() < settings.restProbability) { @@ -72,21 +64,22 @@ export default { } return _.shuffle(durations); - } + }; let durations = generateRandomDurations(); - while (durations.every((el) => el < 0)) { + while (durations.every(el => el < 0)) { durations = generateRandomDurations(); } // durations = [4, 2, -4, 4]; - const staveNotes = durations.map((duration) => - new Vex.Flow.StaveNote({ - clef: "treble", - keys: ["a/4"], - duration: duration > 0 ? `${duration}` : `${duration}r` - }) + const staveNotes = durations.map( + duration => + new Vex.Flow.StaveNote({ + clef: "treble", + keys: ["a/4"], + duration: duration > 0 ? `${duration}` : `${duration}r` + }) ); return { @@ -114,13 +107,12 @@ export default { const frequencies = { new: settings.automaticDifficulty.newNotesShare, trebleGivenNew: amounts.trebleAndNew / (amounts.new || 1), - trebleGivenOld: amounts.trebleAndOld / (amounts.old || 1), + trebleGivenOld: amounts.trebleAndOld / (amounts.old || 1) }; const trebleProbability = - frequencies.trebleGivenNew * frequencies.new + - frequencies.trebleGivenOld * (1 - frequencies.new); + frequencies.trebleGivenNew * frequencies.new + frequencies.trebleGivenOld * (1 - frequencies.new); return trebleProbability; - } + }; if (onePerTime) { if (level) { @@ -129,64 +121,59 @@ export default { return [1, 0]; } return [0, 1]; - } else { - const lengths = { - treble: _.max(settings.chordSizeRanges.treble), - bass: _.max(settings.chordSizeRanges.bass) - }; - if (lengths.treble > 0 && lengths.bass > 0) { - return _.sample([[0, 1], [1, 0]]); - } - if (lengths.treble === 0) { - return [0, 1]; - } - return [1, 0]; } - } else { - if (level) { - // todo: handle possibility that a level doesn't demand the onePerTime limit - return this.getClefAmounts(settings, true, level); - } else { - return ["treble", "bass"].map((clef) => - _.random.apply(_, settings.chordSizeRanges[clef]) - ); + const lengths = { + treble: _.max(settings.chordSizeRanges.treble), + bass: _.max(settings.chordSizeRanges.bass) + }; + if (lengths.treble > 0 && lengths.bass > 0) { + return _.sample([[0, 1], [1, 0]]); + } + if (lengths.treble === 0) { + return [0, 1]; } + return [1, 0]; } + if (level) { + // todo: handle possibility that a level doesn't demand the onePerTime limit + return this.getClefAmounts(settings, true, level); + } + return ["treble", "bass"].map(clef => _.random.apply(_, settings.chordSizeRanges[clef])); }, generateBars: function(settings, level) { const isMidiAvailable = settings.midi.inputs.get().length > 0; const onePerTime = !isMidiAvailable; - const [trebleNotes, bassNotes] = _.unzip(_.range(0, options.chordsPerBar).map((index) => { - const generatePossibleNotes = (clef) => { - if (level) { - return { - new: level.keys[clef], - old: LevelService.getAllNotesUntilLevelIndex(level.index, clef) - }; - } - const levels = clef === "treble" ? [4, 5] : [2, 3]; - return _.flatten(levels.map((noteLevel) => - baseNotes.map((el) => el + "/" + noteLevel) - )); - }; + const [trebleNotes, bassNotes] = _.unzip( + _.range(0, options.chordsPerBar).map(index => { + const generatePossibleNotes = clef => { + if (level) { + return { + new: level.keys[clef], + old: LevelService.getAllNotesUntilLevelIndex(level.index, clef) + }; + } + const levels = clef === "treble" ? [4, 5] : [2, 3]; + return _.flatten(levels.map(noteLevel => baseNotes.map(el => el + "/" + noteLevel))); + }; - const [possibleTrebleNotes, possibleBassNotes] = [ - generatePossibleNotes("treble"), - generatePossibleNotes("bass") - ]; + const [possibleTrebleNotes, possibleBassNotes] = [ + generatePossibleNotes("treble"), + generatePossibleNotes("bass") + ]; - const [trebleAmount, bassAmount] = this.getClefAmounts(settings, onePerTime, level); + const [trebleAmount, bassAmount] = this.getClefAmounts(settings, onePerTime, level); - // trebleAmount bassAmount - // if length === 0 then amounts cannot be more than 0 + // trebleAmount bassAmount + // if length === 0 then amounts cannot be more than 0 - return [ - this.generateNotesForBeat(settings, "treble", trebleAmount, possibleTrebleNotes), - this.generateNotesForBeat(settings, "bass", bassAmount, possibleBassNotes) - ]; - })); + return [ + this.generateNotesForBeat(settings, "treble", trebleAmount, possibleTrebleNotes), + this.generateNotesForBeat(settings, "bass", bassAmount, possibleBassNotes) + ]; + }) + ); return { treble: trebleNotes, @@ -208,20 +195,20 @@ export default { return _.times(amount, () => { const bothOptionsAreNotEmpty = newPossibleNotes.length > 0 && oldPossibleNotes.length > 0; - const newNoteProbability = bothOptionsAreNotEmpty ? - settings.automaticDifficulty.newNotesShare : - (newPossibleNotes.length > 0 ? 1 : 0); + const newNoteProbability = bothOptionsAreNotEmpty + ? settings.automaticDifficulty.newNotesShare + : newPossibleNotes.length > 0 ? 1 : 0; return randomInvokeAOrB( newNoteProbability, () => sampleWithoutReplacement(newPossibleNotes), () => sampleWithoutReplacement(oldPossibleNotes) - ) + ); }); }, ensureInterval: function(keyStrings) { - const keyNumbers = keyStrings.map((keyString) => { + const keyNumbers = keyStrings.map(keyString => { return KeyConverter.getKeyNumberForKeyString(keyString, "C"); }); return options.maximumInterval >= _.max(keyNumbers) - _.min(keyNumbers); @@ -245,20 +232,17 @@ export default { const staveChord = new Vex.Flow.StaveNote({ clef: clef, keys: randomNoteSet.sort((keyA, keyB) => { - return KeyConverter.getKeyNumberForKeyString(keyA, "C") - - KeyConverter.getKeyNumberForKeyString(keyB, "C"); + return KeyConverter.getKeyNumberForKeyString(keyA, "C") - KeyConverter.getKeyNumberForKeyString(keyB, "C"); }), duration: `${options.chordsPerBar}` }); - randomNoteSet.forEach(({note, modifier}, index) => { + randomNoteSet.forEach(({ note, modifier }, index) => { if (modifier) { staveChord.addAccidental(index, new Vex.Flow.Accidental(modifier)); } }); return staveChord; - } - -} +}; diff --git a/app/scripts/services/key_converter.js b/app/scripts/services/key_converter.js index a79ec97..481cebc 100644 --- a/app/scripts/services/key_converter.js +++ b/app/scripts/services/key_converter.js @@ -4,28 +4,25 @@ const scaleIntervals = [0, 2, 2, 1, 2, 2, 2]; // TODO: generate this const keySignatureOffsets = { - "C#": ['f', 'c', 'g', 'd', 'a', 'e', 'b'], - "F#": ['f', 'c', 'g', 'd', 'a', 'e'], - "B": ['f', 'c', 'g', 'd', 'a'], - "E": ['f', 'c', 'g', 'd'], - "A": ['f', 'c', 'g'], - "D": ['f', 'c'], - "G": ['f'], - "C": [], - "F": ['b'], - "Bb": ['b', 'e'], - "Eb": ['b', 'e', 'a'], - "Ab": ['b', 'e', 'a', 'd'], - "Db": ['b', 'e', 'a', 'd', 'g'], - "Gb": ['b', 'e', 'a', 'd', 'g', 'c'], - "Cb": ['b', 'e', 'a', 'd', 'g', 'c', 'f'] + "C#": ["f", "c", "g", "d", "a", "e", "b"], + "F#": ["f", "c", "g", "d", "a", "e"], + B: ["f", "c", "g", "d", "a"], + E: ["f", "c", "g", "d"], + A: ["f", "c", "g"], + D: ["f", "c"], + G: ["f"], + C: [], + F: ["b"], + Bb: ["b", "e"], + Eb: ["b", "e", "a"], + Ab: ["b", "e", "a", "d"], + Db: ["b", "e", "a", "d", "g"], + Gb: ["b", "e", "a", "d", "g", "c"], + Cb: ["b", "e", "a", "d", "g", "c", "f"] }; - const keySignatures = ["C#", "F#", "B", "E", "A", "D", "G", "C", "F", "Bb", "Eb", "Ab", "Db", "Gb", "Cb"]; -const octaveNotes = [ - "c", "c#", "d", "d#", "e", "f", "f#", "g", "g#", "a", "a#", "b" -]; +const octaveNotes = ["c", "c#", "d", "d#", "e", "f", "f#", "g", "g#", "a", "a#", "b"]; const keyMap = initializeKeyMap(); @@ -44,9 +41,8 @@ function initializeKeyMap() { const octaveCount = 7; const claviature = octaveNotes .slice(claviatureOffset) - .concat( - _.flatten(_.times(octaveCount, () => octaveNotes)) - ).concat([octaveNotes[0]]); + .concat(_.flatten(_.times(octaveCount, () => octaveNotes))) + .concat([octaveNotes[0]]); const keyMap = {}; @@ -61,34 +57,29 @@ function initializeKeyMap() { return keyMap; } - const KeyConverter = { - getPitchRangeInclusive: function(keyA, keyB, includeAccidentals) { const keyArray = _.values(keyMap); - return keyArray.slice( - keyArray.indexOf(keyA), - keyArray.indexOf(keyB) + 1 - ).filter((el) => - includeAccidentals || !this.hasAccidental(el) - ); + return keyArray + .slice(keyArray.indexOf(keyA), keyArray.indexOf(keyB) + 1) + .filter(el => includeAccidentals || !this.hasAccidental(el)); }, hasAccidental: function(keyString) { return keyString.indexOf("#") > -1 || keyString.slice(1).indexOf("b") > -1; }, - getKeyNumberForCanonicalKeyString: function (keyString) { - return parseInt(_.findKey(keyMap, (key) => key === keyString), 10); + getKeyNumberForCanonicalKeyString: function(keyString) { + return parseInt(_.findKey(keyMap, key => key === keyString), 10); }, getCanonicalKeySignature: function(keySignature) { const keyString = keySignature.toLowerCase() + "/1"; - const canonicalKeyString = KeyConverter.getCanonicalKeyString(keyString) + const canonicalKeyString = KeyConverter.getCanonicalKeyString(keyString); return KeyConverter.getNoteFromKeyString(canonicalKeyString).toUpperCase(); }, - getKeyNumberForKeyString: function (keyString, keySignature) { + getKeyNumberForKeyString: function(keyString, keySignature) { keyString = KeyConverter.getCanonicalKeyString(keyString); const keyNumber = KeyConverter.getKeyNumberForCanonicalKeyString(keyString); @@ -111,7 +102,7 @@ const KeyConverter = { return keyNumber; }, - getScaleKeysForBase: function (baseKeyString) { + getScaleKeysForBase: function(baseKeyString) { // Returns canonical key strings. // For example, the last key of the f sharp major scale is e# // The function will return a f (which is harmonically seen the same) @@ -124,18 +115,16 @@ const KeyConverter = { let lastKey = baseKeyString; - return _.times(7, (index) => { + return _.times(7, index => { lastKey += scaleIntervals[index]; return lastKey; }); }, - - getCanonicalKeyString: function (keyString) { - + getCanonicalKeyString: function(keyString) { // strips away the given modifier and returns the strippedKey as well as the // amount of stripped modifiers - const stripKey = function (keyToStrip, modifier) { + const stripKey = function(keyToStrip, modifier) { const regexp = new RegExp(modifier, "g"); // ignore the first character so we only strip b-signs and not b-notes const strippedKey = keyToStrip[0] + keyToStrip.slice(1).replace(regexp, ""); @@ -148,19 +137,16 @@ const KeyConverter = { [keyString, flatDifference] = stripKey(keyString, "b"); [keyString, sharpDifference] = stripKey(keyString, "#"); - const keyNumber = KeyConverter.getKeyNumberForCanonicalKeyString(keyString) + - sharpDifference - - flatDifference; + const keyNumber = KeyConverter.getKeyNumberForCanonicalKeyString(keyString) + sharpDifference - flatDifference; return KeyConverter.getKeyStringForKeyNumber(keyNumber); }, - - getKeyStringForKeyNumber: function (number) { + getKeyStringForKeyNumber: function(number) { return keyMap[number + ""]; }, - keySignatureValueToString: function (value) { + keySignatureValueToString: function(value) { return keySignatures[value]; }, @@ -168,7 +154,7 @@ const KeyConverter = { return keySignatureOffsets[keySignature].length + 1; }, - getModifierTypeOfKeySignature: function (keySignature) { + getModifierTypeOfKeySignature: function(keySignature) { const index = keySignatures.indexOf(keySignature); if (index < 7) { return "#"; @@ -178,11 +164,11 @@ const KeyConverter = { return ""; }, - getNoteFromKeyString: function (keyString) { + getNoteFromKeyString: function(keyString) { return keyString.split("/")[0]; }, - getNotesOutsideScale: function (keySignature) { + getNotesOutsideScale: function(keySignature) { const cScaleKeyNumbers = KeyConverter.getScaleKeysForBase(keySignature); const cScaleNotes = cScaleKeyNumbers .map(KeyConverter.getKeyStringForKeyNumber) @@ -192,5 +178,4 @@ const KeyConverter = { } }; - export default KeyConverter; diff --git a/app/scripts/services/level_service.js b/app/scripts/services/level_service.js index a0de7cb..44016ec 100644 --- a/app/scripts/services/level_service.js +++ b/app/scripts/services/level_service.js @@ -9,14 +9,14 @@ const levelGroups = [ base: { clef: "treble", signature: "C", - accidentals: false, + accidentals: false }, sublevels: [ r("c/4", "e/4"), r("f/4", "a/4"), r("b/4", "d/5"), r("e/5", "g/5"), - r("a/5", "c/6"), + r("a/5", "c/6") // very high and very low end should also go here ] @@ -25,26 +25,26 @@ const levelGroups = [ base: { clef: "bass", signature: "C", - accidentals: false, + accidentals: false }, sublevels: [ r("c/2", "e/2"), r("f/2", "a/2"), r("b/2", "d/3"), r("e/3", "g/3"), - r("a/3", "c/4"), + r("a/3", "c/4") // very high and very low end should also go here ] } ]; -const Levels = _.flatMap(levelGroups, (levelGroup) => { - return levelGroup.sublevels.map((sublevel) => { +const Levels = _.flatMap(levelGroups, levelGroup => { + return levelGroup.sublevels.map(sublevel => { const level = _.assign({}, levelGroup.base, { keys: { - "treble": levelGroup.base.clef === "treble" ? sublevel : [], - "bass": levelGroup.base.clef === "bass" ? sublevel : [], + treble: levelGroup.base.clef === "treble" ? sublevel : [], + bass: levelGroup.base.clef === "bass" ? sublevel : [] } }); delete level.clef; @@ -55,14 +55,13 @@ Levels.forEach((level, index) => { level.index = index; }); - const LevelService = { getLevelByIndex(index) { return Levels[index]; }, getAllNotesUntilLevelIndex(levelIndex, optClef) { return _.flatten( - _.range(levelIndex).map((levelIndex) => { + _.range(levelIndex).map(levelIndex => { const keys = Levels[levelIndex].keys; if (optClef) { return keys[optClef]; @@ -74,8 +73,7 @@ const LevelService = { getLevelOfUser(events) { // check for each level if the user meets all necessary conditions let levelIndex = 0; - while (levelIndex < Levels.length - && this.isInLevelIndex(events, levelIndex)) { + while (levelIndex < Levels.length && this.isInLevelIndex(events, levelIndex)) { levelIndex++; } return levelIndex - 1; @@ -108,11 +106,9 @@ const LevelService = { time: thresholdSettings.timeGoal }; - const filteredEvents = eventsToAssess.filter((event) => - event.keys.some(this.levelContainsKey.bind(this, level)) - ); - const unfoldedEvents = _.flatMap(filteredEvents, (event) => { - return event.keys.map((key) => { + const filteredEvents = eventsToAssess.filter(event => event.keys.some(this.levelContainsKey.bind(this, level))); + const unfoldedEvents = _.flatMap(filteredEvents, event => { + return event.keys.map(key => { const subEvent = { ...event, key @@ -130,7 +126,7 @@ const LevelService = { const eventsLength = eventsToAssess.length; const accuracy = successPartition.length / eventsToAssess.length; - const time = _.sum(successPartition.map((el) => el.time)) / successPartition.length; + const time = _.sum(successPartition.map(el => el.time)) / successPartition.length; const meetsLength = eventsLength >= optThresholds.amount; const meetsAccuracy = accuracy >= optThresholds.accuracy; @@ -142,7 +138,7 @@ const LevelService = { meetsAccuracy, meetsTime, isGoodEnough: meetsLength && meetsAccuracy && meetsTime, - details : { + details: { eventsLength, time, accuracy, @@ -150,7 +146,7 @@ const LevelService = { } }; }); - const goodEnoughPartition = _.partition(evaluation, (el) => el.isGoodEnough); + const goodEnoughPartition = _.partition(evaluation, el => el.isGoodEnough); return { goodEnoughKeys: goodEnoughPartition[0], notGoodEnoughKeys: goodEnoughPartition[1], diff --git a/app/scripts/services/metronome_service.js b/app/scripts/services/metronome_service.js index b14ade6..12fa8d2 100644 --- a/app/scripts/services/metronome_service.js +++ b/app/scripts/services/metronome_service.js @@ -6,18 +6,22 @@ let audioBuffer; function loadMp3() { const request = new XMLHttpRequest(); - request.open('GET', successMp3Url, true); - request.responseType = 'arraybuffer'; + request.open("GET", successMp3Url, true); + request.responseType = "arraybuffer"; request.onload = function() { const audioData = request.response; - audioCtx.decodeAudioData(audioData, function(buffer) { - audioBuffer = buffer; + audioCtx.decodeAudioData( + audioData, + function(buffer) { + audioBuffer = buffer; }, - function(e){"Error with decoding audio data" + e.err} + function(e) { + "Error with decoding audio data" + e.err; + } ); - } + }; request.send(); } diff --git a/app/scripts/services/midi_service.js b/app/scripts/services/midi_service.js index eae7e2a..cf15f5b 100644 --- a/app/scripts/services/midi_service.js +++ b/app/scripts/services/midi_service.js @@ -9,15 +9,8 @@ function getMidiSettings() { } export default class MidiService { - constructor(opts) { - const { - successCallback, - failureCallback, - errorCallback, - errorResolveCallback, - mocked, - } = opts; + const { successCallback, failureCallback, errorCallback, errorResolveCallback, mocked } = opts; this.successCallback = successCallback; this.failureCallback = failureCallback; @@ -37,20 +30,16 @@ export default class MidiService { } if (!navigator.requestMIDIAccess) { - AnalyticsService.sendEvent('MidiService', "no-browser-support"); + AnalyticsService.sendEvent("MidiService", "no-browser-support"); this.errorCallback("Your browser doesn't seem to support MIDI Access."); return; } - - this.promise = navigator.requestMIDIAccess({sysexEnabled: true}); - this.promise.then( - this.onMidiAccess.bind(this), - () => { - AnalyticsService.sendEvent('MidiService', "problem-requesting-midi"); - this.errorCallback("There was a problem while requesting MIDI access.", arguments); - } - ); + this.promise = navigator.requestMIDIAccess({ sysexEnabled: true }); + this.promise.then(this.onMidiAccess.bind(this), () => { + AnalyticsService.sendEvent("MidiService", "problem-requesting-midi"); + this.errorCallback("There was a problem while requesting MIDI access.", arguments); + }); } initializeInputStates() { @@ -65,11 +54,10 @@ export default class MidiService { this.desiredInputState = {}; } - setDesiredKeys(keys, keySignature) { this.desiredInputState = {}; - keys.map((key) => { + keys.map(key => { const number = KeyConverter.getKeyNumberForKeyString(key, keySignature); this.desiredInputState[number] = true; }); @@ -85,7 +73,6 @@ export default class MidiService { this.checkEqual(intensity); } - checkEqual(intensity) { if (_.isEqual(this.currentInputState, this.desiredInputState)) { this.justHadSuccess = true; @@ -119,7 +106,7 @@ export default class MidiService { inputs.push(input.value); } if (inputs.length === 0) { - AnalyticsService.sendEvent('MidiService', "no-midi-device-found"); + AnalyticsService.sendEvent("MidiService", "no-midi-device-found"); this.errorCallback("No MIDI device found."); return; } @@ -128,45 +115,43 @@ export default class MidiService { getMidiSettings().set({ inputs: Freezer.createLeaf(inputs), - activeInputIndex: 0, + activeInputIndex: 0 }); - AppFreezer.on('input:changed', (newIndex) => { + AppFreezer.on("input:changed", newIndex => { const midiSettings = getMidiSettings(); midiSettings.set({ - activeInputIndex: newIndex, + activeInputIndex: newIndex }); this.unlistenToInputs(midiSettings.inputs.get()); this.listenToInput(midiSettings.inputs.get()[newIndex]); - }) + }); console.log("Midi access received. Available inputs", inputs, "Chosen input:", input); - AnalyticsService.sendEvent('MidiService', "available inputs", inputs.length); + AnalyticsService.sendEvent("MidiService", "available inputs", inputs.length); } unlistenToInputs(inputs) { - inputs.forEach((input) => input.onmidimessage = null); + inputs.forEach(input => (input.onmidimessage = null)); } listenToInput(input) { input.onmidimessage = null; input.onmidimessage = this.onMidiMessage.bind(this); - setTimeout( - () => { - if (this.receivingMidiMessages) { - console.log("Receiving events..."); - } else { - console.warn("Firing error callback"); - AnalyticsService.sendEvent('MidiService', "no-messages"); - this.errorCallback(` + setTimeout(() => { + if (this.receivingMidiMessages) { + console.log("Receiving events..."); + } else { + console.warn("Firing error callback"); + AnalyticsService.sendEvent("MidiService", "no-messages"); + this.errorCallback(` A MIDI device could be found, but it doesn't send any messages. Did you press a key, yet? A browser restart could help. `); - this.errorCallbackFired = true; - } - }, 2000 - ); + this.errorCallbackFired = true; + } + }, 2000); } onMidiMessage(msg) { diff --git a/app/scripts/services/pitch_statistic_service.js b/app/scripts/services/pitch_statistic_service.js index 88ef00e..f9ac113 100644 --- a/app/scripts/services/pitch_statistic_service.js +++ b/app/scripts/services/pitch_statistic_service.js @@ -5,12 +5,10 @@ import KeyConverter from "../services/key_converter.js"; const localStoragePitchStatsKey = "pianoTrainerStatistics"; class PitchStatisticService { - constructor() { this.read(); } - register(evt) { /* an event could be something like { @@ -30,9 +28,7 @@ class PitchStatisticService { this.stats = localStorage.getItem(localStoragePitchStatsKey); if (this.stats) { - this.stats = JSON.parse(this.stats) - .map(this.transformDate) - .map(StatEvolver.evolveToLatestSchema); + this.stats = JSON.parse(this.stats).map(this.transformDate).map(StatEvolver.evolveToLatestSchema); } else { this.stats = []; } @@ -48,18 +44,22 @@ class PitchStatisticService { return localStorage.setItem(localStoragePitchStatsKey, JSON.stringify(this.stats)); } - getSuccessCount() { - return _(this.stats).filter((el) => el.success).value().length; + return _(this.stats).filter(el => el.success).value().length; } rateEvent(event) { - return 1 + Math.floor([ - event.success ? 10 : -0.1, - event.keys.length, - 10e6 / Math.pow(event.time, 2), - KeyConverter.rateKeySignatureDifficulty(event.keySignature) - ].reduce((a, b) => a * b, 1)); + return ( + 1 + + Math.floor( + [ + event.success ? 10 : -0.1, + event.keys.length, + 10e6 / Math.pow(event.time, 2), + KeyConverter.rateKeySignatureDifficulty(event.keySignature) + ].reduce((a, b) => a * b, 1) + ) + ); } getCurrentScore() { @@ -67,10 +67,7 @@ class PitchStatisticService { } getLastTimes(n = 10) { - return this.stats - .filter((el) => el.success) - .map((el) => el.time) - .slice(-n); + return this.stats.filter(el => el.success).map(el => el.time).slice(-n); } computeAverage(array) { @@ -85,58 +82,52 @@ class PitchStatisticService { return this.computeAverage(this.getLastTimes(n)); } - getTotalAmountOfChords() { - return _(this.stats) - .filter((el) => el.success) - .map((el) => el.keys) - .size(); + return _(this.stats).filter(el => el.success).map(el => el.keys).size(); } - getTotalAmountOfKeys() { - return _(this.stats) - .filter((el) => el.success) - .map((el) => el.keys) - .flatten() - .size(); + return _(this.stats).filter(el => el.success).map(el => el.keys).flatten().size(); } - getSuccessRate() { - return _.filter(this.stats, (el) => el.success).length / this.stats.length; + return _.filter(this.stats, el => el.success).length / this.stats.length; } calculateMissingSuccessfulNotes() { - const desiredSuccessRate = 0.9 - const currentSuccessCount = this.stats.filter((el) => el.success).length; + const desiredSuccessRate = 0.9; + const currentSuccessCount = this.stats.filter(el => el.success).length; const currentFailureCount = this.stats.length - currentSuccessCount; // t = (1 - sr) / sr * f - const desiredSuccessCount = (desiredSuccessRate * currentFailureCount) / (1 - desiredSuccessRate); + const desiredSuccessCount = desiredSuccessRate * currentFailureCount / (1 - desiredSuccessRate); return Math.ceil(desiredSuccessCount - currentSuccessCount); } getLastDays() { - return _(this.stats).filter((el) => el.success).groupBy(function (el) { - return [ - el.date.getUTCFullYear(), - ("0" + el.date.getMonth()).slice(-2), - ("0" + el.date.getDate()).slice(-2) - ].join("-"); - }).map((aDay, formattedDate) => { - const dayTimes = aDay.map((el) => el.time); - aDay.averageTime = this.computeAverage(dayTimes); - aDay.totalTime = _.sum(dayTimes); - aDay.formattedDate = formattedDate; - return aDay; - }).sortBy("formattedDate").reverse().value(); + return _(this.stats) + .filter(el => el.success) + .groupBy(function(el) { + return [ + el.date.getUTCFullYear(), + ("0" + el.date.getMonth()).slice(-2), + ("0" + el.date.getDate()).slice(-2) + ].join("-"); + }) + .map((aDay, formattedDate) => { + const dayTimes = aDay.map(el => el.time); + aDay.averageTime = this.computeAverage(dayTimes); + aDay.totalTime = _.sum(dayTimes); + aDay.formattedDate = formattedDate; + return aDay; + }) + .sortBy("formattedDate") + .reverse() + .value(); } - getDataCount() { return this.stats.length; } } - export default new PitchStatisticService(); diff --git a/app/scripts/services/rhythm_checker.js b/app/scripts/services/rhythm_checker.js index bde59d9..7120c96 100644 --- a/app/scripts/services/rhythm_checker.js +++ b/app/scripts/services/rhythm_checker.js @@ -1,22 +1,18 @@ import _ from "lodash"; export default { - convertDurationsToTimes: function(durations, barDuration) { const times = []; let lastTick = 0; - const inversedDurations = durations.map((el) => 1 / el); + const inversedDurations = durations.map(el => 1 / el); for (let i = 0; i < inversedDurations.length; i++) { const currentDuration = inversedDurations[i]; let newLastTick; if (currentDuration > 0) { newLastTick = lastTick + currentDuration; - times.push([ - lastTick * barDuration, - newLastTick * barDuration - ]); + times.push([lastTick * barDuration, newLastTick * barDuration]); } else { newLastTick = lastTick + Math.abs(currentDuration); } @@ -25,7 +21,6 @@ export default { return times; }, - /* returns an evaluationObject which holds { @@ -74,14 +69,14 @@ export default { if (givenTimes.length > expectedTimes.length) { beatEvaluations = beatEvaluations.concat( - givenTimes.slice(expectedTimes.length).map((el) => ({superfluous: true, correct: false})) + givenTimes.slice(expectedTimes.length).map(el => ({ superfluous: true, correct: false })) ); } return { beatEvaluations, missesBeat, - success: beatEvaluations.every((el) => el.correct) && !missesBeat, + success: beatEvaluations.every(el => el.correct) && !missesBeat }; }, diff --git a/app/scripts/services/rhythm_statistic_service.js b/app/scripts/services/rhythm_statistic_service.js index 86f50ee..e2d956e 100644 --- a/app/scripts/services/rhythm_statistic_service.js +++ b/app/scripts/services/rhythm_statistic_service.js @@ -3,7 +3,6 @@ import _ from "lodash"; const localStorageRhythmStatsKey = "SheetMusicTutor-RhythmStatistics"; class RhythmStatisticService { - constructor() { this.read(); this.callbacks = []; @@ -15,7 +14,7 @@ class RhythmStatisticService { off(event, callback) { if (callback) { - this.callbacks = this.callbacks.filter((el) => el != callback) + this.callbacks = this.callbacks.filter(el => el != callback); } else { this.callbacks = []; } @@ -37,15 +36,14 @@ class RhythmStatisticService { this.stats.push(evt); this.save(); - this.callbacks.forEach((cb) => cb()); + this.callbacks.forEach(cb => cb()); } read() { this.stats = localStorage.getItem(localStorageRhythmStatsKey); if (this.stats) { - this.stats = JSON.parse(this.stats) - .map(this.transformDate); + this.stats = JSON.parse(this.stats).map(this.transformDate); } else { this.stats = []; } @@ -66,7 +64,7 @@ class RhythmStatisticService { } getSuccessCount() { - return _(this.stats).filter((el) => el.success).value().length; + return _(this.stats).filter(el => el.success).value().length; } getLastScores(n) { @@ -74,76 +72,71 @@ class RhythmStatisticService { } getLastTimes(n = 10) { - return this.stats - .filter((el) => el.success) - .map((el) => el.time) - .slice(-n); + return this.stats.filter(el => el.success).map(el => el.time).slice(-n); } - computeAverage(array) { return _.sum(array) / (array.length || 1); } - getAverageTimeOfLast(n = 10) { return this.computeAverage(this.getLastTimes(n)); } - getTotalAmountOfRhythms() { - return this.stats.length + return this.stats.length; } - getTotalAmountOfBeats() { - return _(this.stats) - .map((el) => el.durations) - .flatten() - .size(); + return _(this.stats).map(el => el.durations).flatten().size(); } getTotalRhythmTime() { - return _.sum(this.stats.map((el) => el.barDuration)); + return _.sum(this.stats.map(el => el.barDuration)); } rateEvent(event) { - return Math.round([ - event.success ? 10 : 1, - _.sum(event.durations.map((el) => Math.abs(el))), - 10e5 / Math.pow(event.barDuration, 2), - event.liveBeatBars ? 0.5 : 1, - event.liveBeatBars && event.labelBeats ? 0.5 : 1 - ].reduce((a, b) => a * b, 1)); + return Math.round( + [ + event.success ? 10 : 1, + _.sum(event.durations.map(el => Math.abs(el))), + 10e5 / Math.pow(event.barDuration, 2), + event.liveBeatBars ? 0.5 : 1, + event.liveBeatBars && event.labelBeats ? 0.5 : 1 + ].reduce((a, b) => a * b, 1) + ); } getCurrentScore() { return _.sum(this.stats.map(this.rateEvent)); } - getSuccessRate() { - return _.filter(this.stats, (el) => el.success).length / this.stats.length; + return _.filter(this.stats, el => el.success).length / this.stats.length; } - getLastDays() { - return _(this.stats).filter((el) => el.success).groupBy(function (el) { - return [ - el.date.getUTCFullYear(), - ("0" + el.date.getMonth()).slice(-2), - ("0" + el.date.getDate()).slice(-2) - ].join("-"); - }).map((aDay, formattedDate) => { - const dayTimes = aDay.map((el) => el.time); - aDay.averageTime = this.computeAverage(dayTimes); - aDay.totalTime = _.sum(dayTimes); - aDay.formattedDate = formattedDate; - return aDay; - }).sortBy("formattedDate").reverse().value(); + return _(this.stats) + .filter(el => el.success) + .groupBy(function(el) { + return [ + el.date.getUTCFullYear(), + ("0" + el.date.getMonth()).slice(-2), + ("0" + el.date.getDate()).slice(-2) + ].join("-"); + }) + .map((aDay, formattedDate) => { + const dayTimes = aDay.map(el => el.time); + aDay.averageTime = this.computeAverage(dayTimes); + aDay.totalTime = _.sum(dayTimes); + aDay.formattedDate = formattedDate; + return aDay; + }) + .sortBy("formattedDate") + .reverse() + .value(); } - getDataCount() { return this.stats.length; } diff --git a/app/scripts/services/stat_evolver.js b/app/scripts/services/stat_evolver.js index ac641e3..8212578 100644 --- a/app/scripts/services/stat_evolver.js +++ b/app/scripts/services/stat_evolver.js @@ -1,23 +1,23 @@ // Runs evolutions on statistic objects, so that we can update the schema const evolutions = [ - (statObj) => { + statObj => { if (!statObj.version) { statObj.version = 1; delete statObj.formattedDate; } return statObj; }, - (statObj) => { + statObj => { if (statObj.version < 2) { statObj.version = 2; statObj.keySignature = "C"; } - return statObj + return statObj; } ]; export default { - runEvolution : (statObj, index) => evolutions[index](statObj), - evolveToLatestSchema : (statObj) => _.flow(...evolutions)(statObj), + runEvolution: (statObj, index) => evolutions[index](statObj), + evolveToLatestSchema: statObj => _.flow(...evolutions)(statObj) }; diff --git a/app/scripts/spec/key_converter_spec.js b/app/scripts/spec/key_converter_spec.js index 5db136a..7873f1c 100644 --- a/app/scripts/spec/key_converter_spec.js +++ b/app/scripts/spec/key_converter_spec.js @@ -1,9 +1,7 @@ import KeyConverter from "../services/key_converter.js"; -describe("KeyConverter", function () { - - it("resolves a simple note", function () { - +describe("KeyConverter", function() { + it("resolves a simple note", function() { const numberA0 = KeyConverter.getKeyNumberForKeyString("a/0", "C"); expect(numberA0).toBe(21); @@ -20,8 +18,7 @@ describe("KeyConverter", function () { expect(numberB3).toBe(58); }); - it("gets key strings for a number", function () { - + it("gets key strings for a number", function() { const aSharp0 = KeyConverter.getKeyStringForKeyNumber("22"); expect(aSharp0).toBe("a#/0"); @@ -29,14 +26,11 @@ describe("KeyConverter", function () { expect(b7).toBe("b/7"); }); - it("gets scales for an arbitrary base", function () { - - const cScale = KeyConverter.getScaleKeysForBase("c/4") - .map(KeyConverter.getKeyStringForKeyNumber); + it("gets scales for an arbitrary base", function() { + const cScale = KeyConverter.getScaleKeysForBase("c/4").map(KeyConverter.getKeyStringForKeyNumber); expect(cScale).toEqual(["c/4", "d/4", "e/4", "f/4", "g/4", "a/4", "b/4"]); - const fSharpScale = KeyConverter.getScaleKeysForBase("f#/4") - .map(KeyConverter.getKeyStringForKeyNumber); + const fSharpScale = KeyConverter.getScaleKeysForBase("f#/4").map(KeyConverter.getKeyStringForKeyNumber); const expectedFSharpScale = ["f#/4", "g#/4", "a#/4", "b/4", "c#/5", "d#/5", "e#/5"]; const normalizedFSharpScale = expectedFSharpScale.map(KeyConverter.getCanonicalKeyString, KeyConverter); @@ -44,17 +38,15 @@ describe("KeyConverter", function () { expect(fSharpScale).toEqual(normalizedFSharpScale); }); - it("can generate notes outside a given scale", function () { - + it("can generate notes outside a given scale", function() { const actualNonC = KeyConverter.getNotesOutsideScale("c/4"); expect(actualNonC).toEqual(["c#", "d#", "f#", "g#", "a#"]); const actualNonB = KeyConverter.getNotesOutsideScale("b/4"); - expect(actualNonB).toEqual(["c", "d", "f", "g", "a",]); + expect(actualNonB).toEqual(["c", "d", "f", "g", "a"]); }); - it("gets the canonical form of a key", function () { - + it("gets the canonical form of a key", function() { const c4 = KeyConverter.getCanonicalKeyString("c/4"); expect(c4).toBe("c/4"); @@ -64,5 +56,4 @@ describe("KeyConverter", function () { const cFlat = KeyConverter.getCanonicalKeyString("cb/4"); expect(cFlat).toBe("b/3"); }); - }); diff --git a/app/scripts/spec/level_service_spec.js b/app/scripts/spec/level_service_spec.js index 2d566e7..f273013 100644 --- a/app/scripts/spec/level_service_spec.js +++ b/app/scripts/spec/level_service_spec.js @@ -1,18 +1,13 @@ import LevelService from "../services/level_service.js"; -describe("LevelService", function () { +describe("LevelService", function() { + it("getAllNotesUntilLevel", function() { + expect(LevelService.getAllNotesUntilLevelIndex(1)).toEqual(["c/4", "d/4", "e/4"]); - it("getAllNotesUntilLevel", function () { - expect( - LevelService.getAllNotesUntilLevelIndex(1) - ).toEqual(["c/4", "d/4", "e/4"]); - - expect( - LevelService.getAllNotesUntilLevelIndex(2) - ).toEqual(["c/4", "d/4", "e/4", "f/4", "g/4", "a/4"]); + expect(LevelService.getAllNotesUntilLevelIndex(2)).toEqual(["c/4", "d/4", "e/4", "f/4", "g/4", "a/4"]); }); - it("isInLevel", function () { + it("isInLevel", function() { const events = [ { success: true, @@ -45,5 +40,4 @@ describe("LevelService", function () { }); expect(negativeEvaluation.isInLevel).toBe(false); }); - }); diff --git a/app/scripts/spec/midi_service_spec.js b/app/scripts/spec/midi_service_spec.js index 3e4bf4c..c10373f 100644 --- a/app/scripts/spec/midi_service_spec.js +++ b/app/scripts/spec/midi_service_spec.js @@ -1,12 +1,10 @@ import MidiService from "../services/midi_service.js"; -describe("MidiService", function () { - +describe("MidiService", function() { const onKeyStatus = MidiService.onKeyStatus; const offKeyStatus = MidiService.offKeyStatus; - it("notifies about a simple desired key state (midi implementation 1)", function () { - + it("notifies about a simple desired key state (midi implementation 1)", function() { let [successCounter, failedCounter] = [0, 0]; const successCallback = () => successCounter++; const failureCallback = () => failedCounter++; @@ -15,21 +13,16 @@ describe("MidiService", function () { midiService.setDesiredKeys(["a/0"], "C"); // onKeyStatus is an on event - midiService.onMidiMessage( - {data: [onKeyStatus, 21, 1]} - ); + midiService.onMidiMessage({ data: [onKeyStatus, 21, 1] }); // 143 is an off event - midiService.onMidiMessage( - {data: [offKeyStatus, 21, 1]} - ); + midiService.onMidiMessage({ data: [offKeyStatus, 21, 1] }); expect(failedCounter).toBe(0); expect(successCounter).toBe(1); }); - it("notifies about a simple desired key state (midi implementation 2)", function () { - + it("notifies about a simple desired key state (midi implementation 2)", function() { let [successCounter, failedCounter] = [0, 0]; const successCallback = () => successCounter++; const failureCallback = () => failedCounter++; @@ -38,21 +31,16 @@ describe("MidiService", function () { midiService.setDesiredKeys(["a/0"], "C"); // send on event - midiService.onMidiMessage( - {data: [onKeyStatus, 21, 1]} - ); + midiService.onMidiMessage({ data: [onKeyStatus, 21, 1] }); // send another on event but set velocity to 0 - midiService.onMidiMessage( - {data: [onKeyStatus, 21, 0]} - ); + midiService.onMidiMessage({ data: [onKeyStatus, 21, 0] }); expect(failedCounter).toBe(0); expect(successCounter).toBe(1); }); - it("notifies about a complex desired key state", function () { - + it("notifies about a complex desired key state", function() { let [successCounter, failedCounter] = [0, 0]; const successCallback = () => successCounter++; const failureCallback = () => failedCounter++; @@ -60,24 +48,17 @@ describe("MidiService", function () { const midiService = new MidiService({ successCallback, failureCallback, mocked: true }); midiService.setDesiredKeys(["a/0", "c/8"], "C"); - midiService.onMidiMessage( - {data: [onKeyStatus, 21, 1]} - ); + midiService.onMidiMessage({ data: [onKeyStatus, 21, 1] }); - midiService.onMidiMessage( - {data: [onKeyStatus, 108, 1]} - ); + midiService.onMidiMessage({ data: [onKeyStatus, 108, 1] }); - midiService.onMidiMessage( - {data: [onKeyStatus, 108, 0]} - ); + midiService.onMidiMessage({ data: [onKeyStatus, 108, 0] }); expect(successCounter).toBe(1); expect(failedCounter).toBe(0); }); - it("notifies once about a wrong desired key state", function () { - + it("notifies once about a wrong desired key state", function() { let [successCounter, failedCounter] = [0, 0]; const successCallback = () => successCounter++; const failureCallback = () => failedCounter++; @@ -85,11 +66,11 @@ describe("MidiService", function () { const midiService = new MidiService({ successCallback, failureCallback, mocked: true }); midiService.setDesiredKeys(["a/0", "c/8"], "C"); - midiService.onMidiMessage({data: [onKeyStatus, 21, 1]}); - midiService.onMidiMessage({data: [onKeyStatus, 107, 1]}); + midiService.onMidiMessage({ data: [onKeyStatus, 21, 1] }); + midiService.onMidiMessage({ data: [onKeyStatus, 107, 1] }); - midiService.onMidiMessage({data: [onKeyStatus, 21, 0]}); - midiService.onMidiMessage({data: [onKeyStatus, 107, 0]}); + midiService.onMidiMessage({ data: [onKeyStatus, 21, 0] }); + midiService.onMidiMessage({ data: [onKeyStatus, 107, 0] }); expect(failedCounter).toBe(1); expect(successCounter).toBe(0); diff --git a/app/scripts/spec/rhythm_checker_spec.js b/app/scripts/spec/rhythm_checker_spec.js index 674ed44..bd190c0 100644 --- a/app/scripts/spec/rhythm_checker_spec.js +++ b/app/scripts/spec/rhythm_checker_spec.js @@ -1,86 +1,53 @@ import RhythmChecker from "../services/rhythm_checker.js"; -describe("RhythmChecker", function () { - - it("converts durations to times - without rests", function () { +describe("RhythmChecker", function() { + it("converts durations to times - without rests", function() { const durations = [4, 4, 4, 2]; const times = RhythmChecker.convertDurationsToTimes(durations, 1000); - const expectedTimes = [ - [0, 250], - [250, 500], - [500, 750], - [750, 1250] - ]; + const expectedTimes = [[0, 250], [250, 500], [500, 750], [750, 1250]]; expect(times).toEqual(expectedTimes); }); - it("converts durations to times - with rests", function () { + it("converts durations to times - with rests", function() { const durations = [4, -4, 2, 4]; const times = RhythmChecker.convertDurationsToTimes(durations, 1000); - const expectedTimes = [ - [0, 250], - [500, 1000], - [1000, 1250] - ]; + const expectedTimes = [[0, 250], [500, 1000], [1000, 1250]]; expect(times).toEqual(expectedTimes); }); + it("compares times within a given tolerance", function() { + const expectedTimes = [[0, 250], [500, 1000], [1000, 1250]]; - it("compares times within a given tolerance", function () { - const expectedTimes = [ - [0, 250], - [500, 1000], - [1000, 1250] - ]; - - const givenTimesCorrect = [ - [0, 230], - [560, 960], - [970, 1300] - ]; + const givenTimesCorrect = [[0, 230], [560, 960], [970, 1300]]; const settings1 = { barDuration: 3000, eighthNotes: true, - sixteenthNotes: false, + sixteenthNotes: false }; const result1 = RhythmChecker.compare(expectedTimes, givenTimesCorrect, settings1); expect(result1.success).toBe(true); - const givenTimesImprecise = [ - [0, 230], - [290, 960], - [970, 1300] - ]; + const givenTimesImprecise = [[0, 230], [290, 960], [970, 1300]]; const result2 = RhythmChecker.compare(expectedTimes, givenTimesImprecise, settings1); expect(result2.success).toBe(false); expect(result2.missesBeat).toBe(false); - const givenTimesTooMany = [ - [0, 250], - [500, 1000], - [1000, 1250], - [1250, 1500] - ]; + const givenTimesTooMany = [[0, 250], [500, 1000], [1000, 1250], [1250, 1500]]; const result3 = RhythmChecker.compare(expectedTimes, givenTimesTooMany, settings1); expect(result3.success).toBe(false); expect(result3.missesBeat).toBe(false); expect(result3.beatEvaluations[3].superfluous).toBe(true); - const givenTimesMissesBeat = [ - [0, 250], - [500, 1000] - ]; + const givenTimesMissesBeat = [[0, 250], [500, 1000]]; const result4 = RhythmChecker.compare(expectedTimes, givenTimesMissesBeat, settings1); expect(result4.success).toBe(false); expect(result4.missesBeat).toBe(true); }); - }); - diff --git a/app/scripts/spec/stat_evolver_spec.js b/app/scripts/spec/stat_evolver_spec.js index c5cea05..49d837c 100644 --- a/app/scripts/spec/stat_evolver_spec.js +++ b/app/scripts/spec/stat_evolver_spec.js @@ -1,46 +1,46 @@ import StatisticService from "../services/pitch_statistic_service.js"; import StatEvolver from "../services/stat_evolver.js"; -describe("StatEvolver", function () { - +describe("StatEvolver", function() { const versions = [ - StatisticService.transformDate(JSON.parse(`{ + StatisticService.transformDate( + JSON.parse(`{ "success":true, "keys":["f/4","e/3","b/3","d/3"], "time": 14622, "date":"2016-03-01T23:59:04.462Z", "formattedDate":"2016-02-01" - }`)), + }`) + ), { success: true, - keys: ["f/4","e/3","b/3","d/3"], + keys: ["f/4", "e/3", "b/3", "d/3"], time: 14622, date: new Date("2016-03-01T23:59:04.462Z"), - version: 1, + version: 1 }, { success: true, - keys: ["f/4","e/3","b/3","d/3"], + keys: ["f/4", "e/3", "b/3", "d/3"], time: 14622, date: new Date("2016-03-01T23:59:04.462Z"), version: 2, - keySignature: "C", + keySignature: "C" } ]; - it("transforms from version 0 to 1", function () { + it("transforms from version 0 to 1", function() { const evolvedVersion1 = StatEvolver.runEvolution(versions[0], 0); expect(evolvedVersion1).toEqual(versions[1]); }); - it("transforms from version 1 to 2", function () { + it("transforms from version 1 to 2", function() { const evolvedVersion2 = StatEvolver.runEvolution(versions[1], 1); expect(evolvedVersion2).toEqual(versions[2]); }); - it("transforms from version 0 to latest", function () { + it("transforms from version 0 to latest", function() { const evolvedVersion = StatEvolver.evolveToLatestSchema(versions[0]); expect(evolvedVersion).toEqual(versions.slice(-1)[0]); }); - }); diff --git a/app/scripts/views/animated_number.js b/app/scripts/views/animated_number.js index 90dd057..d4dd867 100644 --- a/app/scripts/views/animated_number.js +++ b/app/scripts/views/animated_number.js @@ -1,12 +1,17 @@ -import React, {Component} from "react"; -import {Motion, spring} from 'react-motion'; +import React, { Component } from "react"; +import { Motion, spring } from "react-motion"; export default class AnimatedNumber extends Component { render() { const formatter = this.props.formatter; const number = this.props.number; - return - {value => {formatter ? formatter(value.x) : Math.round(value.x)}} - ; + return ( + + {value => + + {formatter ? formatter(value.x) : Math.round(value.x)} + } + + ); } } diff --git a/app/scripts/views/beat_visualization.js b/app/scripts/views/beat_visualization.js index cf70c9d..55bc6ec 100644 --- a/app/scripts/views/beat_visualization.js +++ b/app/scripts/views/beat_visualization.js @@ -1,39 +1,39 @@ -import React, {Component} from "react"; +import React, { Component } from "react"; import classNames from "classnames"; import _ from "lodash"; import RhythmChecker from "../services/rhythm_checker.js"; - export default class BeatVisualization extends Component { - propTypes: { settings: React.PropTypes.object.isRequired, barDuration: React.PropTypes.number.isRequired, currentRhythm: React.PropTypes.object.isRequired, beatHistory: React.PropTypes.object.isRequired, - result: React.PropTypes.object.isRequired, - } + result: React.PropTypes.object.isRequired + }; convertTicksToBeatNames(tickTime, tickLength) { const tickTimeToIndexFactor = 1 / RhythmChecker.getShortestNote(this.props.settings) * 100; // round to fix non-integers due to numerical imprecision const tickIndex = Math.round(tickTime / tickTimeToIndexFactor); const tickStepCount = Math.round(tickLength / tickTimeToIndexFactor); - const allBeatNames = ['1', 'e', '&', 'a', '2', 'e', '&', 'a', '3', 'e', '&', 'a', '4', 'e', '&', 'a']; + const allBeatNames = ["1", "e", "&", "a", "2", "e", "&", "a", "3", "e", "&", "a", "4", "e", "&", "a"]; const necessaryNameFraction = 16 / RhythmChecker.getShortestNote(this.props.settings); - const necessaryBeatNames = allBeatNames.filter((el, index) => - index % necessaryNameFraction === 0 - ); + const necessaryBeatNames = allBeatNames.filter((el, index) => index % necessaryNameFraction === 0); const ticks = necessaryBeatNames.slice(tickIndex, tickIndex + tickStepCount); - return
- {ticks.map((beatName, index) => -
{beatName}
- )} -
; - }; + return ( +
+ {ticks.map((beatName, index) => +
+ {beatName} +
+ )} +
+ ); + } render() { const barDuration = this.props.barDuration; @@ -52,18 +52,14 @@ export default class BeatVisualization extends Component { currentX = x + width; return [ - marginLeft > 0 ? -
{beatNamesRest}
- : null, -
{beatNames}
+ marginLeft > 0 + ?
+ {beatNamesRest} +
+ : null, +
+ {beatNames} +
]; }; @@ -75,27 +71,24 @@ export default class BeatVisualization extends Component { return _.flatten(createBeat(a, width, getColor(index), index)); }); }; - const expectedTimes = RhythmChecker.convertDurationsToTimes( - this.props.currentRhythm.durations, - barDuration - ); + const expectedTimes = RhythmChecker.convertDurationsToTimes(this.props.currentRhythm.durations, barDuration); - const expectedBeats = drawBeats( - expectedTimes, - _.constant("gray"), - true - ); + const expectedBeats = drawBeats(expectedTimes, _.constant("gray"), true); - const actualBeats = drawBeats(this.props.beatHistory, (index) => { - const result = this.props.result; - if (!result) { - return "gray"; - } - if (result.success) { - return "green"; - } - return result.beatEvaluations[index].correct ? "green" : "red"; - }, false); + const actualBeats = drawBeats( + this.props.beatHistory, + index => { + const result = this.props.result; + if (!result) { + return "gray"; + } + if (result.success) { + return "green"; + } + return result.beatEvaluations[index].correct ? "green" : "red"; + }, + false + ); const className = classNames({ "beat-container": true, @@ -103,9 +96,15 @@ export default class BeatVisualization extends Component { }); // uniqueId avoids that different beat bars are animated into each other - return
-
{expectedBeats}
-
{actualBeats}
-
; + return ( +
+
+ {expectedBeats} +
+
+ {actualBeats} +
+
+ ); } } diff --git a/app/scripts/views/claviature_view.js b/app/scripts/views/claviature_view.js index 5f5bd38..e042745 100644 --- a/app/scripts/views/claviature_view.js +++ b/app/scripts/views/claviature_view.js @@ -1,32 +1,31 @@ -import React, {Component} from "react"; +import React, { Component } from "react"; import KeyConverter from "../services/key_converter.js"; import classNames from "classnames"; export default class ClaviatureView extends Component { - propTypes: { desiredKeys: React.PropTypes.array, keySignature: React.PropTypes.string, successCallback: React.PropTypes.func, failureCallback: React.PropTypes.func, - disabled: React.PropTypes.bool, - } + disabled: React.PropTypes.bool + }; constructor(props, context) { super(props, context); this.state = { // The key which is currently "clicked". Only used for keyboard navigation. - activeKey : null - } + activeKey: null + }; } static contextTypes = { isInActiveView: React.PropTypes.bool - } + }; isNoteCorrect(noteName) { let success = false; - this.props.desiredKeys.map((key) => { + this.props.desiredKeys.map(key => { const keyNumber = KeyConverter.getKeyNumberForKeyString(key, this.props.keySignature); const keyString = KeyConverter.getKeyStringForKeyNumber(keyNumber); const desiredNoteName = KeyConverter.getNoteFromKeyString(keyString); @@ -49,7 +48,7 @@ export default class ClaviatureView extends Component { } if (eventType === "keydown") { this.setState({ - activeKey : noteName + activeKey: noteName }); } else { this.setState({ @@ -57,7 +56,7 @@ export default class ClaviatureView extends Component { }); this.onClick(noteName); } - } + }; this.keyHandlers = { keydown: keyHandler.bind(this, "keydown"), keyup: keyHandler.bind(this, "keyup") @@ -87,13 +86,11 @@ export default class ClaviatureView extends Component { green: this.isNoteCorrect(keyName), active: keyName === this.state.activeKey }); - return
  • - {keyLabel} -
  • + return ( +
  • + {keyLabel} +
  • + ); } render() { @@ -110,12 +107,13 @@ export default class ClaviatureView extends Component { ["a", "A", "white"], ["a#", "A# B♭", "black"], ["b", "B", "white"] - ].map((args) => this.renderKey.apply(this, args)); - return
    -
      - {keys} -
    -
    ; + ].map(args => this.renderKey.apply(this, args)); + return ( +
    +
      + {keys} +
    +
    + ); } } - diff --git a/app/scripts/views/collapsable_container.js b/app/scripts/views/collapsable_container.js index 5a66483..aa78743 100644 --- a/app/scripts/views/collapsable_container.js +++ b/app/scripts/views/collapsable_container.js @@ -1,15 +1,14 @@ -import React, {Component} from "react"; +import React, { Component } from "react"; import classNames from "classnames"; import _ from "lodash"; export default class BeatVisualization extends Component { - propTypes: { collapsed: React.PropTypes.bool.isRequired, maxHeight: React.PropTypes.number, freeze: React.PropTypes.bool, - className: React.PropTypes.string, - } + className: React.PropTypes.string + }; componentWillReceiveProps(nextProps) { if (!nextProps.collapsed && nextProps.children !== this.props.children) { @@ -22,16 +21,19 @@ export default class BeatVisualization extends Component { const className = _.compact([ this.props.className, classNames({ - collapsed, transition: true, + collapsed, + transition: true }) ]).join(" "); const maxHeight = this.props.maxHeight || 300; - const style = collapsed ? {} : {maxHeight}; + const style = collapsed ? {} : { maxHeight }; const children = collapsed && this.props.freeze && this.oldChildren ? this.oldChildren : this.props.children; - return
    - {children} -
    ; + return ( +
    + {children} +
    + ); } } diff --git a/app/scripts/views/game_button.js b/app/scripts/views/game_button.js index 143c8cf..611bc92 100644 --- a/app/scripts/views/game_button.js +++ b/app/scripts/views/game_button.js @@ -1,23 +1,22 @@ -import React, {Component} from "react"; +import React, { Component } from "react"; import classNames from "classnames"; import _ from "lodash"; -import { Button, ButtonToolbar } from 'react-bootstrap'; +import { Button, ButtonToolbar } from "react-bootstrap"; export default class GameButton extends Component { - propTypes: { label: React.PropTypes.string.isRequired, onClick: React.PropTypes.func.isRequired, primary: React.PropTypes.bool, - shortcutLetter: React.PropTypes.string, - } + shortcutLetter: React.PropTypes.string + }; static contextTypes = { isInActiveView: React.PropTypes.bool - } + }; componentDidMount() { - this.keyHandler = (event) => { + this.keyHandler = event => { if (!this.context.isInActiveView) { return; } @@ -30,9 +29,8 @@ export default class GameButton extends Component { const isShortcutLetter = String.fromCharCode(charCode) === this.props.shortcutLetter; if (isPrimaryAndEnter || isShortcutLetter) { return this.props.onClick(); - } else { - return false; } + return false; }; document.addEventListener("keypress", this.keyHandler); @@ -43,18 +41,23 @@ export default class GameButton extends Component { } render() { - const subtext = this.props.shortcutLetter ? -
    - Or press '{this.props.shortcutLetter}' -
    : - null; - - return ; + const subtext = this.props.shortcutLetter + ?
    + + Or press '{this.props.shortcutLetter}' + +
    + : null; + + return ( + + ); } } diff --git a/app/scripts/views/level_view.js b/app/scripts/views/level_view.js index 59274cf..de9545d 100644 --- a/app/scripts/views/level_view.js +++ b/app/scripts/views/level_view.js @@ -1,15 +1,13 @@ import Chartist from "Chartist"; -import React, {Component} from "react"; -import { Tooltip, OverlayTrigger } from 'react-bootstrap'; +import React, { Component } from "react"; +import { Tooltip, OverlayTrigger } from "react-bootstrap"; import LevelService from "../services/level_service.js"; import PieChart from "../views/pie_chart.js"; - export default class LevelView extends Component { - propTypes: { - statisticService: React.PropTypes.object.isRequired, - } + statisticService: React.PropTypes.object.isRequired + }; render() { const statistics = this.props.statisticService; @@ -21,42 +19,46 @@ export default class LevelView extends Component { if (nextLevel) { const evaluation = LevelService.assessLevel(events, nextLevel); - const goodEnoughProgress = evaluation.goodEnoughKeys.length / - (evaluation.goodEnoughKeys.length + evaluation.notGoodEnoughKeys.length) || 0; + const goodEnoughProgress = + evaluation.goodEnoughKeys.length / (evaluation.goodEnoughKeys.length + evaluation.notGoodEnoughKeys.length) || + 0; - const notGoodEnoughEvaluations = _.flatten(evaluation.notGoodEnoughKeys.map((keyEvaluation) => { - const evaluationDetails = keyEvaluation.details; - const evaluationThresholds = evaluationDetails.thresholds; + const notGoodEnoughEvaluations = _.flatten( + evaluation.notGoodEnoughKeys.map(keyEvaluation => { + const evaluationDetails = keyEvaluation.details; + const evaluationThresholds = evaluationDetails.thresholds; - // if you are slower than 30s, it is still considered as 30s - const longestTimeThreshold = 30000; - const clampedTime = _.clamp(evaluationDetails.time, evaluationThresholds.time, longestTimeThreshold); + // if you are slower than 30s, it is still considered as 30s + const longestTimeThreshold = 30000; + const clampedTime = _.clamp(evaluationDetails.time, evaluationThresholds.time, longestTimeThreshold); - const timeProgress = 1 - (clampedTime - evaluationThresholds.time) / (longestTimeThreshold - evaluationThresholds.time); - const accuracyProgress = Math.min(1, evaluationDetails.accuracy / evaluationThresholds.accuracy); - const amountProgress = Math.min(1, evaluationDetails.eventsLength / evaluationThresholds.amount); - return [timeProgress, accuracyProgress, amountProgress].map((el) => el / 3); - })).map((el) => el / evaluation.notGoodEnoughKeys.length * (1 - goodEnoughProgress)); + const timeProgress = + 1 - (clampedTime - evaluationThresholds.time) / (longestTimeThreshold - evaluationThresholds.time); + const accuracyProgress = Math.min(1, evaluationDetails.accuracy / evaluationThresholds.accuracy); + const amountProgress = Math.min(1, evaluationDetails.eventsLength / evaluationThresholds.amount); + return [timeProgress, accuracyProgress, amountProgress].map(el => el / 3); + }) + ).map(el => el / evaluation.notGoodEnoughKeys.length * (1 - goodEnoughProgress)); const pieParts = [_.sum([goodEnoughProgress].concat(notGoodEnoughEvaluations))]; if (pieParts[0] > 0) { content = ; } else { - content =
    - -

    - Congratulations! You reached the next level! -

    -

    The new level contains some new notes.

    -
    ; + content = ( +
    + +

    Congratulations! You reached the next level!

    +

    The new level contains some new notes.

    +
    + ); } } else { - content =
    -

    - Congratulations! You finished the final level! -

    -

    You may want to increase your goals in the settings pane or switch to the manual training mode.

    -
    ; + content = ( +
    +

    Congratulations! You finished the final level!

    +

    You may want to increase your goals in the settings pane or switch to the manual training mode.

    +
    + ); } // Missing notes: {statistics.calculateMissingSuccessfulNotes()} @@ -70,6 +72,3 @@ export default class LevelView extends Component { ); } } - - - diff --git a/app/scripts/views/metronome_view.js b/app/scripts/views/metronome_view.js index bf768a4..05bc8f3 100644 --- a/app/scripts/views/metronome_view.js +++ b/app/scripts/views/metronome_view.js @@ -1,4 +1,4 @@ -import React, {Component} from "react"; +import React, { Component } from "react"; import classNames from "classnames"; import _ from "lodash"; @@ -6,11 +6,10 @@ import MetronomeService from "../services/metronome_service.js"; import CollapsableContainer from "./collapsable_container.js"; export default class MetronomeView extends Component { - propTypes: { settings: React.PropTypes.object.isRequired, statisticService: React.PropTypes.object.isRequired - } + }; constructor(props, context) { super(props, context); @@ -38,37 +37,37 @@ export default class MetronomeView extends Component { // this is the first beat of the actual bar this.firstBarBeatTime = startTime + 4 * beatLength + metronomeSoundLength * magicPercentileOfAudibleBeat; - _.range(beatAmount + 1).map((beatIndex) => { + _.range(beatAmount + 1).map(beatIndex => { const beatTime = startTime + beatIndex * beatLength; const delay = beatTime - now; if (beatIndex < beatAmount) { MetronomeService.play(delay); } - setTimeout( - () => { - this.setState({ - currentMetronomeBeat: beatIndex < 4 ? beatIndex : -1 - }); + setTimeout(() => { + this.setState({ + currentMetronomeBeat: beatIndex < 4 ? beatIndex : -1 + }); - if (beatIndex === beatAmount) { - this.props.onMetronomeEnded(); - } - }, - delay - ) + if (beatIndex === beatAmount) { + this.props.onMetronomeEnded(); + } + }, delay); }); } render() { - return -

    - {this.state.currentMetronomeBeat + 1} -

    -
    ; + return ( + +

    + {this.state.currentMetronomeBeat + 1} +

    +
    + ); } } diff --git a/app/scripts/views/newsletter_form.js b/app/scripts/views/newsletter_form.js index 836b1cc..a5c55e6 100644 --- a/app/scripts/views/newsletter_form.js +++ b/app/scripts/views/newsletter_form.js @@ -1,12 +1,11 @@ -import React, {Component} from "react"; -import { Input } from 'react-bootstrap'; +import React, { Component } from "react"; +import { Input } from "react-bootstrap"; export default class NewsLetterForm extends Component { - componentDidMount() { // This avoids that global keyhandlers for keyboard navigation are triggered // when email input is used. - const dontPropagate = (evt) => evt.stopPropagation(); + const dontPropagate = evt => evt.stopPropagation(); const email = this.refs.email; email.addEventListener("keypress", dontPropagate); email.addEventListener("keyup", dontPropagate); @@ -17,12 +16,14 @@ export default class NewsLetterForm extends Component { return (
    + noValidate + >
    @@ -35,19 +36,27 @@ export default class NewsLetterForm extends Component { ref="email" className="form-control" groupClassName="group-class" - labelClassName="label-class"/> + labelClassName="label-class" + />
    -
    -
    +
    +
    -
    @@ -56,4 +65,4 @@ export default class NewsLetterForm extends Component {
    ); } -}; +} diff --git a/app/scripts/views/pie_chart.js b/app/scripts/views/pie_chart.js index 2fb5d5c..07a4d7d 100644 --- a/app/scripts/views/pie_chart.js +++ b/app/scripts/views/pie_chart.js @@ -1,12 +1,11 @@ import Chartist from "Chartist"; -import React, {Component} from "react"; -import PureRenderMixin from 'react-addons-pure-render-mixin'; +import React, { Component } from "react"; +import PureRenderMixin from "react-addons-pure-render-mixin"; export default class LevelView extends Component { - propTypes: { - pieParts: React.PropTypes.array.isRequired, - } + pieParts: React.PropTypes.array.isRequired + }; constructor(props, context) { super(props, context); @@ -18,17 +17,18 @@ export default class LevelView extends Component { } componentDidUpdate() { - new Chartist.Pie(this.refs.chart, { - series: this.props.pieParts - }, { - donut: true, - donutWidth: 30, - startAngle: 0, - total: 1, - showLabel: false - }); + new Chartist.Pie( + this.refs.chart, + { + series: this.props.pieParts + }, + { + donut: true, + donutWidth: 30, + startAngle: 0, + total: 1, + showLabel: false + } + ); } } - - - diff --git a/app/scripts/views/pitch_reading_view.js b/app/scripts/views/pitch_reading_view.js index 8edf8f3..172dc75 100644 --- a/app/scripts/views/pitch_reading_view.js +++ b/app/scripts/views/pitch_reading_view.js @@ -1,4 +1,4 @@ -import React, {Component} from "react"; +import React, { Component } from "react"; import classNames from "classnames"; import _ from "lodash"; @@ -16,17 +16,15 @@ import CollapsableContainer from "./collapsable_container.js"; const successMp3Url = require("file!../../resources/success.mp3"); export default class PitchReadingView extends Component { - propTypes: { statisticService: React.PropTypes.object.isRequired, settings: React.PropTypes.object.isRequired, - isActive: React.PropTypes.bool.isRequired, - } - + isActive: React.PropTypes.bool.isRequired + }; static childContextTypes = { isInActiveView: React.PropTypes.bool - } + }; getChildContext() { return { @@ -39,14 +37,14 @@ export default class PitchReadingView extends Component { successCallback: this.onSuccess.bind(this), failureCallback: this.onFailure.bind(this), errorCallback: this.onError.bind(this), - errorResolveCallback: this.onErrorResolve.bind(this), + errorResolveCallback: this.onErrorResolve.bind(this) }); this.startDate = new Date(); this.midiService.setDesiredKeys(this.getAllCurrentKeys(), this.state.currentKeySignature); const debugMode = true; if (debugMode) { - this.debugKeyUpCallback = (event) => { + this.debugKeyUpCallback = event => { const yesKeyCode = 89; const noKeyCode = 78; if (event.keyCode === yesKeyCode) { @@ -65,7 +63,7 @@ export default class PitchReadingView extends Component { componentWillReceiveProps(nextProps) { function checkIfSomePropChanged(oldObj, newObj, keys) { - return keys.some((key) => _.at(oldObj, key) !== _.at(newObj, key)); + return keys.some(key => _.at(oldObj, key) !== _.at(newObj, key)); } const nextSettings = nextProps.settings; @@ -78,15 +76,17 @@ export default class PitchReadingView extends Component { let newCurrentKeys = this.state.currentKeys; let keySignature = this.state.currentKeySignature; - let shouldRegenerateAll = checkIfSomePropChanged( - prevSettings, - nextSettings, - ["useAccidentals", "useAutomaticDifficulty", "automaticDifficulty.newNotesShare"] - ); + let shouldRegenerateAll = checkIfSomePropChanged(prevSettings, nextSettings, [ + "useAccidentals", + "useAutomaticDifficulty", + "automaticDifficulty.newNotesShare" + ]); - if (shouldRegenerateAll || + if ( + shouldRegenerateAll || nextChordSizeRanges.treble !== chordSizeRanges.treble || - nextChordSizeRanges.bass !== chordSizeRanges.bass) { + nextChordSizeRanges.bass !== chordSizeRanges.bass + ) { newCurrentKeys = this.generateNewBars(nextSettings); } if (shouldRegenerateAll || !_.isEqual(prevSettings.keySignature, nextSettings.keySignature)) { @@ -96,15 +96,14 @@ export default class PitchReadingView extends Component { this.setState({ currentChordIndex: 0, currentKeys: newCurrentKeys, - currentKeySignature: keySignature, + currentKeySignature: keySignature }); this.startDate = new Date(); } } generateNewBars(settings) { - const levelIndex = - LevelService.getLevelOfUser(this.props.statisticService.getAllEvents()) + 1; + const levelIndex = LevelService.getLevelOfUser(this.props.statisticService.getAllEvents()) + 1; const level = LevelService.getLevelByIndex(levelIndex); return BarGenerator.generateBars(settings, settings.useAutomaticDifficulty ? level : null); } @@ -122,16 +121,13 @@ export default class PitchReadingView extends Component { this.state = { errorMessage: null, running: false, - ...(this.generateNewBarState()) + ...this.generateNewBarState() }; } startStopTraining() { - AnalyticsService.sendEvent( - 'PitchReading', - this.state.running ? "Stop" : "Start" - ); - this.setState({running: !this.state.running}); + AnalyticsService.sendEvent("PitchReading", this.state.running ? "Stop" : "Start"); + this.setState({ running: !this.state.running }); this.startDate = new Date(); } @@ -143,50 +139,54 @@ export default class PitchReadingView extends Component { const isMidiAvailable = this.props.settings.midi.inputs.get().length > 0; const noErrors = this.state.errorMessage !== null; - const miniClaviature = (isMidiAvailable && noErrors) - ? null : - ; - - const startStopButton = ; - - const midiSetUpText =

    - {`The generated notes will be so that you play only one note at a time. + const miniClaviature = + isMidiAvailable && noErrors + ? null + : ; + + const startStopButton = ( + + ); + + const midiSetUpText = ( +

    + {`The generated notes will be so that you play only one note at a time. If you want to practice chords, have a look into the `} - - Set Up - - {" section to hook up your digital piano."} -

    ; - - const welcomeText = -
    -

    - Welcome to this pitch training! -

    -

    - {"When you hit Start, notes will be displayed in the stave above. "} - {isMidiAvailable ? - "Since we found a connected piano, you can use it to play the notes. " : - "Just use the mini claviature below to play the notes. " - } - {"Don't worry about the rhythm or speed for now."} -

    - {isMidiAvailable ? null : midiSetUpText} -
    -
    ; + Set Up + {" section to hook up your digital piano."} +

    + ); + + const welcomeText = ( + +
    +

    Welcome to this pitch training!

    +

    + {"When you hit Start, notes will be displayed in the stave above. "} + {isMidiAvailable + ? "Since we found a connected piano, you can use it to play the notes. " + : "Just use the mini claviature below to play the notes. "} + {"Don't worry about the rhythm or speed for now."} +

    + {isMidiAvailable ? null : midiSetUpText} +
    +
    + ); const emptyKeySet = { treble: [], @@ -194,7 +194,7 @@ export default class PitchReadingView extends Component { }; return ( -
    +
    @@ -205,9 +205,11 @@ export default class PitchReadingView extends Component { keySignature={this.state.currentKeySignature} /> -
    +
    {welcomeText} {startStopButton} @@ -217,11 +219,15 @@ export default class PitchReadingView extends Component {
    {miniClaviature} -
    -

    {this.state.errorMessage}

    +
    +

    + {this.state.errorMessage} +

    @@ -229,9 +235,7 @@ export default class PitchReadingView extends Component {
    - +
    @@ -243,27 +247,27 @@ export default class PitchReadingView extends Component { this.midiService.setDesiredKeys(this.getAllCurrentKeys(), this.state.currentKeySignature); } - onError(msg) { console.error.apply(console, arguments); - this.setState({errorMessage: msg}); + this.setState({ errorMessage: msg }); } - onErrorResolve() { - this.setState({errorMessage: null}); + this.setState({ errorMessage: null }); } - getAllCurrentKeys() { - return _.compact(_.flatten(["treble", "bass"].map((clef) => { - const note = this.state.currentKeys[clef][this.state.currentChordIndex]; - // Ignore rests - return note.noteType !== "r" ? note.getKeys() : null; - }))); + return _.compact( + _.flatten( + ["treble", "bass"].map(clef => { + const note = this.state.currentKeys[clef][this.state.currentChordIndex]; + // Ignore rests + return note.noteType !== "r" ? note.getKeys() : null; + }) + ) + ); } - onSuccess() { if (!this.state.running) { return; @@ -272,7 +276,7 @@ export default class PitchReadingView extends Component { success: true, keys: this.getAllCurrentKeys(), keySignature: this.state.currentKeySignature, - time: new Date() - this.startDate, + time: new Date() - this.startDate }; this.startDate = new Date(); @@ -280,24 +284,22 @@ export default class PitchReadingView extends Component { if (this.state.currentChordIndex + 1 >= this.state.currentKeys.treble.length) { this.setState({ - ...(this.generateNewBarState()) + ...this.generateNewBarState() }); } else { this.setState({ - currentChordIndex: this.state.currentChordIndex + 1, + currentChordIndex: this.state.currentChordIndex + 1 }); } this.playSuccessSound(); - AnalyticsService.sendEvent('PitchReading', "success"); + AnalyticsService.sendEvent("PitchReading", "success"); } - playSuccessSound() { this.refs.successPlayer.play(); } - onFailure() { if (!this.state.running) { return; @@ -307,9 +309,8 @@ export default class PitchReadingView extends Component { success: false, keys: this.getAllCurrentKeys(), time: new Date() - this.startDate, - keySignature: this.state.currentKeySignature, + keySignature: this.state.currentKeySignature }); - AnalyticsService.sendEvent('PitchReading', "failure"); + AnalyticsService.sendEvent("PitchReading", "failure"); } - } diff --git a/app/scripts/views/pitch_settings_view.js b/app/scripts/views/pitch_settings_view.js index 3b025ec..1e45359 100644 --- a/app/scripts/views/pitch_settings_view.js +++ b/app/scripts/views/pitch_settings_view.js @@ -1,4 +1,4 @@ -import React, {Component} from "react"; +import React, { Component } from "react"; import RangeSettingComponent from "./range_setting_component"; import SettingLine from "./setting_line"; import KeyConverter from "../services/key_converter"; @@ -7,13 +7,12 @@ import _ from "lodash"; import AnalyticsService from "../services/analytics_service.js"; export default class PitchSettingsView extends Component { - constructor(props, context) { super(props, context); } buildStateChanger(stateKey) { - return (newValue) => { + return newValue => { if (_.isObject(newValue)) { const keys = stateKey.split("."); const keyToChange = _.reduce(keys, (acc, key) => acc[key], this.props.settings); @@ -27,7 +26,7 @@ export default class PitchSettingsView extends Component { }); } - AnalyticsService.sendEvent('PitchReading-Settings', stateKey + " - " + JSON.stringify(newValue)); + AnalyticsService.sendEvent("PitchReading-Settings", stateKey + " - " + JSON.stringify(newValue)); }; } @@ -46,100 +45,101 @@ export default class PitchSettingsView extends Component { const midiSettings = this.props.settings.midi; const midiInputs = midiSettings.inputs.get(); const isMidiAvailable = midiInputs.length > 0; - const deviceSelector = !isMidiAvailable ? - null : - - - ; + const deviceSelector = !isMidiAvailable + ? null + : + + ; const useAutomaticDifficulty = this.props.settings.useAutomaticDifficulty; const accuracyStateChanger = this.buildStateChanger("automaticDifficulty.accuracyGoal"); const newNotesShareStateChanger = this.buildStateChanger("automaticDifficulty.newNotesShare"); - const automaticDifficultySection =
    - `${el}ms`} - label={"Time goal"} - /> - accuracyStateChanger(value / 100)} - valueToString={(el) => `${el}%`} - label={"Accuracy goal"} - /> - newNotesShareStateChanger(value / 100)} - valueToString={(el) => `${el}%`} - label={"Share of new notes"} - /> -
    ; + const automaticDifficultySection = ( +
    + `${el}ms`} + label={"Time goal"} + /> + accuracyStateChanger(value / 100)} + valueToString={el => `${el}%`} + label={"Accuracy goal"} + /> + newNotesShareStateChanger(value / 100)} + valueToString={el => `${el}%`} + label={"Share of new notes"} + /> +
    + ); - const manualDifficultySection =
    - - - -
    ; + const manualDifficultySection = ( +
    + + + +
    + ); return (
    -

    Settings

    +

    Settings

    {deviceSelector} - + {useAutomaticDifficulty ? automaticDifficultySection : manualDifficultySection}
    diff --git a/app/scripts/views/pitch_statistic_view.js b/app/scripts/views/pitch_statistic_view.js index 2fbeeb5..4e5e382 100644 --- a/app/scripts/views/pitch_statistic_view.js +++ b/app/scripts/views/pitch_statistic_view.js @@ -1,6 +1,6 @@ import Chartist from "Chartist"; -import React, {Component} from "react"; -import { Tooltip, OverlayTrigger } from 'react-bootstrap'; +import React, { Component } from "react"; +import { Tooltip, OverlayTrigger } from "react-bootstrap"; import LevelView from "./level_view.js"; import CollapsableContainer from "./collapsable_container.js"; @@ -8,11 +8,10 @@ import AnimatedNumber from "./animated_number.js"; import StarAnimation from "./star_animation.js"; export default class PitchStatisticView extends Component { - propTypes: { statisticService: React.PropTypes.object.isRequired, - settings: React.PropTypes.object.isRequired, - } + settings: React.PropTypes.object.isRequired + }; render() { const statistics = this.props.statisticService; @@ -22,7 +21,7 @@ export default class PitchStatisticView extends Component { return (
    -
    +
    @@ -36,17 +35,18 @@ export default class PitchStatisticView extends Component {
    Average time}> - + el.toFixed(2) + "s"} /> + number={statistics.getAverageTimeOfLast(100) / 1000} + formatter={el => el.toFixed(2) + "s"} + />
    Played notes}> - + @@ -54,8 +54,8 @@ export default class PitchStatisticView extends Component {
    Success rate}> - - el.toFixed(2) * 100} /> + + el.toFixed(2) * 100} /> % @@ -85,7 +85,7 @@ export default class PitchStatisticView extends Component { }, axisY: { labelInterpolationFnc: function(value) { - return (value / 1000) + "s"; + return value / 1000 + "s"; } } }; diff --git a/app/scripts/views/privacy_policy_modal.js b/app/scripts/views/privacy_policy_modal.js index 58eaa88..d6e0026 100644 --- a/app/scripts/views/privacy_policy_modal.js +++ b/app/scripts/views/privacy_policy_modal.js @@ -1,5 +1,5 @@ -import React, {Component} from "react"; -import { Modal, Button } from 'react-bootstrap'; +import React, { Component } from "react"; +import { Modal, Button } from "react-bootstrap"; export default class PrivacyPolicyModal extends Component { render() { @@ -9,10 +9,30 @@ export default class PrivacyPolicyModal extends Component { Privacy Policy -

    This website uses Google Analytics, a web analytics service provided by Google Inc. („Google“). Google Analytics uses „cookies“, text files that are saved on your computer and enable an analysis of your use of this website. The information generated by the cookie about your use of this website are usually transmitted to a Google server in the USA and stored there.

    -

    In case of activation of the IP anonymization on this website, your IP address will be shortened within Member States of the European Union or other parties to the Agreement on the European Economic Area. Only in exceptional cases is the complete IP address transmitted to a Google server in the USA and shortened there.

    -

    On behalf of the operator of this website, Google will use this information to evaluate your use of the website, compiling reports on website activity and providing other with website and internet related services to the website operator. The IP address transmitted by your browser as part of Google Analytics is not merged with other data of Google.

    -

    You can refuse the use of cookies by selecting the appropriate settings on your browser software. We point out that you may not be able to use all features of this site in this case. You can also prevent the data generated by the cookie about your use of the website (incl. your IP address) to Google and the processing of these data by Google, by downloading and installing the browser plugin available at the following link http://tools.google.com/dlpage/gaoptout?hl=de.

    +

    + This website uses Google Analytics, a web analytics service provided by Google Inc. („Google“). Google + Analytics uses „cookies“, text files that are saved on your computer and enable an analysis of your use of + this website. The information generated by the cookie about your use of this website are usually transmitted + to a Google server in the USA and stored there. +

    +

    + In case of activation of the IP anonymization on this website, your IP address will be shortened within + Member States of the European Union or other parties to the Agreement on the European Economic Area. Only in + exceptional cases is the complete IP address transmitted to a Google server in the USA and shortened there. +

    +

    + On behalf of the operator of this website, Google will use this information to evaluate your use of the + website, compiling reports on website activity and providing other with website and internet related + services to the website operator. The IP address transmitted by your browser as part of Google Analytics is + not merged with other data of Google. +

    +

    + You can refuse the use of cookies by selecting the appropriate settings on your browser software. We point + out that you may not be able to use all features of this site in this case. You can also prevent the data + generated by the cookie about your use of the website (incl. your IP address) to Google and the processing + of these data by Google, by downloading and installing the browser plugin available at the following link + http://tools.google.com/dlpage/gaoptout?hl=de. +

    @@ -20,4 +40,4 @@ export default class PrivacyPolicyModal extends Component { ); } -}; +} diff --git a/app/scripts/views/range_setting_component.js b/app/scripts/views/range_setting_component.js index 40c5854..b1ae609 100644 --- a/app/scripts/views/range_setting_component.js +++ b/app/scripts/views/range_setting_component.js @@ -1,4 +1,4 @@ -import React, {Component, PropTypes} from "react"; +import React, { Component, PropTypes } from "react"; import RangeSlider from "react-range-slider-bem"; import SettingLine from "./setting_line"; import _ from "lodash"; @@ -6,11 +6,10 @@ import _ from "lodash"; const multiplier = 10; export default class SettingsView extends Component { - static defaultProps = { valueToString: _.identity, label: "" - } + }; propTypes: { rangeMin: PropTypes.number.isRequired, @@ -19,8 +18,8 @@ export default class SettingsView extends Component { onChange: PropTypes.func.isRequired, label: PropTypes.string, valueToString: PropTypes.func, - disabled: PropTypes.bool, - } + disabled: PropTypes.bool + }; constructor(props, context) { super(props, context); @@ -30,7 +29,7 @@ export default class SettingsView extends Component { receiveValueAsProps(props) { let values = _.isArray(props.values) ? props.values : [props.values]; this.state = { - values: values.map((el) => el * multiplier) + values: values.map(el => el * multiplier) }; } @@ -40,7 +39,7 @@ export default class SettingsView extends Component { onChange(event, index, values) { this.setState({ - values: values.map((el) => el.value), + values: values.map(el => el.value) }); } @@ -48,7 +47,7 @@ export default class SettingsView extends Component { if (this.props.disabled) { return; } - let newValues = this.state.values.map(this.quantitizeValue).map((el) => el / multiplier); + let newValues = this.state.values.map(this.quantitizeValue).map(el => el / multiplier); this.props.onChange(newValues.length === 1 ? newValues[0] : newValues); } @@ -63,26 +62,24 @@ export default class SettingsView extends Component { let renderedRangeValues = this.state.values; let quantitizedValues = this.state.values.map(this.quantitizeValue); - const downScaledValues = quantitizedValues.map((el) => Math.round(el / multiplier)); + const downScaledValues = quantitizedValues.map(el => Math.round(el / multiplier)); const rangeContainerStyle = { - marginBottom: -2, + marginBottom: -2 }; - const valueLabel = this.props.label + ": " + downScaledValues - .map(this.props.valueToString) - .join(" - "); + const valueLabel = this.props.label + ": " + downScaledValues.map(this.props.valueToString).join(" - "); return (
    diff --git a/app/scripts/views/rhythm_reading_view.js b/app/scripts/views/rhythm_reading_view.js index a119e9a..376cf55 100644 --- a/app/scripts/views/rhythm_reading_view.js +++ b/app/scripts/views/rhythm_reading_view.js @@ -1,4 +1,4 @@ -import React, {Component} from "react"; +import React, { Component } from "react"; import classNames from "classnames"; import _ from "lodash"; @@ -21,16 +21,15 @@ const keydown = "keydown"; const Phases = { welcome: "welcome", running: "running", - feedback: "feedback", + feedback: "feedback" }; export default class RhythmReadingView extends Component { - propTypes: { settings: React.PropTypes.object.isRequired, statisticService: React.PropTypes.object.isRequired, - isActive: React.PropTypes.bool.isRequired, - } + isActive: React.PropTypes.bool.isRequired + }; constructor(props, context) { super(props, context); @@ -46,7 +45,7 @@ export default class RhythmReadingView extends Component { static childContextTypes = { isInActiveView: React.PropTypes.bool - } + }; getChildContext() { return { @@ -65,24 +64,17 @@ export default class RhythmReadingView extends Component { onMetronomeEnded() { const settings = this.props.settings; const barDuration = settings.barDuration; - const expectedTimes = RhythmChecker.convertDurationsToTimes( - this.state.currentRhythm.durations, - barDuration - ); + const expectedTimes = RhythmChecker.convertDurationsToTimes(this.state.currentRhythm.durations, barDuration); this.fixBeatHistory(); - const result = RhythmChecker.compare( - expectedTimes, - this.beatHistory, - settings - ); + const result = RhythmChecker.compare(expectedTimes, this.beatHistory, settings); this.props.statisticService.register({ success: result.success, durations: this.state.currentRhythm.durations, barDuration, liveBeatBars: settings.liveBeatBars, - labelBeats: settings.labelBeats, + labelBeats: settings.labelBeats }); this.setState({ @@ -90,7 +82,7 @@ export default class RhythmReadingView extends Component { result: result }); - AnalyticsService.sendEvent('RhythmReading-Result', result.success); + AnalyticsService.sendEvent("RhythmReading-Result", result.success); } fixBeatHistory() { @@ -117,7 +109,7 @@ export default class RhythmReadingView extends Component { let lastSpaceEvent = keyup; const keyHandler = (eventType, event) => { const spaceCode = 32; - AnalyticsService.sendEvent('RhythmReading', "keyPress", event.keyCode); + AnalyticsService.sendEvent("RhythmReading", "keyPress", event.keyCode); if (event.keyCode !== spaceCode && event.type.indexOf("touch") === -1) { return; @@ -154,21 +146,19 @@ export default class RhythmReadingView extends Component { } this.beatHistory.slice(-1)[0].push(newBeatTime); } - } else { - if (lastSpaceEvent === keydown && eventType === keyup) { - lastSpaceEvent = keyup; - return; - } + } else if (lastSpaceEvent === keydown && eventType === keyup) { + lastSpaceEvent = keyup; + return; } - } + }; - [keydown, keyup].forEach((eventType) => { + [keydown, keyup].forEach(eventType => { this.keyHandlers[eventType] = keyHandler.bind(null, eventType); document.addEventListener(eventType, this.keyHandlers[eventType]); document.addEventListener(eventType === keydown ? "touchstart" : "touchend", this.keyHandlers[eventType]); }); - this.keyHandlers.contextmenu = (event) => { + this.keyHandlers.contextmenu = event => { if (this.state.phase === Phases.running) { // Circumvent long taps triggering a context menu. event.preventDefault(); @@ -179,7 +169,7 @@ export default class RhythmReadingView extends Component { } componentWillUnmount() { - [keydown, keyup].forEach((eventType) => { + [keydown, keyup].forEach(eventType => { document.removeEventListener(eventType, this.keyHandlers[eventType]); document.removeEventListener(eventType === keydown ? "touchstart" : "touchend", this.keyHandlers[eventType]); }); @@ -190,11 +180,11 @@ export default class RhythmReadingView extends Component { if (this.state.phase === Phases.running) { return; } - AnalyticsService.sendEvent('RhythmReading', "repeatBar"); + AnalyticsService.sendEvent("RhythmReading", "repeatBar"); this.beatHistory = []; this.setState({ phase: Phases.running, - result: null, + result: null }); } @@ -202,102 +192,93 @@ export default class RhythmReadingView extends Component { if (this.state.phase === Phases.running) { return; } - AnalyticsService.sendEvent('RhythmReading', "nextBar"); + AnalyticsService.sendEvent("RhythmReading", "nextBar"); this.beatHistory = []; const newRhythm = BarGenerator.generateRhythmBar(this.props.settings); this.setState({ phase: Phases.running, result: null, - currentRhythm: newRhythm, + currentRhythm: newRhythm }); } - render() { const messageContainerClasses = classNames({ hide: this.state.errorMessage === null }); - const welcomeText = + const welcomeText = ( -

    - Welcome to this rhythm training! -

    +

    Welcome to this rhythm training!

    - When you start the training, we will count in for 4 beats and afterwards - you can tap the given rhythm (either use your 'space' button or your touchscreen). - Make sure your speakers are on so that you can hear the metronome. + When you start the training, we will count in for 4 beats and afterwards you can tap the given rhythm (either + use your 'space' button or your touchscreen). Make sure your speakers are on so that you can hear the + metronome.

    -
    ; + + ); - const feedbackSection = + const feedbackSection = (

    - {(this.state.result && this.state.result.success) ? - "Yay! You nailed the rhythm!" : - "Oh no, you didn't get the rhythm right :(" - } + {this.state.result && this.state.result.success + ? "Yay! You nailed the rhythm!" + : "Oh no, you didn't get the rhythm right :("}

    -

    - Have a look at your performance: -

    -
    ; +

    Have a look at your performance:

    + + ); - const beatBarSection = + const beatBarSection = ( + freeze={true} + collapsed={ + !( + this.state.phase === Phases.feedback || + (this.state.phase === Phases.running && this.props.settings.liveBeatBars) + ) + } + > - ; + /> + + ); console.log(this.state.currentRhythm.keys); - const metronomeBeat = ; + const metronomeBeat = ( + + ); const buttons = - this.state.phase !== Phases.feedback ? - - : (this.state.result.success ? -
    - - -
    - : -
    - - -
    - ); + this.state.phase !== Phases.feedback + ? + : this.state.result.success + ?
    + + +
    + :
    + + +
    ; return ( -
    +
    @@ -309,7 +290,7 @@ export default class RhythmReadingView extends Component { staveCount={1} /> -
    +
    {metronomeBeat} {welcomeText} {feedbackSection} @@ -317,7 +298,7 @@ export default class RhythmReadingView extends Component {
    -
    +
    {buttons}
    @@ -334,5 +315,4 @@ export default class RhythmReadingView extends Component {
    ); } - } diff --git a/app/scripts/views/rhythm_settings_view.js b/app/scripts/views/rhythm_settings_view.js index 5f5704f..94c1a48 100644 --- a/app/scripts/views/rhythm_settings_view.js +++ b/app/scripts/views/rhythm_settings_view.js @@ -1,5 +1,5 @@ -import React, {Component} from "react"; -import PureRenderMixin from 'react-addons-pure-render-mixin'; +import React, { Component } from "react"; +import PureRenderMixin from "react-addons-pure-render-mixin"; import RangeSettingComponent from "./range_setting_component"; import SettingLine from "./setting_line"; import KeyConverter from "../services/key_converter"; @@ -8,14 +8,13 @@ import AnalyticsService from "../services/analytics_service.js"; import _ from "lodash"; export default class PitchSettingsView extends Component { - constructor(props, context) { super(props, context); this.shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate.bind(this); } buildStateChanger(stateKey) { - return (newValue) => { + return newValue => { if (_.isObject(newValue)) { const keys = stateKey.split("."); const keyToChange = _.reduce(keys, (acc, key) => acc[key], this.props.settings); @@ -29,7 +28,7 @@ export default class PitchSettingsView extends Component { }); } - AnalyticsService.sendEvent('RhythmReading-Settings', stateKey + " - " + JSON.stringify(newValue)); + AnalyticsService.sendEvent("RhythmReading-Settings", stateKey + " - " + JSON.stringify(newValue)); }; } @@ -43,7 +42,7 @@ export default class PitchSettingsView extends Component { render() { return (
    -

    Settings

    +

    Settings

    - +
    ); - // - // - // - // + // + // + // + // - // - // - // - // + // + // + // + // } } diff --git a/app/scripts/views/rhythm_statistic_view.js b/app/scripts/views/rhythm_statistic_view.js index a71570b..896353e 100644 --- a/app/scripts/views/rhythm_statistic_view.js +++ b/app/scripts/views/rhythm_statistic_view.js @@ -1,16 +1,15 @@ import Chartist from "Chartist"; -import React, {Component} from "react"; -import { Tooltip, OverlayTrigger } from 'react-bootstrap'; +import React, { Component } from "react"; +import { Tooltip, OverlayTrigger } from "react-bootstrap"; import AnimatedNumber from "./animated_number.js"; import StarAnimation from "./star_animation.js"; -import PureRenderMixin from 'react-addons-pure-render-mixin'; +import PureRenderMixin from "react-addons-pure-render-mixin"; export default class RhythmStatisticView extends Component { - propTypes: { - statisticService: React.PropTypes.object.isRequired, - } + statisticService: React.PropTypes.object.isRequired + }; constructor(props) { super(props); @@ -25,24 +24,25 @@ export default class RhythmStatisticView extends Component { unit: "y" }, { - amount: Math.floor((seconds % 31536000) / 86400), + amount: Math.floor(seconds % 31536000 / 86400), unit: "d" }, { - amount: Math.floor(((seconds % 31536000) % 86400) / 3600), + amount: Math.floor(seconds % 31536000 % 86400 / 3600), unit: "h" }, { - amount: Math.floor((((seconds % 31536000) % 86400) % 3600) / 60), + amount: Math.floor(seconds % 31536000 % 86400 % 3600 / 60), unit: "m" }, { - amount: (((seconds % 31536000) % 86400) % 3600) % 60, + amount: seconds % 31536000 % 86400 % 3600 % 60, unit: "s" - }, - ].filter((el) => el.amount !== 0) - .map((el) => `${Math.ceil(el.amount)} ${el.unit}`) - .join(" "); + } + ] + .filter(el => el.amount !== 0) + .map(el => `${Math.ceil(el.amount)} ${el.unit}`) + .join(" "); } componentDidMount() { @@ -67,7 +67,7 @@ export default class RhythmStatisticView extends Component { return (
    -
    +
    Your current score}> @@ -80,15 +80,18 @@ export default class RhythmStatisticView extends Component {
    Total time you played rhythms}> - +
    - Played bars / played beats}> + Played bars / played beats} + > - + / @@ -98,7 +101,7 @@ export default class RhythmStatisticView extends Component {
    Success rate}> - + % @@ -117,7 +120,7 @@ export default class RhythmStatisticView extends Component { const scoreBeforeLastHundred = currentScore - _.sum(lastScores); const scoreDevelopmentValues = [scoreBeforeLastHundred]; - lastScores.forEach((el) => { + lastScores.forEach(el => { scoreDevelopmentValues.push(el + scoreDevelopmentValues.slice(-1)[0]); }); diff --git a/app/scripts/views/setting_line.js b/app/scripts/views/setting_line.js index 530481d..5de21a4 100644 --- a/app/scripts/views/setting_line.js +++ b/app/scripts/views/setting_line.js @@ -1,18 +1,15 @@ -import React, {Component, PropTypes} from "react"; +import React, { Component, PropTypes } from "react"; import _ from "lodash"; - export default class SettingLine extends Component { - static defaultProps = { label: "" - } + }; propTypes: { label: PropTypes.string, - className: PropTypes.string, - - } + className: PropTypes.string + }; constructor(props, context) { super(props, context); @@ -25,7 +22,7 @@ export default class SettingLine extends Component { } return (
    -
    +
    {this.props.label}
    diff --git a/app/scripts/views/star_animation.js b/app/scripts/views/star_animation.js index 707be0d..7cc8ca2 100644 --- a/app/scripts/views/star_animation.js +++ b/app/scripts/views/star_animation.js @@ -1,5 +1,5 @@ -import React, {Component} from "react"; -import {Motion, spring} from 'react-motion'; +import React, { Component } from "react"; +import { Motion, spring } from "react-motion"; export default class StarAnimation extends Component { constructor() { @@ -18,6 +18,6 @@ export default class StarAnimation extends Component { } render() { const className = "fa fa-star star-animation-" + this.state.animationCount % 2; - return + return ; } } diff --git a/app/scripts/views/stave_renderer.js b/app/scripts/views/stave_renderer.js index fc70052..35db0d0 100644 --- a/app/scripts/views/stave_renderer.js +++ b/app/scripts/views/stave_renderer.js @@ -1,21 +1,20 @@ import Vex from "vexflow"; -import React, {Component} from "react"; +import React, { Component } from "react"; import classNames from "classnames"; import _ from "lodash"; -import PureRenderMixin from 'react-addons-pure-render-mixin'; +import PureRenderMixin from "react-addons-pure-render-mixin"; class StaveRenderer extends Component { - static defaultProps = { staveCount: 2 - } + }; propTypes: { keys: React.PropTypes.array, chordIndex: React.PropTypes.number, keySignature: React.PropTypes.string, - staveCount: React.PropTypes.number, - } + staveCount: React.PropTypes.number + }; constructor(props) { super(props); @@ -40,7 +39,6 @@ class StaveRenderer extends Component { window.addEventListener("resize", this.debouncedResizeHandler); } - setCanvasExtent(canvas, width, height, ratio) { canvas.width = ratio * width; canvas.height = ratio * height; @@ -52,11 +50,13 @@ class StaveRenderer extends Component { getPixelRatio() { const ctx = this.ctx, dpr = window.devicePixelRatio || 1, - bsr = ctx.webkitBackingStorePixelRatio || - ctx.mozBackingStorePixelRatio || - ctx.msBackingStorePixelRatio || - ctx.oBackingStorePixelRatio || - ctx.backingStorePixelRatio || 1; + bsr = + ctx.webkitBackingStorePixelRatio || + ctx.mozBackingStorePixelRatio || + ctx.msBackingStorePixelRatio || + ctx.oBackingStorePixelRatio || + ctx.backingStorePixelRatio || + 1; return dpr / bsr; } @@ -65,9 +65,7 @@ class StaveRenderer extends Component { this.renderer = new Vex.Flow.Renderer(canvas, Vex.Flow.Renderer.Backends.CANVAS); this.ctx = this.renderer.getContext(); - const windowWidth = window.innerWidth - || document.documentElement.clientWidth - || document.body.clientWidth; + const windowWidth = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth; const widthThreshold = 600; const margin = 20; @@ -82,16 +80,10 @@ class StaveRenderer extends Component { this.setCanvasExtent(canvas, width, height, ratio); const rightHandStave = new Vex.Flow.Stave(10, 0, staveWidth); - rightHandStave - .addClef("treble") - .setKeySignature(this.props.keySignature) - .setContext(ctx); + rightHandStave.addClef("treble").setKeySignature(this.props.keySignature).setContext(ctx); const leftHandStave = new Vex.Flow.Stave(10, 80, staveWidth); - leftHandStave - .addClef("bass") - .setKeySignature(this.props.keySignature) - .setContext(ctx); + leftHandStave.addClef("bass").setKeySignature(this.props.keySignature).setContext(ctx); this.colorizeKeys(); @@ -104,20 +96,17 @@ class StaveRenderer extends Component { }); } - colorizeKeys() { - Object.keys(this.props.keys).map((key) => { + Object.keys(this.props.keys).map(key => { const clef = this.props.keys[key]; clef.forEach((staveNote, index) => { const color = index < this.props.chordIndex ? "#398439" : "black"; - _.range(staveNote.getKeys().length).map((noteIndex) => { - staveNote.setKeyStyle(noteIndex, {fillStyle: color}); + _.range(staveNote.getKeys().length).map(noteIndex => { + staveNote.setKeyStyle(noteIndex, { fillStyle: color }); }); }); }); } - } - export default StaveRenderer; diff --git a/karma.conf.js b/karma.conf.js index 6057870..a6a3f15 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -3,43 +3,35 @@ var webpackConfig = require("./webpack.config"); module.exports = function(config) { config.set({ - // base path that will be used to resolve all patterns (eg. files, exclude) - basePath: 'app', + basePath: "app", // frameworks to use // available frameworks: https://npmjs.org/browse/keyword/karma-adapter - frameworks: ['jasmine'], - + frameworks: ["jasmine"], // list of files / patterns to load in the browser - files: [ - 'scripts/spec/*.js' - ], - + files: ["scripts/spec/*.js"], // list of files to exclude - exclude: [ - 'bower_components/**/*spec.js' - ], - + exclude: ["bower_components/**/*spec.js"], // preprocess matching files before serving them to the browser // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor preprocessors: { // add webpack as preprocessor - 'scripts/spec/*.js': ['webpack', 'sourcemap'] + "scripts/spec/*.js": ["webpack", "sourcemap"] }, webpack: { - devtool: 'inline-source-map', + devtool: "inline-source-map", module: webpackConfig.module }, webpackMiddleware: { - // webpack-dev-middleware configuration - // i. e. - noInfo: true + // webpack-dev-middleware configuration + // i. e. + noInfo: true }, // test results reporter to use @@ -47,28 +39,22 @@ module.exports = function(config) { // available reporters: https://npmjs.org/browse/keyword/karma-reporter reporters: ["mocha", "beep"], - // web server port port: 9876, - // enable / disable colors in the output (reporters and logs) colors: true, - // level of logging // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG logLevel: config.LOG_INFO, - // enable / disable watching file and executing tests whenever any file changes autoWatch: true, - // start these browsers // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher - browsers: ['Chrome'], - + browsers: ["Chrome"], // Continuous Integration mode // if true, Karma captures browsers, runs the tests and exits diff --git a/server.js b/server.js index 6cb4f67..44ad718 100644 --- a/server.js +++ b/server.js @@ -1,7 +1,7 @@ /* eslint-disable no-var, strict */ -var webpack = require('webpack'); -var WebpackDevServer = require('webpack-dev-server'); -var config = require('./webpack.config'); +var webpack = require("webpack"); +var WebpackDevServer = require("webpack-dev-server"); +var config = require("./webpack.config"); var port = config.port; new WebpackDevServer(webpack(config), { @@ -9,9 +9,9 @@ new WebpackDevServer(webpack(config), { contentBase: "./app/", hot: true, historyApiFallback: true -}).listen(port, 'localhost', function (err) { - if (err) { - console.log(err); - } - console.log('Listening at localhost:' + port); - }); +}).listen(port, "localhost", function(err) { + if (err) { + console.log(err); + } + console.log("Listening at localhost:" + port); +}); diff --git a/webpack.config.js b/webpack.config.js index 6e9c8d3..980f345 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -41,47 +41,43 @@ */ /* eslint-disable no-var */ -var webpack = require('webpack'); -var path = require('path'); +var webpack = require("webpack"); +var path = require("path"); var port = 1234; module.exports = { port: port, - entry: [ - 'webpack-dev-server/client?http://localhost:' + port, - 'webpack/hot/dev-server', - './app/scripts/index.js' - ], + entry: ["webpack-dev-server/client?http://localhost:" + port, "webpack/hot/dev-server", "./app/scripts/index.js"], output: { path: path.resolve(__dirname, "build"), - filename: 'bundle.js', - publicPath: '/' + filename: "bundle.js", + publicPath: "/" }, resolve: { - extensions: ['', '.js'] + extensions: ["", ".js"] }, resolveLoader: { - root: path.join(__dirname, 'node_modules') + root: path.join(__dirname, "node_modules") }, - devtool: 'inline-source-map', - plugins: [ - new webpack.HotModuleReplacementPlugin(), - new webpack.NoErrorsPlugin() - ], + devtool: "inline-source-map", + plugins: [new webpack.HotModuleReplacementPlugin(), new webpack.NoErrorsPlugin()], module: { loaders: [ { test: /\.jsx?$/, - loaders: ['babel'], - include: path.join(__dirname, 'app', 'scripts') + loaders: ["babel"], + include: path.join(__dirname, "app", "scripts") }, { test: /\.less$/, - loader: "style!css!less", + loader: "style!css!less" + }, + { + test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/, + loader: "url-loader?limit=10000&minetype=application/font-woff" }, - { test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/, loader: "url-loader?limit=10000&minetype=application/font-woff" }, { test: /\.(ttf|eot|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/, loader: "file-loader" }, - { test: /\.html/, loader: 'file?name=[name].[ext]' }, + { test: /\.html/, loader: "file?name=[name].[ext]" } ] } }; diff --git a/webpack.config.production.js b/webpack.config.production.js index 1a37167..0a2061c 100644 --- a/webpack.config.production.js +++ b/webpack.config.production.js @@ -39,28 +39,28 @@ */ /* eslint-disable no-var */ -var webpack = require('webpack'); -var path = require('path'); +var webpack = require("webpack"); +var path = require("path"); module.exports = { - entry: './app/scripts/index.js', + entry: "./app/scripts/index.js", output: { - path: path.join(__dirname, 'dist/Piano-Trainer'), - filename: 'bundle.js', - publicPath: './' + path: path.join(__dirname, "dist/Piano-Trainer"), + filename: "bundle.js", + publicPath: "./" }, resolve: { - extensions: ['', '.js'] + extensions: ["", ".js"] }, resolveLoader: { - root: path.join(__dirname, 'node_modules') + root: path.join(__dirname, "node_modules") }, - devtool: 'source-map', + devtool: "source-map", plugins: [ new webpack.optimize.OccurenceOrderPlugin(), new webpack.DefinePlugin({ - 'process.env': { - 'NODE_ENV': JSON.stringify('production') + "process.env": { + NODE_ENV: JSON.stringify("production") } }), new webpack.optimize.UglifyJsPlugin({ @@ -73,16 +73,19 @@ module.exports = { loaders: [ { test: /\.jsx?$/, - loaders: ['babel'], - include: path.join(__dirname, 'app', 'scripts') + loaders: ["babel"], + include: path.join(__dirname, "app", "scripts") }, { test: /\.less$/, - loader: "style!css!less", + loader: "style!css!less" + }, + { + test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/, + loader: "url-loader?limit=10000&minetype=application/font-woff" }, - { test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/, loader: "url-loader?limit=10000&minetype=application/font-woff" }, { test: /\.(ttf|eot|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/, loader: "file-loader" }, - { test: /\.html/, loader: 'file?name=[name].[ext]' }, + { test: /\.html/, loader: "file?name=[name].[ext]" } ] } };