<commit_before>'use strict';

var asyncreduce = require('asyncreduce');
var xtend = require('xtend');
var setImmediate = setImmediate || function (fn) { setTimeout(fn, 0) } ;

// most likely not overridden
function defaultCheck (pack, cb) {
  // info: { owner: owner, name: 'reponame', repoUrl: 'repo url if it was given' }
  // check needs to return null if nothing found
  // needs to return same or corrected login and/or repo

  // call back with error if something went wrong
  // call back with null if no url could be resolved
  // call back with url if it was resolved
  cb(null, null);
  //cb(null, 'some url');
}

// more likely overridden
function defaultFallbackCheck (pack, cb) {
  cb(null, 'fallback url');
}

function findUrl(check, fallbackCheck, trust, pack, cb) {
  if (trust && pack.repoUrl) return setImmediate(cb.bind(null, pack.repoUrl));

  check(pack, function (err, url) {
    if (err)  return cb(err);
    if (!url) return fallbackCheck(pack, cb);

    cb(null, url);
  });
}

var go = module.exports = function (opts, cb) {

  var packages      =  opts.packages;
  var logins        =  opts.logins;
  var trust         =  typeof opts.trust === 'undefined' ? true : opts.trust
  var check         =  opts.check         || defaultCheck;
  var fallbackCheck =  opts.fallbackCheck || defaultFallbackCheck;

  asyncreduce(
      packages
    , []
    , function url (acc, pack, cb_) {
        findUrl(check, fallbackCheck, trust, pack, function (err, url) {
          if (err) return cb_(err);
          acc.push(xtend(pack, { repoUrl: url }))
          cb_(null, acc);
        });
      }
    , cb
  )
};

// Test
if (!module.parent) {
  var tj = require('../test/fixtures/tj')

  var opts = {
      logins   :  require('./logins')(tj)
    , packages :  tj
    , trust    :  false
  };

  go(opts, function (err, repos) {
    if (err) return console.error(err);
    console.log('repos: ', repos.length);
  });
}
<commit_msg>removed defaultFallback<commit_after>'use strict';

var asyncreduce = require('asyncreduce');
var xtend = require('xtend');
var setImmediate = setImmediate || function (fn) { setTimeout(fn, 0) } ;

// most likely not overridden
function defaultCheck (pack, cb) {
  // info: { owner: owner, name: 'reponame', repoUrl: 'repo url if it was given' }
  // check needs to return null if nothing found
  // needs to return same or corrected login and/or repo

  // call back with error if something went wrong
  // call back with null if no url could be resolved
  // call back with url if it was resolved
  cb(null, 'some url');
}

function findUrl(check, fallbackCheck, trust, pack, cb) {
  if (trust && pack.repoUrl) return setImmediate(cb.bind(null, pack.repoUrl));

  check(pack, function (err, url) {
    if (err)  return cb(err);
    if (!url && fallbackCheck) return fallbackCheck(pack, cb);

    cb(null, url);
  });
}

var go = module.exports = function (opts, cb) {

  var packages      =  opts.packages;
  var logins        =  opts.logins;
  var trust         =  typeof opts.trust === 'undefined' ? true : opts.trust
  var check         =  opts.check || defaultCheck;

  // fallbackCheck can be provided to provide another alternative to find url
  var fallbackCheck =  opts.fallbackCheck;

  asyncreduce(
      packages
    , []
    , function url (acc, pack, cb_) {
        findUrl(check, fallbackCheck, trust, pack, function (err, url) {
          if (err) return cb_(err);
          acc.push(xtend(pack, { repoUrl: url }))
          cb_(null, acc);
        });
      }
    , cb
  )
};

// Test
if (!module.parent) {
  var tj = require('../test/fixtures/tj')

  var opts = {
      logins   :  require('./logins')(tj)
    , packages :  tj
    , trust    :  false
  };

  go(opts, function (err, repos) {
    if (err) return console.error(err);
    console.log('repos: ', repos);
  });
}
<|endoftext|>


<commit_before>var _ = require('lodash');

var kramed = require('kramed');
var renderer = require('kramed-markdown-renderer');

var sections = require('./sections');
var isExercise = require('./is_exercise');
var section2obj = require('./section2obj');

// Get nodes
function lex(content) {
    return kramed.lexer(content);
}

// Render lexed nodes back to markdown
function render(lexed) {
    // Options to parser
    var options = Object.create(kramed.defaults);
    options.renderer = renderer();

    if(!lexed.links) {
        lexed.links = {};
    }

    return kramed.parser(lexed, options);
}

function exerciseBlock(exercise) {
    return [
    '{% exercise %}',
    render(exercise.content),
    '{% initial %}',
    exercise.initial,
    '{% solution %}',
    exercise.solution,
    '{% validation %}',
    exercise.validation,
    '{% context %}',
    exerciseObj.context,
    '{% endexercise %}',
    ].join('\n');
}

function retroSection(section) {
    // Extract exercise data
    var exercise = section2obj(section);

    // Convert to new format
    var blockCode = newNode(exercise);

    return [
        // Use a html node so it's
        // Rawly injected during rendering
        {
            type: 'html',
            text: blockCode,
        }
    ];
}

// retro takes an input markdown file
// and outputs a new markdown file
// converting in the process old exercises to new ones
function retro(content) {
    // Lex incoming markdown file
    var lexed = lex(content);

    // Break it into sections
    var newSections = sections.split(lexed)
    .map(function(section) {
        // Leave non exercise sections untouched
        if(!isExercise(section)) {
            return section;
        }
        // Convert old section to new format
        return retroSection(section);
    });

    // Bring sections back together into a list of lexed nodes
    var newLexed = sections.join(newSections)

    // Render back to markdown
    return render(newLexed);
}

module.exports = retro;
<commit_msg>Fix exercise generation<commit_after>var _ = require('lodash');

var kramed = require('kramed');
var renderer = require('kramed-markdown-renderer');

var sections = require('./sections');
var isExercise = require('./is_exercise');
var section2obj = require('./section2obj');

// Get nodes
function lex(content) {
    return kramed.lexer(content);
}

// Render lexed nodes back to markdown
function render(lexed) {
    // Options to parser
    var options = Object.create(kramed.defaults);
    options.renderer = renderer();

    if(!lexed.links) {
        lexed.links = {};
    }

    return kramed.parser(lexed, options);
}

function exerciseBlock(exercise) {
    return [
    '{% exercise %}',
    render(exercise.content),
    '{% initial %}',
    exercise.code.initial,
    '{% solution %}',
    exercise.code.solution,
    '{% validation %}',
    exercise.code.validation,
    '{% context %}',
    exercise.code.context,
    '{% endexercise %}',
    ].join('\n');
}

function retroSection(section) {
    // Extract exercise data
    var exercise = section2obj(section);

    // Convert to new format
    var blockCode = newNode(exercise);

    return [
        // Use a html node so it's
        // Rawly injected during rendering
        {
            type: 'html',
            text: blockCode,
        }
    ];
}

// retro takes an input markdown file
// and outputs a new markdown file
// converting in the process old exercises to new ones
function retro(content) {
    // Lex incoming markdown file
    var lexed = lex(content);

    // Break it into sections
    var newSections = sections.split(lexed)
    .map(function(section) {
        // Leave non exercise sections untouched
        if(!isExercise(section)) {
            return section;
        }
        // Convert old section to new format
        return retroSection(section);
    });

    // Bring sections back together into a list of lexed nodes
    var newLexed = sections.join(newSections)

    // Render back to markdown
    return render(newLexed);
}

module.exports = retro;
<|endoftext|>


<commit_before>var MapModule = require("./map_module");
var indoors = require("../public/indoors/indoors");
var IndoorWatermarkController = require("./indoor_watermark_controller");

var IndoorsModule = function(emscriptenApi, mapController, mapId, indoorId, floorIndex) {

    var _emscriptenApi = emscriptenApi;
    var _mapController = mapController;

    var _activeIndoorMap = null;
    var _entrances = {};

    var _ready = false;
    var _pendingEnterTransition = null;
    var _transitioningToIndoorMap = false;

    var _indoorWatermarkController = new IndoorWatermarkController(mapId);

    var _startingIndoorId = indoorId;
    var _startingFloorIndex = floorIndex;

    var _this = this;

    var _createIndoorMapObject = function() {
        var mapId = _emscriptenApi.indoorsApi.getActiveIndoorMapId();
        var mapName = _emscriptenApi.indoorsApi.getActiveIndoorMapName();
        var sourceVendor = _emscriptenApi.indoorsApi.getActiveIndoorMapSourceVendor();
        var floorCount = _emscriptenApi.indoorsApi.getActiveIndoorMapFloorCount();
        var floors = _createFloorsArray(floorCount);
        var searchTags = _createSearchTagsArray();
        var exitFunc = _this.exit;
        var indoorMap = new indoors.IndoorMap(mapId, mapName, sourceVendor, floorCount, floors, searchTags, exitFunc);
        return indoorMap;
    };

    var _createFloorsArray = function(floorCount) {
        var floors = [];
        for (var i=0; i<floorCount; ++i) {
            var floorIndex = i;
            var floorName = _emscriptenApi.indoorsApi.getFloorName(i);
            var floorShortName = _emscriptenApi.indoorsApi.getFloorShortName(i);
            var floorNumber = _emscriptenApi.indoorsApi.getFloorNumber(i);

            var floorId = floorNumber;

            var floor = new indoors.IndoorMapFloor(floorId, floorIndex, floorName, floorShortName);
            floors.push(floor);
        }
        return floors;
    };

    var _createSearchTagsArray = function() {
        var userData;
        try {
            userData = JSON.parse(_emscriptenApi.indoorsApi.getActiveIndoorMapUserData());
        }
        catch (e) {
            return [];
        }

        if (typeof userData.search_menu_items !== "object") { return []; }
        if (!(userData.search_menu_items.items instanceof Array)) { return []; }

        var searchTags = [];
        userData.search_menu_items.items.forEach(function(item) {
            searchTags.push({
                name: item.name,
                search_tag: item.search_tag,
                icon_key: item.icon_key
            });
        });
        return searchTags;
    };

    var _executeIndoorMapEnteredCallbacks = function() {
        _activeIndoorMap = _createIndoorMapObject();
        _this.fire("indoormapenter", {indoorMap: _activeIndoorMap});
    };

    var _executeIndoorMapExitedCallbacks = function() {
        var indoorMap = _activeIndoorMap;
        _activeIndoorMap = null;
        _this.fire("indoormapexit", {indoorMap: indoorMap});
    };

    var _executeIndoorMapFloorChangedCallbacks = function() {
        _this.fire("indoormapfloorchange", {floor: _this.getFloor()});
    };

    var _executeIndoorMapEntranceAddedCallbacks = function(indoorMapId, indoorMapName, indoorMapLatLng) {
        // discard the altitude, as we're going to use the SDK IPointOnMap positioning to snap it to the terrain (this is now default)
        // alternative is to use the altitude, but use "elevationMode: heightAboveSeaLevel" when creating the indoor entrance marker
        var entrance = new indoors.IndoorMapEntrance(indoorMapId, indoorMapName, L.latLng([indoorMapLatLng.lat, indoorMapLatLng.lng]));
        _entrances[entrance.getIndoorMapId()] = entrance;
        _this.fire("indoorentranceadd", {entrance: entrance});
    };

    var _executeIndoorMapEntranceRemovedCallbacks = function(indoorMapId, indoorMapName, indoorMapLatLng) {
        var entrance = new indoors.IndoorMapEntrance(indoorMapId, indoorMapName, indoorMapLatLng);
        delete _entrances[entrance.getIndoorMapId()];
        _this.fire("indoorentranceremove", {entrance: entrance});
    };

    var _executeEntityClickedCallbacks = function(ids) {
        var idArray = ids.split("|");
        _this.fire("indoorentityclick", {ids: idArray});
    };

    var _onCollapseStart = function() {
        _this.fire("collapsestart");
    };

    var _onCollapse = function() {
        _this.fire("collapse");
    };

    var _onCollapseEnd = function() {
        _this.fire("collapseend");
    };

    var _onExpandStart = function() {
        _this.fire("expandstart");
    };

    var _onExpand = function() {
        _this.fire("expand");
    };

    var _onExpandEnd = function() {
        _this.fire("expandend");
    };

    var _enterIndoorMap = function(indoorMapId) {
        _emscriptenApi.indoorsApi.enterIndoorMap(indoorMapId);
    };

    var _transitionToIndoorMap = function(config) {

        _transitioningToIndoorMap = true;

        if (!_ready) {
            _pendingEnterTransition = config;
            return;
        }

        _emscriptenApi.cameraApi.setView({location: config.latLng, distance: config.distance, allowInterruption: false, headingDegrees: 0});
        _mapController._setIndoorTransitionCompleteEventListener(function() { _enterIndoorMap(config.indoorMapId); });

        _this.once("indoormapenter", function() {
            _transitioningToIndoorMap = false;
            var vendorKey = _activeIndoorMap.getIndoorMapSourceVendor();
            _indoorWatermarkController.showWatermarkForVendor(vendorKey);
        });
    };

    this.onInitialized = function() {
        _emscriptenApi.indoorsApi.registerIndoorMapEnteredCallback(_executeIndoorMapEnteredCallbacks);
        _emscriptenApi.indoorsApi.registerIndoorMapExitedCallback(_executeIndoorMapExitedCallbacks);
        _emscriptenApi.indoorsApi.registerIndoorMapFloorChangedCallback(_executeIndoorMapFloorChangedCallbacks);
        _emscriptenApi.indoorsApi.registerIndoorMapMarkerAddedCallback(_executeIndoorMapEntranceAddedCallbacks);
        _emscriptenApi.indoorsApi.registerIndoorMapMarkerRemovedCallback(_executeIndoorMapEntranceRemovedCallbacks);
        _emscriptenApi.highlightApi.registerEntityClickedCallback(_executeEntityClickedCallbacks);

        _emscriptenApi.expandFloorsApi.setCollapseStartCallback(_onCollapseStart);
        _emscriptenApi.expandFloorsApi.setCollapseCallback(_onCollapse);
        _emscriptenApi.expandFloorsApi.setCollapseEndCallback(_onCollapseEnd);
        _emscriptenApi.expandFloorsApi.setExpandStartCallback(_onExpandStart);
        _emscriptenApi.expandFloorsApi.setExpandCallback(_onExpand);
        _emscriptenApi.expandFloorsApi.setExpandEndCallback(_onExpandEnd);
    };

    this.onInitialStreamingCompleted = function() {
        _ready = true;

        if(_startingIndoorId)
        {
            var foundIndoorMap = this.enter(_startingIndoorId);

            if(foundIndoorMap === false)
            {
                // todo - this failing lint
                //console.error('Failed to enter indoor map. Please check your indoor Id is correct and the starting location is the same as your indoor map.');
            }

            if(_startingFloorIndex)
            {
                this.once("indoormapenter", function() { this.setFloor(_startingFloorIndex); });
            }
        }

        if (_pendingEnterTransition !== null) {
            _transitionToIndoorMap(_pendingEnterTransition);
            _pendingEnterTransition = null;
        }
    };

    this.exit = function() {
        if (_emscriptenApi.ready()) {
            _emscriptenApi.indoorsApi.exitIndoorMap();
            _indoorWatermarkController.hideWatermark();
        }
        _pendingEnterTransition = null;
        return this;
    };

    this.isIndoors = function() {
        return _activeIndoorMap !== null;
    };

    this.getActiveIndoorMap = function() {
        return _activeIndoorMap;
    };

    this.getFloor = function() {
        if (this.isIndoors()) {
            var index = _emscriptenApi.indoorsApi.getSelectedFloorIndex();
            return _activeIndoorMap.getFloors()[index];
        }
        return null;
    };

    this.setFloor = function(floor) {
        if (!this.isIndoors()) {
            return false;
        }

        var index = null;
        var floors = _activeIndoorMap.getFloors();

        if (typeof floor === "number") {
            index = floor;
        }
        else if (typeof floor === "object") {
            var floorIndex = floors.indexOf(floor);
            index = (floorIndex >= 0) ? floorIndex : null;
        }
        else if (typeof floor === "string") {
            for (var i=0; i<floors.length; ++i) {
                if (floors[i].getFloorShortName() === floor) {
                    index = i;
                    break;
                }
            }
        }

        if (index !== null) {
            return _emscriptenApi.indoorsApi.setSelectedFloorIndex(index);
        }

        return false;
    };

    this.moveUp = function(numberOfFloors) {
        var delta = (typeof numberOfFloors === "undefined") ? 1 : numberOfFloors;
        var thisFloor = this.getFloor();
        if (thisFloor === null) {
            return false;
        }
        return this.setFloor(thisFloor.getFloorIndex() + delta);
    };

    this.moveDown = function(numberOfFloors) {
        var delta = (typeof numberOfFloors === "undefined") ? -1 : -numberOfFloors;
        return this.moveUp(delta);
    };

    this.enter = function(indoorMap) {
        if (this.isIndoors() || _transitioningToIndoorMap) {
            return false;
        }

        var indoorMapId = null;
        if (typeof indoorMap === "object" && "getIndoorMapId" in indoorMap && typeof indoorMap["getIndoorMapId"] === "function") {
            indoorMapId = indoorMap.getIndoorMapId();
        }
        else if (typeof indoorMap === "string") {
            indoorMapId = indoorMap;
        }

        var entrance = _entrances[indoorMapId] || null;
        if (entrance === null) {
            return false;
        }

        var latLng = entrance.getLatLng();
        var distance = 400;

        var enterConfig = {
            latLng: latLng,
            distance: distance,
            indoorMapId: indoorMapId
        };

        _transitionToIndoorMap(enterConfig);

        return true;
    };

    this.getFloorInterpolation = function() {
        if (_activeIndoorMap !== null) {
            var floorParam = _emscriptenApi.expandFloorsApi.getFloorParam();
            var normalizedValue = floorParam / _activeIndoorMap.getFloorCount();
            return normalizedValue;
        }
        return 0;
    };

    this.getFloorHeightAboveSeaLevel = function(floorIndex) {
        if (this.isIndoors() &&
            floorIndex >= 0 &&
            floorIndex < _activeIndoorMap.getFloorCount())
        {
            return _emscriptenApi.indoorsApi.getFloorHeightAboveSeaLevel(floorIndex);
        }

        return null;
    };

    this.setFloorInterpolation = function(value) {
        if (_activeIndoorMap !== null) {
            var floorParam = value * _activeIndoorMap.getFloorCount();
            _emscriptenApi.expandFloorsApi.setFloorParam(floorParam);
        }
        return this;
    };

    this.setFloorFromInterpolation = function(interpolationParam) {
        if (_activeIndoorMap === null) {
            return false;
        }

        var t = (typeof interpolationParam === "undefined") ? this.getFloorInterpolation() : interpolationParam;
        var floorIndex = Math.round(t * _activeIndoorMap.getFloorCount());
        return this.setFloor(floorIndex);
    };

    this.expand = function() {
        _emscriptenApi.expandFloorsApi.expandIndoorMap();
        return this;
    };

    this.collapse = function() {
        _emscriptenApi.expandFloorsApi.collapseIndoorMap();
        return this;
    };

    this.setEntityHighlights = function(ids, color) {
        if (!_ready) return;
        _emscriptenApi.highlightApi.setEntityHighlights(ids, color);
    };

    this.clearEntityHighlights = function(ids) {
        if (!_ready) return;
        _emscriptenApi.highlightApi.clearEntityHighlights(ids);
    };
};

var IndoorsPrototype = L.extend({}, MapModule, L.Mixin.Events);

IndoorsModule.prototype = IndoorsPrototype;

module.exports = IndoorsModule;<commit_msg>Removing console error for failing to enter indoor map on startup. Buddy: Mark<commit_after>var MapModule = require("./map_module");
var indoors = require("../public/indoors/indoors");
var IndoorWatermarkController = require("./indoor_watermark_controller");

var IndoorsModule = function(emscriptenApi, mapController, mapId, indoorId, floorIndex) {

    var _emscriptenApi = emscriptenApi;
    var _mapController = mapController;

    var _activeIndoorMap = null;
    var _entrances = {};

    var _ready = false;
    var _pendingEnterTransition = null;
    var _transitioningToIndoorMap = false;

    var _indoorWatermarkController = new IndoorWatermarkController(mapId);

    var _startingIndoorId = indoorId;
    var _startingFloorIndex = floorIndex;

    var _this = this;

    var _createIndoorMapObject = function() {
        var mapId = _emscriptenApi.indoorsApi.getActiveIndoorMapId();
        var mapName = _emscriptenApi.indoorsApi.getActiveIndoorMapName();
        var sourceVendor = _emscriptenApi.indoorsApi.getActiveIndoorMapSourceVendor();
        var floorCount = _emscriptenApi.indoorsApi.getActiveIndoorMapFloorCount();
        var floors = _createFloorsArray(floorCount);
        var searchTags = _createSearchTagsArray();
        var exitFunc = _this.exit;
        var indoorMap = new indoors.IndoorMap(mapId, mapName, sourceVendor, floorCount, floors, searchTags, exitFunc);
        return indoorMap;
    };

    var _createFloorsArray = function(floorCount) {
        var floors = [];
        for (var i=0; i<floorCount; ++i) {
            var floorIndex = i;
            var floorName = _emscriptenApi.indoorsApi.getFloorName(i);
            var floorShortName = _emscriptenApi.indoorsApi.getFloorShortName(i);
            var floorNumber = _emscriptenApi.indoorsApi.getFloorNumber(i);

            var floorId = floorNumber;

            var floor = new indoors.IndoorMapFloor(floorId, floorIndex, floorName, floorShortName);
            floors.push(floor);
        }
        return floors;
    };

    var _createSearchTagsArray = function() {
        var userData;
        try {
            userData = JSON.parse(_emscriptenApi.indoorsApi.getActiveIndoorMapUserData());
        }
        catch (e) {
            return [];
        }

        if (typeof userData.search_menu_items !== "object") { return []; }
        if (!(userData.search_menu_items.items instanceof Array)) { return []; }

        var searchTags = [];
        userData.search_menu_items.items.forEach(function(item) {
            searchTags.push({
                name: item.name,
                search_tag: item.search_tag,
                icon_key: item.icon_key
            });
        });
        return searchTags;
    };

    var _executeIndoorMapEnteredCallbacks = function() {
        _activeIndoorMap = _createIndoorMapObject();
        _this.fire("indoormapenter", {indoorMap: _activeIndoorMap});
    };

    var _executeIndoorMapExitedCallbacks = function() {
        var indoorMap = _activeIndoorMap;
        _activeIndoorMap = null;
        _this.fire("indoormapexit", {indoorMap: indoorMap});
    };

    var _executeIndoorMapFloorChangedCallbacks = function() {
        _this.fire("indoormapfloorchange", {floor: _this.getFloor()});
    };

    var _executeIndoorMapEntranceAddedCallbacks = function(indoorMapId, indoorMapName, indoorMapLatLng) {
        // discard the altitude, as we're going to use the SDK IPointOnMap positioning to snap it to the terrain (this is now default)
        // alternative is to use the altitude, but use "elevationMode: heightAboveSeaLevel" when creating the indoor entrance marker
        var entrance = new indoors.IndoorMapEntrance(indoorMapId, indoorMapName, L.latLng([indoorMapLatLng.lat, indoorMapLatLng.lng]));
        _entrances[entrance.getIndoorMapId()] = entrance;
        _this.fire("indoorentranceadd", {entrance: entrance});
    };

    var _executeIndoorMapEntranceRemovedCallbacks = function(indoorMapId, indoorMapName, indoorMapLatLng) {
        var entrance = new indoors.IndoorMapEntrance(indoorMapId, indoorMapName, indoorMapLatLng);
        delete _entrances[entrance.getIndoorMapId()];
        _this.fire("indoorentranceremove", {entrance: entrance});
    };

    var _executeEntityClickedCallbacks = function(ids) {
        var idArray = ids.split("|");
        _this.fire("indoorentityclick", {ids: idArray});
    };

    var _onCollapseStart = function() {
        _this.fire("collapsestart");
    };

    var _onCollapse = function() {
        _this.fire("collapse");
    };

    var _onCollapseEnd = function() {
        _this.fire("collapseend");
    };

    var _onExpandStart = function() {
        _this.fire("expandstart");
    };

    var _onExpand = function() {
        _this.fire("expand");
    };

    var _onExpandEnd = function() {
        _this.fire("expandend");
    };

    var _enterIndoorMap = function(indoorMapId) {
        _emscriptenApi.indoorsApi.enterIndoorMap(indoorMapId);
    };

    var _transitionToIndoorMap = function(config) {

        _transitioningToIndoorMap = true;

        if (!_ready) {
            _pendingEnterTransition = config;
            return;
        }

        _emscriptenApi.cameraApi.setView({location: config.latLng, distance: config.distance, allowInterruption: false, headingDegrees: 0});
        _mapController._setIndoorTransitionCompleteEventListener(function() { _enterIndoorMap(config.indoorMapId); });

        _this.once("indoormapenter", function() {
            _transitioningToIndoorMap = false;
            var vendorKey = _activeIndoorMap.getIndoorMapSourceVendor();
            _indoorWatermarkController.showWatermarkForVendor(vendorKey);
        });
    };

    this.onInitialized = function() {
        _emscriptenApi.indoorsApi.registerIndoorMapEnteredCallback(_executeIndoorMapEnteredCallbacks);
        _emscriptenApi.indoorsApi.registerIndoorMapExitedCallback(_executeIndoorMapExitedCallbacks);
        _emscriptenApi.indoorsApi.registerIndoorMapFloorChangedCallback(_executeIndoorMapFloorChangedCallbacks);
        _emscriptenApi.indoorsApi.registerIndoorMapMarkerAddedCallback(_executeIndoorMapEntranceAddedCallbacks);
        _emscriptenApi.indoorsApi.registerIndoorMapMarkerRemovedCallback(_executeIndoorMapEntranceRemovedCallbacks);
        _emscriptenApi.highlightApi.registerEntityClickedCallback(_executeEntityClickedCallbacks);

        _emscriptenApi.expandFloorsApi.setCollapseStartCallback(_onCollapseStart);
        _emscriptenApi.expandFloorsApi.setCollapseCallback(_onCollapse);
        _emscriptenApi.expandFloorsApi.setCollapseEndCallback(_onCollapseEnd);
        _emscriptenApi.expandFloorsApi.setExpandStartCallback(_onExpandStart);
        _emscriptenApi.expandFloorsApi.setExpandCallback(_onExpand);
        _emscriptenApi.expandFloorsApi.setExpandEndCallback(_onExpandEnd);
    };

    this.onInitialStreamingCompleted = function() {
        _ready = true;

        if(_startingIndoorId)
        {
            this.enter(_startingIndoorId);

            if(_startingFloorIndex)
            {
                this.once("indoormapenter", function() { this.setFloor(_startingFloorIndex); });
            }
        }

        if (_pendingEnterTransition !== null) {
            _transitionToIndoorMap(_pendingEnterTransition);
            _pendingEnterTransition = null;
        }
    };

    this.exit = function() {
        if (_emscriptenApi.ready()) {
            _emscriptenApi.indoorsApi.exitIndoorMap();
            _indoorWatermarkController.hideWatermark();
        }
        _pendingEnterTransition = null;
        return this;
    };

    this.isIndoors = function() {
        return _activeIndoorMap !== null;
    };

    this.getActiveIndoorMap = function() {
        return _activeIndoorMap;
    };

    this.getFloor = function() {
        if (this.isIndoors()) {
            var index = _emscriptenApi.indoorsApi.getSelectedFloorIndex();
            return _activeIndoorMap.getFloors()[index];
        }
        return null;
    };

    this.setFloor = function(floor) {
        if (!this.isIndoors()) {
            return false;
        }

        var index = null;
        var floors = _activeIndoorMap.getFloors();

        if (typeof floor === "number") {
            index = floor;
        }
        else if (typeof floor === "object") {
            var floorIndex = floors.indexOf(floor);
            index = (floorIndex >= 0) ? floorIndex : null;
        }
        else if (typeof floor === "string") {
            for (var i=0; i<floors.length; ++i) {
                if (floors[i].getFloorShortName() === floor) {
                    index = i;
                    break;
                }
            }
        }

        if (index !== null) {
            return _emscriptenApi.indoorsApi.setSelectedFloorIndex(index);
        }

        return false;
    };

    this.moveUp = function(numberOfFloors) {
        var delta = (typeof numberOfFloors === "undefined") ? 1 : numberOfFloors;
        var thisFloor = this.getFloor();
        if (thisFloor === null) {
            return false;
        }
        return this.setFloor(thisFloor.getFloorIndex() + delta);
    };

    this.moveDown = function(numberOfFloors) {
        var delta = (typeof numberOfFloors === "undefined") ? -1 : -numberOfFloors;
        return this.moveUp(delta);
    };

    this.enter = function(indoorMap) {
        if (this.isIndoors() || _transitioningToIndoorMap) {
            return false;
        }

        var indoorMapId = null;
        if (typeof indoorMap === "object" && "getIndoorMapId" in indoorMap && typeof indoorMap["getIndoorMapId"] === "function") {
            indoorMapId = indoorMap.getIndoorMapId();
        }
        else if (typeof indoorMap === "string") {
            indoorMapId = indoorMap;
        }

        var entrance = _entrances[indoorMapId] || null;
        if (entrance === null) {
            return false;
        }

        var latLng = entrance.getLatLng();
        var distance = 400;

        var enterConfig = {
            latLng: latLng,
            distance: distance,
            indoorMapId: indoorMapId
        };

        _transitionToIndoorMap(enterConfig);

        return true;
    };

    this.getFloorInterpolation = function() {
        if (_activeIndoorMap !== null) {
            var floorParam = _emscriptenApi.expandFloorsApi.getFloorParam();
            var normalizedValue = floorParam / _activeIndoorMap.getFloorCount();
            return normalizedValue;
        }
        return 0;
    };

    this.getFloorHeightAboveSeaLevel = function(floorIndex) {
        if (this.isIndoors() &&
            floorIndex >= 0 &&
            floorIndex < _activeIndoorMap.getFloorCount())
        {
            return _emscriptenApi.indoorsApi.getFloorHeightAboveSeaLevel(floorIndex);
        }

        return null;
    };

    this.setFloorInterpolation = function(value) {
        if (_activeIndoorMap !== null) {
            var floorParam = value * _activeIndoorMap.getFloorCount();
            _emscriptenApi.expandFloorsApi.setFloorParam(floorParam);
        }
        return this;
    };

    this.setFloorFromInterpolation = function(interpolationParam) {
        if (_activeIndoorMap === null) {
            return false;
        }

        var t = (typeof interpolationParam === "undefined") ? this.getFloorInterpolation() : interpolationParam;
        var floorIndex = Math.round(t * _activeIndoorMap.getFloorCount());
        return this.setFloor(floorIndex);
    };

    this.expand = function() {
        _emscriptenApi.expandFloorsApi.expandIndoorMap();
        return this;
    };

    this.collapse = function() {
        _emscriptenApi.expandFloorsApi.collapseIndoorMap();
        return this;
    };

    this.setEntityHighlights = function(ids, color) {
        if (!_ready) return;
        _emscriptenApi.highlightApi.setEntityHighlights(ids, color);
    };

    this.clearEntityHighlights = function(ids) {
        if (!_ready) return;
        _emscriptenApi.highlightApi.clearEntityHighlights(ids);
    };
};

var IndoorsPrototype = L.extend({}, MapModule, L.Mixin.Events);

IndoorsModule.prototype = IndoorsPrototype;

module.exports = IndoorsModule;<|endoftext|>


<commit_before>import React from 'react'
import PropTypes from 'prop-types'
import { View, Text } from 'react-native'
import { List, ListItem, Button } from 'react-native-elements'
import { connect } from 'react-redux'
import { get, isEmpty } from 'lodash'

import INGLogo from '../../assets/ing-logo.png'
import router from '../../router'

import styles from './Accounts.styles'

const Accounts = ({ accounts, navigator }) => (
  <View style={styles.Home}>
    {!isEmpty(accounts) && (
      <List containerStyle={styles.AccountsList}>
        {accounts.map(({ _id, bankName, accountName, amount = 0 }) => (
          <ListItem
            key={_id}
            containerStyle={styles.ListItem}
            title={bankName}
            subtitle={accountName}
            avatar={INGLogo}
            rightTitle={`${amount.toFixed(2)} €`}
            onPress={() => navigator.push(router.getRoute('AccountSingle', { _id, accountName }))}
            roundAvatar
          />
        ))}
      </List>
    )}

    <Button
      icon={{ name: 'ios-add-circle-outline', type: 'ionicon' }}
      title='Add an account'
      onPress={() => navigator.push('AddNewAccount')}
      buttonStyle={styles.AddButton}
    />

    {isEmpty(accounts) && (
      <View style={styles.infos}>
        <Text style={styles.instructions}>
          Add an account before using app functionnalities
        </Text>
      </View>
    )}
  </View>
)

Accounts.propTypes = {
  dispatch: PropTypes.func.isRequired,
  accounts: PropTypes.array.isRequired,
  navigator: PropTypes.object.isRequired,
}

Accounts.route = {
  navigationBar: { title: 'Accounts' },
}

const mapStateToProps = ({ accounts }) => ({
  accounts: get(accounts, 'accounts', []),
})

export default connect(mapStateToProps)(Accounts)
<commit_msg>[UPD] AccountHome w/ ScrollView<commit_after>import React from 'react'
import PropTypes from 'prop-types'
import { ScrollView, View, Text } from 'react-native'
import { List, ListItem, Button } from 'react-native-elements'
import { connect } from 'react-redux'
import { get, isEmpty } from 'lodash'

import INGLogo from '../../assets/ing-logo.png'
import router from '../../router'

import styles from './Accounts.styles'

const Accounts = ({ accounts, navigator }) => (
  <ScrollView style={styles.Home}>
    {!isEmpty(accounts) && (
      <List containerStyle={styles.AccountsList}>
        {accounts.map(({ _id, bankName, accountName, amount = 0 }) => (
          <ListItem
            key={_id}
            containerStyle={styles.ListItem}
            title={bankName}
            subtitle={accountName}
            avatar={INGLogo}
            rightTitle={`${amount.toFixed(2)} €`}
            onPress={() => navigator.push(router.getRoute('AccountSingle', { _id, accountName }))}
            roundAvatar
          />
        ))}
      </List>
    )}

    <Button
      icon={{ name: 'ios-add-circle-outline', type: 'ionicon' }}
      title='Add an account'
      onPress={() => navigator.push('AddNewAccount')}
      buttonStyle={styles.AddButton}
    />

    {isEmpty(accounts) && (
      <View style={styles.infos}>
        <Text style={styles.instructions}>
          Add an account before using app functionnalities
        </Text>
      </View>
    )}
  </ScrollView>
)

Accounts.propTypes = {
  dispatch: PropTypes.func.isRequired,
  accounts: PropTypes.array.isRequired,
  navigator: PropTypes.object.isRequired,
}

Accounts.route = {
  navigationBar: { title: 'Accounts' },
}

const mapStateToProps = ({ accounts }) => ({
  accounts: get(accounts, 'accounts', []),
})

export default connect(mapStateToProps)(Accounts)
<|endoftext|>


<commit_before>/**
 * Role Management (ACL) for NodeJS
 *
 * User -> Profile -> Roles <- Application
 **/

// CONSTANTS
var APP_ROLE_SEPARATOR = ".";

// INCLUDES
var util = require("util"),
    events = require("events");

// VARIABLES
var applications = {},
    profiles = {},
    users = {},
    monitor;

function Monitor() {
	events.EventEmitter.call(this);
}
util.inherits(Monitor, events.EventEmitter);

function Application(name, roles) {
	this.name = name;
	this.roles = [];

	if (roles && roles.length) {
		this.addRoles.apply(this, roles);
	}
}
util.inherits(Application, events.EventEmitter);

Application.prototype.addRoles = function () {
	for (var i = 0; i < arguments.length; i++) {
		if (this.roles.indexOf(arguments[i]) == -1) {
			this.roles.push(arguments[i]);

			this.emit("role.add", arguments[i]);
			monitor.emit("app.addrole", this.name, arguments[i]);
		}
	}
	return this;
};
Application.prototype.removeRoles = function () {
	for (var i = 0; i < arguments.length; i++) {
		if (this.roles.indexOf(arguments[i]) != -1) {
			this.roles.splice(this.roles.indexOf(arguments[i]), 1);

			this.emit("role.del", arguments[i]);
			monitor.emit("app.delrole", this.name, arguments[i]);
		}
	}
	return this;
};

function Profile(name, roles) {
	this.name = name;
	this.roles = [];
	this.applications = [];

	if (roles && roles.length) {
		this.addRoles.apply(this, roles);
	}
}
Profile.prototype.addRoles = function () {
	for (var i = 0; i < arguments.length; i++) {
		var role = arguments[i];

		if (role.indexOf(APP_ROLE_SEPARATOR) <= 0) {
			throw new Error("A role must have an associated application");
		}

		role = role.split(APP_ROLE_SEPARATOR, 2);

		if (!applications.hasOwnProperty(role[0])) {
			throw new Error("Application not found");
		}

		if (role[1] == "*") {
			for (var j = 0; j < applications[role[0]].roles.length; j++) {
				this.__addRole(role[0], applications[role[0]].roles[j]);
			}

			if (this.applications.indexOf(role[0]) == -1) {
				applications[role[0]].on("role.add", this.__watchNewRoles(this, role[0]));
				applications[role[0]].on("role.del", this.__watchRemoveRoles(this, role[0]));

				this.applications.push(role[0]);
			}
		} else {
			if (applications[role[0]].roles.indexOf(role[1]) == -1) {
				if (monitor.listeners("role.invalid").length == 0) {
					throw new Error("Application " + role[0] + " does not have " + role[1] + " role");
				} else {
					monitor.emit("role.invalid", role[0], role[1]);
				}
			}

			this.__addRole(role[0], role[1]);
		}
	}
	return this;
};
Profile.prototype.removeRoles = function () {
	for (var i = 0; i < arguments.length; i++) {
		var role = arguments[i];

		if (role.indexOf(APP_ROLE_SEPARATOR) <= 0) {
			throw new Error("A role must have an associated application");
		}

		role = role.split(APP_ROLE_SEPARATOR, 2);

		if (!applications.hasOwnProperty(role[0])) {
			throw new Error("Application not found");
		}

		if (role[1] == "*") {
			for (var j = 0; j < applications[role[0]].roles.length; j++) {
				this.__removeRole(role[0], applications[role[0]].roles[j]);
			}

			if (this.applications.indexOf(role[0]) != -1) {
				applications[role[0]].removeListener("role.add", this.__watchNewRoles(this, role[0]));
				applications[role[0]].removeListener("role.del", this.__watchRemoveRoles(this, role[0]));

				this.applications.splice(this.applications.indexOf(role[0]), 1);
			}
		} else {
			if (applications[role[0]].roles.indexOf(role[1]) == -1) {
				if (monitor.listeners("role.invalid").length == 0) {
					throw new Error("Application " + role[0] + " does not have " + role[1] + " role");
				} else {
					monitor.emit("role.invalid", role[0], role[1]);
				}
			}

			this.__removeRole(role[0], role[1]);
		}
	}

	return this;
};
Profile.prototype.hasRoles = function () {
	for (var i = 0; i < arguments.length; i++) {
		var role = arguments[i];

		if (role.indexOf(APP_ROLE_SEPARATOR) <= 0) return false;

		role = role.split(APP_ROLE_SEPARATOR, 2);

		if (!applications.hasOwnProperty(role[0])) return false;

		if (role[1] == "*") {
			for (var j = 0; j < applications[role[0]].roles.length; j++) {
				if (this.roles.indexOf(role[0] + APP_ROLE_SEPARATOR + applications[role[0]].roles[j]) == -1) return false;
			}
		} else {
			if (applications[role[0]].roles.indexOf(role[1]) == -1) {
				if (monitor.listeners("role.invalid").length == 0) {
					throw new Error("Application " + role[0] + " does not have " + role[1] + " role");
				} else {
					monitor.emit("role.invalid", role[0], role[1]);
				}
			}
			if (this.roles.indexOf(role.join(APP_ROLE_SEPARATOR)) == -1) return false;
		}
	}

	return true;
};
Profile.prototype.hasAnyRoles = function () {
	for (var i = 0; i < arguments.length; i++) {
		var role = arguments[i];

		if (role.indexOf(APP_ROLE_SEPARATOR) <= 0) continue;

		role = role.split(APP_ROLE_SEPARATOR, 2);

		if (!applications.hasOwnProperty(role[0])) continue;

		if (role[1] == "*") {
			for (var j = 0; j < applications[role[0]].roles.length; j++) {
				if (this.roles.indexOf(role[0] + APP_ROLE_SEPARATOR + applications[role[0]].roles[j]) != -1) return true;
			}
		} else {
			if (applications[role[0]].roles.indexOf(role[1]) == -1) {
				if (monitor.listeners("role.invalid").length == 0) {
					throw new Error("Application " + role[0] + " does not have " + role[1] + " role");
				} else {
					monitor.emit("role.invalid", role[0], role[1]);
				}
			}
			if (this.roles.indexOf(role.join(APP_ROLE_SEPARATOR)) != -1) return true;
		}
	}

	return false;
};
Profile.prototype.__addRole = function (app, role) {
	var app_role = app + APP_ROLE_SEPARATOR + role;

	if (this.roles.indexOf(app_role) == -1) {
		this.roles.push(app_role);
		monitor.emit("profile.addrole", this.name, app, role);
	}
};
Profile.prototype.__removeRole = function (app, role) {
	var app_role = app + APP_ROLE_SEPARATOR + role;

	if (this.roles.indexOf(app_role) != -1) {
		this.roles.splice(this.roles.indexOf(app_role), 1);
		monitor.emit("profile.delrole", this.name, app, role);
	}
};
Profile.prototype.__watchNewRoles = function (profile, app) {
	return function (role) {
		var app_role = app + APP_ROLE_SEPARATOR + role;
		if (profile.roles.indexOf(app_role) == -1) {
			profile.roles.push(app_role);
		}
	};
};
Profile.prototype.__watchRemoveRoles = function (profile, app) {
	return function (role) {
		var app_role = app + APP_ROLE_SEPARATOR + role;
		if (profile.roles.indexOf(app_role) != -1) {
			profile.roles.splice(profile.roles.indexOf(app_role), 1);
		}
	};
};

function getApplication(name) {
	if (applications.hasOwnProperty(name)) {
		return applications[name];
	}
	return null;
}
function getProfile(name) {
	if (profiles.hasOwnProperty(name)) {
		return profiles[name];
	}
	return null;
}
function addApplication(name, roles) {
	var app = getApplication(name);

	if (app === null) {
		applications[name] = new Application(name, roles);
	} else if (roles && roles.length) {
		applications[name].addRoles(roles);
	}

	if (!module.exports.hasOwnProperty(name)) {
		module.exports[name] = application[name];
	}

	return applications[name];
}
function addProfile(name, roles) {
	var profile = getProfile(name);

	if (profile === null) {
		profiles[name] = new Profile(name, roles);
	} else if (roles && roles.length) {
		profiles[name].addRoles(roles);
	}

	if (!module.exports.hasOwnProperty(name)) {
		module.exports[name] = profiles[name];
	}

	return profiles[name];
}
function exportRoles() {
	var a = {}, p = {};

	for (app in applications) {
		if (applications.hasOwnProperty(app)) {
			a[app] = [];

			for (var i = 0; i < applications[app].roles.length; i++) {
				a[app].push(applications[app].roles[i]);
			}
		}
	}
	for (prof in profiles) {
		if (profiles.hasOwnProperty(prof)) {
			p[prof] = [];

			for (var i = 0; i < profiles[prof].roles.length; i++) {
				p[prof].push(profiles[prof].roles[i]);
			}
		}
	}
	return {
		applications: a,
		profiles: p
	};
}
function importRoles(data) {
	applications = {};
	profiles = {};

	for (app in data.applications) {
		if (data.applications.hasOwnProperty(app)) {
			applications[app] = new Application(app);
			applications[app].addRoles.apply(applications[app], data.applications[app]);
		}
	}
	for (prof in data.profiles) {
		if (data.profiles.hasOwnProperty(prof)) {
			profiles[prof] = new Profile(prof);
			profiles[prof].addRoles.apply(profiles[prof], data.profiles[prof]);
		}
	}
}

monitor = new Monitor();

module.exports = {
	addApplication: addApplication,
	    addProfile: addProfile,
	getApplication: getApplication,
	    getProfile: getProfile,
	        import: importRoles,
	        export: exportRoles,
	    getMonitor: function () { return monitor; },
	  setSeparator: function (sep) { APP_ROLE_SEPARATOR = sep; return this; }
};<commit_msg>Fix bug from previous commit about exposing applications to module.exports<commit_after>/**
 * Role Management (ACL) for NodeJS
 *
 * User -> Profile -> Roles <- Application
 **/

// CONSTANTS
var APP_ROLE_SEPARATOR = ".";

// INCLUDES
var util = require("util"),
    events = require("events");

// VARIABLES
var applications = {},
    profiles = {},
    users = {},
    monitor;

function Monitor() {
	events.EventEmitter.call(this);
}
util.inherits(Monitor, events.EventEmitter);

function Application(name, roles) {
	this.name = name;
	this.roles = [];

	if (roles && roles.length) {
		this.addRoles.apply(this, roles);
	}
}
util.inherits(Application, events.EventEmitter);

Application.prototype.addRoles = function () {
	for (var i = 0; i < arguments.length; i++) {
		if (this.roles.indexOf(arguments[i]) == -1) {
			this.roles.push(arguments[i]);

			this.emit("role.add", arguments[i]);
			monitor.emit("app.addrole", this.name, arguments[i]);
		}
	}
	return this;
};
Application.prototype.removeRoles = function () {
	for (var i = 0; i < arguments.length; i++) {
		if (this.roles.indexOf(arguments[i]) != -1) {
			this.roles.splice(this.roles.indexOf(arguments[i]), 1);

			this.emit("role.del", arguments[i]);
			monitor.emit("app.delrole", this.name, arguments[i]);
		}
	}
	return this;
};

function Profile(name, roles) {
	this.name = name;
	this.roles = [];
	this.applications = [];

	if (roles && roles.length) {
		this.addRoles.apply(this, roles);
	}
}
Profile.prototype.addRoles = function () {
	for (var i = 0; i < arguments.length; i++) {
		var role = arguments[i];

		if (role.indexOf(APP_ROLE_SEPARATOR) <= 0) {
			throw new Error("A role must have an associated application");
		}

		role = role.split(APP_ROLE_SEPARATOR, 2);

		if (!applications.hasOwnProperty(role[0])) {
			throw new Error("Application not found");
		}

		if (role[1] == "*") {
			for (var j = 0; j < applications[role[0]].roles.length; j++) {
				this.__addRole(role[0], applications[role[0]].roles[j]);
			}

			if (this.applications.indexOf(role[0]) == -1) {
				applications[role[0]].on("role.add", this.__watchNewRoles(this, role[0]));
				applications[role[0]].on("role.del", this.__watchRemoveRoles(this, role[0]));

				this.applications.push(role[0]);
			}
		} else {
			if (applications[role[0]].roles.indexOf(role[1]) == -1) {
				if (monitor.listeners("role.invalid").length == 0) {
					throw new Error("Application " + role[0] + " does not have " + role[1] + " role");
				} else {
					monitor.emit("role.invalid", role[0], role[1]);
				}
			}

			this.__addRole(role[0], role[1]);
		}
	}
	return this;
};
Profile.prototype.removeRoles = function () {
	for (var i = 0; i < arguments.length; i++) {
		var role = arguments[i];

		if (role.indexOf(APP_ROLE_SEPARATOR) <= 0) {
			throw new Error("A role must have an associated application");
		}

		role = role.split(APP_ROLE_SEPARATOR, 2);

		if (!applications.hasOwnProperty(role[0])) {
			throw new Error("Application not found");
		}

		if (role[1] == "*") {
			for (var j = 0; j < applications[role[0]].roles.length; j++) {
				this.__removeRole(role[0], applications[role[0]].roles[j]);
			}

			if (this.applications.indexOf(role[0]) != -1) {
				applications[role[0]].removeListener("role.add", this.__watchNewRoles(this, role[0]));
				applications[role[0]].removeListener("role.del", this.__watchRemoveRoles(this, role[0]));

				this.applications.splice(this.applications.indexOf(role[0]), 1);
			}
		} else {
			if (applications[role[0]].roles.indexOf(role[1]) == -1) {
				if (monitor.listeners("role.invalid").length == 0) {
					throw new Error("Application " + role[0] + " does not have " + role[1] + " role");
				} else {
					monitor.emit("role.invalid", role[0], role[1]);
				}
			}

			this.__removeRole(role[0], role[1]);
		}
	}

	return this;
};
Profile.prototype.hasRoles = function () {
	for (var i = 0; i < arguments.length; i++) {
		var role = arguments[i];

		if (role.indexOf(APP_ROLE_SEPARATOR) <= 0) return false;

		role = role.split(APP_ROLE_SEPARATOR, 2);

		if (!applications.hasOwnProperty(role[0])) return false;

		if (role[1] == "*") {
			for (var j = 0; j < applications[role[0]].roles.length; j++) {
				if (this.roles.indexOf(role[0] + APP_ROLE_SEPARATOR + applications[role[0]].roles[j]) == -1) return false;
			}
		} else {
			if (applications[role[0]].roles.indexOf(role[1]) == -1) {
				if (monitor.listeners("role.invalid").length == 0) {
					throw new Error("Application " + role[0] + " does not have " + role[1] + " role");
				} else {
					monitor.emit("role.invalid", role[0], role[1]);
				}
			}
			if (this.roles.indexOf(role.join(APP_ROLE_SEPARATOR)) == -1) return false;
		}
	}

	return true;
};
Profile.prototype.hasAnyRoles = function () {
	for (var i = 0; i < arguments.length; i++) {
		var role = arguments[i];

		if (role.indexOf(APP_ROLE_SEPARATOR) <= 0) continue;

		role = role.split(APP_ROLE_SEPARATOR, 2);

		if (!applications.hasOwnProperty(role[0])) continue;

		if (role[1] == "*") {
			for (var j = 0; j < applications[role[0]].roles.length; j++) {
				if (this.roles.indexOf(role[0] + APP_ROLE_SEPARATOR + applications[role[0]].roles[j]) != -1) return true;
			}
		} else {
			if (applications[role[0]].roles.indexOf(role[1]) == -1) {
				if (monitor.listeners("role.invalid").length == 0) {
					throw new Error("Application " + role[0] + " does not have " + role[1] + " role");
				} else {
					monitor.emit("role.invalid", role[0], role[1]);
				}
			}
			if (this.roles.indexOf(role.join(APP_ROLE_SEPARATOR)) != -1) return true;
		}
	}

	return false;
};
Profile.prototype.__addRole = function (app, role) {
	var app_role = app + APP_ROLE_SEPARATOR + role;

	if (this.roles.indexOf(app_role) == -1) {
		this.roles.push(app_role);
		monitor.emit("profile.addrole", this.name, app, role);
	}
};
Profile.prototype.__removeRole = function (app, role) {
	var app_role = app + APP_ROLE_SEPARATOR + role;

	if (this.roles.indexOf(app_role) != -1) {
		this.roles.splice(this.roles.indexOf(app_role), 1);
		monitor.emit("profile.delrole", this.name, app, role);
	}
};
Profile.prototype.__watchNewRoles = function (profile, app) {
	return function (role) {
		var app_role = app + APP_ROLE_SEPARATOR + role;
		if (profile.roles.indexOf(app_role) == -1) {
			profile.roles.push(app_role);
		}
	};
};
Profile.prototype.__watchRemoveRoles = function (profile, app) {
	return function (role) {
		var app_role = app + APP_ROLE_SEPARATOR + role;
		if (profile.roles.indexOf(app_role) != -1) {
			profile.roles.splice(profile.roles.indexOf(app_role), 1);
		}
	};
};

function getApplication(name) {
	if (applications.hasOwnProperty(name)) {
		return applications[name];
	}
	return null;
}
function getProfile(name) {
	if (profiles.hasOwnProperty(name)) {
		return profiles[name];
	}
	return null;
}
function addApplication(name, roles) {
	var app = getApplication(name);

	if (app === null) {
		applications[name] = new Application(name, roles);
	} else if (roles && roles.length) {
		applications[name].addRoles(roles);
	}

	if (!module.exports.hasOwnProperty(name)) {
		module.exports[name] = applications[name];
	}

	return applications[name];
}
function addProfile(name, roles) {
	var profile = getProfile(name);

	if (profile === null) {
		profiles[name] = new Profile(name, roles);
	} else if (roles && roles.length) {
		profiles[name].addRoles(roles);
	}

	if (!module.exports.hasOwnProperty(name)) {
		module.exports[name] = profiles[name];
	}

	return profiles[name];
}
function exportRoles() {
	var a = {}, p = {};

	for (app in applications) {
		if (applications.hasOwnProperty(app)) {
			a[app] = [];

			for (var i = 0; i < applications[app].roles.length; i++) {
				a[app].push(applications[app].roles[i]);
			}
		}
	}
	for (prof in profiles) {
		if (profiles.hasOwnProperty(prof)) {
			p[prof] = [];

			for (var i = 0; i < profiles[prof].roles.length; i++) {
				p[prof].push(profiles[prof].roles[i]);
			}
		}
	}
	return {
		applications: a,
		profiles: p
	};
}
function importRoles(data) {
	applications = {};
	profiles = {};

	for (app in data.applications) {
		if (data.applications.hasOwnProperty(app)) {
			applications[app] = new Application(app);
			applications[app].addRoles.apply(applications[app], data.applications[app]);
		}
	}
	for (prof in data.profiles) {
		if (data.profiles.hasOwnProperty(prof)) {
			profiles[prof] = new Profile(prof);
			profiles[prof].addRoles.apply(profiles[prof], data.profiles[prof]);
		}
	}
}

monitor = new Monitor();

module.exports = {
	addApplication: addApplication,
	    addProfile: addProfile,
	getApplication: getApplication,
	    getProfile: getProfile,
	        import: importRoles,
	        export: exportRoles,
	    getMonitor: function () { return monitor; },
	  setSeparator: function (sep) { APP_ROLE_SEPARATOR = sep; return this; }
};<|endoftext|>


<commit_before>'use strict'

import {
  default as _,
  forEach,
  map
} from 'lodash'
import includes from 'core-js/fn/array/includes'
import find from 'core-js/fn/array/find'
import repeat from 'core-js/fn/string/repeat'
import clone from 'clone'
import pick from 'object-pick'
import merge from 'deep-extend'
import esprima from 'esprima'
import escodegen from 'escodegen'

export {
  getOptions,
  replaceRefs,
  replaceFunctions,
  coerceVal,
  replaceChildSyntax,
  replaceFirebaseIdentifiers,
  parse
}

const SPECIAL_KEYS = ['.functions', '.refs', '.parent']
const SNAPSHOT_IDENTIFIERS = {
  next: 'newData',
  prev: 'data',
  root: 'root'
}
const IGNORE_IDENTIFIERS = ['auth']

/**
 * Check if a node contains a CallExpression identifier
 *
 * @param {object} node
 * @param {string} name
 * @returns {boolean}
 */
function hasCallExpressionIdentifier (rootNode, name) {
  let found = false
  function walk (node) {
    if (node.type === 'CallExpression' && node.callee.property.name === name) {
      found = true
    } else {
      forEach(node, function (child) {
        if (Object.isObject(child)) {
          walk(...arguments)
        }
      })
    }
  }
  walk(rootNode)
  return found
}

/**
 * Get the root object from from a MemberExpresion
 *
 * @param {object} node
 * @returns {object}
 */
function getMemberExpressionRootName (memberNode) {
  let name = null
  function walk (node) {
    if (node.type === 'Identifier') {
      name = node.name
    } else {
      forEach(node, (child, key) => {
        if (Object.isObject(child) && !Array.isArray(child) && key !== 'property') {
          walk(child)
        }
      })
    }
  }
  walk(memberNode)
  return name
}

/**
 * Return a escodegen string from an esprima syntax object
 * and remove the trailing ";"
 *
 * @param {object} syntax
 * @returns {string}
 */
function cleanSyntax (syntax) {
  return escodegen.generate(syntax).replace(/;$/, '')
}

/**
 * Expand a .refs map of strings to objects
 *
 * @param {object} refs
 */
function expandRefs (refs) {
  forEach(refs, (ref, key) => {
    if (typeof ref === 'string') {
      ref = refs[key] = { value: ref }
    }
    ref.depth = typeof ref.depth === 'number' ? ++ref.depth : 0
  })
}

/**
 * Expand a .functions map of strings to objects
 *
 * @param {object} functions
 */
function expandFunctions (functions) {
  forEach(functions, (fn, fnName) => {
    let syntax
    try {
      syntax = esprima.parse(fnName).body[0]
    } catch(e) {
      throw new Error(`Invalid .function declaration: ${fnName}`)
    }
    if (syntax.type !== 'ExpressionStatement' || syntax.expression.type !== 'CallExpression') {
      throw new Error(`Invalid .function declaration: ${fnName}`)
    }
    syntax.expression.arguments.forEach(arg => {
      if (arg.type !== 'Identifier') {
        throw new Error(`Invalid .function declaration: ${fnName}`)
      }
    })
    if (typeof fn === 'string') {
      fn = functions[fnName] = {
        body: fn,
        name: syntax.expression.callee.name,
        args: syntax.expression.arguments.map(arg => arg.name)
      }
    }
  })
}

/**
 * Get the options for a ruleset.
 * They are recursively merged down.
 *
 * @param {object} rules - will be mutated to remove SPECIAL_KEYS
 * @param {object} [options] - options from the parent ruleset
 * @returns {object}
 */
function getOptions (rules, options={}) {
  // Merge defaults, the previous options,
  // and the current options
  const prev = pick(options, SPECIAL_KEYS)
  const next = pick(rules, SPECIAL_KEYS)
  options = merge({}, {
    '.functions': {},
    '.refs': {}
  }, prev, next)
  const {
    '.functions': functions,
    '.refs': refs,
    '.parent': parent
  } = options
  // Remove the options from the rule set
  forEach(SPECIAL_KEYS, key => delete rules[key])
  // See if there is a parent ruleset
  if (parent) {
    // Check to see if any parent was a $wildcard
    forEach(parent, (value, key) => {
      if (/^\$/.test(key)) {
        refs[key] = 'prev'
      }
    })
  }
  // Refs
  expandRefs(refs)
  // Functions
  expandFunctions(functions)
  // Misc
  options['.parent'] = rules
  return options
}

/**
 * Replace "^REF_NAME" with the necessary snapshot method
 * chains to reach the specified child
 *
 * "REF_NAME" will be replaced with the value of "REF_NAME"
 * in the first matched ".refs" object plus the required
 * numbet of .parent() calls
 *
 * Alternatley, "^REF_NAME(next|prev)" syntax can be used and
 * will override the value of .refs[REF_NAME]
 *
 * @param {string} value
 * @param {object} options
 * @returns {string}
 */
function replaceRefs (value, options) {
  const {'.refs': refs} = options
  forEach(refs, (ref, name) => {
    name = RegExp.escape(name)
    const pattern = new RegExp(`\\^${name}(?:\\((next|prev)\\))?`, 'g')
    value = value.replace(pattern, (match, keyword) => {
      const snapshot = keyword || ref.value
      const parents = repeat('.parent()', ref.depth)
      return match.replace(match, `${snapshot}${parents}`)
    })
  })
  return value
}

/**
 * Replace function () calls inside a rule expression
 *
 * @param {string} value
 * @param {object} options
 * @returns {object}
 */
function replaceFunctions (value, options) {
  const {'.functions': fns} = options
  // Get a list of available fn names
  const fnNames = map(fns, fn => fn.name)
  // Parse the rule expression
  const valueSyntax = esprima.parse(value)
  /**
   * Walk the .function body and replace argument
   * identifiers with the specified value
   *
   * @param {object} node
   * @param {string} key
   * @param {object} parentNode
   * @param {object} fn - a function definition from the .functions map
   * @param {array} args - an array of esprima nodes that were passed
   *  passed into the function call
   */
  function walkFn (node, key, parentNode, fn, args) {
    // We found an identifier — see if it is one of the fn args
    if (node.type === 'Identifier' && includes(fn.args, node.name)) {
      // Get the argument
      const arg = args[fn.args.indexOf(node.name)]
      // Replace the identifier with the supplied argument node
      parentNode[key] = arg
    } else {
      // Keep walking
      forEach(node, function (child, childKey) {
        // Only objects that arent "property"
        if (Object.isObject(child)) {
          if (childKey === 'property' && node.computed === false) return
          walkFn(...[...arguments].concat(fn, [args]))
        }
      })
    }
  }
  /**
   * Walk the rule expression for CallExpression nodes that are included
   * in "fnNames" and then replace them with the fn.body
   *
   * @param {object} node
   * @param {string} key
   * @param {object} parentNode
   */
  function walkValue (node, key, parentNode) {
    // A CallExpression that matches one of the fnNames to be replaced
    if (node.type === 'CallExpression' && includes(fnNames, node.callee.name)) {
      // Get the fn definition by name
      const fn = find(Object.values(fns), ({name}) => name === node.callee.name)
      // Parse the fn definition
      const fnSyntax = esprima.parse(replaceFunctions(fn.body, options).code)
      // Walk the definition and replace the argument identifiers with the
      // provided arguments
      walkFn(fnSyntax.body[0], key, parentNode, fn, node.arguments)
      // Replace the CallExpression with a node that represents the fn.body
      parentNode[key] = fnSyntax.body[0].expression
    } else {
      // Keep walking
      // Can't use pick because we need to keep references
      // to nodes inside valueSyntax
      forEach(node, function (child) {
        if (Object.isObject(child) && !Array.isArray(child)) {
          walkValue(...arguments)
        }
      })
    }
  }
  walkValue(valueSyntax.body[0])
  return {
    syntax: valueSyntax,
    code: cleanSyntax(valueSyntax)
  }
}

/**
 * Append .val() if an expression is next to an operator
 * or in an array like child selector
 *
 * @param {string} value
 * @returns {object}
 */
function coerceVal (value) {
  const syntax = esprima.parse(value)
  /**
   * Walk the rule expression and append .val() to MemberExpressions
   *
   * @param {object} node
   * @param {string} key
   * @param {object} parentNode
   * @param {string} parentKey
   */
  function walk (node) {
    if (node.type === 'MemberExpression' || node.type === 'Identifier') {
      const name = getMemberExpressionRootName(node)
      // Only append .val() to MemberExpression that start
      // with a Firebase identifier
      if (!SNAPSHOT_IDENTIFIERS.hasOwnProperty(name)) {
        return
      }
      // Don't append .val() if it already exists
      if (hasCallExpressionIdentifier(node, 'val')) {
        return
      }
      const newNode = clone(node)
      node.type = 'CallExpression'
      node.callee = {
        type: 'MemberExpression',
        computed: false,
        object: newNode,
        property: {
          type: 'Identifier',
          name: 'val'
        }
      }
      node.arguments = []
      delete node.object
      delete node.property
    } else {
      forEach(node, function (child, childKey) {
        if (Object.isObject(child)) {
          if (node.type === 'CallExpression' && childKey === 'callee') {
            return
          }
          walk(...arguments)
        }
      })
    }
  }
  walk(syntax.body[0])
  return {
    syntax,
    code: cleanSyntax(syntax)
  }
}

/**
 * Replace dot/bracket syntax with the necessary
 * snapshot.child() calls
 *
 * @param {string} value
 * @returns {object}
 */
function replaceChildSyntax (value) {
  // Parse the rule expression
  const syntax = esprima.parse(value)
  /**
   * Check a MemberExpression to see if the root
   * is a Firebase identifier — if it is, return the node,
   * otherwise replaceChildSyntax()
   *
   * @param {object} node
   * @returns {object}
   */
  function filterMemeberExpression (node) {
    const name = getMemberExpressionRootName(node)
    if (includes(IGNORE_IDENTIFIERS, name)) {
      return node
    } else {
      return replaceChildSyntax(escodegen.generate(node)).syntax.body[0].expression
    }
  }
  /**
   * Walk the rule expression for Identifier nodes and convert parentNode
   * to a CallExpression: .child(Identifier)
   *
   * @param {object} node
   * @param {string} key
   * @param {object} parentNode
   * @param {string} parentKey
   */
  function walkValue (node, key, parentNode, parentKey) {
    const isCallIdentifier = node.type === 'Identifier' && parentKey === 'callee'
    const isParentComputed = parentNode && parentNode.computed === true
    // Only look at Identifiers that are properties
    // and not part of a CallExpression
    if (key === 'property' && (!isCallIdentifier || isParentComputed)) {
      const isIdentifier = parentNode.property.type === 'Identifier'
      parentNode.computed = false
      let newNode = clone(parentNode)
      let proposedArgument = newNode.property
      let argument
      // MemberExprssions
      if (proposedArgument.type === 'MemberExpression') {
        argument = filterMemeberExpression(proposedArgument)
      } else {
        // Identifier / Literal
        // If the node was computed, make sure the CallExpression
        // argument is an Identifier and not a Literal
        let argumentValue = isIdentifier
          ? proposedArgument.name
          : proposedArgument.value
        argument = isParentComputed && isIdentifier
          ? { type: 'Identifier', name: argumentValue }
          : { type: 'Literal', value: argumentValue }
      }
      // Convert the parent to a CallExpression
      parentNode.type = 'CallExpression'
      parentNode.callee = newNode
      parentNode.arguments = [argument]
      delete parentNode.object
      delete parentNode.property
      proposedArgument.name = 'child'
    } else {
      // Keep walking
      if (node.type === 'CallExpression') {
        node.arguments = node.arguments.map(argument => {
          if (argument.type === 'MemberExpression' || node.type === 'CallExpression') {
            return filterMemeberExpression(argument)
          }
          return argument
        })
      }
      forEach(node, function (child) {
        if (Object.isObject(child) && !Array.isArray(child)) {
          // Ignore some identifiers
          if (node.type === 'MemberExpression') {
            const name = getMemberExpressionRootName(node)
            if (includes(IGNORE_IDENTIFIERS, name)) return
          }
          walkValue(...[...arguments].concat(key))
        }
      })
    }
  }
  walkValue(syntax.body[0])
  return {
    syntax,
    code: cleanSyntax(syntax)
  }
}

/**
 * Replace items in the SNAPSHOT_IDENTIFIERS map
 *
 * @param {string} value
 * @returns {object}
 */
function replaceFirebaseIdentifiers (value) {
  const syntax = esprima.parse(value)
  /**
   * Walk the rule expression and rename Identifier nodes that are the root of
   * an expresston and match identifers in SNAPSHOT_IDENTIFIERS
   *
   * @param {object} node
   * @param {string} key
   * @param {object} parentNode
   * @param {string} parentKey
   */
  function walk (node, key) {
    if (node.type === 'Identifier' && key !== 'property') {
      const name = getMemberExpressionRootName(node)
      if (SNAPSHOT_IDENTIFIERS.hasOwnProperty(name)) {
        node.name = SNAPSHOT_IDENTIFIERS[name]
      }
    } else {
      forEach(node, function (child) {
        if (Object.isObject(child)) {
          walk(...[...arguments].concat(key))
        }
      })
    }
  }
  walk(syntax.body[0])
  return {
    syntax,
    code: cleanSyntax(syntax)
  }
}

/**
 * Parse a collection of rulesets and replace all Butane syntax
 *
 * @param {object} rules
 * @param {object} [options]
 * @returns {object}
 */
function parse (rules, options={}) {
  options = getOptions(rules, options)
  forEach(rules, (rule, key) => {
    switch (typeof rule) {
    case 'object':
      parse(rule, options)
      break
    case 'string':
      rule = replaceRefs(rule, options)
      rule = replaceFunctions(rule, options).code
      rule = coerceVal(rule).code
      rule = replaceChildSyntax(rule).code
      rule = replaceFirebaseIdentifiers(rule).code
      rules[key] = rule
      break
    }
  })
  return rules
}
<commit_msg>Use Object.values + [].map instead of _.map<commit_after>'use strict'

import {
  default as _,
  forEach
} from 'lodash'
import includes from 'core-js/fn/array/includes'
import find from 'core-js/fn/array/find'
import repeat from 'core-js/fn/string/repeat'
import clone from 'clone'
import pick from 'object-pick'
import merge from 'deep-extend'
import esprima from 'esprima'
import escodegen from 'escodegen'

export {
  getOptions,
  replaceRefs,
  replaceFunctions,
  coerceVal,
  replaceChildSyntax,
  replaceFirebaseIdentifiers,
  parse
}

const SPECIAL_KEYS = ['.functions', '.refs', '.parent']
const SNAPSHOT_IDENTIFIERS = {
  next: 'newData',
  prev: 'data',
  root: 'root'
}
const IGNORE_IDENTIFIERS = ['auth']

/**
 * Check if a node contains a CallExpression identifier
 *
 * @param {object} node
 * @param {string} name
 * @returns {boolean}
 */
function hasCallExpressionIdentifier (rootNode, name) {
  let found = false
  function walk (node) {
    if (node.type === 'CallExpression' && node.callee.property.name === name) {
      found = true
    } else {
      forEach(node, function (child) {
        if (Object.isObject(child)) {
          walk(...arguments)
        }
      })
    }
  }
  walk(rootNode)
  return found
}

/**
 * Get the root object from from a MemberExpresion
 *
 * @param {object} node
 * @returns {object}
 */
function getMemberExpressionRootName (memberNode) {
  let name = null
  function walk (node) {
    if (node.type === 'Identifier') {
      name = node.name
    } else {
      forEach(node, (child, key) => {
        if (Object.isObject(child) && !Array.isArray(child) && key !== 'property') {
          walk(child)
        }
      })
    }
  }
  walk(memberNode)
  return name
}

/**
 * Return a escodegen string from an esprima syntax object
 * and remove the trailing ";"
 *
 * @param {object} syntax
 * @returns {string}
 */
function cleanSyntax (syntax) {
  return escodegen.generate(syntax).replace(/;$/, '')
}

/**
 * Expand a .refs map of strings to objects
 *
 * @param {object} refs
 */
function expandRefs (refs) {
  forEach(refs, (ref, key) => {
    if (typeof ref === 'string') {
      ref = refs[key] = { value: ref }
    }
    ref.depth = typeof ref.depth === 'number' ? ++ref.depth : 0
  })
}

/**
 * Expand a .functions map of strings to objects
 *
 * @param {object} functions
 */
function expandFunctions (functions) {
  forEach(functions, (fn, fnName) => {
    let syntax
    try {
      syntax = esprima.parse(fnName).body[0]
    } catch(e) {
      throw new Error(`Invalid .function declaration: ${fnName}`)
    }
    if (syntax.type !== 'ExpressionStatement' || syntax.expression.type !== 'CallExpression') {
      throw new Error(`Invalid .function declaration: ${fnName}`)
    }
    syntax.expression.arguments.forEach(arg => {
      if (arg.type !== 'Identifier') {
        throw new Error(`Invalid .function declaration: ${fnName}`)
      }
    })
    if (typeof fn === 'string') {
      fn = functions[fnName] = {
        body: fn,
        name: syntax.expression.callee.name,
        args: syntax.expression.arguments.map(arg => arg.name)
      }
    }
  })
}

/**
 * Get the options for a ruleset.
 * They are recursively merged down.
 *
 * @param {object} rules - will be mutated to remove SPECIAL_KEYS
 * @param {object} [options] - options from the parent ruleset
 * @returns {object}
 */
function getOptions (rules, options={}) {
  // Merge defaults, the previous options,
  // and the current options
  const prev = pick(options, SPECIAL_KEYS)
  const next = pick(rules, SPECIAL_KEYS)
  options = merge({}, {
    '.functions': {},
    '.refs': {}
  }, prev, next)
  const {
    '.functions': functions,
    '.refs': refs,
    '.parent': parent
  } = options
  // Remove the options from the rule set
  forEach(SPECIAL_KEYS, key => delete rules[key])
  // See if there is a parent ruleset
  if (parent) {
    // Check to see if any parent was a $wildcard
    forEach(parent, (value, key) => {
      if (/^\$/.test(key)) {
        refs[key] = 'prev'
      }
    })
  }
  // Refs
  expandRefs(refs)
  // Functions
  expandFunctions(functions)
  // Misc
  options['.parent'] = rules
  return options
}

/**
 * Replace "^REF_NAME" with the necessary snapshot method
 * chains to reach the specified child
 *
 * "REF_NAME" will be replaced with the value of "REF_NAME"
 * in the first matched ".refs" object plus the required
 * numbet of .parent() calls
 *
 * Alternatley, "^REF_NAME(next|prev)" syntax can be used and
 * will override the value of .refs[REF_NAME]
 *
 * @param {string} value
 * @param {object} options
 * @returns {string}
 */
function replaceRefs (value, options) {
  const {'.refs': refs} = options
  forEach(refs, (ref, name) => {
    name = RegExp.escape(name)
    const pattern = new RegExp(`\\^${name}(?:\\((next|prev)\\))?`, 'g')
    value = value.replace(pattern, (match, keyword) => {
      const snapshot = keyword || ref.value
      const parents = repeat('.parent()', ref.depth)
      return match.replace(match, `${snapshot}${parents}`)
    })
  })
  return value
}

/**
 * Replace function () calls inside a rule expression
 *
 * @param {string} value
 * @param {object} options
 * @returns {object}
 */
function replaceFunctions (value, options) {
  const {'.functions': fns} = options
  // Get a list of available fn names
  const fnNames = Object.values(fns).map(fn => fn.name)
  // Parse the rule expression
  const valueSyntax = esprima.parse(value)
  /**
   * Walk the .function body and replace argument
   * identifiers with the specified value
   *
   * @param {object} node
   * @param {string} key
   * @param {object} parentNode
   * @param {object} fn - a function definition from the .functions map
   * @param {array} args - an array of esprima nodes that were passed
   *  passed into the function call
   */
  function walkFn (node, key, parentNode, fn, args) {
    // We found an identifier — see if it is one of the fn args
    if (node.type === 'Identifier' && includes(fn.args, node.name)) {
      // Get the argument
      const arg = args[fn.args.indexOf(node.name)]
      // Replace the identifier with the supplied argument node
      parentNode[key] = arg
    } else {
      // Keep walking
      forEach(node, function (child, childKey) {
        // Only objects that arent "property"
        if (Object.isObject(child)) {
          if (childKey === 'property' && node.computed === false) return
          walkFn(...[...arguments].concat(fn, [args]))
        }
      })
    }
  }
  /**
   * Walk the rule expression for CallExpression nodes that are included
   * in "fnNames" and then replace them with the fn.body
   *
   * @param {object} node
   * @param {string} key
   * @param {object} parentNode
   */
  function walkValue (node, key, parentNode) {
    // A CallExpression that matches one of the fnNames to be replaced
    if (node.type === 'CallExpression' && includes(fnNames, node.callee.name)) {
      // Get the fn definition by name
      const fn = find(Object.values(fns), ({name}) => name === node.callee.name)
      // Parse the fn definition
      const fnSyntax = esprima.parse(replaceFunctions(fn.body, options).code)
      // Walk the definition and replace the argument identifiers with the
      // provided arguments
      walkFn(fnSyntax.body[0], key, parentNode, fn, node.arguments)
      // Replace the CallExpression with a node that represents the fn.body
      parentNode[key] = fnSyntax.body[0].expression
    } else {
      // Keep walking
      // Can't use pick because we need to keep references
      // to nodes inside valueSyntax
      forEach(node, function (child) {
        if (Object.isObject(child) && !Array.isArray(child)) {
          walkValue(...arguments)
        }
      })
    }
  }
  walkValue(valueSyntax.body[0])
  return {
    syntax: valueSyntax,
    code: cleanSyntax(valueSyntax)
  }
}

/**
 * Append .val() if an expression is next to an operator
 * or in an array like child selector
 *
 * @param {string} value
 * @returns {object}
 */
function coerceVal (value) {
  const syntax = esprima.parse(value)
  /**
   * Walk the rule expression and append .val() to MemberExpressions
   *
   * @param {object} node
   * @param {string} key
   * @param {object} parentNode
   * @param {string} parentKey
   */
  function walk (node) {
    if (node.type === 'MemberExpression' || node.type === 'Identifier') {
      const name = getMemberExpressionRootName(node)
      // Only append .val() to MemberExpression that start
      // with a Firebase identifier
      if (!SNAPSHOT_IDENTIFIERS.hasOwnProperty(name)) {
        return
      }
      // Don't append .val() if it already exists
      if (hasCallExpressionIdentifier(node, 'val')) {
        return
      }
      const newNode = clone(node)
      node.type = 'CallExpression'
      node.callee = {
        type: 'MemberExpression',
        computed: false,
        object: newNode,
        property: {
          type: 'Identifier',
          name: 'val'
        }
      }
      node.arguments = []
      delete node.object
      delete node.property
    } else {
      forEach(node, function (child, childKey) {
        if (Object.isObject(child)) {
          if (node.type === 'CallExpression' && childKey === 'callee') {
            return
          }
          walk(...arguments)
        }
      })
    }
  }
  walk(syntax.body[0])
  return {
    syntax,
    code: cleanSyntax(syntax)
  }
}

/**
 * Replace dot/bracket syntax with the necessary
 * snapshot.child() calls
 *
 * @param {string} value
 * @returns {object}
 */
function replaceChildSyntax (value) {
  // Parse the rule expression
  const syntax = esprima.parse(value)
  /**
   * Check a MemberExpression to see if the root
   * is a Firebase identifier — if it is, return the node,
   * otherwise replaceChildSyntax()
   *
   * @param {object} node
   * @returns {object}
   */
  function filterMemeberExpression (node) {
    const name = getMemberExpressionRootName(node)
    if (includes(IGNORE_IDENTIFIERS, name)) {
      return node
    } else {
      return replaceChildSyntax(escodegen.generate(node)).syntax.body[0].expression
    }
  }
  /**
   * Walk the rule expression for Identifier nodes and convert parentNode
   * to a CallExpression: .child(Identifier)
   *
   * @param {object} node
   * @param {string} key
   * @param {object} parentNode
   * @param {string} parentKey
   */
  function walkValue (node, key, parentNode, parentKey) {
    const isCallIdentifier = node.type === 'Identifier' && parentKey === 'callee'
    const isParentComputed = parentNode && parentNode.computed === true
    // Only look at Identifiers that are properties
    // and not part of a CallExpression
    if (key === 'property' && (!isCallIdentifier || isParentComputed)) {
      const isIdentifier = parentNode.property.type === 'Identifier'
      parentNode.computed = false
      let newNode = clone(parentNode)
      let proposedArgument = newNode.property
      let argument
      // MemberExprssions
      if (proposedArgument.type === 'MemberExpression') {
        argument = filterMemeberExpression(proposedArgument)
      } else {
        // Identifier / Literal
        // If the node was computed, make sure the CallExpression
        // argument is an Identifier and not a Literal
        let argumentValue = isIdentifier
          ? proposedArgument.name
          : proposedArgument.value
        argument = isParentComputed && isIdentifier
          ? { type: 'Identifier', name: argumentValue }
          : { type: 'Literal', value: argumentValue }
      }
      // Convert the parent to a CallExpression
      parentNode.type = 'CallExpression'
      parentNode.callee = newNode
      parentNode.arguments = [argument]
      delete parentNode.object
      delete parentNode.property
      proposedArgument.name = 'child'
    } else {
      // Keep walking
      if (node.type === 'CallExpression') {
        node.arguments = node.arguments.map(argument => {
          if (argument.type === 'MemberExpression' || node.type === 'CallExpression') {
            return filterMemeberExpression(argument)
          }
          return argument
        })
      }
      forEach(node, function (child) {
        if (Object.isObject(child) && !Array.isArray(child)) {
          // Ignore some identifiers
          if (node.type === 'MemberExpression') {
            const name = getMemberExpressionRootName(node)
            if (includes(IGNORE_IDENTIFIERS, name)) return
          }
          walkValue(...[...arguments].concat(key))
        }
      })
    }
  }
  walkValue(syntax.body[0])
  return {
    syntax,
    code: cleanSyntax(syntax)
  }
}

/**
 * Replace items in the SNAPSHOT_IDENTIFIERS map
 *
 * @param {string} value
 * @returns {object}
 */
function replaceFirebaseIdentifiers (value) {
  const syntax = esprima.parse(value)
  /**
   * Walk the rule expression and rename Identifier nodes that are the root of
   * an expresston and match identifers in SNAPSHOT_IDENTIFIERS
   *
   * @param {object} node
   * @param {string} key
   * @param {object} parentNode
   * @param {string} parentKey
   */
  function walk (node, key) {
    if (node.type === 'Identifier' && key !== 'property') {
      const name = getMemberExpressionRootName(node)
      if (SNAPSHOT_IDENTIFIERS.hasOwnProperty(name)) {
        node.name = SNAPSHOT_IDENTIFIERS[name]
      }
    } else {
      forEach(node, function (child) {
        if (Object.isObject(child)) {
          walk(...[...arguments].concat(key))
        }
      })
    }
  }
  walk(syntax.body[0])
  return {
    syntax,
    code: cleanSyntax(syntax)
  }
}

/**
 * Parse a collection of rulesets and replace all Butane syntax
 *
 * @param {object} rules
 * @param {object} [options]
 * @returns {object}
 */
function parse (rules, options={}) {
  options = getOptions(rules, options)
  forEach(rules, (rule, key) => {
    switch (typeof rule) {
    case 'object':
      parse(rule, options)
      break
    case 'string':
      rule = replaceRefs(rule, options)
      rule = replaceFunctions(rule, options).code
      rule = coerceVal(rule).code
      rule = replaceChildSyntax(rule).code
      rule = replaceFirebaseIdentifiers(rule).code
      rules[key] = rule
      break
    }
  })
  return rules
}
<|endoftext|>


<commit_before>const async = require('async')
const utils = require('ethereumjs-util')
const BN = utils.BN
const Bloom = require('./bloom.js')
const Block = require('ethereumjs-block')

/**
 * Process a transaction. Run the vm. Transfers eth. checks balaces
 * @method processTx
 * @param opts
 * @param opts.tx {Transaction} - a transaction
 * @param opts.skipNonce - skips the nonce check
 * @param opts.skipBalance - skips the balance check
 * @param opts.block {Block} needed to process the transaction, if no block is given a default one is created
 * @param cb {Function} - the callback
 */
module.exports = function (opts, cb) {
  var self = this
  var block = opts.block
  var tx = opts.tx
  var gasLimit
  var results
  var basefee

  // create a reasonable default if no block is given
  if (!block) {
    block = new Block()
  }

  if (new BN(block.header.gasLimit).cmp(new BN(tx.gasLimit)) === -1) {
    cb(new Error('tx has a higher gas limit than the block'))
    return
  }

  if (opts.populateCache === undefined) {
    opts.populateCache = true
  }

  // run everything
  async.series([
    populateCache,
    runTxHook,
    runCall,
    saveTries,
    runAfterTxHook,
    function (cb) {
      self.stateManager.cache.flush(function () {
        if (opts.populateCache) {
          self.stateManager.cache.clear()
        }
        cb()
      })
    }
  ], function (err) {
    cb(err, results)
  })

  // run the transaction hook
  function runTxHook (cb) {
    self.emit('beforeTx', tx, cb)
  }

  // run the transaction hook
  function runAfterTxHook (cb) {
    self.emit('afterTx', results, cb)
  }

  /**
   * populates the cache with the two and from of the tx
   */
  function populateCache (cb) {
    var accounts = new Set()
    accounts.add(tx.from.toString('hex'))
    accounts.add(tx.to.toString('hex'))
    accounts.add(block.header.coinbase.toString('hex'))

    if (opts.populateCache === false) {
      return cb()
    }

    self.stateManager.warmCache(accounts, cb)
  }

  // sets up the envorment and runs a `call`
  function runCall (cb) {
    // check to the sender's account to make sure it has enought wei and the
    // correct nonce
    var fromAccount = self.stateManager.cache.get(tx.from)
    var message

    if (!opts.skipBalance && new BN(fromAccount.balance).cmp(tx.getUpfrontCost()) === -1) {
      message = "sender doesn't have enough funds to send tx. The upfront cost is: " + tx.getUpfrontCost().toString() + ' and the sender\s account only has: ' + new BN(fromAccount.balance).toString()
      cb(new Error(message))
      return
    } else if (!opts.skipNonce && new BN(fromAccount.nonce).cmp(new BN(tx.nonce)) !== 0) {
      message = "the tx doesn't have the correct nonce. account has nonce of: " + new BN(fromAccount.nonce).toString() + ' tx has nonce of: ' + new BN(tx.nonce).toString()
      cb(new Error(message))
      return
    }

    // increment the nonce
    fromAccount.nonce = new BN(fromAccount.nonce).addn(1)

    basefee = tx.getBaseFee()
    gasLimit = new BN(tx.gasLimit)
    if (gasLimit.lt(basefee)) {
      return cb(new Error('base fee exceeds gas limit'))
    }
    gasLimit.isub(basefee)

    fromAccount.balance = new BN(fromAccount.balance).sub(new BN(tx.gasLimit).mul(new BN(tx.gasPrice)))
    self.stateManager.cache.put(tx.from, fromAccount)

    var options = {
      caller: tx.from,
      gasLimit: gasLimit,
      gasPrice: tx.gasPrice,
      to: tx.to,
      value: tx.value,
      data: tx.data,
      block: block,
      populateCache: false
    }

    if (tx.to.toString('hex') === '') {
      delete options.to
    }

    // run call
    self.runCall(options, parseResults)

    function parseResults (err, _results) {
      if (err) return cb(err)
      results = _results

      // generate the bloom for the tx
      results.bloom = txLogsBloom(results.vm.logs)
      fromAccount = self.stateManager.cache.get(tx.from)

      // caculate the totall gas used
      results.gasUsed = results.gasUsed.add(basefee)

      // refund the accoun.stateManagert
      var gasRefund = results.vm.gasRefund
      if (gasRefund) {
        if (gasRefund.cmp(results.gasUsed.divn(2)) === -1) {
          results.gasUsed.isub(gasRefund)
        } else {
          results.gasUsed.isub(results.gasUsed.divn(2))
        }
      }

      results.amountSpent = results.gasUsed.mul(new BN(tx.gasPrice))
      // refund the left over gas amount
      fromAccount.balance = new BN(tx.gasLimit).sub(results.gasUsed)
        .mul(new BN(tx.gasPrice))
        .add(new BN(fromAccount.balance))

      self.stateManager.cache.put(tx.from, fromAccount)

      var minerAccount = self.stateManager.cache.get(block.header.coinbase)
      // add the amount spent on gas to the miner's account
      minerAccount.balance = new BN(minerAccount.balance)
        .add(results.amountSpent)

      // save the miner's account
      self.stateManager.cache.put(block.header.coinbase, minerAccount)

      if (!results.vm.suicides) {
        results.vm.suicides = {}
      }

      var keys = Object.keys(results.vm.suicides)

      keys.forEach(function (s) {
        self.stateManager.cache.del(new Buffer(s, 'hex'))
      })

      cb()
    }
  }

  function saveTries (cb) {
    self.stateManager.commitContracts(cb)
  }
}

/**
 * @method txLogsBloom
 */
function txLogsBloom (logs) {
  var bloom = new Bloom()
  if (logs) {
    for (var i = 0; i < logs.length; i++) {
      var log = logs[i]
      // add the address
      bloom.add(log[0])
      // add the topics
      var topics = log[1]
      for (var q = 0; q < topics.length; q++) {
        bloom.add(topics[q])
      }
    }
  }
  return bloom
}
<commit_msg>Improve comments in runTx<commit_after>const async = require('async')
const utils = require('ethereumjs-util')
const BN = utils.BN
const Bloom = require('./bloom.js')
const Block = require('ethereumjs-block')

/**
 * Process a transaction. Run the vm. Transfers eth. Checks balances.
 * @method processTx
 * @param opts
 * @param opts.tx {Transaction} - a transaction
 * @param opts.skipNonce - skips the nonce check
 * @param opts.skipBalance - skips the balance check
 * @param opts.block {Block} needed to process the transaction, if no block is given a default one is created
 * @param cb {Function} - the callback
 */
module.exports = function (opts, cb) {
  var self = this
  var block = opts.block
  var tx = opts.tx
  var gasLimit
  var results
  var basefee

  // create a reasonable default if no block is given
  if (!block) {
    block = new Block()
  }

  if (new BN(block.header.gasLimit).cmp(new BN(tx.gasLimit)) === -1) {
    cb(new Error('tx has a higher gas limit than the block'))
    return
  }

  if (opts.populateCache === undefined) {
    opts.populateCache = true
  }

  // run everything
  async.series([
    populateCache,
    runTxHook,
    runCall,
    saveTries,
    runAfterTxHook,
    function (cb) {
      self.stateManager.cache.flush(function () {
        if (opts.populateCache) {
          self.stateManager.cache.clear()
        }
        cb()
      })
    }
  ], function (err) {
    cb(err, results)
  })

  // run the transaction hook
  function runTxHook (cb) {
    self.emit('beforeTx', tx, cb)
  }

  // run the transaction hook
  function runAfterTxHook (cb) {
    self.emit('afterTx', results, cb)
  }

  /**
   * populates the cache with the 'to' and 'from' of the tx
   */
  function populateCache (cb) {
    var accounts = new Set()
    accounts.add(tx.from.toString('hex'))
    accounts.add(tx.to.toString('hex'))
    accounts.add(block.header.coinbase.toString('hex'))

    if (opts.populateCache === false) {
      return cb()
    }

    self.stateManager.warmCache(accounts, cb)
  }

  // sets up the environment and runs a `call`
  function runCall (cb) {
    // check to the sender's account to make sure it has enough wei and the correct nonce
    var fromAccount = self.stateManager.cache.get(tx.from)
    var message

    if (!opts.skipBalance && new BN(fromAccount.balance).cmp(tx.getUpfrontCost()) === -1) {
      message = "sender doesn't have enough funds to send tx. The upfront cost is: " + tx.getUpfrontCost().toString() + ' and the sender\s account only has: ' + new BN(fromAccount.balance).toString()
      cb(new Error(message))
      return
    } else if (!opts.skipNonce && new BN(fromAccount.nonce).cmp(new BN(tx.nonce)) !== 0) {
      message = "the tx doesn't have the correct nonce. account has nonce of: " + new BN(fromAccount.nonce).toString() + ' tx has nonce of: ' + new BN(tx.nonce).toString()
      cb(new Error(message))
      return
    }

    // increment the nonce
    fromAccount.nonce = new BN(fromAccount.nonce).addn(1)

    basefee = tx.getBaseFee()
    gasLimit = new BN(tx.gasLimit)
    if (gasLimit.lt(basefee)) {
      return cb(new Error('base fee exceeds gas limit'))
    }
    gasLimit.isub(basefee)

    fromAccount.balance = new BN(fromAccount.balance).sub(new BN(tx.gasLimit).mul(new BN(tx.gasPrice)))
    self.stateManager.cache.put(tx.from, fromAccount)

    var options = {
      caller: tx.from,
      gasLimit: gasLimit,
      gasPrice: tx.gasPrice,
      to: tx.to,
      value: tx.value,
      data: tx.data,
      block: block,
      populateCache: false
    }

    if (tx.to.toString('hex') === '') {
      delete options.to
    }

    // run call
    self.runCall(options, parseResults)

    function parseResults (err, _results) {
      if (err) return cb(err)
      results = _results

      // generate the bloom for the tx
      results.bloom = txLogsBloom(results.vm.logs)
      fromAccount = self.stateManager.cache.get(tx.from)

      // caculate the total gas used
      results.gasUsed = results.gasUsed.add(basefee)

      // process any gas refund
      var gasRefund = results.vm.gasRefund
      if (gasRefund) {
        if (gasRefund.cmp(results.gasUsed.divn(2)) === -1) {
          results.gasUsed.isub(gasRefund)
        } else {
          results.gasUsed.isub(results.gasUsed.divn(2))
        }
      }

      results.amountSpent = results.gasUsed.mul(new BN(tx.gasPrice))
      // refund the leftover gas amount
      fromAccount.balance = new BN(tx.gasLimit).sub(results.gasUsed)
        .mul(new BN(tx.gasPrice))
        .add(new BN(fromAccount.balance))

      self.stateManager.cache.put(tx.from, fromAccount)

      var minerAccount = self.stateManager.cache.get(block.header.coinbase)
      // add the amount spent on gas to the miner's account
      minerAccount.balance = new BN(minerAccount.balance)
        .add(results.amountSpent)

      // save the miner's account
      self.stateManager.cache.put(block.header.coinbase, minerAccount)

      if (!results.vm.suicides) {
        results.vm.suicides = {}
      }

      var keys = Object.keys(results.vm.suicides)

      keys.forEach(function (s) {
        self.stateManager.cache.del(new Buffer(s, 'hex'))
      })

      cb()
    }
  }

  function saveTries (cb) {
    self.stateManager.commitContracts(cb)
  }
}

/**
 * @method txLogsBloom
 */
function txLogsBloom (logs) {
  var bloom = new Bloom()
  if (logs) {
    for (var i = 0; i < logs.length; i++) {
      var log = logs[i]
      // add the address
      bloom.add(log[0])
      // add the topics
      var topics = log[1]
      for (var q = 0; q < topics.length; q++) {
        bloom.add(topics[q])
      }
    }
  }
  return bloom
}
<|endoftext|>