<commit_before>import React, { Component } from "react";
import PropTypes from "prop-types";
import { connect } from "react-redux";
import { map, filter, find, size } from "lodash";
import Theme from "theme";
import { browserHistory } from "react-router";

import {
  firebaseConnect,
  isLoaded,
  pathToJS,
  dataToJS // needed for full list and once
  // orderedToJS, // needed for ordered list
  // populatedDataToJS // needed for populated list
} from "react-redux-firebase";
import CircularProgress from "material-ui/CircularProgress";
import Snackbar from "material-ui/Snackbar";
import RaisedButton from "material-ui/RaisedButton";
import Subheader from "material-ui/Subheader";

import ScoreWithOptions from "../components/ScoreWithOptions";

import classes from "./HomeContainer.scss";

// const populates = [{ child: 'owner', root: 'users', keyProp: 'uid' }]

@firebaseConnect([
  { path: "votes" },
  { path: "optionsNumber" },
  { path: "options", queryParams: ["orderByKey"] }
])
@connect(({ firebase }) => ({
  auth: pathToJS(firebase, "auth"),
  account: pathToJS(firebase, "profile"),
  votes: dataToJS(firebase, "votes"),
  options: dataToJS(firebase, "options"),
  optionsNumber: dataToJS(firebase, "optionsNumber")
}))
export default class Home extends Component {
  static propTypes = {
    votes: PropTypes.oneOfType([
      PropTypes.object, // object if using dataToJS
      PropTypes.array // array if using orderedToJS
    ]),
    options: PropTypes.oneOfType([
      PropTypes.object, // object if using dataToJS
      PropTypes.array // array if using orderedToJS
    ]),
    firebase: PropTypes.shape({
      set: PropTypes.func.isRequired,
      remove: PropTypes.func.isRequired,
      push: PropTypes.func.isRequired,
      database: PropTypes.oneOfType([PropTypes.object, PropTypes.func])
    }),
    auth: PropTypes.shape({
      uid: PropTypes.string
    })
  };

  state = {
    error: null,
    currentVote: {}
  };

  handleChange = (score, option) => {
    let newVote = { ...this.state.currentVote };
    newVote[score] = option;
    this.setState({
      currentVote: newVote
    });
  };

  handleSubmit = (score, option) => {
    const { auth, firebase, optionsNumber, votes } = this.props;
    const currentVoteSize = size(this.state.currentVote);

    const myVotes = votes && votes[auth.uid] ? votes[auth.uid] : {};

    if (size(myVotes) > 0) {
      return this.setState({ error: "Sorry, you can vote one time only" });
    }

    if (currentVoteSize !== parseInt(optionsNumber)) {
      return this.setState({ error: "One or more votes are missing" });
    }

    if (!auth || !auth.uid) {
      return this.setState({ error: "You must be Logged into Toggle Done" });
    }

    return firebase.set(`/votes/${auth.uid}/`, this.state.currentVote, err => {
      if (err) {
        return this.setState({ error: "ERROR! please try again :-(" });
      }
      this.setState({ error: "Your votes submitted successfully" });
    });
  };

  handleClean() {
    const { auth, firebase } = this.props;
    return firebase.remove(`/votes/${auth.uid}`).catch(err => {
      console.error("Error cleaning votes: ", err); // eslint-disable-line no-console
      this.setState({ error: "Error cleaning todo" });
      return Promise.reject(err);
    });
  }
  componentWillMount() {
    const { auth } = this.props;
    if (!auth || !auth.uid) {
      browserHistory.push("/login");
    }
  }
  render() {
    const { votes, options, auth, optionsNumber } = this.props;
    const { error, currentVote } = this.state;

    if (!auth || !auth.uid) {
      return <Subheader>Please login</Subheader>;
    }

    const optionsArray = map(options, (option, id) => option);

    const scoresArray = optionsNumber
      ? [...Array(parseInt(optionsNumber))]
      : [];

    return isLoaded(optionsNumber) ? (

        {scoresArray.map((x, i) => (

            <ScoreWithOptions
              key={scoresArray.length - i}
              value={scoresArray.length - i}
              selectedOption={currentVote[scoresArray.length - i]}
              options={optionsArray}
              handleChange={this.handleChange}
              currentVote={currentVote}
            />

        ))}

          <RaisedButton
            label="Submit"
            primary={true}
            onClick={() => this.handleSubmit()}
          />

        {error ? (
          <Snackbar
            open={!!error}
            message={error}
            autoHideDuration={4000}
            onRequestClose={() => this.setState({ error: null })}
          />
        ) : null}

    ) : null;
  }
}
<commit_msg>Adding submit loader<commit_after>import React, { Component } from "react";
import PropTypes from "prop-types";
import { connect } from "react-redux";
import { map, filter, find, size } from "lodash";
import Theme from "theme";
import { browserHistory } from "react-router";

import {
  firebaseConnect,
  isLoaded,
  pathToJS,
  dataToJS // needed for full list and once
  // orderedToJS, // needed for ordered list
  // populatedDataToJS // needed for populated list
} from "react-redux-firebase";
import CircularProgress from "material-ui/CircularProgress";
import Snackbar from "material-ui/Snackbar";
import RaisedButton from "material-ui/RaisedButton";
import Subheader from "material-ui/Subheader";

import ScoreWithOptions from "../components/ScoreWithOptions";

import classes from "./HomeContainer.scss";

// const populates = [{ child: 'owner', root: 'users', keyProp: 'uid' }]

@firebaseConnect([
  { path: "votes" },
  { path: "optionsNumber" },
  { path: "options", queryParams: ["orderByKey"] }
])
@connect(({ firebase }) => ({
  auth: pathToJS(firebase, "auth"),
  account: pathToJS(firebase, "profile"),
  votes: dataToJS(firebase, "votes"),
  options: dataToJS(firebase, "options"),
  optionsNumber: dataToJS(firebase, "optionsNumber")
}))
export default class Home extends Component {
  static propTypes = {
    votes: PropTypes.oneOfType([
      PropTypes.object, // object if using dataToJS
      PropTypes.array // array if using orderedToJS
    ]),
    options: PropTypes.oneOfType([
      PropTypes.object, // object if using dataToJS
      PropTypes.array // array if using orderedToJS
    ]),
    firebase: PropTypes.shape({
      set: PropTypes.func.isRequired,
      remove: PropTypes.func.isRequired,
      push: PropTypes.func.isRequired,
      database: PropTypes.oneOfType([PropTypes.object, PropTypes.func])
    }),
    auth: PropTypes.shape({
      uid: PropTypes.string
    })
  };

  state = {
    error: null,
    currentVote: {},
    submitting: false
  };

  handleChange = (score, option) => {
    let newVote = { ...this.state.currentVote };
    newVote[score] = option;
    this.setState({
      currentVote: newVote
    });
  };

  handleSubmit = (score, option) => {
    const { auth, firebase, optionsNumber, votes } = this.props;
    const currentVoteSize = size(this.state.currentVote);

    this.setState({ submitting: true });

    const myVotes = votes && votes[auth.uid] ? votes[auth.uid] : {};

    if (size(myVotes) > 0) {
      return this.setState({
        error: "Sorry, you can vote one time only",
        submitting: false
      });
    }

    if (currentVoteSize !== parseInt(optionsNumber)) {
      return this.setState({
        error: "One or more votes are missing",
        submitting: false
      });
    }

    if (!auth || !auth.uid) {
      return this.setState({
        error: "You must be Logged into Toggle Done",
        submitting: false
      });
    }

    return firebase.set(`/votes/${auth.uid}/`, this.state.currentVote, err => {
      if (err) {
        return this.setState({
          error: "ERROR! please try again :-(",
          submitting: false
        });
      }
      this.setState({
        error: "Your votes submitted successfully",
        submitting: false
      });
    });
  };

  handleClean() {
    const { auth, firebase } = this.props;
    return firebase.remove(`/votes/${auth.uid}`).catch(err => {
      console.error("Error cleaning votes: ", err); // eslint-disable-line no-console
      this.setState({ error: "Error cleaning todo" });
      return Promise.reject(err);
    });
  }
  componentWillMount() {
    const { auth } = this.props;
    if (!auth || !auth.uid) {
      browserHistory.push("/login");
    }
  }
  render() {
    const { votes, options, auth, optionsNumber } = this.props;
    const { error, currentVote } = this.state;

    if (!auth || !auth.uid) {
      return <Subheader>Please login</Subheader>;
    }

    const optionsArray = map(options, (option, id) => option);

    const scoresArray = optionsNumber
      ? [...Array(parseInt(optionsNumber))]
      : [];

    return isLoaded(optionsNumber) ? (

        {scoresArray.map((x, i) => (

            <ScoreWithOptions
              key={scoresArray.length - i}
              value={scoresArray.length - i}
              selectedOption={currentVote[scoresArray.length - i]}
              options={optionsArray}
              handleChange={this.handleChange}
              currentVote={currentVote}
            />

        ))}

          {this.state.submitting ? (
            <CircularProgress />
          ) : (
            <RaisedButton
              label="Submit"
              primary={true}
              disabled={this.state.submitting}
              onClick={() => this.handleSubmit()}
            />
          )}

        {error ? (
          <Snackbar
            open={!!error}
            message={error}
            autoHideDuration={4000}
            onRequestClose={() => this.setState({ error: null })}
          />
        ) : null}

    ) : null;
  }
}
<|endoftext|>


<commit_before>import React, { Component, PropTypes } from 'react';
import PlaygroundRenderer from 'rsg-components/Playground/PlaygroundRenderer';

export default class Playground extends Component {
	static propTypes = {
		code: PropTypes.string.isRequired,
		evalInContext: PropTypes.func.isRequired,
	};
	static contextTypes = {
		config: PropTypes.object.isRequired,
	};

	constructor(props, context) {
		super(props, context);
		const { code } = props;
		const { showCode } = context.config;
		this.state = {
			code,
			showCode,
		};
	}

	componentWillReceiveProps(nextProps) {
		let { code } = nextProps;
		if (code) {
			this.setState({
				code,
			});
		}
	}

	handleChange(code) {
		this.setState({
			code,
		});
	}

	handleCodeToggle() {
		this.setState({
			showCode: !this.state.showCode,
		});
	}

	render() {
		const { code, showCode } = this.state;
		const { evalInContext } = this.props;
		return (
			<PlaygroundRenderer
				code={code}
				showCode={showCode}
				evalInContext={evalInContext}
				onChange={code => this.handleChange(code)}
				onCodeToggle={() => this.handleCodeToggle()}
			/>
		);
	}
}
<commit_msg>Tweak update check.<commit_after>import React, { Component, PropTypes } from 'react';
import PlaygroundRenderer from 'rsg-components/Playground/PlaygroundRenderer';

export default class Playground extends Component {
	static propTypes = {
		code: PropTypes.string.isRequired,
		evalInContext: PropTypes.func.isRequired,
	};
	static contextTypes = {
		config: PropTypes.object.isRequired,
	};

	constructor(props, context) {
		super(props, context);
		const { code } = props;
		const { showCode } = context.config;
		this.state = {
			code,
			showCode,
		};
	}

	componentWillReceiveProps(nextProps) {
		const { code } = nextProps;
		this.setState({
			code,
		});
	}

	shouldComponentUpdate(nextProps, nextState) {
		return (
			nextState.code !== this.state.code ||
			nextState.showCode !== this.state.showCode
		);
	}

	handleChange(code) {
		this.setState({
			code,
		});
	}

	handleCodeToggle() {
		this.setState({
			showCode: !this.state.showCode,
		});
	}

	render() {
		const { code, showCode } = this.state;
		const { evalInContext } = this.props;
		return (
			<PlaygroundRenderer
				code={code}
				showCode={showCode}
				evalInContext={evalInContext}
				onChange={code => this.handleChange(code)}
				onCodeToggle={() => this.handleCodeToggle()}
			/>
		);
	}
}
<|endoftext|>


<commit_before>/**
 * Pimcore
 *
 * LICENSE
 *
 * This source file is subject to the new BSD license that is bundled
 * with this package in the file LICENSE.txt.
 * It is also available through the world-wide-web at this URL:
 * http://www.pimcore.org/license
 *
 * @copyright  Copyright (c) 2009-2014 pimcore GmbH (http://www.pimcore.org)
 * @license    http://www.pimcore.org/license     New BSD License
 */

pimcore.registerNS("pimcore.document.edit.dnd");
pimcore.document.edit.dnd = Class.create({

    dndManager: null,

    globalDropZone: null,

    initialize: function(parentExt, body, iframeElement) {

        this.dndManager = parentExt.dd.DragDropMgr;
        var iFrameElement = parent.Ext.get('document_iframe_' + window.editWindow.document.id);

        parentExt.EventManager.on(body, 'mousemove', this.ddMouseMove.bind(this));
        parentExt.EventManager.on(body, 'mouseup', this.ddMouseUp.bind(this));

        this.globalDropZone = new parent.Ext.dd.DropZone(iframeElement, {
            ddGroup: "element",
            validElements: [],

            getTargetFromEvent: function(e) {
                var element = null;
                var elLength = this.validElements.length;

                for (var i = 0; i < elLength; i++) {
                    element = this.validElements[i];
                    if (element["el"].dndOver) {
                        if(element["drop"]) {
                            this.onNodeDrop = element["drop"];
                        }
                        if(element["over"]) {
                            this.onNodeOver = element["over"];
                        }
                        return element["el"];
                    }
                }
            }
        });

        window.setInterval(this.setIframeOffset.bind(this),1000);
        this.setIframeOffset();
    },

    addDropTarget: function (el, overCallback, dropCallback) {

        el.on("mouseover", function (e) {
            this.dndOver = true;
        }.bind(el));
        el.on("mouseout", function (e) {
            this.dndOver = false;
        }.bind(el));

        el.dndOver = false;

        this.globalDropZone.validElements.push({
            el: el,
            over: overCallback,
            drop: dropCallback
        });
    },

    ddMouseMove: function (e) {
        // update the xy of the event if necessary
        this.setDDPos(e);
        // *** Note that the 'this' scope is the drag drop manager
        this.dndManager.handleMouseMove(e);
    },

    ddMouseUp : function (e) {
        // update the xy of the event if necessary
        this.setDDPos(e);
        // *** Note that the 'this' scope is the drag drop manager
        this.dndManager.handleMouseUp(e);
    },

    setDDPos: function (e) {

        var scrollTop = 0;
        var scrollLeft = 0;

        var doc = (window.contentDocument || window.document);
        scrollTop = doc.documentElement.scrollTop || doc.body.scrollTop;
        scrollLeft = doc.documentElement.scrollLeft || doc.body.scrollLeft;

        e.xy = [e.xy[0] + this.iframeOffset[0] - scrollLeft, e.xy[1] + this.iframeOffset[1] - scrollTop];
    },

    setIframeOffset: function () {
        try {
            this.iframeOffset = parent.Ext.get('document_iframe_'
                                                    + window.editWindow.document.id).getOffsetsTo(parent.Ext.getBody());
        } catch (e) {

        }
    },

    disable: function () {
        this.globalDropZone.lock();
    },

    enable: function () {
        this.globalDropZone.unlock();
    }

});
<commit_msg>dnd xy calculation<commit_after>/**
 * Pimcore
 *
 * LICENSE
 *
 * This source file is subject to the new BSD license that is bundled
 * with this package in the file LICENSE.txt.
 * It is also available through the world-wide-web at this URL:
 * http://www.pimcore.org/license
 *
 * @copyright  Copyright (c) 2009-2014 pimcore GmbH (http://www.pimcore.org)
 * @license    http://www.pimcore.org/license     New BSD License
 */

pimcore.registerNS("pimcore.document.edit.dnd");
pimcore.document.edit.dnd = Class.create({

    dndManager: null,

    globalDropZone: null,

    initialize: function(parentExt, body, iframeElement) {

        this.dndManager = parentExt.dd.DragDropMgr;
        var iFrameElement = parent.Ext.get('document_iframe_' + window.editWindow.document.id);

        parentExt.EventManager.on(body, 'mousemove', this.ddMouseMove.bind(this));
        parentExt.EventManager.on(body, 'mouseup', this.ddMouseUp.bind(this));

        this.globalDropZone = new parent.Ext.dd.DropZone(iframeElement, {
            ddGroup: "element",
            validElements: [],

            getTargetFromEvent: function(e) {
                var element = null;
                var elLength = this.validElements.length;

                for (var i = 0; i < elLength; i++) {
                    element = this.validElements[i];
                    if (element["el"].dndOver) {
                        if(element["drop"]) {
                            this.onNodeDrop = element["drop"];
                        }
                        if(element["over"]) {
                            this.onNodeOver = element["over"];
                        }
                        return element["el"];
                    }
                }
            }
        });

        window.setInterval(this.setIframeOffset.bind(this),1000);
        this.setIframeOffset();
    },

    addDropTarget: function (el, overCallback, dropCallback) {

        el.on("mouseover", function (e) {
            this.dndOver = true;
        }.bind(el));
        el.on("mouseout", function (e) {
            this.dndOver = false;
        }.bind(el));

        el.dndOver = false;

        this.globalDropZone.validElements.push({
            el: el,
            over: overCallback,
            drop: dropCallback
        });
    },

    ddMouseMove: function (e) {
        // update the xy of the event if necessary
        this.setDDPos(e);
        // *** Note that the 'this' scope is the drag drop manager
        this.dndManager.handleMouseMove(e);
    },

    ddMouseUp : function (e) {
        // update the xy of the event if necessary
        this.setDDPos(e);
        // *** Note that the 'this' scope is the drag drop manager
        this.dndManager.handleMouseUp(e);
    },

    setDDPos: function (e) {

        var scrollTop = 0;
        var scrollLeft = 0;

        var doc = (window.contentDocument || window.document);
        scrollTop = doc.documentElement.scrollTop || doc.body.scrollTop;
        scrollLeft = doc.documentElement.scrollLeft || doc.body.scrollLeft;

        var xy = e.getXY();
        e.pageX = e.pageX + this.iframeOffset[0] - scrollLeft;
        e.pageY = e.pageY + this.iframeOffset[1] - scrollTop;
    },

    setIframeOffset: function () {
        try {
            this.iframeOffset = parent.Ext.get('document_iframe_'
                                                    + window.editWindow.document.id).getOffsetsTo(parent.Ext.getBody());
        } catch (e) {
            console.log(e);
        }
    },

    disable: function () {
        this.globalDropZone.lock();
    },

    enable: function () {
        this.globalDropZone.unlock();
    }

});
<|endoftext|>


<commit_before>var hgotsAdmin = angular.module('HGOTSAdmin', [ 'HGOTSApp', 'HGOTSServices', 'PasswordStrengthView' ]);

hgotsAdmin.service('AdminShared', [ '$http', '$location', 'AppShared', function($http, $location, AppShared) {
  var obj = {
    userTypes: [ 'User', 'Controller', 'Admin' ],
    showUser: function(user) {
      if ( !!user ) {
        $location.path('/admin/user/' + user.id);
      } else {
        $location.path('/admin');
      }
    },
    currentUser: AppShared.currentUser,

    // variable
    selectedUser: {}
  };

  return obj;
}]);
hgotsAdmin.controller('AdminController', [ '$scope', '$routeParams', '$window', '$location', 'User', 'AdminShared', function($scope, $routeParams, $window, $location, User, AdminShared) {
  $scope.getIncludeFile = function() {
    var retVal = "views/admin-";

    if ( !!$routeParams.new ) {
      retVal += "new";
    } else if ( !!$routeParams.userId ) {
      retVal += "user";
    } else {
      retVal += "welcome";
    }

    return retVal + ".html";
  };
  $scope.includeFile = $scope.getIncludeFile();

  $scope.users = User.query(function() {
    if ( $scope.includeFile.indexOf("user") != -1 ) {

      var filteredList = $scope.users.filter(function(user) {
        return ( user.id === parseInt($routeParams.userId) );
      });

      if ( filteredList.length === 1 ) {
        AdminShared.selectedUser = filteredList[0];
      } else {
        console.error("User could not be found.");
      }
    }
  });
  $scope.ifSelectedUserStyle = function(user) {
    if ( !!AdminShared.selectedUser ) {
      if ( AdminShared.selectedUser === user ) {
        return "selected-user";
      }
    }

    return "";
  };

  $scope.showUser = function(user) {
    // redirect to page linked to in link, but on element click already
    AdminShared.showUser(user);
  };

  $scope.userTypeBackgroundStyle = function(user) {
    if ( user.type === "Admin" ) {
      return "bg-primary";
    } else if ( user.type === "Controller" ) {
      return "bg-info";
    }
  };

  $scope.infoPanelStyle = { "margin-top": "0px" };
  // little scroll "hack"
  $($window).on('scroll', function() {
    var oldOffset = parseInt($scope.infoPanelStyle["margin-top"]);
    var newOffset = oldOffset || 0;

    // this needs a certain min width b/c of the responsive layout
    if ( $window.innerWidth >= 992 ) {
      var navbar = $(".navbar");
      var navbarHeight = navbar.height();
      var navbarMarginBottom = parseInt(navbar.css('margin-bottom'));
      var adminInfoPanelHeight = $('.admin-scroll-info').height();

      // a minimum height is required, otherwise the controls hide
      var minHeight = navbarHeight + navbarMarginBottom + adminInfoPanelHeight;

      if ( $window.innerHeight > minHeight ) {
        var scrollY = $window.scrollY;
        //newOffset = ( scrollY > navbarHeight ) ? ( scrollY - navbarHeight ) : 0;
        newOffset = scrollY - navbarHeight;
      }
    }

    newOffset = ( newOffset >= 0 ) ? newOffset : 0;
    if ( newOffset !== oldOffset ) {
      $scope.infoPanelStyle = { "margin-top": newOffset + "px" };
      $scope.$apply();
    }
  });
}]);

hgotsAdmin.controller('AdminUserController', [ '$scope', '$routeParams', '$http', '$location', 'AdminShared', 'User', 'Card', 'confirm', '$translate', function($scope, $routeParams, $http, $location, AdminShared, User, Card, confirm, $translate) {
  // set all user types
  $scope.userTypes = AdminShared.userTypes;
  // watch for user changes
  function setNewValuesByUser(user) {
    $scope.newUsername = user.username;
    $scope.newType = user.type;
    $scope.newPassword = "";
    $scope.newPasswordRepeat = "";
  }
  $scope.$watch(function() {
    return AdminShared.selectedUser;
  }, function() {
    $scope.user = AdminShared.selectedUser;

    setNewValuesByUser($scope.user);
  });
  $scope.$watch('user', 'checkIfDirty()');

  $scope.userIsSelf = function() {
    return ( AdminShared.currentUser.id === AdminShared.selectedUser.id );
  };

  // make save available
  $scope.canSaveChanges = false;
  $scope.checkIfDirty = function() {
    var user = $scope.user;

    if ( !user ) {
      return false;
    }

    var usernameChanged = ( !!$scope.newUsername && user.username !== $scope.newUsername );
    var typeChanged = ( !!$scope.newType && user.type !== $scope.newType );
    var passwordChanged = ( !!$scope.newPassword && $scope.newPassword === $scope.newPasswordRepeat );

    console.log($scope.newPassword, $scope.newPasswordRepeat);

    var dirty = ( usernameChanged || typeChanged || passwordChanged );
    if ( dirty && $scope.newPassword && !passwordChanged ) {
      dirty = false;
    }

    $scope.canSaveChanges = dirty;
  };
  // also check on every 'new' variable
  ['newUsername', 'newType', 'newPassword', 'newPasswordRepeat'].forEach(function(scopeVariable) {
    $scope.$watch(scopeVariable, function() {
      $scope.checkIfDirty();
    });
  });

  // save changes to server
  $scope.saveUser = function() {
    $scope.user.username = $scope.newUsername;
    $scope.user.type = $scope.newType;
    if ( $scope.newPassword ) {
      $scope.user.password = $scope.newPassword;
    }

    $scope.user.$update(function(newUser, putResponseHeader) {
      $scope.user = newUser;
      setNewValuesByUser(newUser);
      $scope.checkIfDirty();
    });
  };

  $scope.deleteUser = function() {
    $translate('ADMIN.USER.CONFIRM_DELETE_USER', {
      username: $scope.user.username
    }).then(function(confirmText) {

      confirm(confirmText).then(function(confirm) {
        if ( confirm ) {
          $scope.user.$delete(function() {
            AdminShared.showUser(null);
          });
        }
      });

    });
  };

  // Cards
  $scope.getRFID = function() {
    $scope.currentlyLoadingRFIDResponse = true;

    $http({ method: 'GET', url: '/api/v2/getrfid' }).success(function(data) {
      $scope.newCardID = data.rfid;

      $scope.currentlyLoadingRFIDResponse = false;
    }).error(function(data) {
      console.log("Error getting rfid:", data);

      $scope.currentlyLoadingRFIDResponse = false;
    });
  };

  $scope.$watch('newCardID', function() {
    $scope.newCardDisabled = ( !$scope.newCardID );
  });
  $scope.addCard = function() {
    var card = new Card({
      user_id: $scope.user.id,
      uid: $scope.newCardID
    });
    card.$save(function() {
      $scope.newCardID = "";

      $scope.user.cards.push(card.card);
    });
  };
  $scope.deleteCard = function(card) {
    $translate('ADMIN.USER.CONFIRM_DELETE_CARD', {
      uid: card.uid
    }).then(function(confirmText) {

      confirm(confirmText).then(function(confirm) {
        if ( confirm ) {
          $http({ method: 'DELETE', url: '/api/v2/users/' + $scope.user.id + '/cards/' + card.id }).success(function() {
            $scope.user.cards = $scope.user.cards.filter(function(cardItem) {
              return ( cardItem.id !== card.id );
            });
          });
        }
      });

    });
  };
}]);

hgotsAdmin.controller('AdminNewController', [ '$scope', 'AdminShared', 'User', function($scope, AdminShared, User) {
  $scope.userTypes = AdminShared.userTypes;

  $scope.newUsername = "";
  $scope.newType = $scope.userTypes[0];
  $scope.newPassword = "";
  $scope.newPasswordRepeat = "";
  $scope.createDisabled = true;
  // make create available
  $scope.checkIfValid = function() {
    var usernamePresent = ( !!$scope.newUsername );
    var passwordPresent = ( !!$scope.newPassword && $scope.newPassword === $scope.newPasswordRepeat );

    var valid = ( usernamePresent && passwordPresent );
    $scope.createDisabled = !valid;
    return valid;
  };
  // also check on every 'new' variable
  ['newUsername', 'newType', 'newPassword', 'newPasswordRepeat'].forEach(function(scopeVariable) {
    $scope.$watch(scopeVariable, function() {
      $scope.checkIfValid();
    });
  });

  $scope.createUser = function() {
    var newUser = new User({
      username: $scope.newUsername,
      type: $scope.newType,
      password: $scope.newPassword
    });
    newUser.$save(function(user) {
      AdminShared.showUser(user);
    });
  };
}]);
<commit_msg>Removed log.<commit_after>var hgotsAdmin = angular.module('HGOTSAdmin', [ 'HGOTSApp', 'HGOTSServices', 'PasswordStrengthView' ]);

hgotsAdmin.service('AdminShared', [ '$http', '$location', 'AppShared', function($http, $location, AppShared) {
  var obj = {
    userTypes: [ 'User', 'Controller', 'Admin' ],
    showUser: function(user) {
      if ( !!user ) {
        $location.path('/admin/user/' + user.id);
      } else {
        $location.path('/admin');
      }
    },
    currentUser: AppShared.currentUser,

    // variable
    selectedUser: {}
  };

  return obj;
}]);
hgotsAdmin.controller('AdminController', [ '$scope', '$routeParams', '$window', '$location', 'User', 'AdminShared', function($scope, $routeParams, $window, $location, User, AdminShared) {
  $scope.getIncludeFile = function() {
    var retVal = "views/admin-";

    if ( !!$routeParams.new ) {
      retVal += "new";
    } else if ( !!$routeParams.userId ) {
      retVal += "user";
    } else {
      retVal += "welcome";
    }

    return retVal + ".html";
  };
  $scope.includeFile = $scope.getIncludeFile();

  $scope.users = User.query(function() {
    if ( $scope.includeFile.indexOf("user") != -1 ) {

      var filteredList = $scope.users.filter(function(user) {
        return ( user.id === parseInt($routeParams.userId) );
      });

      if ( filteredList.length === 1 ) {
        AdminShared.selectedUser = filteredList[0];
      } else {
        console.error("User could not be found.");
      }
    }
  });
  $scope.ifSelectedUserStyle = function(user) {
    if ( !!AdminShared.selectedUser ) {
      if ( AdminShared.selectedUser === user ) {
        return "selected-user";
      }
    }

    return "";
  };

  $scope.showUser = function(user) {
    // redirect to page linked to in link, but on element click already
    AdminShared.showUser(user);
  };

  $scope.userTypeBackgroundStyle = function(user) {
    if ( user.type === "Admin" ) {
      return "bg-primary";
    } else if ( user.type === "Controller" ) {
      return "bg-info";
    }
  };

  $scope.infoPanelStyle = { "margin-top": "0px" };
  // little scroll "hack"
  $($window).on('scroll', function() {
    var oldOffset = parseInt($scope.infoPanelStyle["margin-top"]);
    var newOffset = oldOffset || 0;

    // this needs a certain min width b/c of the responsive layout
    if ( $window.innerWidth >= 992 ) {
      var navbar = $(".navbar");
      var navbarHeight = navbar.height();
      var navbarMarginBottom = parseInt(navbar.css('margin-bottom'));
      var adminInfoPanelHeight = $('.admin-scroll-info').height();

      // a minimum height is required, otherwise the controls hide
      var minHeight = navbarHeight + navbarMarginBottom + adminInfoPanelHeight;

      if ( $window.innerHeight > minHeight ) {
        var scrollY = $window.scrollY;
        //newOffset = ( scrollY > navbarHeight ) ? ( scrollY - navbarHeight ) : 0;
        newOffset = scrollY - navbarHeight;
      }
    }

    newOffset = ( newOffset >= 0 ) ? newOffset : 0;
    if ( newOffset !== oldOffset ) {
      $scope.infoPanelStyle = { "margin-top": newOffset + "px" };
      $scope.$apply();
    }
  });
}]);

hgotsAdmin.controller('AdminUserController', [ '$scope', '$routeParams', '$http', '$location', 'AdminShared', 'User', 'Card', 'confirm', '$translate', function($scope, $routeParams, $http, $location, AdminShared, User, Card, confirm, $translate) {
  // set all user types
  $scope.userTypes = AdminShared.userTypes;
  // watch for user changes
  function setNewValuesByUser(user) {
    $scope.newUsername = user.username;
    $scope.newType = user.type;
    $scope.newPassword = "";
    $scope.newPasswordRepeat = "";
  }
  $scope.$watch(function() {
    return AdminShared.selectedUser;
  }, function() {
    $scope.user = AdminShared.selectedUser;

    setNewValuesByUser($scope.user);
  });
  $scope.$watch('user', 'checkIfDirty()');

  $scope.userIsSelf = function() {
    return ( AdminShared.currentUser.id === AdminShared.selectedUser.id );
  };

  // make save available
  $scope.canSaveChanges = false;
  $scope.checkIfDirty = function() {
    var user = $scope.user;

    if ( !user ) {
      return false;
    }

    var usernameChanged = ( !!$scope.newUsername && user.username !== $scope.newUsername );
    var typeChanged = ( !!$scope.newType && user.type !== $scope.newType );
    var passwordChanged = ( !!$scope.newPassword && $scope.newPassword === $scope.newPasswordRepeat );

    var dirty = ( usernameChanged || typeChanged || passwordChanged );
    if ( dirty && $scope.newPassword && !passwordChanged ) {
      dirty = false;
    }

    $scope.canSaveChanges = dirty;
  };
  // also check on every 'new' variable
  ['newUsername', 'newType', 'newPassword', 'newPasswordRepeat'].forEach(function(scopeVariable) {
    $scope.$watch(scopeVariable, function() {
      $scope.checkIfDirty();
    });
  });

  // save changes to server
  $scope.saveUser = function() {
    $scope.user.username = $scope.newUsername;
    $scope.user.type = $scope.newType;
    if ( $scope.newPassword ) {
      $scope.user.password = $scope.newPassword;
    }

    $scope.user.$update(function(newUser, putResponseHeader) {
      $scope.user = newUser;
      setNewValuesByUser(newUser);
      $scope.checkIfDirty();
    });
  };

  $scope.deleteUser = function() {
    $translate('ADMIN.USER.CONFIRM_DELETE_USER', {
      username: $scope.user.username
    }).then(function(confirmText) {

      confirm(confirmText).then(function(confirm) {
        if ( confirm ) {
          $scope.user.$delete(function() {
            AdminShared.showUser(null);
          });
        }
      });

    });
  };

  // Cards
  $scope.getRFID = function() {
    $scope.currentlyLoadingRFIDResponse = true;

    $http({ method: 'GET', url: '/api/v2/getrfid' }).success(function(data) {
      $scope.newCardID = data.rfid;

      $scope.currentlyLoadingRFIDResponse = false;
    }).error(function(data) {
      console.log("Error getting rfid:", data);

      $scope.currentlyLoadingRFIDResponse = false;
    });
  };

  $scope.$watch('newCardID', function() {
    $scope.newCardDisabled = ( !$scope.newCardID );
  });
  $scope.addCard = function() {
    var card = new Card({
      user_id: $scope.user.id,
      uid: $scope.newCardID
    });
    card.$save(function() {
      $scope.newCardID = "";

      $scope.user.cards.push(card.card);
    });
  };
  $scope.deleteCard = function(card) {
    $translate('ADMIN.USER.CONFIRM_DELETE_CARD', {
      uid: card.uid
    }).then(function(confirmText) {

      confirm(confirmText).then(function(confirm) {
        if ( confirm ) {
          $http({ method: 'DELETE', url: '/api/v2/users/' + $scope.user.id + '/cards/' + card.id }).success(function() {
            $scope.user.cards = $scope.user.cards.filter(function(cardItem) {
              return ( cardItem.id !== card.id );
            });
          });
        }
      });

    });
  };
}]);

hgotsAdmin.controller('AdminNewController', [ '$scope', 'AdminShared', 'User', function($scope, AdminShared, User) {
  $scope.userTypes = AdminShared.userTypes;

  $scope.newUsername = "";
  $scope.newType = $scope.userTypes[0];
  $scope.newPassword = "";
  $scope.newPasswordRepeat = "";
  $scope.createDisabled = true;
  // make create available
  $scope.checkIfValid = function() {
    var usernamePresent = ( !!$scope.newUsername );
    var passwordPresent = ( !!$scope.newPassword && $scope.newPassword === $scope.newPasswordRepeat );

    var valid = ( usernamePresent && passwordPresent );
    $scope.createDisabled = !valid;
    return valid;
  };
  // also check on every 'new' variable
  ['newUsername', 'newType', 'newPassword', 'newPasswordRepeat'].forEach(function(scopeVariable) {
    $scope.$watch(scopeVariable, function() {
      $scope.checkIfValid();
    });
  });

  $scope.createUser = function() {
    var newUser = new User({
      username: $scope.newUsername,
      type: $scope.newType,
      password: $scope.newPassword
    });
    newUser.$save(function(user) {
      AdminShared.showUser(user);
    });
  };
}]);
<|endoftext|>


<commit_before>var BaseView = require('views/map-view');

module.exports = BaseView.extend({
    gRouteOptions: {
        polylineOptions: {
            strokeColor: '#e6a640'
        }
    },
    initialize: function () {
        this.collection.on('sort', this.renderDirections, this);
        this.collection.on('reset', this.render, this);
        BaseView.prototype.initialize.apply(this, arguments);
    },

    handleReset: function () {
        this.renderWaypoints();
    },

    render: function() {

        this.renderSelf();
        this.renderWaypoints();
        this.fitBounds();
        return this;
    }
});
<commit_msg>Clear the optimized route after changing scenario<commit_after>var BaseView = require('views/map-view');

module.exports = BaseView.extend({
    gRouteOptions: {
        polylineOptions: {
            strokeColor: '#e6a640'
        }
    },
    initialize: function () {
        this.collection.on('sort', this.renderDirections, this);
        this.collection.on('reset', this.render, this);
        BaseView.prototype.initialize.apply(this, arguments);
    },

    handleReset: function () {
        this.renderWaypoints();
    },

    render: function() {

        this.renderSelf();
        this.renderWaypoints();
        this.fitBounds();
        this.clearRoutes();
        return this;
    }
});
<|endoftext|>


<commit_before>import Map from 'es6-map';
/* eslint no-unused-vars: 0 */
/* eslint spaced-comment: 0 */

class MapWrapper {
    constructor( // eslint-disable-line max-params
        leafletMap, mapId, imageOverlayService, datasourceService, options, thumbnailService
    ) {
        this.thumbnailService = thumbnailService;
        this.map = leafletMap;
        this.mapId = mapId;
        this.imageOverlayService = imageOverlayService;
        this.datasourceService = datasourceService;
        this.options = options;
        // counter is not global - each map has its own.
        // So don't combine lists of listeners across maps
        this._callbackCounter = 0;
        this._callbacks = new Map();
        // internal id -> geojson layer map
        // TODO: Make geojson functionality an extension on the normal geojson layer
        // that way we can treat them the same as any other.
        this._geoJsonMap = new Map();
        this._geoJsonLayerGroup = L.geoJSON().addTo(this.map);
        this._layerMap = new Map();
        this._layerGroup = L.featureGroup().addTo(this.map);
        this.persistedThumbnails = new Map();
        this.disableFootprints = false;
        this.basemapsAdded = false;

        this._controls = L.control({position: 'topright'});
        this._controls.onAdd = function () {
            let div = L.DomUtil.create('div', 'map-control-panel');

            div.innerHTML =
                '<button class="btn btn-default"></button>';
            return div;
        };
        this.changeOptions(this.options);
    }

    getBaseMapLayer(layerName) {
        let url =
            `https://cartodb-basemaps-{s}.global.ssl.fastly.net/${layerName}/{z}/{x}/{y}.png`;
        let properties = {
            attribution: '© ' +
                'OpenStreetMap ©CartoDB',
            maxZoom: 19
        };
        return L.tileLayer(url, properties);
    }

    changeOptions(options) {
        let mapInteractionOptions = [
            this.map.scrollWheelZoom,
            this.map.doubleClickZoom,
            this.map.dragging,
            this.map.touchZoom,
            this.map.boxZoom,
            this.map.keyboard
        ];
        let baseMaps = {
            Light: this.getBaseMapLayer('light_all'),
            Dark: this.getBaseMapLayer('dark_all')
        };
        if (!this.basemapsAdded) {
            baseMaps.Light.addTo(this.map);
            this.basemapsAdded = true;
        }
        if (options && options.static) {
            this._controls.remove();
            mapInteractionOptions.map((option) => {
                option.disable();
            });
        } else {
            mapInteractionOptions.map((option) => {
                option.enable();
            });

            // Add zoom control to map's controls
            if (!this.controlsAdded) {
                this.controlsAdded = true;
                let zoomControl = L.control.zoom({position: 'topright'});
                this._controls.addTo(this.map);

                // Add basemap controls to map's controls
                let baseMapControl = L.control.layers(baseMaps, {});
                baseMapControl.addTo(this.map);
                zoomControl.addTo(this.map);

                // eslint-disable-next-line no-underscore-dangle
                let mapContainer = $(this.map._container);
                let $zoom = mapContainer.find('.leaflet-control-zoom');
                let $lpicker = mapContainer.find('.leaflet-control-layers');
                let $mpc = mapContainer.find('.map-control-panel');
                $mpc.prepend($zoom);
                $mpc.append($lpicker);
            }
        }

        if (options && options.fitToGeojson) {
            let resetBounds = () => {
                let bounds = this._geoJsonLayerGroup.getBounds();
                if (bounds.isValid()) {
                    this.map.fitBounds(bounds, {
                        padding: [35, 35]
                    });
                }
            };
            resetBounds();
            if (!this._followLayerAddListener) {
                this._followLayerAddListener = this.on('layeradd', resetBounds);
            }
            if (!this._followResizeListener) {
                this._followResizeListener = this.on('resize', resetBounds);
            }
        } else {
            this.off(this._followLayerAddListener);
            this.off(this._followResizeListener);
            delete this._followLayerAddListener;
            delete this._followResizeListener;
        }
    }

    onLayerGroupEvent(event, callback, scope = this) {
        let callbackId = this._callbackCounter;
        this._callbackCounter += 1;
        this._callbacks.set(callbackId, [event, callback]);
        this._layerGroup.on(event, callback, scope);
        return callbackId;
    }

    offLayerGroupEvent(callbackId) {
        if (this._callbacks.has(callbackId)) {
            let offPair = this._callbacks.get(callbackId);
            this._layerGroup.off(offPair[0], offPair[1]);
            this._callbacks.delete(callbackId);
        }
    }

    /*
     * Events functions:
     *  each listener is identified with a number.
     *  Event listeners are registered in a controller eg:
     *    let listener = map.on('click', ($event, map) => { //... });
     *  and removed in a scope destructor eg:
     *    this.listeners.forEach((listener) => {this.map.off(listener)});
     */

    /*
     * Params:
     *   event String string identifier for event which the map fires
     *   callback function callback with signature ($event) => {//...}
     *
     * Note: We need some mechanism for preventing multiple listeners on an event from
     *       triggering an event at once.
     */
    on(event, callback) {
        // wrap callback to track listeners internally
        let callbackWrapper = (e) => callback(e, this);
        let callbackId = this._callbackCounter;
        this._callbackCounter = this._callbackCounter + 1;
        this._callbacks.set(callbackId, [event, callbackWrapper]);
        // create listener for map event
        this.map.on(event, callbackWrapper);
        return callbackId;
    }

    /*
     * Params:
     *   callbackId int id for the callback to deregister on the map
     */
    off(callbackId) {
        // check for valid parameters
        // delete event listener
        if (this._callbacks.has(callbackId)) {
            let offPair = this._callbacks.get(callbackId);
            this.map.off(offPair[0], offPair[1]);
            this._callbacks.delete(callbackId);
        } else {
            // throw exception
        }
    }

    /** Add a geojson object to a given layer
     *  @param {string} id layer id
     *  @param {Object} geojson geojson Feature or FeatureCollection to add to the layer
     *  @param {Object} options Any default options to apply to the geojson. Optional parameter
     *  @returns {this} map wrapper object
     */
    addGeojson(id, geojson, options) {
        let features;
        let allOptions = Object.assign({}, this._geoJsonLayerGroup.options, options);
        if (Array.isArray(geojson)) {
            features = geojson;
        } else {
            features = geojson.features;
            if (geojson.properties) {
                Object.assign(allOptions, geojson.properties.options);
            }
        }

        if (features) {
            for (let feature of features) {
                if (feature.geometries || feature.geometry ||
                    feature.features || feature.coordinates) {
                    this.addGeojson(id, feature, allOptions);
                }
            }
            return this;
        }

        let layer = L.GeoJSON.geometryToLayer(geojson, allOptions);
        if (!layer) {
            return this;
        }
        layer.feature = L.GeoJSON.asFeature(geojson);
        layer.defaultOptions = layer.options;
        this._geoJsonLayerGroup.resetStyle(layer);
        if (allOptions.onEachFeature) {
            allOptions.onEachFeature(geojson, layer);
        }
        this._geoJsonLayerGroup.addLayer(layer);
        if (this._geoJsonMap.has(id)) {
            let layerList = this._geoJsonMap.get(id);
            layerList.push(layer);
        } else {
            this._geoJsonMap.set(id, [layer]);
        }
        return this;
    }

    /** Update a geojson layer. Overwrites current contents
     *  @param {string} id unique identifier for the geojson layer.
     *  @param {Object} geoJson geojson feature(s) to replace with
     *  @returns {this} map wrapper
     */
    setGeojson(id, geoJson) {
        this.deleteGeojson(id);
        this.addGeojson(id, geoJson);
    }

    /** Get geojson layers for an id
     * @param {string} id id for the set of geojson objects
     * @return {Array} The set of geojson objects for an id
     */
    getGeojson(id) {
        let layerList = this._geoJsonMap.get(id);
        return layerList;
    }

    /** Delete a geojson layers associated with an id
     *  @param {string} id layer id
     *  @returns {this} this
     */
    deleteGeojson(id) {
        if (this._geoJsonMap.has(id)) {
            let layerList = this._geoJsonMap.get(id);
            for (let layer of layerList) {
                this._geoJsonLayerGroup.removeLayer(layer);
            }
            this._geoJsonMap.delete(id);
        }
        return this;
    }

    /** Add a layer to the set of layers identified by an id
     * @param {string} id layer id
     * @param {L.Layer} layer layer to add
     * @returns {this} this
     */
    addLayer(id, layer) {
        this._layerGroup.addLayer(layer);
        let layerList = this.getLayers(id);
        if (layerList && layerList.length) {
            layerList.push(layer);
        } else {
            this._layerMap.set(id, [layer]);
        }
        return this;
    }

    /** Add a Layer or LayerGroup to the map, identified by an id
     * @param {string} id id to refer to the layer by
     * @param {L.Layer | L.LayerGroup} layer layer to add
     * @returns {this} this
     */
    setLayer(id, layer) {
        return this.deleteLayers(id)
            .addLayer(id, layer);
    }

    /** Get a set of layers by id.
     * @param {string} id layer id
     * @returns {Array} list of layers
     */
    getLayers(id) {
        return this._layerMap.get(id);
    }

    /** Delete a layer set from the map
     * @param {string} id layer id
     * @returns {this} this
     */
    deleteLayers(id) {
        let layerList = this.getLayers(id);
        if (layerList && layerList.length) {
            for (const layer of layerList) {
                this._layerGroup.removeLayer(layer);
            }
            this._layerMap.delete(id);
        }
        return this;
    }

    /** Display a thumbnail of the scene
     * @param {object} scene Scene with footprint to display on the map
     * @param {boolean?} useSmall Use the smallest thumbnail
     * @param {boolean?} persist Whether to persist this thumbnail on the map
     * @returns {this} this
     */
    setThumbnail(scene, useSmall, persist) {
        if (this.persistedThumbnails.has(scene.id)) {
            return this;
        }
        let footprintGeojson = Object.assign({
            properties: {
                options: {
                    fillOpacity: 0,
                    weight: 0,
                    interactive: false
                }
            }
        }, scene.dataFootprint);
        if (scene.tileFootprint && scene.thumbnails && scene.thumbnails.length) {
            // get smallest thumbnail - it's a small map
            let thumbUrl = this.thumbnailService.getBestFitUrl(
                scene.thumbnails, useSmall ? 100 : 1000
            );

            let boundsGeoJson = L.geoJSON();
            boundsGeoJson.addData(scene.tileFootprint);
            let imageBounds = boundsGeoJson.getBounds();
            this.datasourceService.get(scene.datasource).then(d => {
                let overlay = this.imageOverlayService.createNewImageOverlay(
                    thumbUrl,
                    imageBounds, {
                        opacity: 1,
                        dataMask: scene.dataFootprint,
                        thumbnail: thumbUrl,
                        attribution: `©${d.name}` +
                            ' | Previews are not representative of actual scene quality.'
                    }
                );
                if (!persist) {
                    this.setLayer('thumbnail', overlay);
                } else {
                    overlay.addTo(this.map);
                    this.persistedThumbnails.set(scene.id, overlay);
                }
                this.setGeojson(
                    'thumbnail',
                    footprintGeojson
                );
            });
        } else if (scene.dataFootprint) {
            this.deleteLayers('thumbnail');
            this.setGeojson(
                'thumbnail',
                footprintGeojson
            );
        }
        return this;
    }

    /** Delete any thumbnails from the map
     * @param {str} scene id of the scene to remove thumbnail for
     * @returns {this} this
     */
    deleteThumbnail(scene) {
        if (!scene) {
            this.deleteLayers('thumbnail');
            this.deleteGeojson('thumbnail');
        } else if (this.persistedThumbnails.has(scene.id)) {
            this.map.removeLayer(this.persistedThumbnails.get(scene.id));
            this.persistedThumbnails.delete(scene.id);
        }
        return this;
    }

    /** Hold the state of the map in case it's needed later
      * @param {state} state to hold, e.g. specifying latlng center and zoom
      * @returns {this} this
      */
    holdState(state) {
        this.heldState = state;
        return this;
    }
}

export default (app) => {
    class MapService {
        constructor($q, imageOverlayService, datasourceService, thumbnailService) {
            'ngInject';
            this.maps = new Map();
            this._mapPromises = new Map();
            this.$q = $q;
            this.imageOverlayService = imageOverlayService;
            this.datasourceService = datasourceService;
            this.thumbnailService = thumbnailService;
        }

        // Leaflet components should self-register using this method.
        registerMap(map, id, options) {
            let mapWrapper =
                new MapWrapper(
                    map, id, this.imageOverlayService, this.datasourceService,
                    options, this.thumbnailService);
            this.maps.set(id, mapWrapper);
            // if any promises , resolve them
            if (this._mapPromises.has(id)) {
                this._mapPromises.get(id).forEach((promise) => {
                    promise.resolve(mapWrapper);
                });
                this._mapPromises.delete(id);
            }
            return mapWrapper;
        }

        deregisterMap(id) {
            // unregister all listeners on the map
            this.getMap(id).then((mapWrapper) => {
                if (this._mapPromises.has(id)) {
                    this._mapPromises.get(id).forEach((promise) => {
                        promise.reject('Map has been deleted');
                    });
                }
                // eslint-disable-next-line no-underscore-dangle
                mapWrapper._callbacks.forEach(
                    (value, key) => {
                        mapWrapper.off(key);
                    }
                );
                this.maps.delete(id);
            });
            // delete reference to map
            // if any promises, resolve them with a failure
        }

        getMap(id) {
            let deferred = this.$q.defer();
            let mapPromise = deferred.promise;
            // If map is registered, get map from this.maps
            if (this.maps.has(id)) {
                deferred.resolve(this.maps.get(id));
            } else if (this._mapPromises.has(id)) {
                // otherwise, wait return a promise which will get resolved once the map
                // is registered
                let promises = this._mapPromises.get(id);
                promises.push(deferred);
                this._mapPromises.set(id, promises);
            } else {
                this._mapPromises.set(id, [deferred]);
            }
            return mapPromise;
        }
    }

    app.service('mapService', MapService);
};
<commit_msg>Fix max zoom level<commit_after>import Map from 'es6-map';
/* eslint no-unused-vars: 0 */
/* eslint spaced-comment: 0 */

class MapWrapper {
    constructor( // eslint-disable-line max-params
        leafletMap, mapId, imageOverlayService, datasourceService, options, thumbnailService
    ) {
        this.thumbnailService = thumbnailService;
        this.map = leafletMap;
        this.mapId = mapId;
        this.imageOverlayService = imageOverlayService;
        this.datasourceService = datasourceService;
        this.options = options;
        // counter is not global - each map has its own.
        // So don't combine lists of listeners across maps
        this._callbackCounter = 0;
        this._callbacks = new Map();
        // internal id -> geojson layer map
        // TODO: Make geojson functionality an extension on the normal geojson layer
        // that way we can treat them the same as any other.
        this._geoJsonMap = new Map();
        this._geoJsonLayerGroup = L.geoJSON().addTo(this.map);
        this._layerMap = new Map();
        this._layerGroup = L.featureGroup().addTo(this.map);
        this.persistedThumbnails = new Map();
        this.disableFootprints = false;
        this.basemapsAdded = false;

        this._controls = L.control({position: 'topright'});
        this._controls.onAdd = function () {
            let div = L.DomUtil.create('div', 'map-control-panel');

            div.innerHTML =
                '<button class="btn btn-default"></button>';
            return div;
        };
        this.changeOptions(this.options);
    }

    getBaseMapLayer(layerName) {
        let url =
            `https://cartodb-basemaps-{s}.global.ssl.fastly.net/${layerName}/{z}/{x}/{y}.png`;
        let properties = {
            attribution: '© ' +
                'OpenStreetMap ©CartoDB',
            maxZoom: 30
        };
        return L.tileLayer(url, properties);
    }

    changeOptions(options) {
        let mapInteractionOptions = [
            this.map.scrollWheelZoom,
            this.map.doubleClickZoom,
            this.map.dragging,
            this.map.touchZoom,
            this.map.boxZoom,
            this.map.keyboard
        ];
        let baseMaps = {
            Light: this.getBaseMapLayer('light_all'),
            Dark: this.getBaseMapLayer('dark_all')
        };
        if (!this.basemapsAdded) {
            baseMaps.Light.addTo(this.map);
            this.basemapsAdded = true;
        }
        if (options && options.static) {
            this._controls.remove();
            mapInteractionOptions.map((option) => {
                option.disable();
            });
        } else {
            mapInteractionOptions.map((option) => {
                option.enable();
            });

            // Add zoom control to map's controls
            if (!this.controlsAdded) {
                this.controlsAdded = true;
                let zoomControl = L.control.zoom({position: 'topright'});
                this._controls.addTo(this.map);

                // Add basemap controls to map's controls
                let baseMapControl = L.control.layers(baseMaps, {});
                baseMapControl.addTo(this.map);
                zoomControl.addTo(this.map);

                // eslint-disable-next-line no-underscore-dangle
                let mapContainer = $(this.map._container);
                let $zoom = mapContainer.find('.leaflet-control-zoom');
                let $lpicker = mapContainer.find('.leaflet-control-layers');
                let $mpc = mapContainer.find('.map-control-panel');
                $mpc.prepend($zoom);
                $mpc.append($lpicker);
            }
        }

        if (options && options.fitToGeojson) {
            let resetBounds = () => {
                let bounds = this._geoJsonLayerGroup.getBounds();
                if (bounds.isValid()) {
                    this.map.fitBounds(bounds, {
                        padding: [35, 35]
                    });
                }
            };
            resetBounds();
            if (!this._followLayerAddListener) {
                this._followLayerAddListener = this.on('layeradd', resetBounds);
            }
            if (!this._followResizeListener) {
                this._followResizeListener = this.on('resize', resetBounds);
            }
        } else {
            this.off(this._followLayerAddListener);
            this.off(this._followResizeListener);
            delete this._followLayerAddListener;
            delete this._followResizeListener;
        }
    }

    onLayerGroupEvent(event, callback, scope = this) {
        let callbackId = this._callbackCounter;
        this._callbackCounter += 1;
        this._callbacks.set(callbackId, [event, callback]);
        this._layerGroup.on(event, callback, scope);
        return callbackId;
    }

    offLayerGroupEvent(callbackId) {
        if (this._callbacks.has(callbackId)) {
            let offPair = this._callbacks.get(callbackId);
            this._layerGroup.off(offPair[0], offPair[1]);
            this._callbacks.delete(callbackId);
        }
    }

    /*
     * Events functions:
     *  each listener is identified with a number.
     *  Event listeners are registered in a controller eg:
     *    let listener = map.on('click', ($event, map) => { //... });
     *  and removed in a scope destructor eg:
     *    this.listeners.forEach((listener) => {this.map.off(listener)});
     */

    /*
     * Params:
     *   event String string identifier for event which the map fires
     *   callback function callback with signature ($event) => {//...}
     *
     * Note: We need some mechanism for preventing multiple listeners on an event from
     *       triggering an event at once.
     */
    on(event, callback) {
        // wrap callback to track listeners internally
        let callbackWrapper = (e) => callback(e, this);
        let callbackId = this._callbackCounter;
        this._callbackCounter = this._callbackCounter + 1;
        this._callbacks.set(callbackId, [event, callbackWrapper]);
        // create listener for map event
        this.map.on(event, callbackWrapper);
        return callbackId;
    }

    /*
     * Params:
     *   callbackId int id for the callback to deregister on the map
     */
    off(callbackId) {
        // check for valid parameters
        // delete event listener
        if (this._callbacks.has(callbackId)) {
            let offPair = this._callbacks.get(callbackId);
            this.map.off(offPair[0], offPair[1]);
            this._callbacks.delete(callbackId);
        } else {
            // throw exception
        }
    }

    /** Add a geojson object to a given layer
     *  @param {string} id layer id
     *  @param {Object} geojson geojson Feature or FeatureCollection to add to the layer
     *  @param {Object} options Any default options to apply to the geojson. Optional parameter
     *  @returns {this} map wrapper object
     */
    addGeojson(id, geojson, options) {
        let features;
        let allOptions = Object.assign({}, this._geoJsonLayerGroup.options, options);
        if (Array.isArray(geojson)) {
            features = geojson;
        } else {
            features = geojson.features;
            if (geojson.properties) {
                Object.assign(allOptions, geojson.properties.options);
            }
        }

        if (features) {
            for (let feature of features) {
                if (feature.geometries || feature.geometry ||
                    feature.features || feature.coordinates) {
                    this.addGeojson(id, feature, allOptions);
                }
            }
            return this;
        }

        let layer = L.GeoJSON.geometryToLayer(geojson, allOptions);
        if (!layer) {
            return this;
        }
        layer.feature = L.GeoJSON.asFeature(geojson);
        layer.defaultOptions = layer.options;
        this._geoJsonLayerGroup.resetStyle(layer);
        if (allOptions.onEachFeature) {
            allOptions.onEachFeature(geojson, layer);
        }
        this._geoJsonLayerGroup.addLayer(layer);
        if (this._geoJsonMap.has(id)) {
            let layerList = this._geoJsonMap.get(id);
            layerList.push(layer);
        } else {
            this._geoJsonMap.set(id, [layer]);
        }
        return this;
    }

    /** Update a geojson layer. Overwrites current contents
     *  @param {string} id unique identifier for the geojson layer.
     *  @param {Object} geoJson geojson feature(s) to replace with
     *  @returns {this} map wrapper
     */
    setGeojson(id, geoJson) {
        this.deleteGeojson(id);
        this.addGeojson(id, geoJson);
    }

    /** Get geojson layers for an id
     * @param {string} id id for the set of geojson objects
     * @return {Array} The set of geojson objects for an id
     */
    getGeojson(id) {
        let layerList = this._geoJsonMap.get(id);
        return layerList;
    }

    /** Delete a geojson layers associated with an id
     *  @param {string} id layer id
     *  @returns {this} this
     */
    deleteGeojson(id) {
        if (this._geoJsonMap.has(id)) {
            let layerList = this._geoJsonMap.get(id);
            for (let layer of layerList) {
                this._geoJsonLayerGroup.removeLayer(layer);
            }
            this._geoJsonMap.delete(id);
        }
        return this;
    }

    /** Add a layer to the set of layers identified by an id
     * @param {string} id layer id
     * @param {L.Layer} layer layer to add
     * @returns {this} this
     */
    addLayer(id, layer) {
        this._layerGroup.addLayer(layer);
        let layerList = this.getLayers(id);
        if (layerList && layerList.length) {
            layerList.push(layer);
        } else {
            this._layerMap.set(id, [layer]);
        }
        return this;
    }

    /** Add a Layer or LayerGroup to the map, identified by an id
     * @param {string} id id to refer to the layer by
     * @param {L.Layer | L.LayerGroup} layer layer to add
     * @returns {this} this
     */
    setLayer(id, layer) {
        return this.deleteLayers(id)
            .addLayer(id, layer);
    }

    /** Get a set of layers by id.
     * @param {string} id layer id
     * @returns {Array} list of layers
     */
    getLayers(id) {
        return this._layerMap.get(id);
    }

    /** Delete a layer set from the map
     * @param {string} id layer id
     * @returns {this} this
     */
    deleteLayers(id) {
        let layerList = this.getLayers(id);
        if (layerList && layerList.length) {
            for (const layer of layerList) {
                this._layerGroup.removeLayer(layer);
            }
            this._layerMap.delete(id);
        }
        return this;
    }

    /** Display a thumbnail of the scene
     * @param {object} scene Scene with footprint to display on the map
     * @param {boolean?} useSmall Use the smallest thumbnail
     * @param {boolean?} persist Whether to persist this thumbnail on the map
     * @returns {this} this
     */
    setThumbnail(scene, useSmall, persist) {
        if (this.persistedThumbnails.has(scene.id)) {
            return this;
        }
        let footprintGeojson = Object.assign({
            properties: {
                options: {
                    fillOpacity: 0,
                    weight: 0,
                    interactive: false
                }
            }
        }, scene.dataFootprint);
        if (scene.tileFootprint && scene.thumbnails && scene.thumbnails.length) {
            // get smallest thumbnail - it's a small map
            let thumbUrl = this.thumbnailService.getBestFitUrl(
                scene.thumbnails, useSmall ? 100 : 1000
            );

            let boundsGeoJson = L.geoJSON();
            boundsGeoJson.addData(scene.tileFootprint);
            let imageBounds = boundsGeoJson.getBounds();
            this.datasourceService.get(scene.datasource).then(d => {
                let overlay = this.imageOverlayService.createNewImageOverlay(
                    thumbUrl,
                    imageBounds, {
                        opacity: 1,
                        dataMask: scene.dataFootprint,
                        thumbnail: thumbUrl,
                        attribution: `©${d.name}` +
                            ' | Previews are not representative of actual scene quality.'
                    }
                );
                if (!persist) {
                    this.setLayer('thumbnail', overlay);
                } else {
                    overlay.addTo(this.map);
                    this.persistedThumbnails.set(scene.id, overlay);
                }
                this.setGeojson(
                    'thumbnail',
                    footprintGeojson
                );
            });
        } else if (scene.dataFootprint) {
            this.deleteLayers('thumbnail');
            this.setGeojson(
                'thumbnail',
                footprintGeojson
            );
        }
        return this;
    }

    /** Delete any thumbnails from the map
     * @param {str} scene id of the scene to remove thumbnail for
     * @returns {this} this
     */
    deleteThumbnail(scene) {
        if (!scene) {
            this.deleteLayers('thumbnail');
            this.deleteGeojson('thumbnail');
        } else if (this.persistedThumbnails.has(scene.id)) {
            this.map.removeLayer(this.persistedThumbnails.get(scene.id));
            this.persistedThumbnails.delete(scene.id);
        }
        return this;
    }

    /** Hold the state of the map in case it's needed later
      * @param {state} state to hold, e.g. specifying latlng center and zoom
      * @returns {this} this
      */
    holdState(state) {
        this.heldState = state;
        return this;
    }
}

export default (app) => {
    class MapService {
        constructor($q, imageOverlayService, datasourceService, thumbnailService) {
            'ngInject';
            this.maps = new Map();
            this._mapPromises = new Map();
            this.$q = $q;
            this.imageOverlayService = imageOverlayService;
            this.datasourceService = datasourceService;
            this.thumbnailService = thumbnailService;
        }

        // Leaflet components should self-register using this method.
        registerMap(map, id, options) {
            let mapWrapper =
                new MapWrapper(
                    map, id, this.imageOverlayService, this.datasourceService,
                    options, this.thumbnailService);
            this.maps.set(id, mapWrapper);
            // if any promises , resolve them
            if (this._mapPromises.has(id)) {
                this._mapPromises.get(id).forEach((promise) => {
                    promise.resolve(mapWrapper);
                });
                this._mapPromises.delete(id);
            }
            return mapWrapper;
        }

        deregisterMap(id) {
            // unregister all listeners on the map
            this.getMap(id).then((mapWrapper) => {
                if (this._mapPromises.has(id)) {
                    this._mapPromises.get(id).forEach((promise) => {
                        promise.reject('Map has been deleted');
                    });
                }
                // eslint-disable-next-line no-underscore-dangle
                mapWrapper._callbacks.forEach(
                    (value, key) => {
                        mapWrapper.off(key);
                    }
                );
                this.maps.delete(id);
            });
            // delete reference to map
            // if any promises, resolve them with a failure
        }

        getMap(id) {
            let deferred = this.$q.defer();
            let mapPromise = deferred.promise;
            // If map is registered, get map from this.maps
            if (this.maps.has(id)) {
                deferred.resolve(this.maps.get(id));
            } else if (this._mapPromises.has(id)) {
                // otherwise, wait return a promise which will get resolved once the map
                // is registered
                let promises = this._mapPromises.get(id);
                promises.push(deferred);
                this._mapPromises.set(id, promises);
            } else {
                this._mapPromises.set(id, [deferred]);
            }
            return mapPromise;
        }
    }

    app.service('mapService', MapService);
};
<|endoftext|>


<commit_before>#!/usr/bin/env node
"use strict";

// TODO
// - multiple random tests

const assert = require("assert").strict;
const fs = require("fs");
const encodings = require("../../gen/x86_table.js");
const Prand = require("./prand.js");

generate_tests();

function generate_tests()
{
    const build_folder = __dirname + "/build/";

    try
    {
        fs.mkdirSync(build_folder);
    }
    catch(e)
    {
        if(e.code !== "EEXIST")
        {
            throw e;
        }
    }

    for(const op of encodings)
    {
        const configurations = [
            { mem: 0, size: 16, },
            { mem: 0, size: 32, },
            { mem: 1, size: 16, },
            { mem: 1, size: 32, },
        ];

        let i = 0;

        for(const config of configurations)
        {
            for(const code of create_nasm(op, config))
            {
                const filename = "gen_" + format_opcode(op.opcode) + "_" + (op.fixed_g || 0) + "_" + i + ".asm";
                const dirname = build_folder + filename;

                let old_code = undefined;

                try
                {
                    old_code = fs.readFileSync(dirname, { encoding: "ascii" });
                }
                catch(e)
                {
                }

                if(old_code !== code)
                {
                    console.log("Creating %s", filename);
                    fs.writeFileSync(dirname, code);
                }
                else
                {
                    console.log("Unchanged: %s", filename);
                }

                i++;
            }
        }
    }
}

function format_opcode(n)
{
    let x = n.toString(16);
    return (x.length === 1 || x.length === 3) ? "0" + x : x;
}

function create_nasm_modrm_combinations_16()
{
    let result = [];

    for(let modrm = 0; modrm < 0xC0; modrm++)
    {
        let mod = modrm >> 6;
        let rm = modrm & 7;

        let has_imm8 = mod === 1;
        let has_imm16 = mod === 2 || rm === 6 && mod === 0;

        assert(!has_imm8 || !has_imm16);

        let line = ["db " + modrm];
        if(has_imm8) line.push("db 9ah");
        if(has_imm16) line.push("dw 9a1fh");
        result.push(line);
    }

    return result;
}

function create_nasm_modrm_combinations_32()
{
    let result = [];

    let sample_sib_bytes = [0x05, 0x65, 0xAD, 0xCD, 0x20, 0xFF];
    let exhaustive_sib_bytes = [];
    for(let sib = 0; sib < 0x100; sib++) exhaustive_sib_bytes.push(sib);

    for(let modrm = 0; modrm < 0xC0; modrm++)
    {
        let mod = modrm >> 6;
        let reg = modrm >> 3 & 7;
        let rm = modrm & 7;

        let has_imm8 = mod === 1;
        let has_imm32 = mod === 2 || rm === 5 && mod === 0;
        let has_sib = rm === 4;

        assert(!has_imm8 || !has_imm32);

        if(has_sib)
        {
            // avoid generating an excessive number of tests
            let sib_bytes = reg === 0 ? exhaustive_sib_bytes : sample_sib_bytes;

            for(let sib of sib_bytes)
            {
                let line = ["db " + modrm, "db " + sib];
                if(has_imm8) line.push("db 9ah");
                if(has_imm32 || mod === 0 && (sib & 7) === 5) line.push("dd 9a1fbcdeh");
                result.push(line);
            }
        }
        else
        {
            let line = ["db " + modrm];
            if(has_imm8) line.push("db 9ah");
            if(has_imm32) line.push("dd 9a1fbcdeh");
            result.push(line);
        }
    }

    return result;
}

function create_nasm(op, config)
{
    const op_rand = new Prand(op.opcode);
    if(op.prefix || op.skip)
    {
        return [];
    }

    if(config.mem ? op.skip_mem : op.skip_reg)
    {
        // Not supported by test
        return [];
    }

    if(!op.e)
    {
        if(config.mem)
        {
            // doesn't use memory, don't test both
            return [];
        }
    }

    if(!op.os)
    {
        if(config.size === 16)
        {
            // equivalent to 32-bit version, don't test both
            return [];
        }
    }

    const size = (op.os || op.opcode % 2 === 1) ? config.size : 8;
    const is_modrm = op.e || op.fixed_g !== undefined;

    const codes = [];

    for(let reg of ["eax", "ecx", "edx", "ebx", "ebp", "esi", "edi"])
    {
        let rand = op_rand.next();
        codes.push("mov " + reg + ", " + rand);
    }

    if(!op.is_fpu) // generate random mmx registers
    {
        codes.push("sub esp, 8");
        for(let i = 0; i < 8; i++)
        {
            codes.push("mov dword [esp], " + op_rand.next());
            codes.push("mov dword [esp + 4], " + op_rand.next());
            codes.push("movq mm" + i + ", [esp]");
        }
        codes.push("add esp, 8");
    }
    else // generate random fpu registers
    {
        codes.push("finit");
        codes.push("sub esp, 8");

        for(let i = 0; i < 8; i++)
        {
            codes.push("mov dword [esp], " + op_rand.next());
            codes.push("mov dword [esp + 4], " + op_rand.next());
            codes.push("fld qword [esp]");
        }

        for(let i = 0; i < 4; i++) // half full stack
        {
            codes.push("fstp qword [esp]");
        }

        codes.push("add esp, 8");
    }

    if(true) // generate random xmm registers
    {
        codes.push("sub esp, 16");
        for(let i = 0; i < 8; i++)
        {
            codes.push("mov dword [esp], " + op_rand.next());
            codes.push("mov dword [esp + 4], " + op_rand.next());
            codes.push("mov dword [esp + 8], " + op_rand.next());
            codes.push("mov dword [esp + 12], " + op_rand.next());
            codes.push("movdqu xmm" + i + ", [esp]");
        }
        codes.push("add esp, 16");
    }

    if(true) // generate random stack memory
    {
        for(let i = 0; i < 8; i++)
        {
            codes.push("sub esp, 4");
            codes.push("mov dword [esp], " + op_rand.next());
        }
    }

    codes.push("push dword " + (op_rand.next() & ~(1 << 8 | 1 << 9)));
    codes.push("popf");

    if(true)
    {
        // generate random flags using arithmatic instruction
        // not well-distributed, but can trigger bugs in lazy flag calculation
        if(true)
        {
            // rarely sets zero flag, other flags mostly well-distributed
            codes.push("add al, ah");
        }
        else
        {
            // always sets zero flag
            codes.push("sub al, al");
        }
    }

    if(op.is_string)
    {
        codes.push("mov ecx, 3");
        codes.push("mov edi, (120000h-16)");
        codes.push("mov esi, (120000h-20)");
    }

    if(size === 16)
    {
        codes.push("db 66h ; 16 bit");
    }

    let opcode = op.opcode;

    if(opcode === 0x8D)
    {
        // special case: lea: generate 16-bit addressing and all modrm combinations
        assert(is_modrm);

        codes.push([].concat(
            create_nasm_modrm_combinations_16().map(lines => ["db 67h", "db 8dh"].concat(lines).join("\n")),
            create_nasm_modrm_combinations_32().map(lines => ["db 8dh"].concat(lines).join("\n"))
        ));
    }
    else
    {
        assert(opcode < 0x1000000);
        if(opcode >= 0x10000)
        {
            let c = opcode >> 16;
            assert(c === 0x66 || c === 0xF3 || c === 0xF2);
            codes.push("db " + c);
            opcode &= ~0xFF0000;
        }
        if(opcode >= 0x100)
        {
            let c = opcode >> 8;
            assert(c === 0x0F || c === 0xF2 || c === 0xF3, "Expected 0F, F2, or F3 prefix, got " + c.toString(16));
            codes.push("db " + c);
            opcode &= ~0xFF00;
        }
        codes.push("db " + opcode);

        if(is_modrm)
        {
            let g = 7; // edi / di / bh

            if(op.fixed_g !== undefined)
            {
                g = op.fixed_g;
            }

            if(config.mem)
            {
                const e = 0x04; // [esp]
                const sib = 0x24;

                codes.push("db " + (e | g << 3));
                codes.push("db " + sib);
            }
            else
            {
                const es = op.is_fpu ? [0, 1, 2, 3, 4, 5, 6, 7] : [
                    2 // edx
                ];
                const modrm_bytes = es.map(e => "db " + (0xC0 | g << 3 | e));
                codes.push(modrm_bytes);
            }
        }
    }

    if(op.opcode === 0xC8) // special case: enter
    {
        codes.push("dw 8h");
        codes.push("db 0h");
    }
    else if(op.imm8 || op.imm8s || op.imm16 || op.imm1632 || op.imm32 || op.immaddr)
    {
        if(op.imm8 || op.imm8s)
        {
            codes.push("db 12h");
        }
        else
        {
            if(op.immaddr)
            {
                // immaddr: depends on address size
                // generate valid pointer into bss section
                codes.push("dd (120000h-16)");
            }
            else
            {
                assert(op.imm1632 || op.imm16 || op.imm32);

                if(op.imm1632 && size === 16 || op.imm16)
                {
                    codes.push("dw 34cdh");
                }
                else
                {
                    assert(op.imm1632 && size === 32 || op.imm32);
                    codes.push("dd 1234abcdh");
                }
            }
        }
    }

    if(op.mask_flags)
    {
        codes.push(
            "pushf",
            "and dword [esp], ~" + op.mask_flags,
            "popf"
        );
    }

    return all_combinations(codes).map(c => {
        return (
            "global _start\n" +
            '%include "header.inc"\n\n' +
            c.join("\n") + "\n" +
            '%include "footer.inc"\n'
        );
    });
}

function all_combinations(xs)
{
    let result = [xs];

    for(let i = 0; i < xs.length; i++)
    {
        let x = xs[i];

        if(x instanceof Array)
        {
            let new_result = [];

            for(let r of result)
            {
                for(let x_ of x)
                {
                    r = r.slice();
                    r[i] = x_;
                    new_result.push(r);
                }
            }

            result = new_result;
        }
    }

    return result;
}
<commit_msg>nasmtests: Reduce verbosity<commit_after>#!/usr/bin/env node
"use strict";

// TODO
// - multiple random tests

const assert = require("assert").strict;
const fs = require("fs");
const encodings = require("../../gen/x86_table.js");
const Prand = require("./prand.js");

generate_tests();

function generate_tests()
{
    const build_folder = __dirname + "/build/";

    try
    {
        fs.mkdirSync(build_folder);
    }
    catch(e)
    {
        if(e.code !== "EEXIST")
        {
            throw e;
        }
    }

    for(const op of encodings)
    {
        const configurations = [
            { mem: 0, size: 16, },
            { mem: 0, size: 32, },
            { mem: 1, size: 16, },
            { mem: 1, size: 32, },
        ];

        let i = 0;

        for(const config of configurations)
        {
            for(const code of create_nasm(op, config))
            {
                const filename = "gen_" + format_opcode(op.opcode) + "_" + (op.fixed_g || 0) + "_" + i + ".asm";
                const dirname = build_folder + filename;

                let old_code = undefined;

                try
                {
                    old_code = fs.readFileSync(dirname, { encoding: "ascii" });
                }
                catch(e)
                {
                }

                if(old_code !== code)
                {
                    console.log("Creating %s", filename);
                    fs.writeFileSync(dirname, code);
                }

                i++;
            }
        }
    }
}

function format_opcode(n)
{
    let x = n.toString(16);
    return (x.length === 1 || x.length === 3) ? "0" + x : x;
}

function create_nasm_modrm_combinations_16()
{
    let result = [];

    for(let modrm = 0; modrm < 0xC0; modrm++)
    {
        let mod = modrm >> 6;
        let rm = modrm & 7;

        let has_imm8 = mod === 1;
        let has_imm16 = mod === 2 || rm === 6 && mod === 0;

        assert(!has_imm8 || !has_imm16);

        let line = ["db " + modrm];
        if(has_imm8) line.push("db 9ah");
        if(has_imm16) line.push("dw 9a1fh");
        result.push(line);
    }

    return result;
}

function create_nasm_modrm_combinations_32()
{
    let result = [];

    let sample_sib_bytes = [0x05, 0x65, 0xAD, 0xCD, 0x20, 0xFF];
    let exhaustive_sib_bytes = [];
    for(let sib = 0; sib < 0x100; sib++) exhaustive_sib_bytes.push(sib);

    for(let modrm = 0; modrm < 0xC0; modrm++)
    {
        let mod = modrm >> 6;
        let reg = modrm >> 3 & 7;
        let rm = modrm & 7;

        let has_imm8 = mod === 1;
        let has_imm32 = mod === 2 || rm === 5 && mod === 0;
        let has_sib = rm === 4;

        assert(!has_imm8 || !has_imm32);

        if(has_sib)
        {
            // avoid generating an excessive number of tests
            let sib_bytes = reg === 0 ? exhaustive_sib_bytes : sample_sib_bytes;

            for(let sib of sib_bytes)
            {
                let line = ["db " + modrm, "db " + sib];
                if(has_imm8) line.push("db 9ah");
                if(has_imm32 || mod === 0 && (sib & 7) === 5) line.push("dd 9a1fbcdeh");
                result.push(line);
            }
        }
        else
        {
            let line = ["db " + modrm];
            if(has_imm8) line.push("db 9ah");
            if(has_imm32) line.push("dd 9a1fbcdeh");
            result.push(line);
        }
    }

    return result;
}

function create_nasm(op, config)
{
    const op_rand = new Prand(op.opcode);
    if(op.prefix || op.skip)
    {
        return [];
    }

    if(config.mem ? op.skip_mem : op.skip_reg)
    {
        // Not supported by test
        return [];
    }

    if(!op.e)
    {
        if(config.mem)
        {
            // doesn't use memory, don't test both
            return [];
        }
    }

    if(!op.os)
    {
        if(config.size === 16)
        {
            // equivalent to 32-bit version, don't test both
            return [];
        }
    }

    const size = (op.os || op.opcode % 2 === 1) ? config.size : 8;
    const is_modrm = op.e || op.fixed_g !== undefined;

    const codes = [];

    for(let reg of ["eax", "ecx", "edx", "ebx", "ebp", "esi", "edi"])
    {
        let rand = op_rand.next();
        codes.push("mov " + reg + ", " + rand);
    }

    if(!op.is_fpu) // generate random mmx registers
    {
        codes.push("sub esp, 8");
        for(let i = 0; i < 8; i++)
        {
            codes.push("mov dword [esp], " + op_rand.next());
            codes.push("mov dword [esp + 4], " + op_rand.next());
            codes.push("movq mm" + i + ", [esp]");
        }
        codes.push("add esp, 8");
    }
    else // generate random fpu registers
    {
        codes.push("finit");
        codes.push("sub esp, 8");

        for(let i = 0; i < 8; i++)
        {
            codes.push("mov dword [esp], " + op_rand.next());
            codes.push("mov dword [esp + 4], " + op_rand.next());
            codes.push("fld qword [esp]");
        }

        for(let i = 0; i < 4; i++) // half full stack
        {
            codes.push("fstp qword [esp]");
        }

        codes.push("add esp, 8");
    }

    if(true) // generate random xmm registers
    {
        codes.push("sub esp, 16");
        for(let i = 0; i < 8; i++)
        {
            codes.push("mov dword [esp], " + op_rand.next());
            codes.push("mov dword [esp + 4], " + op_rand.next());
            codes.push("mov dword [esp + 8], " + op_rand.next());
            codes.push("mov dword [esp + 12], " + op_rand.next());
            codes.push("movdqu xmm" + i + ", [esp]");
        }
        codes.push("add esp, 16");
    }

    if(true) // generate random stack memory
    {
        for(let i = 0; i < 8; i++)
        {
            codes.push("sub esp, 4");
            codes.push("mov dword [esp], " + op_rand.next());
        }
    }

    codes.push("push dword " + (op_rand.next() & ~(1 << 8 | 1 << 9)));
    codes.push("popf");

    if(true)
    {
        // generate random flags using arithmatic instruction
        // not well-distributed, but can trigger bugs in lazy flag calculation
        if(true)
        {
            // rarely sets zero flag, other flags mostly well-distributed
            codes.push("add al, ah");
        }
        else
        {
            // always sets zero flag
            codes.push("sub al, al");
        }
    }

    if(op.is_string)
    {
        codes.push("mov ecx, 3");
        codes.push("mov edi, (120000h-16)");
        codes.push("mov esi, (120000h-20)");
    }

    if(size === 16)
    {
        codes.push("db 66h ; 16 bit");
    }

    let opcode = op.opcode;

    if(opcode === 0x8D)
    {
        // special case: lea: generate 16-bit addressing and all modrm combinations
        assert(is_modrm);

        codes.push([].concat(
            create_nasm_modrm_combinations_16().map(lines => ["db 67h", "db 8dh"].concat(lines).join("\n")),
            create_nasm_modrm_combinations_32().map(lines => ["db 8dh"].concat(lines).join("\n"))
        ));
    }
    else
    {
        assert(opcode < 0x1000000);
        if(opcode >= 0x10000)
        {
            let c = opcode >> 16;
            assert(c === 0x66 || c === 0xF3 || c === 0xF2);
            codes.push("db " + c);
            opcode &= ~0xFF0000;
        }
        if(opcode >= 0x100)
        {
            let c = opcode >> 8;
            assert(c === 0x0F || c === 0xF2 || c === 0xF3, "Expected 0F, F2, or F3 prefix, got " + c.toString(16));
            codes.push("db " + c);
            opcode &= ~0xFF00;
        }
        codes.push("db " + opcode);

        if(is_modrm)
        {
            let g = 7; // edi / di / bh

            if(op.fixed_g !== undefined)
            {
                g = op.fixed_g;
            }

            if(config.mem)
            {
                const e = 0x04; // [esp]
                const sib = 0x24;

                codes.push("db " + (e | g << 3));
                codes.push("db " + sib);
            }
            else
            {
                const es = op.is_fpu ? [0, 1, 2, 3, 4, 5, 6, 7] : [
                    2 // edx
                ];
                const modrm_bytes = es.map(e => "db " + (0xC0 | g << 3 | e));
                codes.push(modrm_bytes);
            }
        }
    }

    if(op.opcode === 0xC8) // special case: enter
    {
        codes.push("dw 8h");
        codes.push("db 0h");
    }
    else if(op.imm8 || op.imm8s || op.imm16 || op.imm1632 || op.imm32 || op.immaddr)
    {
        if(op.imm8 || op.imm8s)
        {
            codes.push("db 12h");
        }
        else
        {
            if(op.immaddr)
            {
                // immaddr: depends on address size
                // generate valid pointer into bss section
                codes.push("dd (120000h-16)");
            }
            else
            {
                assert(op.imm1632 || op.imm16 || op.imm32);

                if(op.imm1632 && size === 16 || op.imm16)
                {
                    codes.push("dw 34cdh");
                }
                else
                {
                    assert(op.imm1632 && size === 32 || op.imm32);
                    codes.push("dd 1234abcdh");
                }
            }
        }
    }

    if(op.mask_flags)
    {
        codes.push(
            "pushf",
            "and dword [esp], ~" + op.mask_flags,
            "popf"
        );
    }

    return all_combinations(codes).map(c => {
        return (
            "global _start\n" +
            '%include "header.inc"\n\n' +
            c.join("\n") + "\n" +
            '%include "footer.inc"\n'
        );
    });
}

function all_combinations(xs)
{
    let result = [xs];

    for(let i = 0; i < xs.length; i++)
    {
        let x = xs[i];

        if(x instanceof Array)
        {
            let new_result = [];

            for(let r of result)
            {
                for(let x_ of x)
                {
                    r = r.slice();
                    r[i] = x_;
                    new_result.push(r);
                }
            }

            result = new_result;
        }
    }

    return result;
}
<|endoftext|>


<commit_before><commit_msg>changelog generation script<commit_after><|endoftext|>