diff --git a/src/js/__tests__/LevelStore.spec.js b/src/js/__tests__/LevelStore.spec.js new file mode 100644 index 00000000..042474fa --- /dev/null +++ b/src/js/__tests__/LevelStore.spec.js @@ -0,0 +1,34 @@ +var LevelActions = require('../actions/LevelActions'); +var LevelStore = require('../stores/LevelStore'); + +describe('this store', function() { + + it('has sequences and levels', function() { + var sequenceMap = LevelStore.getSequenceToLevels(); + Object.keys(sequenceMap).forEach(function(levelSequence) { + expect(LevelStore.getSequences().indexOf(levelSequence) >= 0) + .toEqual(true); + + sequenceMap[levelSequence].forEach(function(level) { + expect(LevelStore.getLevel(level.id)).toEqual(level); + }.bind(this)); + }.bind(this)); + }); + + it('can solve a level and then reset', function() { + var sequenceMap = LevelStore.getSequenceToLevels(); + var firstLevel = sequenceMap[ + Object.keys(sequenceMap)[0] + ][0]; + + expect(LevelStore.isLevelSolved(firstLevel.id)) + .toEqual(false); + LevelActions.setLevelSolved(firstLevel.id); + expect(LevelStore.isLevelSolved(firstLevel.id)) + .toEqual(true); + LevelActions.resetLevelsSolved(); + expect(LevelStore.isLevelSolved(firstLevel.id)) + .toEqual(false); + }); + +}); diff --git a/src/js/actions/LevelStoreActions.js b/src/js/actions/LevelActions.js similarity index 88% rename from src/js/actions/LevelStoreActions.js rename to src/js/actions/LevelActions.js index 96e4f518..80638ccd 100644 --- a/src/js/actions/LevelStoreActions.js +++ b/src/js/actions/LevelActions.js @@ -5,7 +5,7 @@ var AppDispatcher = require('../dispatcher/AppDispatcher'); var ActionTypes = AppConstants.ActionTypes; -var LevelStoreActions = { +var LevelActions = { setLevelSolved: function(levelID) { AppDispatcher.handleViewAction({ @@ -22,4 +22,4 @@ var LevelStoreActions = { }; -module.exports = LevelStoreActions; +module.exports = LevelActions; diff --git a/src/js/level/arbiter.js b/src/js/level/arbiter.js index 28038e12..aee13875 100644 --- a/src/js/level/arbiter.js +++ b/src/js/level/arbiter.js @@ -27,7 +27,6 @@ function LevelArbiter() { } LevelArbiter.prototype.init = function() { - var previousLevelID; _.each(this.levelSequences, function(levels, levelSequenceName) { this.sequences.push(levelSequenceName); if (!levels || !levels.length) { diff --git a/src/js/stores/LevelStore.js b/src/js/stores/LevelStore.js index a3a57dda..558f593a 100644 --- a/src/js/stores/LevelStore.js +++ b/src/js/stores/LevelStore.js @@ -9,9 +9,29 @@ var assign = require('object-assign'); var levelSequences = require('../../levels').levelSequences; var sequenceInfo = require('../../levels').sequenceInfo; +var ActionTypes = AppConstants.ActionTypes; +var SOLVED_MAP_STORAGE_KEY = 'solvedMap'; + var _levelMap = {}; +var _solvedMap = {}; var _sequences = []; +try { + _solvedMap = JSON.parse( + localStorage.getItem(SOLVED_MAP_STORAGE_KEY) || '{}' + ) || {}; +} catch (e) { + console.warn('local storage failed', e); +} + +function _syncToStorage() { + try { + localStorage.setItem(SOLVED_MAP_STORAGE_KEY, JSON.stringify(_solvedMap)); + } catch (e) { + console.warn('local storage failed on set', e); + } +} + var validateLevel = function(level) { level = level || {}; var requiredFields = [ @@ -21,12 +41,6 @@ var validateLevel = function(level) { 'solutionCommand' ]; - var optionalFields = [ - 'hint', - 'disabledMap', - 'startTree' - ]; - _.each(requiredFields, function(field) { if (level[field] === undefined) { console.log(level); @@ -35,37 +49,35 @@ var validateLevel = function(level) { }); }; -var unpackSequences = function() { - _.each(levelSequences, function(levels, levelSequenceName) { - _sequences.push(levelSequenceName); - if (!levels || !levels.length) { - throw new Error('no empty sequences allowed'); - } +/** + * Unpact the level sequences. + */ +_.each(levelSequences, function(levels, levelSequenceName) { + _sequences.push(levelSequenceName); + if (!levels || !levels.length) { + throw new Error('no empty sequences allowed'); + } - // for this particular sequence... - _.each(levels, function(level, index) { - validateLevel(level); + // for this particular sequence... + _.each(levels, function(level, index) { + validateLevel(level); - var id = levelSequenceName + String(index + 1); - var compiledLevel = assign( - {}, - level, - { - index: index, - id: id, - sequenceName: levelSequenceName - } - ); + var id = levelSequenceName + String(index + 1); + var compiledLevel = assign( + {}, + level, + { + index: index, + id: id, + sequenceName: levelSequenceName + } + ); - // update our internal data - _levelMap[id] = compiledLevel; - levelSequences[levelSequenceName][index] = compiledLevel; - }); + // update our internal data + _levelMap[id] = compiledLevel; + levelSequences[levelSequenceName][index] = compiledLevel; }); -}; -unpackSequences(); - -var ActionTypes = AppConstants.ActionTypes; +}); var LevelStore = assign( {}, @@ -73,15 +85,78 @@ EventEmitter.prototype, AppConstants.StoreSubscribePrototype, { + getSequenceToLevels: function() { + return levelSequences; + }, + + getSequences: function() { + return _.keys(levelSequences); + }, + + getLevelsInSequence: function(sequenceName) { + if (!levelSequences[sequenceName]) { + throw new Error('that sequecne name ' + sequenceName + 'does not exist'); + } + return levelSequences[sequenceName]; + }, + + getSequenceInfo: function(sequenceName) { + return sequenceInfo[sequenceName]; + }, + + getLevel: function(id) { + return _levelMap[id]; + }, + + getNextLevel: function(id) { + if (!_levelMap[id]) { + console.warn('that level doesnt exist!!!'); + return null; + } + + // meh, this method could be better. It's a tradeoff between + // having the sequence structure be really simple JSON + // and having no connectivity information between levels, which means + // you have to build that up yourself on every query + var level = _levelMap[id]; + var sequenceName = level.sequenceName; + var sequence = levelSequences[sequenceName]; + + var nextIndex = level.index + 1; + if (nextIndex < sequence.length) { + return sequence[nextIndex]; + } + + var nextSequenceIndex = _sequences.indexOf(sequenceName) + 1; + if (nextSequenceIndex < _sequences.length) { + var nextSequenceName = _sequences[nextSequenceIndex]; + return levelSequences[nextSequenceName][0]; + } + + // they finished the last level! + return null; + }, + + isLevelSolved: function(levelID) { + if (!_levelMap[levelID]) { + throw new Error('that level doesnt exist!'); + } + return !!_solvedMap[levelID]; + }, + dispatchToken: AppDispatcher.register(function(payload) { var action = payload.action; var shouldInform = false; switch (action.type) { case ActionTypes.RESET_LEVELS_SOLVED: + _solvedMap = {}; + _syncToStorage(); shouldInform = true; break; case ActionTypes.SET_LEVEL_SOLVED: + _solvedMap[action.levelID] = true; + _syncToStorage(); shouldInform = true; break; } diff --git a/src/js/stores/LocaleStore.js b/src/js/stores/LocaleStore.js index b104096b..75e7f8c0 100644 --- a/src/js/stores/LocaleStore.js +++ b/src/js/stores/LocaleStore.js @@ -54,6 +54,7 @@ var LocaleStore = assign( EventEmitter.prototype, AppConstants.StoreSubscribePrototype, { + getDefaultLocale: function() { return DEFAULT_LOCALE; }, diff --git a/src/js/util/debug.js b/src/js/util/debug.js index 88b40b60..a87e564c 100644 --- a/src/js/util/debug.js +++ b/src/js/util/debug.js @@ -6,6 +6,8 @@ var toGlobalize = { Visuals: require('../visuals'), Git: require('../git'), CommandModel: require('../models/commandModel'), + LevelActions: require('../actions/LevelActions'), + LevelStore: require('../stores/LevelStore'), LocaleActions: require('../actions/LocaleActions'), LocaleStore: require('../stores/LocaleStore'), Levels: require('../graph/treeCompare'), @@ -36,7 +38,11 @@ var toGlobalize = { _.each(toGlobalize, function(module, moduleName) { for (var key in module) { - window['debug_' + moduleName + '_' + key] = module[key]; + var value = module[key]; + if (value instanceof Function) { + value = value.bind(module); + } + window['debug_' + moduleName + '_' + key] = value; } });