<commit_before>define([
    'ash',
    'game/GameGlobals',
    'game/GlobalSignals',
	'game/constants/GameConstants',
    'game/constants/SystemPriorities',
    'game/GameState',
    'game/systems/GameManager',
    'game/systems/SaveSystem',
    'game/systems/AutoPlaySystem',
    'game/systems/ui/UIOutHeaderSystem',
    'game/systems/ui/UIOutElementsSystem',
    'game/systems/ui/UIOutLevelSystem',
    'game/systems/ui/UIOutCampSystem',
    'game/systems/ui/UIOutCampVisSystem',
    'game/systems/ui/UIOutProjectsSystem',
    'game/systems/ui/UIOutEmbarkSystem',
    'game/systems/ui/UIOutBagSystem',
    'game/systems/ui/UIOutFollowersSystem',
    'game/systems/ui/UIOutMapSystem',
    'game/systems/ui/UIOutTradeSystem',
    'game/systems/ui/UIOutUpgradesSystem',
    'game/systems/ui/UIOutTribeSystem',
    'game/systems/ui/UIOutBlueprintsSystem',
    'game/systems/ui/UIOutFightSystem',
    'game/systems/ui/UIOutLogSystem',
    'game/systems/ui/UIOutManageSaveSystem',
    'game/systems/ui/UIOutPopupInnSystem',
    'game/systems/ui/UIOutPopupTradeSystem',
    'game/systems/ui/UIOutPopupInventorySystem',
    'game/systems/CheatSystem',
    'game/systems/SlowUpdateSystem',
    'game/systems/VisionSystem',
    'game/systems/StaminaSystem',
    'game/systems/PlayerPositionSystem',
    'game/systems/PlayerActionSystem',
    'game/systems/SectorStatusSystem',
    'game/systems/LevelPassagesSystem',
    'game/systems/CollectorSystem',
    'game/systems/FightSystem',
    'game/systems/PopulationSystem',
    'game/systems/WorkerSystem',
    'game/systems/FaintingSystem',
    'game/systems/ReputationSystem',
    'game/systems/RumourSystem',
    'game/systems/EvidenceSystem',
    'game/systems/EndingSystem',
    'game/systems/GlobalResourcesSystem',
    'game/systems/GlobalResourcesResetSystem',
    'game/systems/BagSystem',
    'game/systems/HazardSystem',
    'game/systems/UnlockedFeaturesSystem',
    'game/systems/occurrences/CampEventsSystem',
    'game/PlayerActionFunctions',
    'game/UIFunctions',
    'game/helpers/PlayerActionsHelper',
    'game/helpers/PlayerActionResultsHelper',
    'game/helpers/ui/ChangeLogHelper',
    'game/helpers/ItemsHelper',
    'game/helpers/EndingHelper',
    'game/helpers/ResourcesHelper',
    'game/helpers/MovementHelper',
    'game/helpers/FightHelper',
    'game/helpers/LevelHelper',
    'game/helpers/SectorHelper',
    'game/helpers/CampHelper',
    'game/helpers/ButtonHelper',
    'game/helpers/SaveHelper',
    'game/helpers/UpgradeEffectsHelper',
    'game/helpers/ui/UIMapHelper',
    'game/helpers/ui/UITechTreeHelper',
    'brejep/tickprovider',
], function (
    Ash,
    GameGlobals,
    GlobalSignals,
	GameConstants,
    SystemPriorities,
    GameState,
    GameManager,
    SaveSystem,
    AutoPlaySystem,
    UIOutHeaderSystem,
    UIOutElementsSystem,
    UIOutLevelSystem,
    UIOutCampSystem,
    UIOutCampVisSystem,
    UIOutProjectsSystem,
    UIOutEmbarkSystem,
    UIOutBagSystem,
    UIOutFollowersSystem,
    UIOutMapSystem,
    UIOutTradeSystem,
    UIOutUpgradesSystem,
    UIOutTribeSystem,
    UIOutBlueprintsSystem,
    UIOutFightSystem,
    UIOutLogSystem,
    UIOutManageSaveSystem,
    UIOutPopupInnSystem,
    UIOutPopupTradeSystem,
    UIOutPopupInventorySystem,
    CheatSystem,
    SlowUpdateSystem,
    VisionSystem,
    StaminaSystem,
    PlayerPositionSystem,
    PlayerActionSystem,
    SectorStatusSystem,
    LevelPassagesSystem,
    CollectorSystem,
    FightSystem,
    PopulationSystem,
    WorkerSystem,
    FaintingSystem,
    ReputationSystem,
    RumourSystem,
    EvidenceSystem,
    EndingSystem,
    GlobalResourcesSystem,
    GlobalResourcesResetSystem,
    BagSystem,
    HazardSystem,
    UnlockedFeaturesSystem,
    CampEventsSystem,
    PlayerActionFunctions,
    UIFunctions,
    PlayerActionsHelper,
    PlayerActionResultsHelper,
    ChangeLogHelper,
    ItemsHelper,
    EndingHelper,
    ResourcesHelper,
    MovementHelper,
    FightHelper,
    LevelHelper,
    SectorHelper,
    CampHelper,
    ButtonHelper,
    SaveHelper,
    UpgradeEffectsHelper,
    UIMapHelper,
    UITechTreeHelper,
    TickProvider
) {
    var Level13 = Ash.Class.extend({

        engine: null,
		gameManager: null,
        tickProvider: null,

        constructor: function (plugins) {
            var game = this;
            this.engine = new Ash.Engine();
            this.engine.extraUpdateTime = 0;
			this.tickProvider = new TickProvider(null, function (ex) { game.handleException(ex) });
			this.gameManager = new GameManager(this.tickProvider);

            this.initializeGameGlobals();
			this.addSystems();
            this.initializePlugins(plugins);

            var sys = this;
            GlobalSignals.worldReadySignal.addOnce(function () {
                sys.start();
            });

            this.gameManager.setupGame();
        },

        initializeGameGlobals: function () {
			GameGlobals.gameState = new GameState();
            GameGlobals.playerActionsHelper = new PlayerActionsHelper(this.engine);
			GameGlobals.playerActionFunctions = new PlayerActionFunctions(this.engine);

            GameGlobals.resourcesHelper = new ResourcesHelper(this.engine);
            GameGlobals.levelHelper = new LevelHelper(this.engine);
			GameGlobals.movementHelper = new MovementHelper(this.engine);
			GameGlobals.sectorHelper = new SectorHelper(this.engine);
            GameGlobals.fightHelper = new FightHelper(this.engine);
			GameGlobals.campHelper = new CampHelper(this.engine);
            GameGlobals.endingHelper = new EndingHelper(this.engine);
			GameGlobals.playerActionResultsHelper = new PlayerActionResultsHelper(this.engine);

            GameGlobals.itemsHelper = new ItemsHelper();
			GameGlobals.upgradeEffectsHelper = new UpgradeEffectsHelper();
			GameGlobals.saveHelper = new SaveHelper();
            GameGlobals.changeLogHelper = new ChangeLogHelper();

            GameGlobals.uiMapHelper = new UIMapHelper(this.engine);
            GameGlobals.uiTechTreeHelper = new UITechTreeHelper(this.engine);
            GameGlobals.buttonHelper = new ButtonHelper();

			GameGlobals.uiFunctions = new UIFunctions();
        },

        initializePlugins: function (plugins) {
            if (!plugins) return;
            var game = this;
            require(plugins, function (plugin) {
                game.engine.addSystem(new plugin(), SystemPriorities.update);
            });
        },

		addSystems: function () {
			this.engine.addSystem(this.gameManager, SystemPriorities.preUpdate);

			if (GameConstants.isDebugOutputEnabled) console.log("START " + GameConstants.STARTTimeNow() + "\t initializing systems");

			this.engine.addSystem(new SaveSystem(), SystemPriorities.preUpdate);
			this.engine.addSystem(new PlayerPositionSystem(), SystemPriorities.preupdate);

			this.engine.addSystem(new GlobalResourcesResetSystem(), SystemPriorities.update);
			this.engine.addSystem(new VisionSystem(), SystemPriorities.update);
			this.engine.addSystem(new StaminaSystem(), SystemPriorities.update);
			this.engine.addSystem(new BagSystem(), SystemPriorities.update);
			this.engine.addSystem(new HazardSystem(), SystemPriorities.update);
			this.engine.addSystem(new CollectorSystem(), SystemPriorities.update);
			this.engine.addSystem(new FightSystem(), SystemPriorities.update);
			this.engine.addSystem(new PopulationSystem(), SystemPriorities.update);
			this.engine.addSystem(new WorkerSystem(), SystemPriorities.update);
			this.engine.addSystem(new FaintingSystem(), SystemPriorities.update);
			this.engine.addSystem(new ReputationSystem(), SystemPriorities.update);
			this.engine.addSystem(new RumourSystem(), SystemPriorities.update);
			this.engine.addSystem(new EvidenceSystem(), SystemPriorities.update);
			this.engine.addSystem(new PlayerActionSystem(), SystemPriorities.update);
			this.engine.addSystem(new SectorStatusSystem(), SystemPriorities.update);
			this.engine.addSystem(new LevelPassagesSystem(), SystemPriorities.update);
			this.engine.addSystem(new UnlockedFeaturesSystem(), SystemPriorities.update);
			this.engine.addSystem(new GlobalResourcesSystem(), SystemPriorities.update);
			this.engine.addSystem(new CampEventsSystem(), SystemPriorities.update);
            this.engine.addSystem(new EndingSystem(), SystemPriorities.update);

			this.engine.addSystem(new AutoPlaySystem(), SystemPriorities.postUpdate);
			this.engine.addSystem(new SlowUpdateSystem(), SystemPriorities.postUpdate);

			this.engine.addSystem(new UIOutHeaderSystem(), SystemPriorities.render);
			this.engine.addSystem(new UIOutElementsSystem(), SystemPriorities.render);
			this.engine.addSystem(new UIOutLevelSystem(), SystemPriorities.render);
			this.engine.addSystem(new UIOutCampSystem(), SystemPriorities.render);
            this.engine.addSystem(new UIOutCampVisSystem(), SystemPriorities.render);
			this.engine.addSystem(new UIOutProjectsSystem(), SystemPriorities.render);
			this.engine.addSystem(new UIOutEmbarkSystem(), SystemPriorities.render);
			this.engine.addSystem(new UIOutBagSystem(), SystemPriorities.render);
			this.engine.addSystem(new UIOutFollowersSystem(), SystemPriorities.render);
			this.engine.addSystem(new UIOutMapSystem(), SystemPriorities.render);
			this.engine.addSystem(new UIOutTradeSystem(), SystemPriorities.render);
			this.engine.addSystem(new UIOutUpgradesSystem(), SystemPriorities.render);
			this.engine.addSystem(new UIOutBlueprintsSystem(), SystemPriorities.render);
			this.engine.addSystem(new UIOutTribeSystem(), SystemPriorities.render);
			this.engine.addSystem(new UIOutFightSystem(), SystemPriorities.render);
			this.engine.addSystem(new UIOutLogSystem(), SystemPriorities.render);
			this.engine.addSystem(new UIOutManageSaveSystem(), SystemPriorities.render);
			this.engine.addSystem(new UIOutPopupInventorySystem(), SystemPriorities.render);
			this.engine.addSystem(new UIOutPopupTradeSystem(), SystemPriorities.render);
			this.engine.addSystem(new UIOutPopupInnSystem(), SystemPriorities.render);

            if (GameConstants.isCheatsEnabled) {
                this.engine.addSystem(new CheatSystem(), SystemPriorities.update);
            }
		},

		start: function () {
			this.tickProvider.add(this.engine.update, this.engine);
			this.tickProvider.start();
            this.gameManager.startGame();
		},

        handleException: function (ex) {
            this.gameManager.pauseGame();
            var exshortdesc = (ex.name ? ex.name : "Unknown") + ": " + (ex.message ? ex.message.replace(/\'/g, "%27") : "No message");
            var stack = (ex.stack ? ex.stack.replace(/\n/g, "%0A").replace(/\'/g, "%27") : "Not available");

            // track to ga
            gtag('event', 'exception', {
                'description': exshortdesc,
                'fatal': true,
            });
            // show popup
            var bugTitle = "[JS Error] " + exshortdesc;
            var bugBody =
               "Details:%0A[Fill in any details here that you think will help tracking down this bug, such as what you did in the game just before it happened.]" +
               "%0A%0AStacktrace:%0A" + stack;
            var url = "https://github.com/nroutasuo/level13/issues/new?title=" + bugTitle + "&body=" + bugBody + "&labels=exception";
            GameGlobals.uiFunctions.showInfoPopup(
                "Error",
                "You've found a bug! Please reload the page to continue playing.

" +
                "You can also help the developer by reporting the problem on Github.",
                "ok",
                null
            );
            throw ex;
        },

		cheat: function (input) {
            if (!GameConstants.isCheatsEnabled) return;
			this.engine.getSystem(CheatSystem).applyCheat(input);
		}

    });

    return Level13;
});
<commit_msg>fix error popup being hidden by game paused in error handler<commit_after>define([
    'ash',
    'game/GameGlobals',
    'game/GlobalSignals',
	'game/constants/GameConstants',
    'game/constants/SystemPriorities',
    'game/GameState',
    'game/systems/GameManager',
    'game/systems/SaveSystem',
    'game/systems/AutoPlaySystem',
    'game/systems/ui/UIOutHeaderSystem',
    'game/systems/ui/UIOutElementsSystem',
    'game/systems/ui/UIOutLevelSystem',
    'game/systems/ui/UIOutCampSystem',
    'game/systems/ui/UIOutCampVisSystem',
    'game/systems/ui/UIOutProjectsSystem',
    'game/systems/ui/UIOutEmbarkSystem',
    'game/systems/ui/UIOutBagSystem',
    'game/systems/ui/UIOutFollowersSystem',
    'game/systems/ui/UIOutMapSystem',
    'game/systems/ui/UIOutTradeSystem',
    'game/systems/ui/UIOutUpgradesSystem',
    'game/systems/ui/UIOutTribeSystem',
    'game/systems/ui/UIOutBlueprintsSystem',
    'game/systems/ui/UIOutFightSystem',
    'game/systems/ui/UIOutLogSystem',
    'game/systems/ui/UIOutManageSaveSystem',
    'game/systems/ui/UIOutPopupInnSystem',
    'game/systems/ui/UIOutPopupTradeSystem',
    'game/systems/ui/UIOutPopupInventorySystem',
    'game/systems/CheatSystem',
    'game/systems/SlowUpdateSystem',
    'game/systems/VisionSystem',
    'game/systems/StaminaSystem',
    'game/systems/PlayerPositionSystem',
    'game/systems/PlayerActionSystem',
    'game/systems/SectorStatusSystem',
    'game/systems/LevelPassagesSystem',
    'game/systems/CollectorSystem',
    'game/systems/FightSystem',
    'game/systems/PopulationSystem',
    'game/systems/WorkerSystem',
    'game/systems/FaintingSystem',
    'game/systems/ReputationSystem',
    'game/systems/RumourSystem',
    'game/systems/EvidenceSystem',
    'game/systems/EndingSystem',
    'game/systems/GlobalResourcesSystem',
    'game/systems/GlobalResourcesResetSystem',
    'game/systems/BagSystem',
    'game/systems/HazardSystem',
    'game/systems/UnlockedFeaturesSystem',
    'game/systems/occurrences/CampEventsSystem',
    'game/PlayerActionFunctions',
    'game/UIFunctions',
    'game/helpers/PlayerActionsHelper',
    'game/helpers/PlayerActionResultsHelper',
    'game/helpers/ui/ChangeLogHelper',
    'game/helpers/ItemsHelper',
    'game/helpers/EndingHelper',
    'game/helpers/ResourcesHelper',
    'game/helpers/MovementHelper',
    'game/helpers/FightHelper',
    'game/helpers/LevelHelper',
    'game/helpers/SectorHelper',
    'game/helpers/CampHelper',
    'game/helpers/ButtonHelper',
    'game/helpers/SaveHelper',
    'game/helpers/UpgradeEffectsHelper',
    'game/helpers/ui/UIMapHelper',
    'game/helpers/ui/UITechTreeHelper',
    'brejep/tickprovider',
], function (
    Ash,
    GameGlobals,
    GlobalSignals,
	GameConstants,
    SystemPriorities,
    GameState,
    GameManager,
    SaveSystem,
    AutoPlaySystem,
    UIOutHeaderSystem,
    UIOutElementsSystem,
    UIOutLevelSystem,
    UIOutCampSystem,
    UIOutCampVisSystem,
    UIOutProjectsSystem,
    UIOutEmbarkSystem,
    UIOutBagSystem,
    UIOutFollowersSystem,
    UIOutMapSystem,
    UIOutTradeSystem,
    UIOutUpgradesSystem,
    UIOutTribeSystem,
    UIOutBlueprintsSystem,
    UIOutFightSystem,
    UIOutLogSystem,
    UIOutManageSaveSystem,
    UIOutPopupInnSystem,
    UIOutPopupTradeSystem,
    UIOutPopupInventorySystem,
    CheatSystem,
    SlowUpdateSystem,
    VisionSystem,
    StaminaSystem,
    PlayerPositionSystem,
    PlayerActionSystem,
    SectorStatusSystem,
    LevelPassagesSystem,
    CollectorSystem,
    FightSystem,
    PopulationSystem,
    WorkerSystem,
    FaintingSystem,
    ReputationSystem,
    RumourSystem,
    EvidenceSystem,
    EndingSystem,
    GlobalResourcesSystem,
    GlobalResourcesResetSystem,
    BagSystem,
    HazardSystem,
    UnlockedFeaturesSystem,
    CampEventsSystem,
    PlayerActionFunctions,
    UIFunctions,
    PlayerActionsHelper,
    PlayerActionResultsHelper,
    ChangeLogHelper,
    ItemsHelper,
    EndingHelper,
    ResourcesHelper,
    MovementHelper,
    FightHelper,
    LevelHelper,
    SectorHelper,
    CampHelper,
    ButtonHelper,
    SaveHelper,
    UpgradeEffectsHelper,
    UIMapHelper,
    UITechTreeHelper,
    TickProvider
) {
    var Level13 = Ash.Class.extend({

        engine: null,
		gameManager: null,
        tickProvider: null,

        constructor: function (plugins) {
            var game = this;
            this.engine = new Ash.Engine();
            this.engine.extraUpdateTime = 0;
			this.tickProvider = new TickProvider(null, function (ex) { game.handleException(ex) });
			this.gameManager = new GameManager(this.tickProvider);

            this.initializeGameGlobals();
			this.addSystems();
            this.initializePlugins(plugins);

            var sys = this;
            GlobalSignals.worldReadySignal.addOnce(function () {
                sys.start();
            });

            this.gameManager.setupGame();
        },

        initializeGameGlobals: function () {
			GameGlobals.gameState = new GameState();
            GameGlobals.playerActionsHelper = new PlayerActionsHelper(this.engine);
			GameGlobals.playerActionFunctions = new PlayerActionFunctions(this.engine);

            GameGlobals.resourcesHelper = new ResourcesHelper(this.engine);
            GameGlobals.levelHelper = new LevelHelper(this.engine);
			GameGlobals.movementHelper = new MovementHelper(this.engine);
			GameGlobals.sectorHelper = new SectorHelper(this.engine);
            GameGlobals.fightHelper = new FightHelper(this.engine);
			GameGlobals.campHelper = new CampHelper(this.engine);
            GameGlobals.endingHelper = new EndingHelper(this.engine);
			GameGlobals.playerActionResultsHelper = new PlayerActionResultsHelper(this.engine);

            GameGlobals.itemsHelper = new ItemsHelper();
			GameGlobals.upgradeEffectsHelper = new UpgradeEffectsHelper();
			GameGlobals.saveHelper = new SaveHelper();
            GameGlobals.changeLogHelper = new ChangeLogHelper();

            GameGlobals.uiMapHelper = new UIMapHelper(this.engine);
            GameGlobals.uiTechTreeHelper = new UITechTreeHelper(this.engine);
            GameGlobals.buttonHelper = new ButtonHelper();

			GameGlobals.uiFunctions = new UIFunctions();
        },

        initializePlugins: function (plugins) {
            if (!plugins) return;
            var game = this;
            require(plugins, function (plugin) {
                game.engine.addSystem(new plugin(), SystemPriorities.update);
            });
        },

		addSystems: function () {
			this.engine.addSystem(this.gameManager, SystemPriorities.preUpdate);

			if (GameConstants.isDebugOutputEnabled) console.log("START " + GameConstants.STARTTimeNow() + "\t initializing systems");

			this.engine.addSystem(new SaveSystem(), SystemPriorities.preUpdate);
			this.engine.addSystem(new PlayerPositionSystem(), SystemPriorities.preupdate);

			this.engine.addSystem(new GlobalResourcesResetSystem(), SystemPriorities.update);
			this.engine.addSystem(new VisionSystem(), SystemPriorities.update);
			this.engine.addSystem(new StaminaSystem(), SystemPriorities.update);
			this.engine.addSystem(new BagSystem(), SystemPriorities.update);
			this.engine.addSystem(new HazardSystem(), SystemPriorities.update);
			this.engine.addSystem(new CollectorSystem(), SystemPriorities.update);
			this.engine.addSystem(new FightSystem(), SystemPriorities.update);
			this.engine.addSystem(new PopulationSystem(), SystemPriorities.update);
			this.engine.addSystem(new WorkerSystem(), SystemPriorities.update);
			this.engine.addSystem(new FaintingSystem(), SystemPriorities.update);
			this.engine.addSystem(new ReputationSystem(), SystemPriorities.update);
			this.engine.addSystem(new RumourSystem(), SystemPriorities.update);
			this.engine.addSystem(new EvidenceSystem(), SystemPriorities.update);
			this.engine.addSystem(new PlayerActionSystem(), SystemPriorities.update);
			this.engine.addSystem(new SectorStatusSystem(), SystemPriorities.update);
			this.engine.addSystem(new LevelPassagesSystem(), SystemPriorities.update);
			this.engine.addSystem(new UnlockedFeaturesSystem(), SystemPriorities.update);
			this.engine.addSystem(new GlobalResourcesSystem(), SystemPriorities.update);
			this.engine.addSystem(new CampEventsSystem(), SystemPriorities.update);
            this.engine.addSystem(new EndingSystem(), SystemPriorities.update);

			this.engine.addSystem(new AutoPlaySystem(), SystemPriorities.postUpdate);
			this.engine.addSystem(new SlowUpdateSystem(), SystemPriorities.postUpdate);

			this.engine.addSystem(new UIOutHeaderSystem(), SystemPriorities.render);
			this.engine.addSystem(new UIOutElementsSystem(), SystemPriorities.render);
			this.engine.addSystem(new UIOutLevelSystem(), SystemPriorities.render);
			this.engine.addSystem(new UIOutCampSystem(), SystemPriorities.render);
            this.engine.addSystem(new UIOutCampVisSystem(), SystemPriorities.render);
			this.engine.addSystem(new UIOutProjectsSystem(), SystemPriorities.render);
			this.engine.addSystem(new UIOutEmbarkSystem(), SystemPriorities.render);
			this.engine.addSystem(new UIOutBagSystem(), SystemPriorities.render);
			this.engine.addSystem(new UIOutFollowersSystem(), SystemPriorities.render);
			this.engine.addSystem(new UIOutMapSystem(), SystemPriorities.render);
			this.engine.addSystem(new UIOutTradeSystem(), SystemPriorities.render);
			this.engine.addSystem(new UIOutUpgradesSystem(), SystemPriorities.render);
			this.engine.addSystem(new UIOutBlueprintsSystem(), SystemPriorities.render);
			this.engine.addSystem(new UIOutTribeSystem(), SystemPriorities.render);
			this.engine.addSystem(new UIOutFightSystem(), SystemPriorities.render);
			this.engine.addSystem(new UIOutLogSystem(), SystemPriorities.render);
			this.engine.addSystem(new UIOutManageSaveSystem(), SystemPriorities.render);
			this.engine.addSystem(new UIOutPopupInventorySystem(), SystemPriorities.render);
			this.engine.addSystem(new UIOutPopupTradeSystem(), SystemPriorities.render);
			this.engine.addSystem(new UIOutPopupInnSystem(), SystemPriorities.render);

            if (GameConstants.isCheatsEnabled) {
                this.engine.addSystem(new CheatSystem(), SystemPriorities.update);
            }
		},

		start: function () {
			this.tickProvider.add(this.engine.update, this.engine);
			this.tickProvider.start();
            this.gameManager.startGame();
		},

        handleException: function (ex) {
            var exshortdesc = (ex.name ? ex.name : "Unknown") + ": " + (ex.message ? ex.message.replace(/\'/g, "%27") : "No message");
            var stack = (ex.stack ? ex.stack.replace(/\n/g, "%0A").replace(/\'/g, "%27") : "Not available");

            // track to ga
            gtag('event', 'exception', {
                'description': exshortdesc,
                'fatal': true,
            });
            // show popup
            var bugTitle = "[JS Error] " + exshortdesc;
            var bugBody =
               "Details:%0A[Fill in any details here that you think will help tracking down this bug, such as what you did in the game just before it happened.]" +
               "%0A%0AStacktrace:%0A" + stack;
            var url = "https://github.com/nroutasuo/level13/issues/new?title=" + bugTitle + "&body=" + bugBody + "&labels=exception";
            GameGlobals.uiFunctions.showInfoPopup(
                "Error",
                "You've found a bug! Please reload the page to continue playing.

" +
                "You can also help the developer by reporting the problem on Github.",
                "ok",
                null
            );
            this.gameManager.pauseGame();
            throw ex;
        },

		cheat: function (input) {
            if (!GameConstants.isCheatsEnabled) return;
			this.engine.getSystem(CheatSystem).applyCheat(input);
		}

    });

    return Level13;
});
<|endoftext|>


<commit_before>'use strict';

module.exports = {
  bowerJSON: {
    dependencies: {
      'ionic': '~1.3.0',
      'angular': '~1.5.0',
      'angular-animate': '~1.5.0',
      'angular-sanitize': '~1.5.0',
      'angular-ui-router': '~0.3.1',
      'ngCordova': '~0.1.17-alpha'
    },
    devDependencies: {
      'angular-mocks': '~1.5.0'
    },
    resolutions: {
      // force newest angular versions (not those from ionic)
      'angular': '~1.5.0',
      'angular-animate': '~1.5.0',
      'angular-sanitize': '~1.5.0',
      'angular-ui-router': '~0.3.1'
    }
  },
  optional: [
    {
      type: 'separator',
      line: '---- i18n/l10n ----'
    },
    {
      value: 'angular-dynamic-locale#~0.1.27',
      name: 'angular-dynamic-locale',
      checked: true
    },
    {
      value: 'angular-translate#~2.11.0',
      name: 'angular-translate',
      checked: true
    },
    {
      value: 'angular-translate-loader-static-files#~2.11.0',
      name: 'angular-translate-loader-static-files',
      checked: true
    },
    {
      type: 'separator',
      line: '---- persistence ----'
    },
    {
      value: 'localforage#~1.4.0',
      name: 'localforage',
      checked: true
    }
  ]
};
<commit_msg>chore #472: update bower deps of generated project<commit_after>'use strict';

module.exports = {
  bowerJSON: {
    dependencies: {
      'ionic': '~1.3.0',
      'angular': '~1.5.0',
      'angular-animate': '~1.5.0',
      'angular-sanitize': '~1.5.0',
      'angular-ui-router': '~0.4.2',
      'ngCordova': '~0.1.27-alpha'
    },
    devDependencies: {
      'angular-mocks': '~1.5.0'
    },
    resolutions: {
      // force newest angular versions (not those from ionic)
      'angular': '~1.5.0',
      'angular-animate': '~1.5.0',
      'angular-sanitize': '~1.5.0',
      'angular-ui-router': '~0.4.2'
    }
  },
  optional: [
    {
      type: 'separator',
      line: '---- i18n/l10n ----'
    },
    {
      value: 'angular-dynamic-locale#~0.1.32',
      name: 'angular-dynamic-locale',
      checked: true
    },
    {
      value: 'angular-translate#~2.13.0',
      name: 'angular-translate',
      checked: true
    },
    {
      value: 'angular-translate-loader-static-files#~2.13.0',
      name: 'angular-translate-loader-static-files',
      checked: true
    },
    {
      type: 'separator',
      line: '---- persistence ----'
    },
    {
      value: 'localforage#~1.4.0',
      name: 'localforage',
      checked: true
    }
  ]
};
<|endoftext|>


<commit_before>define(function() {
    describe('Minionette.View', function() {
        beforeEach(function() {
            this.view = new Minionette.View();
        });
        afterEach(function() {
            this.view.remove();
        });

        describe("constructor", function() {
            it("calls Backbone.View's constructor", function() {
                var spy = this.sinon.spy(Backbone, 'View');

                new Minionette.View();

                expect(spy).to.have.been.called;
            });

            it("initializes _subViews", function() {
                expect(this.view._subViews).to.be.defined;
            });

            describe("Model Events", function() {
                before(function() {
                    this.spy = this.sinon.spy();
                    this.ModelEventTest = Minionette.View.extend({
                        modelEvents: {
                            'change': this.spy
                        }
                    });
                    this.model = new Backbone.Model();
                });

                it("listens for model events", function() {
                    var view = new this.ModelEventTest({model: this.model});

                    view.noop;
                    this.model.trigger('change');

                    expect(this.spy).to.have.been.called;
                });
            });

            describe("Collection Events", function() {
                before(function() {
                    this.spy = this.sinon.spy();
                    this.CollectionEventTest = Minionette.View.extend({
                        collectionEvents: {
                            'change': this.spy
                        }
                    });
                    this.collection = new Backbone.Collection();
                });

                it("listens for collection events", function() {
                    var view = new this.CollectionEventTest({collection: this.collection});

                    view.noop;
                    this.collection.trigger('change');

                    expect(this.spy).to.have.been.called;
                });
            });

            describe("Regsions", function() {
                xit('regions');
            });
        });

        describe("instances", function() {
            it("creates #template()", function() {
                expect(this.view.template).to.be.defined;
                expect(this.view.template()).to.be.defined;
            });

            describe("#serializeData()", function() {
                it("returns an object", function() {
                    expect(this.view.serializeData()).to.be.an.object;
                });

                it("has #_viewHelper as 'view' key", function() {
                    expect(this.view.serializeData().view).to.equal(this.view._viewHelper);
                });
            })
            xit("#_viewHelper()");

            describe("#delegateEvents()", function() {
                it("calls Backbone.View's #delegateEvents()", function() {
                    var spy = this.sinon.spy(Backbone.View.prototype, 'delegateEvents');

                    this.view.delegateEvents();

                    expect(spy).to.have.been.called;
                });

                it("sets #_jqueryRemove() listener on $el 'remove' event", function() {
                    var spy = this.sinon.spy(this.view, '_jqueryRemove');
                    this.view.delegateEvents();

                    this.view.$el.trigger('remove');

                    expect(spy).to.have.been.called;
                });

                it("sets #_jqueryRemove() listener that is compatible with #undelegateEvents()", function() {
                    var spy = this.sinon.spy(this.view, '_jqueryRemove');
                    this.view.delegateEvents();
                    this.view.undelegateEvents();

                    this.view.$el.trigger('remove');

                    expect(spy).to.not.have.been.called;
                });
            });

            describe("#remove()", function() {
                it("triggers 'remove:before' event", function() {
                    var spy = this.sinon.spy();
                    this.view.on('remove:before', spy);

                    this.view.remove();

                    expect(spy).to.have.been.called;
                });

                it("triggers 'remove' event", function() {
                    var spy = this.sinon.spy();
                    this.view.on('remove', spy);

                    this.view.remove();

                    expect(spy).to.have.been.called;
                });

                it("removes from parent view", function() {
                    var parentView = new Minionette.View();
                    parentView.addRegion('region', this.view);
                    var spy = this.sinon.spy(parentView.region, '_removeView');

                    this.view.remove();

                    expect(spy).to.have.been.called;
                });

                it("removes regions", function() {
                    var parentView = new Minionette.View(),
                        spy = this.sinon.spy(this.view, 'remove');
                    parentView.addRegion('region', this.view);

                    parentView.remove();

                    expect(spy).to.have.been.called;
                });
            });

            describe("#render()", function() {
                it("triggers 'render:before' event", function() {
                    var spy = this.sinon.spy();
                    this.view.on('render:before', spy);

                    this.view.render();

                    expect(spy).to.have.been.called;
                });

                it("triggers 'render' event", function() {
                    var spy = this.sinon.spy();
                    this.view.on('render', spy);

                    this.view.render();

                    expect(spy).to.have.been.called;
                });

                it("detaches regions before emptying $el", function() {
                    var subView = new Minionette.View(),
                        spy = this.sinon.spy();
                    subView.$el.on('click', spy);
                    this.view.addRegion('region', subView);

                    this.view.render();

                    subView.$el.trigger('click');
                    expect(spy).to.have.been.called;
                });

                it("passes #serializeData() output to #template()", function() {
                    var spy = this.sinon.spy(this.view, 'template'),
                    serializeData = _.uniqueId;
                    this.view.serializeData = function() {
                        return serializeData;
                    };

                    this.view.render();

                    expect(spy).to.have.been.calledWith(serializeData);
                });

                it("passes #template() output to $el#html()", function() {
                    var spy = this.sinon.spy(this.view.$el, 'html'),
                    template = _.uniqueId;
                    this.view.template = function() {
                        return template;
                    };

                    this.view.render();

                    expect(spy).to.have.been.calledWith(template);
                });

                it("reattaches regions", function() {
                    var subView = new Minionette.View();
                    this.view.addRegion('region', subView);
                    var spy = this.sinon.spy(this.view.region, 'reattach');

                    this.view.render();

                    expect(spy).to.have.been.called;
                });

                it("returns the view", function() {
                    var ret = this.view.render();

                    expect(ret).to.equal(this.view);
                });

                it("Integration Test", function() {
                    var subView = new Minionette.View({tagName: 'p'});
                    subView.template = function() {
                        return 'subView';
                    };
                    this.view.addRegion('region', subView).render();
                    this.view.template = _.template('
before
<%= view("region") %>
after
');

                    this.view.render();

                    expect(this.view.$el).to.contain('before');
                    expect(this.view.$el).to.contain('subView');
                    expect(this.view.$el).to.contain('after');
                });

            });

            describe("#_jqueryRemove()", function() {
                it("triggers 'remove:jquery' event", function() {
                    var spy = this.sinon.spy();
                    this.view.on('remove:jquery', spy);

                    this.view._jqueryRemove();

                    expect(spy).to.have.been.called;
                });

                it("calls #remove()", function() {
                    var spy = this.sinon.spy(this.view, 'remove');

                    this.view._jqueryRemove();

                    expect(spy).to.have.been.called;
                });
            });

            describe("#_removeView", function() {
                xit("do something");
            })

            describe("#_findRegionByView", function() {
                xit("do something");
            })

            describe("#_initializeRegions()", function() {
                xit("do something");
            });

            describe("#addRegion()", function() {
                xit("do something");
            });
        });
    });
});
<commit_msg>Spec View#Region<commit_after>define(function() {
    describe('Minionette.View', function() {
        beforeEach(function() {
            this.view = new Minionette.View();
        });
        afterEach(function() {
            this.view.remove();
        });

        describe("constructor", function() {
            it("calls Backbone.View's constructor", function() {
                var spy = this.sinon.spy(Backbone, 'View');

                new Minionette.View();

                expect(spy).to.have.been.called;
            });

            it("initializes _subViews", function() {
                expect(this.view._subViews).to.be.defined;
            });

            describe("Model Events", function() {
                before(function() {
                    this.spy = this.sinon.spy();
                    this.ModelEventTest = Minionette.View.extend({
                        modelEvents: {
                            'change': this.spy
                        }
                    });
                    this.model = new Backbone.Model();
                });

                it("listens for model events", function() {
                    var view = new this.ModelEventTest({model: this.model});

                    view.noop;
                    this.model.trigger('change');

                    expect(this.spy).to.have.been.called;
                });
            });

            describe("Collection Events", function() {
                before(function() {
                    this.spy = this.sinon.spy();
                    this.CollectionEventTest = Minionette.View.extend({
                        collectionEvents: {
                            'change': this.spy
                        }
                    });
                    this.collection = new Backbone.Collection();
                });

                it("listens for collection events", function() {
                    var view = new this.CollectionEventTest({collection: this.collection});

                    view.noop;
                    this.collection.trigger('change');

                    expect(this.spy).to.have.been.called;
                });
            });

            describe("Regsions", function() {
                xit('regions');
            });
        });

        describe("instances", function() {
            it("creates #template()", function() {
                expect(this.view.template).to.be.defined;
                expect(this.view.template()).to.be.defined;
            });

            it("sets Region to Minionette.Region", function() {
                expect(this.view.Region).to.equal(Minionette.Region);
            });

            describe("#serializeData()", function() {
                it("returns an object", function() {
                    expect(this.view.serializeData()).to.be.an.object;
                });

                it("has #_viewHelper as 'view' key", function() {
                    expect(this.view.serializeData().view).to.equal(this.view._viewHelper);
                });
            })
            xit("#_viewHelper()");

            describe("#delegateEvents()", function() {
                it("calls Backbone.View's #delegateEvents()", function() {
                    var spy = this.sinon.spy(Backbone.View.prototype, 'delegateEvents');

                    this.view.delegateEvents();

                    expect(spy).to.have.been.called;
                });

                it("sets #_jqueryRemove() listener on $el 'remove' event", function() {
                    var spy = this.sinon.spy(this.view, '_jqueryRemove');
                    this.view.delegateEvents();

                    this.view.$el.trigger('remove');

                    expect(spy).to.have.been.called;
                });

                it("sets #_jqueryRemove() listener that is compatible with #undelegateEvents()", function() {
                    var spy = this.sinon.spy(this.view, '_jqueryRemove');
                    this.view.delegateEvents();
                    this.view.undelegateEvents();

                    this.view.$el.trigger('remove');

                    expect(spy).to.not.have.been.called;
                });
            });

            describe("#remove()", function() {
                it("triggers 'remove:before' event", function() {
                    var spy = this.sinon.spy();
                    this.view.on('remove:before', spy);

                    this.view.remove();

                    expect(spy).to.have.been.called;
                });

                it("triggers 'remove' event", function() {
                    var spy = this.sinon.spy();
                    this.view.on('remove', spy);

                    this.view.remove();

                    expect(spy).to.have.been.called;
                });

                it("removes from parent view", function() {
                    var parentView = new Minionette.View();
                    parentView.addRegion('region', this.view);
                    var spy = this.sinon.spy(parentView.region, '_removeView');

                    this.view.remove();

                    expect(spy).to.have.been.called;
                });

                it("removes regions", function() {
                    var parentView = new Minionette.View(),
                        spy = this.sinon.spy(this.view, 'remove');
                    parentView.addRegion('region', this.view);

                    parentView.remove();

                    expect(spy).to.have.been.called;
                });
            });

            describe("#render()", function() {
                it("triggers 'render:before' event", function() {
                    var spy = this.sinon.spy();
                    this.view.on('render:before', spy);

                    this.view.render();

                    expect(spy).to.have.been.called;
                });

                it("triggers 'render' event", function() {
                    var spy = this.sinon.spy();
                    this.view.on('render', spy);

                    this.view.render();

                    expect(spy).to.have.been.called;
                });

                it("detaches regions before emptying $el", function() {
                    var subView = new Minionette.View(),
                        spy = this.sinon.spy();
                    subView.$el.on('click', spy);
                    this.view.addRegion('region', subView);

                    this.view.render();

                    subView.$el.trigger('click');
                    expect(spy).to.have.been.called;
                });

                it("passes #serializeData() output to #template()", function() {
                    var spy = this.sinon.spy(this.view, 'template'),
                    serializeData = _.uniqueId;
                    this.view.serializeData = function() {
                        return serializeData;
                    };

                    this.view.render();

                    expect(spy).to.have.been.calledWith(serializeData);
                });

                it("passes #template() output to $el#html()", function() {
                    var spy = this.sinon.spy(this.view.$el, 'html'),
                    template = _.uniqueId;
                    this.view.template = function() {
                        return template;
                    };

                    this.view.render();

                    expect(spy).to.have.been.calledWith(template);
                });

                it("reattaches regions", function() {
                    var subView = new Minionette.View();
                    this.view.addRegion('region', subView);
                    var spy = this.sinon.spy(this.view.region, 'reattach');

                    this.view.render();

                    expect(spy).to.have.been.called;
                });

                it("returns the view", function() {
                    var ret = this.view.render();

                    expect(ret).to.equal(this.view);
                });

                it("Integration Test", function() {
                    var subView = new Minionette.View({tagName: 'p'});
                    subView.template = function() {
                        return 'subView';
                    };
                    this.view.addRegion('region', subView).render();
                    this.view.template = _.template('
before
<%= view("region") %>
after
');

                    this.view.render();

                    expect(this.view.$el).to.contain('before');
                    expect(this.view.$el).to.contain('subView');
                    expect(this.view.$el).to.contain('after');
                });

            });

            describe("#_jqueryRemove()", function() {
                it("triggers 'remove:jquery' event", function() {
                    var spy = this.sinon.spy();
                    this.view.on('remove:jquery', spy);

                    this.view._jqueryRemove();

                    expect(spy).to.have.been.called;
                });

                it("calls #remove()", function() {
                    var spy = this.sinon.spy(this.view, 'remove');

                    this.view._jqueryRemove();

                    expect(spy).to.have.been.called;
                });
            });

            describe("#_removeView", function() {
                xit("do something");
            })

            describe("#_findRegionByView", function() {
                xit("do something");
            })

            describe("#_initializeRegions()", function() {
                xit("do something");
            });

            describe("#addRegion()", function() {
                xit("do something");
            });
        });
    });
});
<|endoftext|>


<commit_before><commit_msg>Updated fire controller<commit_after>/// @file fires.js
/// Controller containing functionality for fire

var config = require('../Javascript/rethinkdb_config.js');

var r = require("rethinkdb");
var bluebird = require("bluebird");
var database = "fires";
module.exports = {

	///@function module.exports.findFires
	/// retrieves all the fires ordered by time as a JSON array
	///@param {object} req object containing  information about HTTP request
	///@param {object} res the desired HTTP response
    findFires: function (req, res) {
        var conn;
        r.connect(config.database).then(function(c) {
            conn = c;

            return r.table("fire").orderBy(
                r.desc(r.row("acq_time"))).run(conn);

        }).then(function(cursor){ return cursor.toArray();}).then(
            function (result) { res.json(result);}).error(function (err) {
            console.log("Error handling request:", err);
            res.status(500).json({success: false, err: err});
        }).finally(function() {
            if(conn)
                conn.close();
        });
    }
}
<|endoftext|>


<commit_before>// User Controller
var Promise = require('bluebird'),
    util    = require('util');

/**
 * This is an example of a user controller
 * This module is responsible for all operations related to user table.
 */
module.exports = function(app){
  return {
    getUsers: function(){
      return new Promise(function(resolve, reject){
        app.get('db').table_user.find(function(err, users){
          if(err !== null){
            resolve({result: users});
          }else{
            reject({error: 'Not found users'});
          }
        });
      });
    }, // getUsers
    getUser: function(id){
      return new Promise(function(resolve, reject){
        app.get('db').table_user.find({id: id}, function(err, user){
          if(err !== null){
            resolve({result: user});
          }else{
            reject({error: 'User not found'});
          }
        });
      });
    }, // getUser
  };
};
<commit_msg>Update usercontroller.js<commit_after>// User Controller
var util    = require('util');

/**
 * This is an example of a user controller
 * This module is responsible for all operations related to user table.
 */
module.exports = function(app){
  return {
    getUsers: function(){
      return Promise.resolve({ then: (resolve, reject) => {
          app.get('db').table_user.find().then((users) => {
            if(users){
              resolve(users);
            }else{
              reject({err: 'Users not found'});
            }
          });
        }
      });
    }, // getUsers
    getUser: function(id){
      return Promise.resolve({ then: (resolve, reject) => {
          app.get('db').table_users.findOne({id: id}).then((User) => {
            if(User){
              resolve(User);
            }else{
              reject({err: 'User not found'});
            }
          });
        }
      });
    }, // getUser
  };
};
<|endoftext|>


<commit_before>/* global paper:false */
/* global breakupGcodeCommand:false analyzeGcode:false extractCommandSequence:false */
/* exported newGcodeRenderer */

/**
 * Create a new gcode renderer.
 *
 * The result is an API object with the following methods:
 *  - renderGcode(string)
 *  - setSize(array<number>)
 *
 * @param canvas {Element} - The canvas element to use.
 */
function newGcodeRenderer(canvas) {
  var analysis = {
    minPos: {X: 0, Y: 0},
    maxPos: {X: 0, Y: 0},
  };

  /* Resize the paperjs view to fit everything rendered. */
  var resizeView = function(options) {
    options = options || {};
    var workspaceWidth = Math.max(analysis.maxPos.X - analysis.minPos.X, 50);
    var workspaceDepth = Math.max(analysis.maxPos.Y - analysis.minPos.Y, 50);

    if (options.bit_diameter) {
      workspaceWidth += options.bit_diameter * 2;
      workspaceDepth += options.bit_diameter * 2;
    }

    // Set an appropriate zoom/centering given our analysis.
    paper.view.setZoom(Math.min(
      paper.view.viewSize.width / (workspaceWidth * 1.1),
      paper.view.viewSize.height / (workspaceDepth * 1.1)));
    paper.view.setCenter(new paper.Point(
      analysis.minPos.X + workspaceWidth / 2,
      analysis.minPos.Y + workspaceDepth / 2));
  };

  /* Set the size of the paper canvas. */
  var setSize = function(bounds) {
    paper.view.viewSize = [bounds.width, bounds.height];
    resizeView();
  };

  var warn = function(str) {
    // TODO: display in the rendered view.
    console.warn(str);
  };

  /**
   * Render the list of gcode commands onto a canvas.
   *
   * Options:
   *  - bit_diameter: The size of the bit to use in millimeters.
   *
   * @param opt_options {Object} - Extra options to use
   */
  var renderGcode = function(gcode, opt_options) {
    var options = opt_options || {};

    console.time("renderGcode");

    var commandSequence = extractCommandSequence(gcode);

    // Run an analysis on the gcode to determine the appropriate bounds for rendering.
    analysis = analyzeGcode(commandSequence);
    resizeView(options);

    // Clear out any previous paths.
    paper.project.clear();

    // A scaling factor from current units to mm.
    var scale = 1;

    // A toggle for absolute v. relative coordinate specification.
    var isRelative = false;

    // The current tool position.
    var pos = {
      "X": 0,
      "Y": 0,
      "Z": 0
    };

    // A paper group for all of the paths to be rendered.
    var allPaths = new paper.Group();

    // Draw a little graph table representing out workspace.
    var gridLineWidth = 1 / paper.view.getZoom();
    var gridWidth = 10 * Math.ceil(Math.max(50, analysis.maxPos.X) / 10);
    var gridDepth = 10 * Math.ceil(Math.max(50, analysis.maxPos.Y) / 10);
    for (var ix = 10; ix <= gridWidth; ix += 10) {
      allPaths.addChild(new paper.Path.Line({
        "from": [ix, 0],
        "to": [ix, gridDepth],
        "strokeColor": "#DCFFFF",
        "strokeWidth": gridLineWidth * (ix % 50 == 0) ? 1.5 : 1,
      }));
    }
    for (var iy = 10; iy <= gridDepth; iy += 10) {
      allPaths.addChild(new paper.Path.Line({
        "from": [0, iy],
        "to": [gridWidth, iy],
        "strokeColor": "#DCFFFF",
        "strokeWidth": gridLineWidth * (iy % 50 == 0) ? 1.5 : 1,
      }));
    }
    allPaths.addChild(new paper.Path.Line({
      "from": [0, 0],
      "to": [gridWidth, 0],
      "strokeColor": "#A3CCCC",
      "strokeWidth": 2 * gridLineWidth,
    }));
    allPaths.addChild(new paper.Path.Line({
      "from": [0, 0],
      "to": [0, gridDepth],
      "strokeColor": "#A3CCCC",
      "strokeWidth": 2 * gridLineWidth,
    }));

    var depthColor = function(depth1, depth2) {
      var depth = Math.min(depth1, depth2);
      if (depth > 0) {
        return "green";
      }

      var c = Math.round(255 - (depth / analysis.minPos.Z) * 224);
      return "rgb(" + c + "," + c + "," + c + ")";
    };

    console.time("renderGcode: gcode processing");
    var bitRadius = options["bit_diameter"] / 2 || (1 / paper.view.getZoom());
    var prevInstruction = "";
    for (var i = 0; i < commandSequence.length; i++) {
      var command = prevInstruction + commandSequence[i];
      var parts = breakupGcodeCommand(command);

      var cType = parts[0][0];
      var cNum = parseInt(parts[0].substr(1), 10);

      // The carriage return can pass new arguments to the previous command.
      prevInstruction = command.endsWith('\r') ? cType + cNum + " " : "";

      // Read the command parameters.
      var params = {};
      for (var j = 1; j < parts.length; j++) {
        params[parts[j][0].toUpperCase()] = parseFloat(parts[j].substr(1)) || 0;
      }

      if (cType == "G" && (cNum === 0 || cNum === 1)) {
        var endX = ((isRelative || params.X === undefined) ? pos.X : 0) +
            ((params.X === undefined) ? 0 : params.X * scale);
        var endY = ((isRelative || params.Y === undefined) ? pos.Y : 0) +
            ((params.Y === undefined) ? 0 : params.Y * scale);
        var endZ = ((isRelative || params.Z === undefined) ? pos.Z : 0) +
            ((params.Z === undefined) ? 0 : params.Z * scale);

        // If we only moved in Z, render a circle.
        if (pos.X == endX && pos.Y == endY && endZ < pos.Z) {
          var drillSpot = new paper.Shape.Circle(
              new paper.Point([pos.X, pos.Y]), bitRadius);
          drillSpot.strokeWidth = 0;
          drillSpot.fillColor = depthColor(pos.Z, endZ);
          allPaths.addChild(drillSpot);
        } else {
          var line = paper.Path.Line({
            from: [pos.X, pos.Y],
            to: [endX, endY],
            strokeColor: depthColor(pos.Z, endZ),
            strokeCap: 'round',
            strokeWidth: 2 * bitRadius,
          });
          allPaths.addChild(line);
          if (cNum === 0) {
            line.dashArray = [1, 2];
            line.strokeWidth =  1 / paper.view.getZoom();
          }
        }

        // Update our known position.
        pos.X = endX;
        pos.Y = endY;
        pos.Z = endZ;
      } else if (cType == "G" && (cNum === 2 || cNum === 3)) {
        if (params.I === undefined || params.J === undefined) {
          warn("implementation only supports specification of both I and J: " + command);
          continue;
        }

        // circular interpolation (clockwise)
        var arcEndX = ((isRelative || params.X === undefined) ? pos.X : 0) +
            ((params.X === undefined) ? 0 : params.X * scale);
        var arcEndY = ((isRelative || params.Y === undefined) ? pos.Y : 0) +
            ((params.Y === undefined) ? 0 : params.Y * scale);
        var arcEndZ = ((isRelative || params.Z === undefined) ? pos.Z : 0) +
            ((params.Z === undefined) ? 0 : params.Z * scale);

        // TODO: implement missing axii (Z, A, B, C, K)
        var arcStart = new paper.Point(pos.X, pos.Y);
        var arcEnd = new paper.Point(arcEndX, arcEndY);

        var center = arcStart.add(new paper.Point(params.I * scale, params.J * scale));
        var through = arcStart.subtract(center);
        through.angle = arcStart.add(arcEnd).subtract(center).subtract(center).angle;
        through = through.add(center);

        allPaths.addChild(paper.Path.Arc({
          from: [pos.X, pos.Y],
          through: [through.x, through.y],
          to: [arcEndX, arcEndY],
          strokeColor: depthColor(pos.Z, arcEndZ),
          strokeCap: 'round',
          strokeWidth: 2 * bitRadius,
        }));

        // Update our known position.
        pos.X = arcEndX;
        pos.Y = arcEndY;
        pos.Z = arcEndZ;
      } else if (cType == "G" && cNum === 4) {
        // dwell
      } else if (cType == "G" && cNum === 9) {
        // exact stop, non-modal
      } else if (cType == "G" && cNum === 17) {
        // XY plane selection
        // TODO: support other axis specification
      } else if (cType == "G" && cNum === 20) {
        // programming in inches
        scale = 25.4;
      } else if (cType == "G" && cNum === 21) {
        // programming in mm
        scale = 1;
      } else if (cType == "G" && cNum === 28) {
        // return to home
        if (params.X !== undefined) {
          pos.X = 0;
        }
        if (params.Y !== undefined) {
          pos.Y = 0;
        }
        if (params.Z !== undefined) {
          pos.Z = 0;
        }
      } else if (cType == "G" && cNum === 40) {
        // tool radius compensation off.
        // TODO: implement tool radius compensation.
      } else if (cType == "G" && cNum === 61) {
        // exact stop, modal
      } else if (cType == "G" && cNum === 64) {
        // cancel exact stop, modal
      } else if (cType == "G" && cNum === 90) {
        // absolute coordinates.
        isRelative = false;
      } else if (cType == "G" && cNum === 91) {
        // relative coordinates.
        isRelative = true;
      } else if (cType == "G" && cNum === 92) {
        // coordinate system offset. This command effectively states that the machine
        // is at the specified coordinates.

        // Fake support for this by validating that the command does not mess
        // with an axis we care about.
        // TODO: implement real support for this.
        if (params.X !== undefined ||
            params.Y !== undefined ||
            params.Z !== undefined) {
          warn("coordinate system offset (G92) not implemented.");
        }
      } else if (cType == "M") {
        // Most M codes can safely be ignored.

        switch(cNum) {
        case 0: // compulsory stop
        case 1: // optional stop
        case 2: // end of program
        case 3: // spindle on clockwise
        case 4: // spindle on counterclockwise
        case 5: // spindle stop
        case 6: // tool change for linuxcnc
        case 7: // coolant on, mist
        case 8: // coolant on, flood
        case 9: // coolant off
        case 30: // end of program with return to top
        case 40: // reprap eject
        case 82: // reprap extruder absolute mode
        case 83: // reprap extruder relative mode
        case 84: // reprap stop idle hold
        case 104: // reprap set extruder temperature
        case 105: // reprap get extruder temperature
        case 106: // reprap fan on
        case 107: // reprap fan off
        case 108: // reprap set extruder speed
        case 109: // reprap set extruder temperature and wait
        case 140: // reprap set bed temperature (fast)
        case 141: // reprap set chamber temperature (fast)
        case 143: // reprap set maximum hot-end temperature
        case 190: // reprap wait for bed temperature to reach target
          continue;
        default:
          warn("unimplemented gcode command: " + parts[0]);
        }
      } else {
        warn("unknown gcode command: " + parts[0]);
      }
    }
    console.timeEnd("renderGcode: gcode processing");

    // Invert everything (to move the origin to the bottom left).
    allPaths.scale(1, -1);

    paper.view.draw();
    console.timeEnd("renderGcode");
  };

  // Initialize paper.js
  paper.setup(canvas);

  // Render an empty workspace.
  renderGcode([]);

  return {
    setSize: setSize,
    resizeView: resizeView,
    renderGcode: renderGcode,
  };
}
<commit_msg>make rendering analysis account for the minimum grid.<commit_after>/* global paper:false */
/* global breakupGcodeCommand:false analyzeGcode:false extractCommandSequence:false */
/* exported newGcodeRenderer */

/**
 * Create a new gcode renderer.
 *
 * The result is an API object with the following methods:
 *  - renderGcode(string)
 *  - setSize(array<number>)
 *
 * @param canvas {Element} - The canvas element to use.
 */
function newGcodeRenderer(canvas) {
  var analysis = {
    minPos: {X: 0, Y: 0},
    maxPos: {X: 0, Y: 0},
  };

  /* Resize the paperjs view to fit everything rendered. */
  var resizeView = function(options) {
    options = options || {};
    var workspaceWidth = Math.max(analysis.maxPos.X - analysis.minPos.X, 50);
    var workspaceDepth = Math.max(analysis.maxPos.Y - analysis.minPos.Y, 50);

    var left = analysis.minPos.X;
    var bottom = analysis.minPos.Y;
    if (options.bit_diameter) {
      workspaceWidth += options.bit_diameter * 2;
      workspaceDepth += options.bit_diameter * 2;
      left -= options.bit_diameter;
      bottom -= options.bit_diameter;
    }

    // Set an appropriate zoom/centering given our analysis.
    paper.view.setZoom(Math.min(
      paper.view.viewSize.width / (workspaceWidth * 1.1),
      paper.view.viewSize.height / (workspaceDepth * 1.1)));
    paper.view.setCenter(new paper.Point(
      left + workspaceWidth / 2,
      bottom + workspaceDepth / 2));
  };

  /* Set the size of the paper canvas. */
  var setSize = function(bounds) {
    paper.view.viewSize = [bounds.width, bounds.height];
    resizeView();
  };

  var warn = function(str) {
    // TODO: display in the rendered view.
    console.warn(str);
  };

  /**
   * Render the list of gcode commands onto a canvas.
   *
   * Options:
   *  - bit_diameter: The size of the bit to use in millimeters.
   *
   * @param opt_options {Object} - Extra options to use
   */
  var renderGcode = function(gcode, opt_options) {
    var options = opt_options || {};

    console.time("renderGcode");

    var commandSequence = extractCommandSequence(gcode);

    // Run an analysis on the gcode to determine the appropriate bounds for rendering.
    analysis = analyzeGcode(commandSequence);
    analysis.maxPos.X = Math.max(50, analysis.maxPos.X);
    analysis.maxPos.Y = Math.max(50, analysis.maxPos.Y);
    resizeView(options);

    // Clear out any previous paths.
    paper.project.clear();

    // A scaling factor from current units to mm.
    var scale = 1;

    // A toggle for absolute v. relative coordinate specification.
    var isRelative = false;

    // The current tool position.
    var pos = {
      "X": 0,
      "Y": 0,
      "Z": 0
    };

    // A paper group for all of the paths to be rendered.
    var allPaths = new paper.Group();

    // Draw a little graph table representing out workspace.
    var gridLineWidth = 1 / paper.view.getZoom();
    var gridWidth = 10 * Math.ceil(Math.max(50, analysis.maxPos.X) / 10);
    var gridDepth = 10 * Math.ceil(Math.max(50, analysis.maxPos.Y) / 10);
    for (var ix = 10; ix <= gridWidth; ix += 10) {
      allPaths.addChild(new paper.Path.Line({
        "from": [ix, 0],
        "to": [ix, gridDepth],
        "strokeColor": "#DCFFFF",
        "strokeWidth": gridLineWidth * (ix % 50 == 0) ? 1.5 : 1,
      }));
    }
    for (var iy = 10; iy <= gridDepth; iy += 10) {
      allPaths.addChild(new paper.Path.Line({
        "from": [0, iy],
        "to": [gridWidth, iy],
        "strokeColor": "#DCFFFF",
        "strokeWidth": gridLineWidth * (iy % 50 == 0) ? 1.5 : 1,
      }));
    }
    allPaths.addChild(new paper.Path.Line({
      "from": [0, 0],
      "to": [gridWidth, 0],
      "strokeColor": "#A3CCCC",
      "strokeWidth": 2 * gridLineWidth,
    }));
    allPaths.addChild(new paper.Path.Line({
      "from": [0, 0],
      "to": [0, gridDepth],
      "strokeColor": "#A3CCCC",
      "strokeWidth": 2 * gridLineWidth,
    }));

    var depthColor = function(depth1, depth2) {
      var depth = Math.min(depth1, depth2);
      if (depth > 0) {
        return "green";
      }

      var c = Math.round(255 - (depth / analysis.minPos.Z) * 224);
      return "rgb(" + c + "," + c + "," + c + ")";
    };

    console.time("renderGcode: gcode processing");
    var bitRadius = options["bit_diameter"] / 2 || (1 / paper.view.getZoom());
    var prevInstruction = "";
    for (var i = 0; i < commandSequence.length; i++) {
      var command = prevInstruction + commandSequence[i];
      var parts = breakupGcodeCommand(command);

      var cType = parts[0][0];
      var cNum = parseInt(parts[0].substr(1), 10);

      // The carriage return can pass new arguments to the previous command.
      prevInstruction = command.endsWith('\r') ? cType + cNum + " " : "";

      // Read the command parameters.
      var params = {};
      for (var j = 1; j < parts.length; j++) {
        params[parts[j][0].toUpperCase()] = parseFloat(parts[j].substr(1)) || 0;
      }

      if (cType == "G" && (cNum === 0 || cNum === 1)) {
        var endX = ((isRelative || params.X === undefined) ? pos.X : 0) +
            ((params.X === undefined) ? 0 : params.X * scale);
        var endY = ((isRelative || params.Y === undefined) ? pos.Y : 0) +
            ((params.Y === undefined) ? 0 : params.Y * scale);
        var endZ = ((isRelative || params.Z === undefined) ? pos.Z : 0) +
            ((params.Z === undefined) ? 0 : params.Z * scale);

        // If we only moved in Z, render a circle.
        if (pos.X == endX && pos.Y == endY && endZ < pos.Z) {
          var drillSpot = new paper.Shape.Circle(
              new paper.Point([pos.X, pos.Y]), bitRadius);
          drillSpot.strokeWidth = 0;
          drillSpot.fillColor = depthColor(pos.Z, endZ);
          allPaths.addChild(drillSpot);
        } else {
          var line = paper.Path.Line({
            from: [pos.X, pos.Y],
            to: [endX, endY],
            strokeColor: depthColor(pos.Z, endZ),
            strokeCap: 'round',
            strokeWidth: 2 * bitRadius,
          });
          allPaths.addChild(line);
          if (cNum === 0) {
            line.dashArray = [1, 2];
            line.strokeWidth =  1 / paper.view.getZoom();
          }
        }

        // Update our known position.
        pos.X = endX;
        pos.Y = endY;
        pos.Z = endZ;
      } else if (cType == "G" && (cNum === 2 || cNum === 3)) {
        if (params.I === undefined || params.J === undefined) {
          warn("implementation only supports specification of both I and J: " + command);
          continue;
        }

        // circular interpolation (clockwise)
        var arcEndX = ((isRelative || params.X === undefined) ? pos.X : 0) +
            ((params.X === undefined) ? 0 : params.X * scale);
        var arcEndY = ((isRelative || params.Y === undefined) ? pos.Y : 0) +
            ((params.Y === undefined) ? 0 : params.Y * scale);
        var arcEndZ = ((isRelative || params.Z === undefined) ? pos.Z : 0) +
            ((params.Z === undefined) ? 0 : params.Z * scale);

        // TODO: implement missing axii (Z, A, B, C, K)
        var arcStart = new paper.Point(pos.X, pos.Y);
        var arcEnd = new paper.Point(arcEndX, arcEndY);

        var center = arcStart.add(new paper.Point(params.I * scale, params.J * scale));
        var through = arcStart.subtract(center);
        through.angle = arcStart.add(arcEnd).subtract(center).subtract(center).angle;
        through = through.add(center);

        allPaths.addChild(paper.Path.Arc({
          from: [pos.X, pos.Y],
          through: [through.x, through.y],
          to: [arcEndX, arcEndY],
          strokeColor: depthColor(pos.Z, arcEndZ),
          strokeCap: 'round',
          strokeWidth: 2 * bitRadius,
        }));

        // Update our known position.
        pos.X = arcEndX;
        pos.Y = arcEndY;
        pos.Z = arcEndZ;
      } else if (cType == "G" && cNum === 4) {
        // dwell
      } else if (cType == "G" && cNum === 9) {
        // exact stop, non-modal
      } else if (cType == "G" && cNum === 17) {
        // XY plane selection
        // TODO: support other axis specification
      } else if (cType == "G" && cNum === 20) {
        // programming in inches
        scale = 25.4;
      } else if (cType == "G" && cNum === 21) {
        // programming in mm
        scale = 1;
      } else if (cType == "G" && cNum === 28) {
        // return to home
        if (params.X !== undefined) {
          pos.X = 0;
        }
        if (params.Y !== undefined) {
          pos.Y = 0;
        }
        if (params.Z !== undefined) {
          pos.Z = 0;
        }
      } else if (cType == "G" && cNum === 40) {
        // tool radius compensation off.
        // TODO: implement tool radius compensation.
      } else if (cType == "G" && cNum === 61) {
        // exact stop, modal
      } else if (cType == "G" && cNum === 64) {
        // cancel exact stop, modal
      } else if (cType == "G" && cNum === 90) {
        // absolute coordinates.
        isRelative = false;
      } else if (cType == "G" && cNum === 91) {
        // relative coordinates.
        isRelative = true;
      } else if (cType == "G" && cNum === 92) {
        // coordinate system offset. This command effectively states that the machine
        // is at the specified coordinates.

        // Fake support for this by validating that the command does not mess
        // with an axis we care about.
        // TODO: implement real support for this.
        if (params.X !== undefined ||
            params.Y !== undefined ||
            params.Z !== undefined) {
          warn("coordinate system offset (G92) not implemented.");
        }
      } else if (cType == "M") {
        // Most M codes can safely be ignored.

        switch(cNum) {
        case 0: // compulsory stop
        case 1: // optional stop
        case 2: // end of program
        case 3: // spindle on clockwise
        case 4: // spindle on counterclockwise
        case 5: // spindle stop
        case 6: // tool change for linuxcnc
        case 7: // coolant on, mist
        case 8: // coolant on, flood
        case 9: // coolant off
        case 30: // end of program with return to top
        case 40: // reprap eject
        case 82: // reprap extruder absolute mode
        case 83: // reprap extruder relative mode
        case 84: // reprap stop idle hold
        case 104: // reprap set extruder temperature
        case 105: // reprap get extruder temperature
        case 106: // reprap fan on
        case 107: // reprap fan off
        case 108: // reprap set extruder speed
        case 109: // reprap set extruder temperature and wait
        case 140: // reprap set bed temperature (fast)
        case 141: // reprap set chamber temperature (fast)
        case 143: // reprap set maximum hot-end temperature
        case 190: // reprap wait for bed temperature to reach target
          continue;
        default:
          warn("unimplemented gcode command: " + parts[0]);
        }
      } else {
        warn("unknown gcode command: " + parts[0]);
      }
    }
    console.timeEnd("renderGcode: gcode processing");

    // Invert everything (to move the origin to the bottom left).
    allPaths.scale(1, -1);

    paper.view.draw();
    console.timeEnd("renderGcode");
  };

  // Initialize paper.js
  paper.setup(canvas);

  // Render an empty workspace.
  renderGcode([]);

  return {
    setSize: setSize,
    resizeView: resizeView,
    renderGcode: renderGcode,
  };
}
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import {
    StyleSheet,
    Text,
    View,
    Image,
    TextInput,
    ListView,
    RefreshControl,
    DeviceEventEmitter,
    TouchableOpacity
} from 'react-native';

import DataRepository,{FlAG_STORAGE} from '../expand/dao/DataRepository'
import NavigationBar from '../common/NavigationBar'
import DetailPage from './RepositoryDetailPage'

import ScrollableTableView,{ScrollableTabBar} from 'react-native-scrollable-tab-view'
import TrendingCell from '../common/TrendingCell'

const API_URL = 'https://github.com/trending/'
import Popover from '../common/Popover'
import TimeSpan from '../model/TimeSpan'
import FavoriteDao from '../expand/dao/FavoriteDao'
import ProjectModel from '../model/ProjectModel'
import Utils from '../Util/FavoriteUtils'
import TimeUtil from '../Util/TimeUtil'
import ActionUtils from '../Util/ActionUtils'
import ViewUtils from '../Util/ViewUtils'
import MoreMenu,{MORE_MENU} from '../common/MoreMenu'
import {FLAG_TAB} from './HomPage'
import BaseComponent from './BaseComponent'
import CustomThemePage from './CustomThemePage'

var timeSpanTextArr = [
    new TimeSpan('今 天','since=daily'),
    new TimeSpan('本 周','since=weekly'),
    new TimeSpan('本 月','since=monthly')];

import LanguageDao ,{FLAG_LANGUAGE} from '../expand/dao/LanguageDao'

var favoriteDao = new FavoriteDao(FlAG_STORAGE.flag_trending)
var dataRepository = new DataRepository(FlAG_STORAGE.flag_trending);

export default class TrendingPage extends BaseComponent {

    constructor(props){
        super(props);
        this.languageDao = new LanguageDao(FLAG_LANGUAGE.flag_language);
        this.state ={
            isVisible: false,
            buttonRect: {},
            timeSpan: timeSpanTextArr[0],
            languages:[],
            theme:this.props.theme
        }
        this.loadData();
    }

    loadData(){
        this.languageDao.fetch()
            .then(result=>{
                this.setState({
                    languages:result
                })
            })
            .catch(error=>{
                console.log(error);
            });
    }

    renderTitleView(){
        return <View>
            <TouchableOpacity
                ref = 'button'
                onPress={()=>this.showPopover()}
            >
                <View style={{flexDirection:'row',alignItems:'center'}}>
                    <Text style={{fontSize:18,color:'white',fontWeight:'400'}}>语言趋势  {this.state.timeSpan.showText}</Text>
                    <Image
                        style={{width:14,height:14,marginLeft:6}}
                        source={require('../../res/images/ic_spinner_triangle.png')}/>
                </View>
            </TouchableOpacity>
        </View>
    }

    showPopover() {
        this.refs.button.measure((ox, oy, width, height, px, py) => {
            this.setState({
                isVisible: true,
                buttonRect: {x: px, y: py, width: width, height: height}
            });
        });
    }

    closePopover(){
        this.setState({
            isVisible:false,
        })
    }

    onSelectTimeSpan(timeSpan){
        this.setState({
            timeSpan:timeSpan,
            isVisible:false,
        })
    }

    renderNavRightButton(){
        return <View style={{flexDirection:'row'}}>
            {ViewUtils.createMoreButton(()=>this.refs.moreMenu.open())}
        </View>
    }

    renderMoreView(){
        let params = {...this.props,fromPage:FLAG_TAB.flag_popularTab}
        return <MoreMenu
            ref = "moreMenu"
            {...params}
            menus={[MORE_MENU.Custom_Language,MORE_MENU.Sort_Language,MORE_MENU.Custom_Theme]}
            anchorView={this.refs.moreMenuButton}
            onMoreMenuSelect={e=>{
                if (e=== MORE_MENU.Custom_Theme){
                    this.setState({
                        customThemeVisible:true,
                    })
                }
            }}
        />
    }

    renderCustomTheme(){
        return (
            <CustomThemePage
                visible={this.state.customThemeVisible}
                {...this.props}
                onClose = {()=>this.setState({customThemeVisible:false})}
            />

        )
    }

    render() {
        let content = this.state.languages.length>0?
            <ScrollableTableView
                tabBarBackgroundColor={this.state.theme.themeColor}
                tabBarInactiveTextColor="mintcream"
                tabBarActiveTextColor="white"
                tabBarUnderlineStyle={{backgroundColor:'#e7e7e7',height:2}}
                renderTabBar={()=><ScrollableTabBar/>}
            >
                {this.state.languages.map((result,i,arr)=>{
                    let item = arr[i];
                    return item.checked? <TrendingTabPage key={i} tabLabel={item.name} timeSpan={this.state.timeSpan} {...this.props} />:null;
                })}
            </ScrollableTableView>:null;

        let timeSpanView =
            <Popover
                isVisible = {this.state.isVisible}
                fromRect = {this.state.buttonRect}
                placement="bottom"
                onClose = {()=>this.closePopover()}
                contentStyle={{backgroundColor:'#343434',opacity:0.82}}
            >
                {timeSpanTextArr.map((result,i,arr)=>{
                    return <TouchableOpacity
                        key={i}
                        underlayColor='transparent={}'
                        onPress={()=>this.onSelectTimeSpan(arr[i])}
                    >
                        <View>
                            <Text
                                style={{fontSize:15,color:'white',fontWeight:'400',paddingTop:2,paddingLeft:6,paddingRight:6}}
                            >{arr[i].showText}</Text>
                        </View>

                    </TouchableOpacity>
                })}
            </Popover>

        return (
            <View style={styles.container}>
                <NavigationBar
                    titleView = {this.renderTitleView()}
                    style={this.state.theme.styles.navBar}
                    statusBar={{backgroundColor:'red'}}
                    rightButton={this.renderNavRightButton()}
                />
                {content}
                {timeSpanView}
                {this.renderMoreView()}
                {this.renderCustomTheme()}
            </View>
        );
    }
}

class TrendingTabPage extends Component{

    constructor(props){
        super(props);
        this.state={
            dataSource:new ListView.DataSource({rowHasChanged:(r1,r2)=>r1!==r2}),
            isLoading:false,
            favoriteKeys:[]
        }
    }

    componentDidMount() {
        this.loadData(this.props.timeSpan,true);
        this.listener = DeviceEventEmitter.addListener('favoriteChanged_trending',()=> {
            this.isFavoriteChanged = true;
        })
    }

    componentWillReceiveProps(nextProps) {
        if(nextProps.timeSpan !== this.props.timeSpan){
            this.loadData(nextProps.timeSpan)
        }else if(this.isFavoriteChanged){
            this.isFavoriteChanged = false
            this.getFavoriteKeys();

        }else if (nextProps!==this.state.theme){

            this.updateState({theme:nextProps.theme})
            this.flushFavoriteState();

        }else {

        }
    }

    componentWillUnmount() {
        if(this.listener){
            this.listener.remove();
        }
    }

    onSelectRepository(projectModel){
        var item = projectModel.item;
        this.props.navigator.push({
            title:item.fullName,
            component:DetailPage,
            params:{
                projectModel:projectModel,
                parentComponent:this,
                flag:FlAG_STORAGE.flag_trending,
                ...this.props
            }
        })
    }

    //跟新project item的 favorite状态
    flushFavoriteState(){

        let projectModels = [];

        if (this.items.length == 0){
            return;
        }

        let items = this.items;
        for (var i=0,len=items.length;i<len;i++){
            projectModels.push(new ProjectModel(items[i],Utils.checkFavorite(items[i],this.state.favoriteKeys)));
        }
        this.updateState({
            isLoading:false,
            dataSource:this.getDataSource(projectModels)
        })
    }

    getFavoriteKeys(){
        favoriteDao.getFavoriteKeys()
            .then(keys=>{
                if (keys){
                    this.updateState({favoriteKeys:keys})
                }
                this.flushFavoriteState();
            })
            .catch(e=>{
                this.flushFavoriteState();
            })
    }

    renderRow(projectModel){
        return <TrendingCell
            key = {projectModel.item.fullName}
            onSelect = {()=>this.onSelectRepository(projectModel)}
            projectModel={projectModel}
            theme={this.props.theme}
            onFavorite={(item,isFavorite)=>ActionUtils.onFavorite(favoriteDao,item,isFavorite,FlAG_STORAGE.flag_trending)}
        />
    }

    onRefresh(){
        this.loadData(this.props.timeSpan);
    }

    render(){
        return <View style={{flex:1}}>
            <ListView
                dataSource={this.state.dataSource}
                renderRow={(data)=>this.renderRow(data)}
                refreshControl={
                    <RefreshControl
                        refreshing={this.state.isLoading}
                        onRefresh={()=>this.onRefresh()}
                        colors={[this.props.theme.themeColor]}
                        tintColor={[this.props.theme.themeColor]}
                        titleColor={[this.props.theme.themeColor]}
                        title={'Loading'}
                    />}
            />
        </View>
    }

    updateState(dict){
        if(!this)return;
        this.setState(dict)
    }

    getFetchURL(timeSpan,category){
        return API_URL + category + '?' + timeSpan.searchText;
    }

    getDataSource(items) {
        return this.state.dataSource.cloneWithRows(items);
    }

    updateState(dict){
        if(!this)return;
        this.setState(dict)
    }

    loadData(timeSpan,isRefresh){

        this.updateState({
            isLoading:true
        })

        let url = this.getFetchURL(timeSpan,this.props.tabLabel);

        dataRepository.fetchRespository(url)

            .then((result)=>{
                this.items = result&&result.items?result.items:result?result:[];
                this.getFavoriteKeys();
                if(!this.items && result && result.update_date && !TimeUtil.checkDate(result.update_date)){
                    return dataRepository.fetchNetRepository(url);
                }
            })

            .then(items => {
                if(!items || items.length===0)return;
                this.items = items;
                this.getFavoriteKeys();
            })

            .catch(error=>{
                console.log(error);
                this.updateState({
                    isLoading:false
                })
            })
    }
}
const styles = StyleSheet.create({

    container: {
        flex: 1,
        backgroundColor: 'white',
    },
    tips:{
        color:'black'
    }
});<commit_msg>解决趋势页崩溃问题<commit_after>import React, { Component } from 'react';
import {
    StyleSheet,
    Text,
    View,
    Image,
    TextInput,
    ListView,
    RefreshControl,
    DeviceEventEmitter,
    TouchableOpacity
} from 'react-native';

import DataRepository,{FlAG_STORAGE} from '../expand/dao/DataRepository'
import NavigationBar from '../common/NavigationBar'
import DetailPage from './RepositoryDetailPage'

import ScrollableTableView,{ScrollableTabBar} from 'react-native-scrollable-tab-view'
import TrendingCell from '../common/TrendingCell'

const API_URL = 'https://github.com/trending/'
import Popover from '../common/Popover'
import TimeSpan from '../model/TimeSpan'
import FavoriteDao from '../expand/dao/FavoriteDao'
import ProjectModel from '../model/ProjectModel'
import Utils from '../Util/FavoriteUtils'
import TimeUtil from '../Util/TimeUtil'
import ActionUtils from '../Util/ActionUtils'
import ViewUtils from '../Util/ViewUtils'
import MoreMenu,{MORE_MENU} from '../common/MoreMenu'
import {FLAG_TAB} from './HomPage'
import BaseComponent from './BaseComponent'
import CustomThemePage from './CustomThemePage'

var timeSpanTextArr = [
    new TimeSpan('今 天','since=daily'),
    new TimeSpan('本 周','since=weekly'),
    new TimeSpan('本 月','since=monthly')];

import LanguageDao ,{FLAG_LANGUAGE} from '../expand/dao/LanguageDao'

var favoriteDao = new FavoriteDao(FlAG_STORAGE.flag_trending)
var dataRepository = new DataRepository(FlAG_STORAGE.flag_trending);

export default class TrendingPage extends BaseComponent {

    constructor(props){
        super(props);
        this.languageDao = new LanguageDao(FLAG_LANGUAGE.flag_language);
        this.state ={
            isVisible: false,
            buttonRect: {},
            timeSpan: timeSpanTextArr[0],
            languages:[],
            theme:this.props.theme
        }
        this.loadData();
    }

    loadData(){
        this.languageDao.fetch()
            .then(result=>{
                this.setState({
                    languages:result
                })
            })
            .catch(error=>{
                console.log(error);
            });
    }

    renderTitleView(){
        return <View>
            <TouchableOpacity
                ref = 'button'
                onPress={()=>this.showPopover()}
            >
                <View style={{flexDirection:'row',alignItems:'center'}}>
                    <Text style={{fontSize:18,color:'white',fontWeight:'400'}}>语言趋势  {this.state.timeSpan.showText}</Text>
                    <Image
                        style={{width:14,height:14,marginLeft:6}}
                        source={require('../../res/images/ic_spinner_triangle.png')}/>
                </View>
            </TouchableOpacity>
        </View>
    }

    showPopover() {
        this.refs.button.measure((ox, oy, width, height, px, py) => {
            this.setState({
                isVisible: true,
                buttonRect: {x: px, y: py, width: width, height: height}
            });
        });
    }

    closePopover(){
        this.setState({
            isVisible:false,
        })
    }

    onSelectTimeSpan(timeSpan){
        this.setState({
            timeSpan:timeSpan,
            isVisible:false,
        })
    }

    renderNavRightButton(){
        return <View style={{flexDirection:'row'}}>
            {ViewUtils.createMoreButton(()=>this.refs.moreMenu.open())}
        </View>
    }

    renderMoreView(){
        let params = {...this.props,fromPage:FLAG_TAB.flag_popularTab}
        return <MoreMenu
            ref = "moreMenu"
            {...params}
            menus={[MORE_MENU.Custom_Language,MORE_MENU.Sort_Language,MORE_MENU.Custom_Theme]}
            anchorView={this.refs.moreMenuButton}
            onMoreMenuSelect={e=>{
                if (e=== MORE_MENU.Custom_Theme){
                    this.setState({
                        customThemeVisible:true,
                    })
                }
            }}
        />
    }

    renderCustomTheme(){
        return (
            <CustomThemePage
                visible={this.state.customThemeVisible}
                {...this.props}
                onClose = {()=>this.setState({customThemeVisible:false})}
            />

        )
    }

    render() {
        let content = this.state.languages.length>0?
            <ScrollableTableView
                tabBarBackgroundColor={this.state.theme.themeColor}
                tabBarInactiveTextColor="mintcream"
                tabBarActiveTextColor="white"
                tabBarUnderlineStyle={{backgroundColor:'#e7e7e7',height:2}}
                renderTabBar={()=><ScrollableTabBar/>}
            >
                {this.state.languages.map((result,i,arr)=>{
                    let item = arr[i];
                    return item.checked? <TrendingTabPage key={i} tabLabel={item.name} timeSpan={this.state.timeSpan} {...this.props} />:null;
                })}
            </ScrollableTableView>:null;

        let timeSpanView =
            <Popover
                isVisible = {this.state.isVisible}
                fromRect = {this.state.buttonRect}
                placement="bottom"
                onClose = {()=>this.closePopover()}
                contentStyle={{backgroundColor:'#343434',opacity:0.82}}
            >
                {timeSpanTextArr.map((result,i,arr)=>{
                    return <TouchableOpacity
                        key={i}
                        underlayColor='transparent={}'
                        onPress={()=>this.onSelectTimeSpan(arr[i])}
                    >
                        <View>
                            <Text
                                style={{fontSize:15,color:'white',fontWeight:'400',paddingTop:2,paddingLeft:6,paddingRight:6}}
                            >{arr[i].showText}</Text>
                        </View>

                    </TouchableOpacity>
                })}
            </Popover>

        return (
            <View style={styles.container}>
                <NavigationBar
                    titleView = {this.renderTitleView()}
                    style={this.state.theme.styles.navBar}
                    statusBar={{backgroundColor:'red'}}
                    rightButton={this.renderNavRightButton()}
                />
                {content}
                {timeSpanView}
                {this.renderMoreView()}
                {this.renderCustomTheme()}
            </View>
        );
    }
}

class TrendingTabPage extends Component{

    constructor(props){
        super(props);
        this.state={
            dataSource:new ListView.DataSource({rowHasChanged:(r1,r2)=>r1!==r2}),
            isLoading:false,
            favoriteKeys:[]
        }
    }

    componentDidMount() {
        this.loadData(this.props.timeSpan,true);
        this.listener = DeviceEventEmitter.addListener('favoriteChanged_trending',()=> {
            this.isFavoriteChanged = true;
        })
    }

    componentWillReceiveProps(nextProps) {
        if(nextProps.timeSpan !== this.props.timeSpan){
            this.loadData(nextProps.timeSpan)
        }else if(this.isFavoriteChanged){
            this.isFavoriteChanged = false
            this.getFavoriteKeys();

        }else if (nextProps!==this.state.theme){

            this.updateState({theme:nextProps.theme})
            this.flushFavoriteState();

        }else {

        }
    }

    componentWillUnmount() {
        if(this.listener){
            this.listener.remove();
        }
    }

    onSelectRepository(projectModel){
        var item = projectModel.item;
        this.props.navigator.push({
            title:item.fullName,
            component:DetailPage,
            params:{
                projectModel:projectModel,
                parentComponent:this,
                flag:FlAG_STORAGE.flag_trending,
                ...this.props
            }
        })
    }

    //跟新project item的 favorite状态
    flushFavoriteState(){

        let projectModels = [];
        let items = this.items;
        //fix修正了item为空时候的bug
        if (!items){
            return;
        }
        for (var i=0,len=items.length;i<len;i++){
            projectModels.push(new ProjectModel(items[i],Utils.checkFavorite(items[i],this.state.favoriteKeys)));
        }
        this.updateState({
            isLoading:false,
            dataSource:this.getDataSource(projectModels)
        })
    }

    getFavoriteKeys(){
        favoriteDao.getFavoriteKeys()
            .then(keys=>{
                if (keys){
                    this.updateState({favoriteKeys:keys})
                }
                this.flushFavoriteState();
            })
            .catch(e=>{
                this.flushFavoriteState();
            })
    }

    renderRow(projectModel){
        return <TrendingCell
            key = {projectModel.item.fullName}
            onSelect = {()=>this.onSelectRepository(projectModel)}
            projectModel={projectModel}
            theme={this.props.theme}
            onFavorite={(item,isFavorite)=>ActionUtils.onFavorite(favoriteDao,item,isFavorite,FlAG_STORAGE.flag_trending)}
        />
    }

    onRefresh(){
        this.loadData(this.props.timeSpan);
    }

    render(){
        return <View style={{flex:1}}>
            <ListView
                dataSource={this.state.dataSource}
                renderRow={(data)=>this.renderRow(data)}
                refreshControl={
                    <RefreshControl
                        refreshing={this.state.isLoading}
                        onRefresh={()=>this.onRefresh()}
                        colors={[this.props.theme.themeColor]}
                        tintColor={[this.props.theme.themeColor]}
                        titleColor={[this.props.theme.themeColor]}
                        title={'Loading'}
                    />}
            />
        </View>
    }

    updateState(dict){
        if(!this)return;
        this.setState(dict)
    }

    getFetchURL(timeSpan,category){
        return API_URL + category + '?' + timeSpan.searchText;
    }

    getDataSource(items) {
        return this.state.dataSource.cloneWithRows(items);
    }

    updateState(dict){
        if(!this)return;
        this.setState(dict)
    }

    loadData(timeSpan,isRefresh){

        this.updateState({
            isLoading:true
        })

        let url = this.getFetchURL(timeSpan,this.props.tabLabel);

        dataRepository.fetchRespository(url)

            .then((result)=>{
                this.items = result&&result.items?result.items:result?result:[];
                this.getFavoriteKeys();
                if(!this.items && result && result.update_date && !TimeUtil.checkDate(result.update_date)){
                    return dataRepository.fetchNetRepository(url);
                }
            })

            .then(items => {
                if(!items || items.length===0)return;
                this.items = items;
                this.getFavoriteKeys();
            })

            .catch(error=>{
                console.log(error);
                this.updateState({
                    isLoading:false
                })
            })
    }
}
const styles = StyleSheet.create({

    container: {
        flex: 1,
        backgroundColor: 'white',
    },
    tips:{
        color:'black'
    }
});<|endoftext|>